SparePartReservationServiceTest.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
  1. <?php
  2. namespace Tests\Unit\Services;
  3. use App\Models\InventoryMovement;
  4. use App\Models\Reclamation;
  5. use App\Models\ReclamationSparePart;
  6. use App\Models\Reservation;
  7. use App\Models\Shortage;
  8. use App\Models\SparePart;
  9. use App\Models\SparePartOrder;
  10. use App\Models\User;
  11. use App\Services\ReservationResult;
  12. use App\Services\ShortageService;
  13. use App\Services\SparePartReservationService;
  14. use Illuminate\Foundation\Testing\RefreshDatabase;
  15. use Tests\TestCase;
  16. class SparePartReservationServiceTest extends TestCase
  17. {
  18. use RefreshDatabase;
  19. protected $seed = true;
  20. private SparePartReservationService $service;
  21. private ShortageService $shortageService;
  22. protected function setUp(): void
  23. {
  24. parent::setUp();
  25. $this->shortageService = new ShortageService();
  26. $this->service = new SparePartReservationService($this->shortageService);
  27. }
  28. public function test_reserve_with_sufficient_stock_creates_reservation(): void
  29. {
  30. // Arrange
  31. $sparePart = SparePart::factory()->create();
  32. $reclamation = Reclamation::factory()->create();
  33. $order = SparePartOrder::factory()
  34. ->inStock()
  35. ->withDocuments(false)
  36. ->withQuantity(10)
  37. ->forSparePart($sparePart)
  38. ->create();
  39. // Act
  40. $result = $this->service->reserve(
  41. sparePartId: $sparePart->id,
  42. quantity: 5,
  43. withDocuments: false,
  44. reclamationId: $reclamation->id
  45. );
  46. // Assert
  47. $this->assertInstanceOf(ReservationResult::class, $result);
  48. $this->assertEquals(5, $result->reserved);
  49. $this->assertEquals(0, $result->missing);
  50. $this->assertTrue($result->isFullyReserved());
  51. $this->assertFalse($result->hasShortage());
  52. $this->assertCount(1, $result->reservations);
  53. // Check reservation was created in DB
  54. $this->assertDatabaseHas('reservations', [
  55. 'spare_part_id' => $sparePart->id,
  56. 'spare_part_order_id' => $order->id,
  57. 'reclamation_id' => $reclamation->id,
  58. 'reserved_qty' => 5,
  59. 'with_documents' => false,
  60. 'status' => Reservation::STATUS_ACTIVE,
  61. ]);
  62. // Check inventory movement was created
  63. $this->assertDatabaseHas('inventory_movements', [
  64. 'spare_part_id' => $sparePart->id,
  65. 'spare_part_order_id' => $order->id,
  66. 'qty' => 5,
  67. 'movement_type' => InventoryMovement::TYPE_RESERVE,
  68. 'source_type' => InventoryMovement::SOURCE_RECLAMATION,
  69. 'source_id' => $reclamation->id,
  70. ]);
  71. }
  72. public function test_reserve_with_partial_stock_creates_shortage(): void
  73. {
  74. // Arrange
  75. $sparePart = SparePart::factory()->create();
  76. $reclamation = Reclamation::factory()->create();
  77. SparePartOrder::factory()
  78. ->inStock()
  79. ->withDocuments(false)
  80. ->withQuantity(3)
  81. ->forSparePart($sparePart)
  82. ->create();
  83. // Act
  84. $result = $this->service->reserve(
  85. sparePartId: $sparePart->id,
  86. quantity: 5,
  87. withDocuments: false,
  88. reclamationId: $reclamation->id
  89. );
  90. // Assert
  91. $this->assertEquals(3, $result->reserved);
  92. $this->assertEquals(2, $result->missing);
  93. $this->assertFalse($result->isFullyReserved());
  94. $this->assertTrue($result->hasShortage());
  95. // Check shortage was created
  96. $this->assertDatabaseHas('shortages', [
  97. 'spare_part_id' => $sparePart->id,
  98. 'reclamation_id' => $reclamation->id,
  99. 'with_documents' => false,
  100. 'required_qty' => 5,
  101. 'reserved_qty' => 3,
  102. 'missing_qty' => 2,
  103. 'status' => Shortage::STATUS_OPEN,
  104. ]);
  105. }
  106. public function test_reserve_with_no_stock_creates_full_shortage(): void
  107. {
  108. // Arrange
  109. $sparePart = SparePart::factory()->create();
  110. $reclamation = Reclamation::factory()->create();
  111. // No orders created = no stock
  112. // Act
  113. $result = $this->service->reserve(
  114. sparePartId: $sparePart->id,
  115. quantity: 5,
  116. withDocuments: false,
  117. reclamationId: $reclamation->id
  118. );
  119. // Assert
  120. $this->assertEquals(0, $result->reserved);
  121. $this->assertEquals(5, $result->missing);
  122. $this->assertFalse($result->isFullyReserved());
  123. $this->assertTrue($result->hasShortage());
  124. $this->assertEmpty($result->reservations);
  125. // Check full shortage was created
  126. $this->assertDatabaseHas('shortages', [
  127. 'spare_part_id' => $sparePart->id,
  128. 'reclamation_id' => $reclamation->id,
  129. 'required_qty' => 5,
  130. 'reserved_qty' => 0,
  131. 'missing_qty' => 5,
  132. 'status' => Shortage::STATUS_OPEN,
  133. ]);
  134. }
  135. public function test_reserve_respects_fifo_order_for_multiple_batches(): void
  136. {
  137. // Arrange
  138. $sparePart = SparePart::factory()->create();
  139. $reclamation = Reclamation::factory()->create();
  140. // Create first batch (older)
  141. $olderOrder = SparePartOrder::factory()
  142. ->inStock()
  143. ->withDocuments(false)
  144. ->withQuantity(3)
  145. ->forSparePart($sparePart)
  146. ->create(['created_at' => now()->subDays(2)]);
  147. // Create second batch (newer)
  148. $newerOrder = SparePartOrder::factory()
  149. ->inStock()
  150. ->withDocuments(false)
  151. ->withQuantity(5)
  152. ->forSparePart($sparePart)
  153. ->create(['created_at' => now()->subDay()]);
  154. // Act - reserve 5 items (should use 3 from older + 2 from newer)
  155. $result = $this->service->reserve(
  156. sparePartId: $sparePart->id,
  157. quantity: 5,
  158. withDocuments: false,
  159. reclamationId: $reclamation->id
  160. );
  161. // Assert
  162. $this->assertEquals(5, $result->reserved);
  163. $this->assertCount(2, $result->reservations);
  164. // Check FIFO: first batch fully used
  165. $this->assertDatabaseHas('reservations', [
  166. 'spare_part_order_id' => $olderOrder->id,
  167. 'reserved_qty' => 3,
  168. ]);
  169. // Check FIFO: second batch partially used
  170. $this->assertDatabaseHas('reservations', [
  171. 'spare_part_order_id' => $newerOrder->id,
  172. 'reserved_qty' => 2,
  173. ]);
  174. }
  175. public function test_reserve_respects_with_documents_flag(): void
  176. {
  177. // Arrange
  178. $sparePart = SparePart::factory()->create();
  179. $reclamation = Reclamation::factory()->create();
  180. // Create batch WITHOUT documents
  181. SparePartOrder::factory()
  182. ->inStock()
  183. ->withDocuments(false)
  184. ->withQuantity(10)
  185. ->forSparePart($sparePart)
  186. ->create();
  187. // Create batch WITH documents
  188. $orderWithDocs = SparePartOrder::factory()
  189. ->inStock()
  190. ->withDocuments(true)
  191. ->withQuantity(10)
  192. ->forSparePart($sparePart)
  193. ->create();
  194. // Act - reserve WITH documents
  195. $result = $this->service->reserve(
  196. sparePartId: $sparePart->id,
  197. quantity: 5,
  198. withDocuments: true,
  199. reclamationId: $reclamation->id
  200. );
  201. // Assert - should only use batch with documents
  202. $this->assertEquals(5, $result->reserved);
  203. $this->assertCount(1, $result->reservations);
  204. $this->assertDatabaseHas('reservations', [
  205. 'spare_part_order_id' => $orderWithDocs->id,
  206. 'with_documents' => true,
  207. ]);
  208. }
  209. public function test_reserve_considers_existing_reservations(): void
  210. {
  211. // Arrange
  212. $sparePart = SparePart::factory()->create();
  213. $reclamation1 = Reclamation::factory()->create();
  214. $reclamation2 = Reclamation::factory()->create();
  215. $order = SparePartOrder::factory()
  216. ->inStock()
  217. ->withDocuments(false)
  218. ->withQuantity(10)
  219. ->forSparePart($sparePart)
  220. ->create();
  221. // Create existing active reservation for 7 items
  222. Reservation::factory()
  223. ->active()
  224. ->withQuantity(7)
  225. ->fromOrder($order)
  226. ->forReclamation($reclamation1)
  227. ->create();
  228. // Act - try to reserve 5 more (only 3 available)
  229. $result = $this->service->reserve(
  230. sparePartId: $sparePart->id,
  231. quantity: 5,
  232. withDocuments: false,
  233. reclamationId: $reclamation2->id
  234. );
  235. // Assert
  236. $this->assertEquals(3, $result->reserved);
  237. $this->assertEquals(2, $result->missing);
  238. $this->assertTrue($result->hasShortage());
  239. }
  240. public function test_cancel_for_reclamation_cancels_all_reservations(): void
  241. {
  242. // Arrange
  243. $sparePart = SparePart::factory()->create();
  244. $reclamation = Reclamation::factory()->create();
  245. $order = SparePartOrder::factory()
  246. ->inStock()
  247. ->withDocuments(false)
  248. ->withQuantity(10)
  249. ->forSparePart($sparePart)
  250. ->create();
  251. // Create reservations
  252. Reservation::factory()
  253. ->active()
  254. ->withQuantity(3)
  255. ->fromOrder($order)
  256. ->forReclamation($reclamation)
  257. ->create();
  258. Reservation::factory()
  259. ->active()
  260. ->withQuantity(2)
  261. ->fromOrder($order)
  262. ->forReclamation($reclamation)
  263. ->create();
  264. // Create shortage
  265. Shortage::factory()
  266. ->open()
  267. ->withQuantities(10, 5)
  268. ->forSparePart($sparePart)
  269. ->forReclamation($reclamation)
  270. ->withDocuments(false)
  271. ->create();
  272. // Act
  273. $cancelled = $this->service->cancelForReclamation($reclamation->id);
  274. // Assert
  275. $this->assertEquals(5, $cancelled);
  276. // Check all reservations are cancelled
  277. $this->assertDatabaseMissing('reservations', [
  278. 'reclamation_id' => $reclamation->id,
  279. 'status' => Reservation::STATUS_ACTIVE,
  280. ]);
  281. $this->assertEquals(2, Reservation::where('reclamation_id', $reclamation->id)
  282. ->where('status', Reservation::STATUS_CANCELLED)->count());
  283. // Check shortage is closed
  284. $this->assertDatabaseHas('shortages', [
  285. 'reclamation_id' => $reclamation->id,
  286. 'status' => Shortage::STATUS_CLOSED,
  287. ]);
  288. }
  289. public function test_cancel_for_reclamation_filters_by_spare_part(): void
  290. {
  291. // Arrange
  292. $sparePart1 = SparePart::factory()->create();
  293. $sparePart2 = SparePart::factory()->create();
  294. $reclamation = Reclamation::factory()->create();
  295. $order1 = SparePartOrder::factory()
  296. ->inStock()
  297. ->forSparePart($sparePart1)
  298. ->create();
  299. $order2 = SparePartOrder::factory()
  300. ->inStock()
  301. ->forSparePart($sparePart2)
  302. ->create();
  303. $reservation1 = Reservation::factory()
  304. ->active()
  305. ->withQuantity(3)
  306. ->fromOrder($order1)
  307. ->forReclamation($reclamation)
  308. ->create();
  309. $reservation2 = Reservation::factory()
  310. ->active()
  311. ->withQuantity(5)
  312. ->fromOrder($order2)
  313. ->forReclamation($reclamation)
  314. ->create();
  315. // Act - cancel only for sparePart1
  316. $cancelled = $this->service->cancelForReclamation(
  317. $reclamation->id,
  318. sparePartId: $sparePart1->id
  319. );
  320. // Assert
  321. $this->assertEquals(3, $cancelled);
  322. $this->assertDatabaseHas('reservations', [
  323. 'id' => $reservation1->id,
  324. 'status' => Reservation::STATUS_CANCELLED,
  325. ]);
  326. $this->assertDatabaseHas('reservations', [
  327. 'id' => $reservation2->id,
  328. 'status' => Reservation::STATUS_ACTIVE,
  329. ]);
  330. }
  331. public function test_get_reservations_for_reclamation_returns_active_only(): void
  332. {
  333. // Arrange
  334. $sparePart = SparePart::factory()->create();
  335. $reclamation = Reclamation::factory()->create();
  336. $order = SparePartOrder::factory()
  337. ->inStock()
  338. ->forSparePart($sparePart)
  339. ->create();
  340. Reservation::factory()
  341. ->active()
  342. ->fromOrder($order)
  343. ->forReclamation($reclamation)
  344. ->create();
  345. Reservation::factory()
  346. ->cancelled()
  347. ->fromOrder($order)
  348. ->forReclamation($reclamation)
  349. ->create();
  350. Reservation::factory()
  351. ->issued()
  352. ->fromOrder($order)
  353. ->forReclamation($reclamation)
  354. ->create();
  355. // Act
  356. $reservations = $this->service->getReservationsForReclamation($reclamation->id);
  357. // Assert
  358. $this->assertCount(1, $reservations);
  359. $this->assertEquals(Reservation::STATUS_ACTIVE, $reservations->first()->status);
  360. }
  361. public function test_adjust_reservation_increases_quantity(): void
  362. {
  363. // Arrange
  364. $sparePart = SparePart::factory()->create();
  365. $reclamation = Reclamation::factory()->create();
  366. $order = SparePartOrder::factory()
  367. ->inStock()
  368. ->withDocuments(false)
  369. ->withQuantity(20)
  370. ->forSparePart($sparePart)
  371. ->create();
  372. // Initial reservation of 5
  373. Reservation::factory()
  374. ->active()
  375. ->withQuantity(5)
  376. ->withDocuments(false)
  377. ->fromOrder($order)
  378. ->forReclamation($reclamation)
  379. ->create();
  380. // Act - increase to 10
  381. $result = $this->service->adjustReservation(
  382. reclamationId: $reclamation->id,
  383. sparePartId: $sparePart->id,
  384. withDocuments: false,
  385. newQuantity: 10
  386. );
  387. // Assert - should have reserved additional 5
  388. $this->assertEquals(5, $result->reserved);
  389. $this->assertEquals(0, $result->missing);
  390. $totalReserved = Reservation::where('reclamation_id', $reclamation->id)
  391. ->where('spare_part_id', $sparePart->id)
  392. ->where('status', Reservation::STATUS_ACTIVE)
  393. ->sum('reserved_qty');
  394. $this->assertEquals(10, $totalReserved);
  395. }
  396. public function test_adjust_reservation_decreases_quantity(): void
  397. {
  398. // Arrange
  399. $sparePart = SparePart::factory()->create();
  400. $reclamation = Reclamation::factory()->create();
  401. $order = SparePartOrder::factory()
  402. ->inStock()
  403. ->withDocuments(false)
  404. ->withQuantity(20)
  405. ->forSparePart($sparePart)
  406. ->create();
  407. // Initial reservation of 10
  408. Reservation::factory()
  409. ->active()
  410. ->withQuantity(10)
  411. ->withDocuments(false)
  412. ->fromOrder($order)
  413. ->forReclamation($reclamation)
  414. ->create();
  415. // Act - decrease to 3
  416. $result = $this->service->adjustReservation(
  417. reclamationId: $reclamation->id,
  418. sparePartId: $sparePart->id,
  419. withDocuments: false,
  420. newQuantity: 3
  421. );
  422. // Assert
  423. $this->assertEquals(3, $result->reserved);
  424. $this->assertEquals(0, $result->missing);
  425. }
  426. public function test_cancel_reservation_does_not_cancel_non_active(): void
  427. {
  428. // Arrange
  429. $sparePart = SparePart::factory()->create();
  430. $reclamation = Reclamation::factory()->create();
  431. $order = SparePartOrder::factory()
  432. ->inStock()
  433. ->forSparePart($sparePart)
  434. ->create();
  435. $reservation = Reservation::factory()
  436. ->issued()
  437. ->fromOrder($order)
  438. ->forReclamation($reclamation)
  439. ->create();
  440. // Act
  441. $result = $this->service->cancelReservation($reservation);
  442. // Assert
  443. $this->assertFalse($result);
  444. $this->assertDatabaseHas('reservations', [
  445. 'id' => $reservation->id,
  446. 'status' => Reservation::STATUS_ISSUED,
  447. ]);
  448. }
  449. public function test_reassign_reservation_to_selected_order_moves_active_reservation_without_issue(): void
  450. {
  451. $user = User::factory()->create();
  452. $this->actingAs($user);
  453. $sparePart = SparePart::factory()->create();
  454. $reclamation = Reclamation::factory()->create();
  455. $oldOrder = SparePartOrder::factory()->inStock()->withDocuments(false)->withQuantity(10)->forSparePart($sparePart)->create();
  456. $selectedOrder = SparePartOrder::factory()->inStock()->withDocuments(false)->withQuantity(8)->forSparePart($sparePart)->create();
  457. ReclamationSparePart::query()->create([
  458. 'reclamation_id' => $reclamation->id,
  459. 'spare_part_id' => $sparePart->id,
  460. 'quantity' => 5,
  461. 'with_documents' => false,
  462. 'status' => 'reserved',
  463. 'reserved_qty' => 5,
  464. 'issued_qty' => 0,
  465. ]);
  466. $reservation = Reservation::factory()
  467. ->active()
  468. ->withQuantity(5)
  469. ->withDocuments(false)
  470. ->fromOrder($oldOrder)
  471. ->forReclamation($reclamation)
  472. ->create();
  473. $newReservation = $this->service->reassignReservationToSelectedOrder($reservation, $selectedOrder->id);
  474. $this->assertSame(Reservation::STATUS_ACTIVE, $newReservation->status);
  475. $this->assertSame($selectedOrder->id, $newReservation->spare_part_order_id);
  476. $this->assertDatabaseHas('reservations', [
  477. 'id' => $reservation->id,
  478. 'status' => Reservation::STATUS_CANCELLED,
  479. ]);
  480. $this->assertDatabaseHas('reservations', [
  481. 'id' => $newReservation->id,
  482. 'reserved_qty' => 5,
  483. 'status' => Reservation::STATUS_ACTIVE,
  484. ]);
  485. $this->assertDatabaseMissing('reservations', [
  486. 'id' => $newReservation->id,
  487. 'status' => Reservation::STATUS_ISSUED,
  488. ]);
  489. $this->assertDatabaseHas('reclamation_spare_part', [
  490. 'reclamation_id' => $reclamation->id,
  491. 'spare_part_id' => $sparePart->id,
  492. 'quantity' => 5,
  493. 'status' => 'reserved',
  494. 'reserved_qty' => 5,
  495. 'issued_qty' => 0,
  496. ]);
  497. $this->assertDatabaseHas('spare_part_orders', [
  498. 'id' => $selectedOrder->id,
  499. 'available_qty' => 8,
  500. ]);
  501. }
  502. public function test_reassign_reservation_to_selected_order_reserves_remaining_quantity_from_other_stock(): void
  503. {
  504. $user = User::factory()->create();
  505. $this->actingAs($user);
  506. $sparePart = SparePart::factory()->create();
  507. $reclamation = Reclamation::factory()->create();
  508. $oldOrder = SparePartOrder::factory()->inStock()->withDocuments(true)->withQuantity(10)->forSparePart($sparePart)->create();
  509. $selectedOrder = SparePartOrder::factory()->inStock()->withDocuments(true)->withQuantity(2)->forSparePart($sparePart)->create([
  510. 'available_qty' => 2,
  511. ]);
  512. ReclamationSparePart::query()->create([
  513. 'reclamation_id' => $reclamation->id,
  514. 'spare_part_id' => $sparePart->id,
  515. 'quantity' => 5,
  516. 'with_documents' => true,
  517. 'status' => 'reserved',
  518. 'reserved_qty' => 5,
  519. 'issued_qty' => 0,
  520. ]);
  521. $reservation = Reservation::factory()
  522. ->active()
  523. ->withQuantity(5)
  524. ->withDocuments(true)
  525. ->fromOrder($oldOrder)
  526. ->forReclamation($reclamation)
  527. ->create();
  528. $newReservation = $this->service->reassignReservationToSelectedOrder($reservation, $selectedOrder->id);
  529. $this->assertSame(2, $newReservation->reserved_qty);
  530. $this->assertDatabaseHas('reservations', [
  531. 'reclamation_id' => $reclamation->id,
  532. 'spare_part_order_id' => $oldOrder->id,
  533. 'reserved_qty' => 3,
  534. 'status' => Reservation::STATUS_ACTIVE,
  535. ]);
  536. $this->assertDatabaseHas('reclamation_spare_part', [
  537. 'reclamation_id' => $reclamation->id,
  538. 'spare_part_id' => $sparePart->id,
  539. 'quantity' => 2,
  540. 'with_documents' => true,
  541. 'status' => 'reserved',
  542. 'reserved_qty' => 2,
  543. 'issued_qty' => 0,
  544. ]);
  545. $this->assertDatabaseHas('reclamation_spare_part', [
  546. 'reclamation_id' => $reclamation->id,
  547. 'spare_part_id' => $sparePart->id,
  548. 'quantity' => 3,
  549. 'with_documents' => true,
  550. 'status' => 'reserved',
  551. 'reserved_qty' => 3,
  552. 'issued_qty' => 0,
  553. ]);
  554. $this->assertDatabaseMissing('shortages', [
  555. 'reclamation_id' => $reclamation->id,
  556. 'spare_part_id' => $sparePart->id,
  557. 'with_documents' => true,
  558. 'status' => Shortage::STATUS_OPEN,
  559. ]);
  560. }
  561. public function test_reassign_reservation_to_selected_order_creates_shortage_only_for_real_missing_quantity(): void
  562. {
  563. $user = User::factory()->create();
  564. $this->actingAs($user);
  565. $sparePart = SparePart::factory()->create();
  566. $reclamation = Reclamation::factory()->create();
  567. $oldOrder = SparePartOrder::factory()->inStock()->withDocuments(true)->withQuantity(1)->forSparePart($sparePart)->create([
  568. 'available_qty' => 1,
  569. ]);
  570. $selectedOrder = SparePartOrder::factory()->inStock()->withDocuments(true)->withQuantity(2)->forSparePart($sparePart)->create([
  571. 'available_qty' => 2,
  572. ]);
  573. ReclamationSparePart::query()->create([
  574. 'reclamation_id' => $reclamation->id,
  575. 'spare_part_id' => $sparePart->id,
  576. 'quantity' => 5,
  577. 'with_documents' => true,
  578. 'status' => 'reserved',
  579. 'reserved_qty' => 5,
  580. 'issued_qty' => 0,
  581. ]);
  582. $reservation = Reservation::factory()
  583. ->active()
  584. ->withQuantity(5)
  585. ->withDocuments(true)
  586. ->fromOrder($oldOrder)
  587. ->forReclamation($reclamation)
  588. ->create([
  589. 'reserved_qty' => 5,
  590. ]);
  591. $newReservation = $this->service->reassignReservationToSelectedOrder($reservation, $selectedOrder->id);
  592. $this->assertSame(2, $newReservation->reserved_qty);
  593. $this->assertDatabaseHas('reservations', [
  594. 'reclamation_id' => $reclamation->id,
  595. 'spare_part_order_id' => $oldOrder->id,
  596. 'reserved_qty' => 1,
  597. 'status' => Reservation::STATUS_ACTIVE,
  598. ]);
  599. $this->assertDatabaseHas('reclamation_spare_part', [
  600. 'reclamation_id' => $reclamation->id,
  601. 'spare_part_id' => $sparePart->id,
  602. 'quantity' => 1,
  603. 'with_documents' => true,
  604. 'status' => 'reserved',
  605. 'reserved_qty' => 1,
  606. 'issued_qty' => 0,
  607. ]);
  608. $this->assertDatabaseHas('reclamation_spare_part', [
  609. 'reclamation_id' => $reclamation->id,
  610. 'spare_part_id' => $sparePart->id,
  611. 'quantity' => 2,
  612. 'with_documents' => true,
  613. 'status' => 'pending',
  614. 'reserved_qty' => 0,
  615. 'issued_qty' => 0,
  616. ]);
  617. $this->assertDatabaseHas('shortages', [
  618. 'reclamation_id' => $reclamation->id,
  619. 'spare_part_id' => $sparePart->id,
  620. 'missing_qty' => 2,
  621. 'reserved_qty' => 1,
  622. 'with_documents' => true,
  623. 'status' => Shortage::STATUS_OPEN,
  624. ]);
  625. }
  626. }