ImportSparePartOrdersServiceTest.php 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. <?php
  2. namespace Tests\Unit\Services\Import;
  3. use App\Models\SparePart;
  4. use App\Models\SparePartOrder;
  5. use App\Services\Import\ImportSparePartOrdersService;
  6. use Illuminate\Foundation\Testing\RefreshDatabase;
  7. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  8. use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
  9. use Tests\TestCase;
  10. class ImportSparePartOrdersServiceTest extends TestCase
  11. {
  12. use RefreshDatabase;
  13. protected $seed = true;
  14. private string $tempFilePath;
  15. private SparePart $sparePart1;
  16. private SparePart $sparePart2;
  17. protected function setUp(): void
  18. {
  19. parent::setUp();
  20. $this->sparePart1 = SparePart::factory()->create(['article' => 'TEST.0001']);
  21. $this->sparePart2 = SparePart::factory()->create(['article' => 'TEST.0002']);
  22. }
  23. protected function tearDown(): void
  24. {
  25. if (isset($this->tempFilePath) && file_exists($this->tempFilePath)) {
  26. unlink($this->tempFilePath);
  27. }
  28. parent::tearDown();
  29. }
  30. public function test_import_creates_spare_part_orders(): void
  31. {
  32. $this->createTestFile([
  33. ['TEST.0001', 10, 'Да', '55/5224', 'На складе'],
  34. ['TEST.0002', 5, 'Нет', '1554-55453', 'Заказано'],
  35. ]);
  36. $service = new ImportSparePartOrdersService($this->tempFilePath, 1);
  37. $result = $service->handle();
  38. $this->assertTrue($result['success']);
  39. $this->assertEquals(2, $result['imported']);
  40. $this->assertEquals(0, $result['errors']);
  41. $this->assertDatabaseHas('spare_part_orders', [
  42. 'spare_part_id' => $this->sparePart1->id,
  43. 'ordered_quantity' => 10,
  44. 'available_qty' => 10,
  45. 'with_documents' => true,
  46. 'order_number' => '55/5224',
  47. 'status' => SparePartOrder::STATUS_IN_STOCK,
  48. ]);
  49. $this->assertDatabaseHas('spare_part_orders', [
  50. 'spare_part_id' => $this->sparePart2->id,
  51. 'ordered_quantity' => 5,
  52. 'available_qty' => 5,
  53. 'with_documents' => false,
  54. 'order_number' => '1554-55453',
  55. 'status' => SparePartOrder::STATUS_ORDERED,
  56. ]);
  57. }
  58. public function test_import_reports_error_for_unknown_article(): void
  59. {
  60. $this->createTestFile([
  61. ['UNKNOWN.ARTICLE', 10, 'Да', '55/5224', 'На складе'],
  62. ['TEST.0001', 5, 'Да', '55/5224', 'На складе'],
  63. ]);
  64. $service = new ImportSparePartOrdersService($this->tempFilePath, 1);
  65. $result = $service->handle();
  66. $this->assertFalse($result['success']);
  67. $this->assertEquals(1, $result['imported']);
  68. $this->assertEquals(1, $result['errors']);
  69. $hasErrorLog = false;
  70. foreach ($result['logs'] as $log) {
  71. if (str_contains($log, 'UNKNOWN.ARTICLE') && str_contains($log, 'не найдена')) {
  72. $hasErrorLog = true;
  73. break;
  74. }
  75. }
  76. $this->assertTrue($hasErrorLog, 'Should log error for unknown article');
  77. }
  78. public function test_import_reports_error_for_unknown_status(): void
  79. {
  80. $this->createTestFile([
  81. ['TEST.0001', 10, 'Да', '55/5224', 'Неизвестный статус'],
  82. ]);
  83. $service = new ImportSparePartOrdersService($this->tempFilePath, 1);
  84. $result = $service->handle();
  85. $this->assertFalse($result['success']);
  86. $this->assertEquals(0, $result['imported']);
  87. $this->assertEquals(1, $result['errors']);
  88. }
  89. public function test_import_skips_empty_rows(): void
  90. {
  91. $this->createTestFile([
  92. ['', '', '', '', ''],
  93. ['TEST.0001', 10, 'Да', '55/5224', 'На складе'],
  94. ]);
  95. $service = new ImportSparePartOrdersService($this->tempFilePath, 1);
  96. $result = $service->handle();
  97. $this->assertTrue($result['success']);
  98. $this->assertEquals(1, $result['imported']);
  99. $this->assertEquals(1, $result['skipped']);
  100. }
  101. public function test_import_parses_with_documents_correctly(): void
  102. {
  103. $sparePart3 = SparePart::factory()->create(['article' => 'TEST.0003']);
  104. $sparePart4 = SparePart::factory()->create(['article' => 'TEST.0004']);
  105. $this->createTestFile([
  106. ['TEST.0001', 1, 'Да', 'order1', 'На складе'],
  107. ['TEST.0002', 1, 'да', 'order2', 'На складе'],
  108. ['TEST.0003', 1, 'Нет', 'order3', 'На складе'],
  109. ['TEST.0004', 1, 'нет', 'order4', 'На складе'],
  110. ]);
  111. $service = new ImportSparePartOrdersService($this->tempFilePath, 1);
  112. $result = $service->handle();
  113. $this->assertTrue($result['success']);
  114. $this->assertDatabaseHas('spare_part_orders', [
  115. 'spare_part_id' => $this->sparePart1->id,
  116. 'with_documents' => true,
  117. ]);
  118. $this->assertDatabaseHas('spare_part_orders', [
  119. 'spare_part_id' => $this->sparePart2->id,
  120. 'with_documents' => true,
  121. ]);
  122. $this->assertDatabaseHas('spare_part_orders', [
  123. 'spare_part_id' => $sparePart3->id,
  124. 'with_documents' => false,
  125. ]);
  126. $this->assertDatabaseHas('spare_part_orders', [
  127. 'spare_part_id' => $sparePart4->id,
  128. 'with_documents' => false,
  129. ]);
  130. }
  131. public function test_import_parses_status_correctly(): void
  132. {
  133. $sparePart3 = SparePart::factory()->create(['article' => 'TEST.0003']);
  134. $this->createTestFile([
  135. ['TEST.0001', 1, 'Да', 'order1', 'На складе'],
  136. ['TEST.0002', 1, 'Да', 'order2', 'Заказано'],
  137. ['TEST.0003', 1, 'Да', 'order3', 'Отгружено'],
  138. ]);
  139. $service = new ImportSparePartOrdersService($this->tempFilePath, 1);
  140. $result = $service->handle();
  141. $this->assertTrue($result['success']);
  142. $this->assertDatabaseHas('spare_part_orders', [
  143. 'spare_part_id' => $this->sparePart1->id,
  144. 'status' => SparePartOrder::STATUS_IN_STOCK,
  145. ]);
  146. $this->assertDatabaseHas('spare_part_orders', [
  147. 'spare_part_id' => $this->sparePart2->id,
  148. 'status' => SparePartOrder::STATUS_ORDERED,
  149. ]);
  150. $this->assertDatabaseHas('spare_part_orders', [
  151. 'spare_part_id' => $sparePart3->id,
  152. 'status' => SparePartOrder::STATUS_SHIPPED,
  153. ]);
  154. }
  155. public function test_import_returns_summary(): void
  156. {
  157. $this->createTestFile([
  158. ['TEST.0001', 10, 'Да', '55/5224', 'На складе'],
  159. ['UNKNOWN', 5, 'Да', '55/5224', 'На складе'],
  160. [' ', '', '', '', ''], // пробел в артикуле, после trim станет пустым
  161. ]);
  162. $service = new ImportSparePartOrdersService($this->tempFilePath, 1);
  163. $result = $service->handle();
  164. $this->assertArrayHasKey('imported', $result);
  165. $this->assertArrayHasKey('skipped', $result);
  166. $this->assertArrayHasKey('errors', $result);
  167. $this->assertArrayHasKey('logs', $result);
  168. $this->assertEquals(1, $result['imported']);
  169. $this->assertEquals(1, $result['skipped']); // строка с пробелом в артикуле
  170. $this->assertEquals(1, $result['errors']); // UNKNOWN артикул
  171. // Check summary in logs
  172. $hasSummary = false;
  173. foreach ($result['logs'] as $log) {
  174. if (str_contains($log, 'РЕЗЮМЕ')) {
  175. $hasSummary = true;
  176. break;
  177. }
  178. }
  179. $this->assertTrue($hasSummary, 'Should include summary in logs');
  180. }
  181. private function createTestFile(array $rows): void
  182. {
  183. $spreadsheet = new Spreadsheet();
  184. $sheet = $spreadsheet->getActiveSheet();
  185. // Headers
  186. $sheet->setCellValue('A1', 'Артикул детали');
  187. $sheet->setCellValue('B1', 'кол-во');
  188. $sheet->setCellValue('C1', 'С документами?');
  189. $sheet->setCellValue('D1', 'Номер заказа');
  190. $sheet->setCellValue('E1', 'Статус');
  191. // Data rows
  192. $rowNum = 2;
  193. foreach ($rows as $row) {
  194. $sheet->setCellValue('A' . $rowNum, $row[0] ?? '');
  195. $sheet->setCellValue('B' . $rowNum, $row[1] ?? '');
  196. $sheet->setCellValue('C' . $rowNum, $row[2] ?? '');
  197. $sheet->setCellValue('D' . $rowNum, $row[3] ?? '');
  198. $sheet->setCellValue('E' . $rowNum, $row[4] ?? '');
  199. $rowNum++;
  200. }
  201. $this->tempFilePath = sys_get_temp_dir() . '/test_spare_part_orders_' . uniqid() . '.xlsx';
  202. $writer = new Xlsx($spreadsheet);
  203. $writer->save($this->tempFilePath);
  204. }
  205. }