|
|
@@ -0,0 +1,246 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace Tests\Unit\Services\Import;
|
|
|
+
|
|
|
+use App\Models\SparePart;
|
|
|
+use App\Models\SparePartOrder;
|
|
|
+use App\Services\Import\ImportSparePartOrdersService;
|
|
|
+use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
|
|
+use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
|
|
+use Tests\TestCase;
|
|
|
+
|
|
|
+class ImportSparePartOrdersServiceTest extends TestCase
|
|
|
+{
|
|
|
+ use RefreshDatabase;
|
|
|
+
|
|
|
+ protected $seed = true;
|
|
|
+
|
|
|
+ private string $tempFilePath;
|
|
|
+ private SparePart $sparePart1;
|
|
|
+ private SparePart $sparePart2;
|
|
|
+
|
|
|
+ protected function setUp(): void
|
|
|
+ {
|
|
|
+ parent::setUp();
|
|
|
+ $this->sparePart1 = SparePart::factory()->create(['article' => 'TEST.0001']);
|
|
|
+ $this->sparePart2 = SparePart::factory()->create(['article' => 'TEST.0002']);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function tearDown(): void
|
|
|
+ {
|
|
|
+ if (isset($this->tempFilePath) && file_exists($this->tempFilePath)) {
|
|
|
+ unlink($this->tempFilePath);
|
|
|
+ }
|
|
|
+ parent::tearDown();
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_import_creates_spare_part_orders(): void
|
|
|
+ {
|
|
|
+ $this->createTestFile([
|
|
|
+ ['TEST.0001', 10, 'Да', '55/5224', 'На складе'],
|
|
|
+ ['TEST.0002', 5, 'Нет', '1554-55453', 'Заказано'],
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $service = new ImportSparePartOrdersService($this->tempFilePath, 1);
|
|
|
+ $result = $service->handle();
|
|
|
+
|
|
|
+ $this->assertTrue($result['success']);
|
|
|
+ $this->assertEquals(2, $result['imported']);
|
|
|
+ $this->assertEquals(0, $result['errors']);
|
|
|
+
|
|
|
+ $this->assertDatabaseHas('spare_part_orders', [
|
|
|
+ 'spare_part_id' => $this->sparePart1->id,
|
|
|
+ 'ordered_quantity' => 10,
|
|
|
+ 'available_qty' => 10,
|
|
|
+ 'with_documents' => true,
|
|
|
+ 'order_number' => '55/5224',
|
|
|
+ 'status' => SparePartOrder::STATUS_IN_STOCK,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $this->assertDatabaseHas('spare_part_orders', [
|
|
|
+ 'spare_part_id' => $this->sparePart2->id,
|
|
|
+ 'ordered_quantity' => 5,
|
|
|
+ 'available_qty' => 5,
|
|
|
+ 'with_documents' => false,
|
|
|
+ 'order_number' => '1554-55453',
|
|
|
+ 'status' => SparePartOrder::STATUS_ORDERED,
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_import_reports_error_for_unknown_article(): void
|
|
|
+ {
|
|
|
+ $this->createTestFile([
|
|
|
+ ['UNKNOWN.ARTICLE', 10, 'Да', '55/5224', 'На складе'],
|
|
|
+ ['TEST.0001', 5, 'Да', '55/5224', 'На складе'],
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $service = new ImportSparePartOrdersService($this->tempFilePath, 1);
|
|
|
+ $result = $service->handle();
|
|
|
+
|
|
|
+ $this->assertFalse($result['success']);
|
|
|
+ $this->assertEquals(1, $result['imported']);
|
|
|
+ $this->assertEquals(1, $result['errors']);
|
|
|
+
|
|
|
+ $hasErrorLog = false;
|
|
|
+ foreach ($result['logs'] as $log) {
|
|
|
+ if (str_contains($log, 'UNKNOWN.ARTICLE') && str_contains($log, 'не найдена')) {
|
|
|
+ $hasErrorLog = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ $this->assertTrue($hasErrorLog, 'Should log error for unknown article');
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_import_reports_error_for_unknown_status(): void
|
|
|
+ {
|
|
|
+ $this->createTestFile([
|
|
|
+ ['TEST.0001', 10, 'Да', '55/5224', 'Неизвестный статус'],
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $service = new ImportSparePartOrdersService($this->tempFilePath, 1);
|
|
|
+ $result = $service->handle();
|
|
|
+
|
|
|
+ $this->assertFalse($result['success']);
|
|
|
+ $this->assertEquals(0, $result['imported']);
|
|
|
+ $this->assertEquals(1, $result['errors']);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_import_skips_empty_rows(): void
|
|
|
+ {
|
|
|
+ $this->createTestFile([
|
|
|
+ ['', '', '', '', ''],
|
|
|
+ ['TEST.0001', 10, 'Да', '55/5224', 'На складе'],
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $service = new ImportSparePartOrdersService($this->tempFilePath, 1);
|
|
|
+ $result = $service->handle();
|
|
|
+
|
|
|
+ $this->assertTrue($result['success']);
|
|
|
+ $this->assertEquals(1, $result['imported']);
|
|
|
+ $this->assertEquals(1, $result['skipped']);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_import_parses_with_documents_correctly(): void
|
|
|
+ {
|
|
|
+ $sparePart3 = SparePart::factory()->create(['article' => 'TEST.0003']);
|
|
|
+ $sparePart4 = SparePart::factory()->create(['article' => 'TEST.0004']);
|
|
|
+
|
|
|
+ $this->createTestFile([
|
|
|
+ ['TEST.0001', 1, 'Да', 'order1', 'На складе'],
|
|
|
+ ['TEST.0002', 1, 'да', 'order2', 'На складе'],
|
|
|
+ ['TEST.0003', 1, 'Нет', 'order3', 'На складе'],
|
|
|
+ ['TEST.0004', 1, 'нет', 'order4', 'На складе'],
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $service = new ImportSparePartOrdersService($this->tempFilePath, 1);
|
|
|
+ $result = $service->handle();
|
|
|
+
|
|
|
+ $this->assertTrue($result['success']);
|
|
|
+
|
|
|
+ $this->assertDatabaseHas('spare_part_orders', [
|
|
|
+ 'spare_part_id' => $this->sparePart1->id,
|
|
|
+ 'with_documents' => true,
|
|
|
+ ]);
|
|
|
+ $this->assertDatabaseHas('spare_part_orders', [
|
|
|
+ 'spare_part_id' => $this->sparePart2->id,
|
|
|
+ 'with_documents' => true,
|
|
|
+ ]);
|
|
|
+ $this->assertDatabaseHas('spare_part_orders', [
|
|
|
+ 'spare_part_id' => $sparePart3->id,
|
|
|
+ 'with_documents' => false,
|
|
|
+ ]);
|
|
|
+ $this->assertDatabaseHas('spare_part_orders', [
|
|
|
+ 'spare_part_id' => $sparePart4->id,
|
|
|
+ 'with_documents' => false,
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_import_parses_status_correctly(): void
|
|
|
+ {
|
|
|
+ $sparePart3 = SparePart::factory()->create(['article' => 'TEST.0003']);
|
|
|
+
|
|
|
+ $this->createTestFile([
|
|
|
+ ['TEST.0001', 1, 'Да', 'order1', 'На складе'],
|
|
|
+ ['TEST.0002', 1, 'Да', 'order2', 'Заказано'],
|
|
|
+ ['TEST.0003', 1, 'Да', 'order3', 'Отгружено'],
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $service = new ImportSparePartOrdersService($this->tempFilePath, 1);
|
|
|
+ $result = $service->handle();
|
|
|
+
|
|
|
+ $this->assertTrue($result['success']);
|
|
|
+
|
|
|
+ $this->assertDatabaseHas('spare_part_orders', [
|
|
|
+ 'spare_part_id' => $this->sparePart1->id,
|
|
|
+ 'status' => SparePartOrder::STATUS_IN_STOCK,
|
|
|
+ ]);
|
|
|
+ $this->assertDatabaseHas('spare_part_orders', [
|
|
|
+ 'spare_part_id' => $this->sparePart2->id,
|
|
|
+ 'status' => SparePartOrder::STATUS_ORDERED,
|
|
|
+ ]);
|
|
|
+ $this->assertDatabaseHas('spare_part_orders', [
|
|
|
+ 'spare_part_id' => $sparePart3->id,
|
|
|
+ 'status' => SparePartOrder::STATUS_SHIPPED,
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_import_returns_summary(): void
|
|
|
+ {
|
|
|
+ $this->createTestFile([
|
|
|
+ ['TEST.0001', 10, 'Да', '55/5224', 'На складе'],
|
|
|
+ ['UNKNOWN', 5, 'Да', '55/5224', 'На складе'],
|
|
|
+ [' ', '', '', '', ''], // пробел в артикуле, после trim станет пустым
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $service = new ImportSparePartOrdersService($this->tempFilePath, 1);
|
|
|
+ $result = $service->handle();
|
|
|
+
|
|
|
+ $this->assertArrayHasKey('imported', $result);
|
|
|
+ $this->assertArrayHasKey('skipped', $result);
|
|
|
+ $this->assertArrayHasKey('errors', $result);
|
|
|
+ $this->assertArrayHasKey('logs', $result);
|
|
|
+
|
|
|
+ $this->assertEquals(1, $result['imported']);
|
|
|
+ $this->assertEquals(1, $result['skipped']); // строка с пробелом в артикуле
|
|
|
+ $this->assertEquals(1, $result['errors']); // UNKNOWN артикул
|
|
|
+
|
|
|
+ // Check summary in logs
|
|
|
+ $hasSummary = false;
|
|
|
+ foreach ($result['logs'] as $log) {
|
|
|
+ if (str_contains($log, 'РЕЗЮМЕ')) {
|
|
|
+ $hasSummary = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ $this->assertTrue($hasSummary, 'Should include summary in logs');
|
|
|
+ }
|
|
|
+
|
|
|
+ private function createTestFile(array $rows): void
|
|
|
+ {
|
|
|
+ $spreadsheet = new Spreadsheet();
|
|
|
+ $sheet = $spreadsheet->getActiveSheet();
|
|
|
+
|
|
|
+ // Headers
|
|
|
+ $sheet->setCellValue('A1', 'Артикул детали');
|
|
|
+ $sheet->setCellValue('B1', 'кол-во');
|
|
|
+ $sheet->setCellValue('C1', 'С документами?');
|
|
|
+ $sheet->setCellValue('D1', 'Номер заказа');
|
|
|
+ $sheet->setCellValue('E1', 'Статус');
|
|
|
+
|
|
|
+ // Data rows
|
|
|
+ $rowNum = 2;
|
|
|
+ foreach ($rows as $row) {
|
|
|
+ $sheet->setCellValue('A' . $rowNum, $row[0] ?? '');
|
|
|
+ $sheet->setCellValue('B' . $rowNum, $row[1] ?? '');
|
|
|
+ $sheet->setCellValue('C' . $rowNum, $row[2] ?? '');
|
|
|
+ $sheet->setCellValue('D' . $rowNum, $row[3] ?? '');
|
|
|
+ $sheet->setCellValue('E' . $rowNum, $row[4] ?? '');
|
|
|
+ $rowNum++;
|
|
|
+ }
|
|
|
+
|
|
|
+ $this->tempFilePath = sys_get_temp_dir() . '/test_spare_part_orders_' . uniqid() . '.xlsx';
|
|
|
+ $writer = new Xlsx($spreadsheet);
|
|
|
+ $writer->save($this->tempFilePath);
|
|
|
+ }
|
|
|
+}
|