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> $orderRows * @param list> $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 */ 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, ]); } }