| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- <?php
- declare(strict_types=1);
- namespace Tests\Unit\Services\Import;
- use App\Enums\ProductionOrderExecutionType;
- use App\Enums\ProductionOrderSource;
- use App\Enums\ProductionOrderStatus;
- use App\Models\CommonCatalogItem;
- use App\Models\Import;
- use App\Models\ProductionOrder;
- use App\Models\ProductionOrderItem;
- use App\Models\User;
- use App\Services\Export\ExportProductionOrdersService;
- use App\Services\Import\ImportProductionOrdersService;
- use Illuminate\Foundation\Testing\RefreshDatabase;
- use Illuminate\Support\Facades\Storage;
- use PhpOffice\PhpSpreadsheet\Spreadsheet;
- use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
- use RuntimeException;
- use Tests\TestCase;
- final class ImportProductionOrdersServiceTest extends TestCase
- {
- use RefreshDatabase;
- public function test_empty_order_id_creates_order_and_imports_items_from_separate_sheet(): void
- {
- Storage::fake('upload');
- $actor = User::factory()->admin()->create();
- $manager = User::factory()->manager()->create(['name' => 'Менеджер Импорта']);
- $catalogItem = CommonCatalogItem::factory()->create([
- 'article' => 'IMP-001',
- 'calculator_name' => 'Импортируемый комплекс',
- ]);
- $import = $this->createImport($actor, [
- $this->orderRow(null, 'НОВЫЙ-001', $manager->name),
- ], [[
- 2,
- null,
- $catalogItem->id,
- $catalogItem->article,
- $catalogItem->calculator_name,
- 'МАФ-НОВЫЙ-1',
- 'ЗН-001',
- '03.09.2026',
- ]]);
- $this->service($import, $actor)->handle();
- $order = ProductionOrder::query()->where('order_number', 'НОВЫЙ-001')->firstOrFail();
- $this->assertSame(ProductionOrderSource::Spreadsheet, $order->source);
- $this->assertSame('ООО Новый заказчик', $order->customer_name);
- $this->assertSame($manager->id, $order->manager_id);
- $this->assertSame('2026-09-09', $order->contract_shipment_date->toDateString());
- $this->assertDatabaseHas('production_order_items', [
- 'production_order_id' => $order->id,
- 'common_catalog_item_id' => $catalogItem->id,
- 'order_item_number' => 'МАФ-НОВЫЙ-1',
- 'factory_number' => 'ЗН-001',
- 'manufacture_date' => '2026-09-03',
- ]);
- $this->assertSame('DONE', $import->refresh()->status);
- }
- public function test_known_order_id_updates_order_and_synchronizes_items(): void
- {
- Storage::fake('upload');
- $actor = User::factory()->admin()->create();
- $manager = User::factory()->manager()->create(['name' => 'Менеджер Обновления']);
- $catalogItem = CommonCatalogItem::factory()->create([
- 'article' => 'IMP-UPDATE',
- 'calculator_name' => 'Обновляемый комплекс',
- ]);
- $order = ProductionOrder::factory()->create([
- 'order_number' => 'ОБНОВИТЬ-001',
- 'order_year' => 2026,
- 'customer_name' => 'Старый заказчик',
- 'manager_id' => $manager->id,
- 'source' => ProductionOrderSource::Manual,
- ]);
- $item = ProductionOrderItem::factory()->create([
- 'production_order_id' => $order->id,
- 'common_catalog_item_id' => $catalogItem->id,
- 'order_item_number' => 'МАФ-СТАРЫЙ',
- ]);
- $row = $this->orderRow($order->id, $order->order_number, $manager->name);
- $row[3] = 'ООО Обновлённый заказчик';
- $row[13] = ProductionOrderStatus::InStock->label();
- $import = $this->createImport($actor, [$row], [[
- 2,
- $item->id,
- $catalogItem->id,
- $catalogItem->article,
- $catalogItem->calculator_name,
- 'МАФ-ОБНОВЛЁННЫЙ',
- 'ЗН-UPDATED',
- '04.09.2026',
- ]]);
- $this->service($import, $actor)->handle();
- $order->refresh();
- $item->refresh();
- $this->assertSame('ООО Обновлённый заказчик', $order->customer_name);
- $this->assertSame(ProductionOrderStatus::InStock, $order->status);
- $this->assertSame(ProductionOrderSource::Manual, $order->source);
- $this->assertSame('МАФ-ОБНОВЛЁННЫЙ', $item->order_item_number);
- $this->assertSame('ЗН-UPDATED', $item->factory_number);
- $this->assertSame('2026-09-04', $item->manufacture_date?->toDateString());
- $this->assertSame('DONE', $import->refresh()->status);
- }
- public function test_unknown_order_id_marks_import_as_error_without_partial_changes(): void
- {
- Storage::fake('upload');
- $actor = User::factory()->admin()->create();
- $manager = User::factory()->manager()->create(['name' => 'Менеджер Ошибки']);
- $order = ProductionOrder::factory()->create([
- 'order_number' => 'БЕЗ-ИЗМЕНЕНИЙ',
- 'order_year' => 2026,
- 'customer_name' => 'Исходный заказчик',
- 'manager_id' => $manager->id,
- ]);
- $validRow = $this->orderRow($order->id, $order->order_number, $manager->name);
- $validRow[3] = 'Не должно сохраниться';
- $import = $this->createImport($actor, [
- $validRow,
- $this->orderRow(999999, 'НЕИЗВЕСТНЫЙ-ID', $manager->name),
- ], []);
- try {
- $this->service($import, $actor)->handle();
- $this->fail('Импорт с неизвестным ID должен завершиться ошибкой.');
- } catch (RuntimeException $exception) {
- $this->assertStringContainsString('заказ с ID 999999 не найден', $exception->getMessage());
- }
- $this->assertSame('Исходный заказчик', $order->refresh()->customer_name);
- $this->assertSame('ERROR', $import->refresh()->status);
- $this->assertStringContainsString('ID 999999', (string) $import->result);
- }
- /**
- * @param list<list<mixed>> $orderRows
- * @param list<list<mixed>> $itemRows
- */
- private function createImport(User $actor, array $orderRows, array $itemRows): Import
- {
- $path = 'production_orders/test-'.uniqid().'.xlsx';
- Storage::disk('upload')->makeDirectory(dirname($path));
- $spreadsheet = new Spreadsheet;
- $ordersSheet = $spreadsheet->getActiveSheet();
- $ordersSheet->setTitle(ExportProductionOrdersService::ORDERS_SHEET);
- $ordersSheet->fromArray(ExportProductionOrdersService::HEADERS, null, 'A1');
- if ($orderRows !== []) {
- $ordersSheet->fromArray($orderRows, null, 'A2');
- }
- $itemsSheet = $spreadsheet->createSheet();
- $itemsSheet->setTitle(ExportProductionOrdersService::ITEMS_SHEET);
- $itemsSheet->fromArray(ExportProductionOrdersService::ITEM_HEADERS, null, 'A1');
- if ($itemRows !== []) {
- $itemsSheet->fromArray($itemRows, null, 'A2');
- }
- (new Xlsx($spreadsheet))->save(Storage::disk('upload')->path($path));
- $spreadsheet->disconnectWorksheets();
- return Import::factory()->create([
- 'type' => 'production_orders',
- 'year' => null,
- 'filename' => $path,
- 'status' => 'new',
- 'user_id' => $actor->id,
- ]);
- }
- /** @return list<mixed> */
- private function orderRow(?int $id, string $number, string $managerName): array
- {
- return [
- $id,
- $number,
- 2026,
- 'ООО Новый заказчик',
- 'г. Москва, ул. Тестовая, 1',
- 'СЧ-001',
- '01.09.2026',
- 'Д-001',
- '31.08.2026',
- '02.09.2026',
- 5,
- null,
- '15.09.2026',
- ProductionOrderStatus::Placed->label(),
- ProductionOrderExecutionType::Delivery->label(),
- $managerName,
- 'Сводка оборудования не импортируется',
- 'Примечание импорта',
- ];
- }
- private function service(Import $import, User $actor): ImportProductionOrdersService
- {
- return app(ImportProductionOrdersService::class, [
- 'import' => $import,
- 'userId' => $actor->id,
- ]);
- }
- }
|