SparePartOrderControllerTest.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. <?php
  2. namespace Tests\Feature;
  3. use App\Models\Reservation;
  4. use App\Models\Role;
  5. use App\Models\SparePart;
  6. use App\Models\SparePartOrder;
  7. use App\Models\User;
  8. use Illuminate\Foundation\Testing\RefreshDatabase;
  9. use Tests\TestCase;
  10. class SparePartOrderControllerTest extends TestCase
  11. {
  12. use RefreshDatabase;
  13. protected $seed = true;
  14. private User $adminUser;
  15. private User $managerUser;
  16. protected function setUp(): void
  17. {
  18. parent::setUp();
  19. $this->adminUser = User::factory()->create(['role' => Role::ADMIN]);
  20. $this->managerUser = User::factory()->create(['role' => Role::MANAGER]);
  21. }
  22. // ==================== Authentication ====================
  23. public function test_guest_cannot_access_spare_part_orders_index(): void
  24. {
  25. $response = $this->get(route('spare_part_orders.index'));
  26. $response->assertRedirect(route('login'));
  27. }
  28. public function test_guest_cannot_access_spare_part_order_create(): void
  29. {
  30. $response = $this->get(route('spare_part_orders.create'));
  31. $response->assertRedirect(route('login'));
  32. }
  33. public function test_guest_cannot_store_spare_part_order(): void
  34. {
  35. $response = $this->post(route('spare_part_orders.store'), []);
  36. $response->assertRedirect(route('login'));
  37. }
  38. // ==================== Index ====================
  39. public function test_admin_can_access_spare_part_orders_index(): void
  40. {
  41. $response = $this->actingAs($this->adminUser)
  42. ->get(route('spare_part_orders.index'));
  43. $response->assertStatus(200);
  44. $response->assertViewIs('spare_parts.index');
  45. }
  46. public function test_manager_can_access_spare_part_orders_index(): void
  47. {
  48. $response = $this->actingAs($this->managerUser)
  49. ->get(route('spare_part_orders.index'));
  50. $response->assertStatus(200);
  51. $response->assertViewIs('spare_parts.index');
  52. }
  53. // ==================== Show ====================
  54. public function test_admin_can_view_spare_part_order(): void
  55. {
  56. $sparePartOrder = SparePartOrder::factory()->create();
  57. $response = $this->actingAs($this->adminUser)
  58. ->get(route('spare_part_orders.show', $sparePartOrder));
  59. $response->assertStatus(200);
  60. $response->assertViewIs('spare_part_orders.edit');
  61. }
  62. public function test_manager_can_view_spare_part_order(): void
  63. {
  64. $sparePartOrder = SparePartOrder::factory()->create();
  65. $response = $this->actingAs($this->managerUser)
  66. ->get(route('spare_part_orders.show', $sparePartOrder));
  67. $response->assertStatus(200);
  68. $response->assertViewIs('spare_part_orders.edit');
  69. }
  70. public function test_guest_cannot_view_spare_part_order(): void
  71. {
  72. $sparePartOrder = SparePartOrder::factory()->create();
  73. $response = $this->get(route('spare_part_orders.show', $sparePartOrder));
  74. $response->assertRedirect(route('login'));
  75. }
  76. // ==================== Create ====================
  77. public function test_admin_can_access_create_form(): void
  78. {
  79. $response = $this->actingAs($this->adminUser)
  80. ->get(route('spare_part_orders.create'));
  81. $response->assertStatus(200);
  82. $response->assertViewIs('spare_part_orders.edit');
  83. }
  84. public function test_manager_can_access_create_form(): void
  85. {
  86. $response = $this->actingAs($this->managerUser)
  87. ->get(route('spare_part_orders.create'));
  88. $response->assertStatus(200);
  89. $response->assertViewIs('spare_part_orders.edit');
  90. }
  91. // ==================== Store ====================
  92. public function test_admin_can_create_spare_part_order(): void
  93. {
  94. $sparePart = SparePart::factory()->create();
  95. $response = $this->actingAs($this->adminUser)
  96. ->post(route('spare_part_orders.store'), [
  97. 'spare_part_id' => $sparePart->id,
  98. 'status' => SparePartOrder::STATUS_ORDERED,
  99. 'ordered_quantity' => 10,
  100. 'source_text' => 'Test Supplier',
  101. ]);
  102. $response->assertRedirect();
  103. $response->assertSessionHas('success');
  104. $this->assertDatabaseHas('spare_part_orders', [
  105. 'spare_part_id' => $sparePart->id,
  106. 'status' => SparePartOrder::STATUS_ORDERED,
  107. 'ordered_quantity' => 10,
  108. ]);
  109. }
  110. public function test_manager_can_create_spare_part_order(): void
  111. {
  112. $sparePart = SparePart::factory()->create();
  113. $response = $this->actingAs($this->managerUser)
  114. ->post(route('spare_part_orders.store'), [
  115. 'spare_part_id' => $sparePart->id,
  116. 'status' => SparePartOrder::STATUS_ORDERED,
  117. 'ordered_quantity' => 5,
  118. ]);
  119. $response->assertRedirect();
  120. $response->assertSessionHas('success');
  121. }
  122. public function test_store_validates_required_spare_part_id(): void
  123. {
  124. $response = $this->actingAs($this->adminUser)
  125. ->post(route('spare_part_orders.store'), [
  126. 'status' => SparePartOrder::STATUS_ORDERED,
  127. 'ordered_quantity' => 10,
  128. ]);
  129. $response->assertSessionHasErrors('spare_part_id');
  130. }
  131. public function test_store_validates_required_ordered_quantity(): void
  132. {
  133. $sparePart = SparePart::factory()->create();
  134. $response = $this->actingAs($this->adminUser)
  135. ->post(route('spare_part_orders.store'), [
  136. 'spare_part_id' => $sparePart->id,
  137. 'status' => SparePartOrder::STATUS_ORDERED,
  138. ]);
  139. $response->assertSessionHasErrors('ordered_quantity');
  140. }
  141. // ==================== Update ====================
  142. public function test_admin_can_update_spare_part_order(): void
  143. {
  144. $sparePartOrder = SparePartOrder::factory()->ordered()->create();
  145. $response = $this->actingAs($this->adminUser)
  146. ->put(route('spare_part_orders.update', $sparePartOrder), [
  147. 'spare_part_id' => $sparePartOrder->spare_part_id,
  148. 'status' => SparePartOrder::STATUS_ORDERED,
  149. 'ordered_quantity' => 20,
  150. 'order_number' => 'ORD-2024-001',
  151. ]);
  152. $response->assertRedirect();
  153. $response->assertSessionHas('success');
  154. $this->assertDatabaseHas('spare_part_orders', [
  155. 'id' => $sparePartOrder->id,
  156. 'ordered_quantity' => 20,
  157. 'order_number' => 'ORD-2024-001',
  158. ]);
  159. }
  160. public function test_manager_can_update_spare_part_order(): void
  161. {
  162. $sparePartOrder = SparePartOrder::factory()->ordered()->create();
  163. $response = $this->actingAs($this->managerUser)
  164. ->put(route('spare_part_orders.update', $sparePartOrder), [
  165. 'spare_part_id' => $sparePartOrder->spare_part_id,
  166. 'status' => SparePartOrder::STATUS_ORDERED,
  167. 'ordered_quantity' => 15,
  168. ]);
  169. $response->assertRedirect();
  170. $response->assertSessionHas('success');
  171. }
  172. public function test_guest_cannot_update_spare_part_order(): void
  173. {
  174. $sparePartOrder = SparePartOrder::factory()->create();
  175. $response = $this->put(route('spare_part_orders.update', $sparePartOrder), []);
  176. $response->assertRedirect(route('login'));
  177. }
  178. // ==================== Destroy ====================
  179. public function test_admin_can_delete_spare_part_order_without_active_reservations(): void
  180. {
  181. $sparePartOrder = SparePartOrder::factory()->create();
  182. $response = $this->actingAs($this->adminUser)
  183. ->delete(route('spare_part_orders.destroy', $sparePartOrder));
  184. $response->assertRedirect(route('spare_part_orders.index'));
  185. $response->assertSessionHas('success');
  186. $this->assertSoftDeleted('spare_part_orders', ['id' => $sparePartOrder->id]);
  187. }
  188. public function test_admin_cannot_delete_spare_part_order_with_active_reservations(): void
  189. {
  190. $sparePartOrder = SparePartOrder::factory()->inStock()->create();
  191. Reservation::factory()->create([
  192. 'spare_part_order_id' => $sparePartOrder->id,
  193. 'spare_part_id' => $sparePartOrder->spare_part_id,
  194. 'status' => Reservation::STATUS_ACTIVE,
  195. ]);
  196. $response = $this->actingAs($this->adminUser)
  197. ->delete(route('spare_part_orders.destroy', $sparePartOrder));
  198. $response->assertRedirect(route('spare_part_orders.show', $sparePartOrder));
  199. $response->assertSessionHas('error');
  200. $this->assertDatabaseHas('spare_part_orders', ['id' => $sparePartOrder->id, 'deleted_at' => null]);
  201. }
  202. public function test_manager_cannot_delete_spare_part_order(): void
  203. {
  204. $sparePartOrder = SparePartOrder::factory()->create();
  205. $response = $this->actingAs($this->managerUser)
  206. ->delete(route('spare_part_orders.destroy', $sparePartOrder));
  207. $response->assertStatus(403);
  208. }
  209. public function test_guest_cannot_delete_spare_part_order(): void
  210. {
  211. $sparePartOrder = SparePartOrder::factory()->create();
  212. $response = $this->delete(route('spare_part_orders.destroy', $sparePartOrder));
  213. $response->assertRedirect(route('login'));
  214. }
  215. // ==================== SetInStock ====================
  216. public function test_admin_can_set_order_in_stock(): void
  217. {
  218. $sparePartOrder = SparePartOrder::factory()->ordered()->create();
  219. $response = $this->actingAs($this->adminUser)
  220. ->post(route('spare_part_orders.set_in_stock', $sparePartOrder));
  221. $response->assertRedirect(route('spare_part_orders.show', $sparePartOrder));
  222. $response->assertSessionHas('success');
  223. $this->assertDatabaseHas('spare_part_orders', [
  224. 'id' => $sparePartOrder->id,
  225. 'status' => SparePartOrder::STATUS_IN_STOCK,
  226. ]);
  227. }
  228. public function test_manager_can_set_order_in_stock(): void
  229. {
  230. $sparePartOrder = SparePartOrder::factory()->ordered()->create();
  231. $response = $this->actingAs($this->managerUser)
  232. ->post(route('spare_part_orders.set_in_stock', $sparePartOrder));
  233. $response->assertRedirect(route('spare_part_orders.show', $sparePartOrder));
  234. $response->assertSessionHas('success');
  235. }
  236. public function test_guest_cannot_set_order_in_stock(): void
  237. {
  238. $sparePartOrder = SparePartOrder::factory()->create();
  239. $response = $this->post(route('spare_part_orders.set_in_stock', $sparePartOrder));
  240. $response->assertRedirect(route('login'));
  241. }
  242. // ==================== Ship ====================
  243. public function test_admin_can_ship_spare_part_order(): void
  244. {
  245. $sparePartOrder = SparePartOrder::factory()->inStock()->withQuantity(20)->create();
  246. $response = $this->actingAs($this->adminUser)
  247. ->post(route('spare_part_orders.ship', $sparePartOrder), [
  248. 'quantity' => 5,
  249. 'note' => 'Тестовая отгрузка',
  250. ]);
  251. $response->assertRedirect(route('spare_part_orders.show', $sparePartOrder));
  252. $response->assertSessionHas('success');
  253. $this->assertDatabaseHas('spare_part_orders', [
  254. 'id' => $sparePartOrder->id,
  255. 'available_qty' => 15,
  256. ]);
  257. }
  258. public function test_ship_fails_if_quantity_exceeds_available(): void
  259. {
  260. $sparePartOrder = SparePartOrder::factory()->inStock()->withQuantity(3)->create();
  261. $response = $this->actingAs($this->adminUser)
  262. ->post(route('spare_part_orders.ship', $sparePartOrder), [
  263. 'quantity' => 10,
  264. 'note' => 'Попытка отгрузить больше чем есть',
  265. ]);
  266. $response->assertRedirect(route('spare_part_orders.show', $sparePartOrder));
  267. $response->assertSessionHas('error');
  268. $this->assertDatabaseHas('spare_part_orders', [
  269. 'id' => $sparePartOrder->id,
  270. 'available_qty' => 3,
  271. ]);
  272. }
  273. public function test_ship_validates_required_quantity(): void
  274. {
  275. $sparePartOrder = SparePartOrder::factory()->inStock()->create();
  276. $response = $this->actingAs($this->adminUser)
  277. ->post(route('spare_part_orders.ship', $sparePartOrder), [
  278. 'note' => 'Без количества',
  279. ]);
  280. $response->assertSessionHasErrors('quantity');
  281. }
  282. public function test_manager_can_ship_spare_part_order(): void
  283. {
  284. $sparePartOrder = SparePartOrder::factory()->inStock()->withQuantity(10)->create();
  285. $response = $this->actingAs($this->managerUser)
  286. ->post(route('spare_part_orders.ship', $sparePartOrder), [
  287. 'quantity' => 2,
  288. 'note' => 'Отгрузка менеджером',
  289. ]);
  290. $response->assertRedirect(route('spare_part_orders.show', $sparePartOrder));
  291. $response->assertSessionHas('success');
  292. }
  293. public function test_guest_cannot_ship_spare_part_order(): void
  294. {
  295. $sparePartOrder = SparePartOrder::factory()->create();
  296. $response = $this->post(route('spare_part_orders.ship', $sparePartOrder), [
  297. 'quantity' => 1,
  298. 'note' => 'Test',
  299. ]);
  300. $response->assertRedirect(route('login'));
  301. }
  302. // ==================== Correct (Inventory) ====================
  303. public function test_admin_can_correct_inventory(): void
  304. {
  305. $sparePartOrder = SparePartOrder::factory()->inStock()->withQuantity(10)->create();
  306. $response = $this->actingAs($this->adminUser)
  307. ->post(route('spare_part_orders.correct', $sparePartOrder), [
  308. 'new_quantity' => 8,
  309. 'reason' => 'Инвентаризация — обнаружена недостача',
  310. ]);
  311. $response->assertRedirect(route('spare_part_orders.show', $sparePartOrder));
  312. $response->assertSessionHas('success');
  313. $this->assertDatabaseHas('spare_part_orders', [
  314. 'id' => $sparePartOrder->id,
  315. 'available_qty' => 8,
  316. ]);
  317. }
  318. public function test_correct_fails_if_quantity_unchanged(): void
  319. {
  320. $sparePartOrder = SparePartOrder::factory()->inStock()->withQuantity(10)->create();
  321. $response = $this->actingAs($this->adminUser)
  322. ->post(route('spare_part_orders.correct', $sparePartOrder), [
  323. 'new_quantity' => 10,
  324. 'reason' => 'Без изменений',
  325. ]);
  326. $response->assertRedirect(route('spare_part_orders.show', $sparePartOrder));
  327. $response->assertSessionHas('error');
  328. }
  329. public function test_manager_cannot_correct_inventory(): void
  330. {
  331. $sparePartOrder = SparePartOrder::factory()->inStock()->create();
  332. $response = $this->actingAs($this->managerUser)
  333. ->post(route('spare_part_orders.correct', $sparePartOrder), [
  334. 'new_quantity' => 5,
  335. 'reason' => 'Test',
  336. ]);
  337. $response->assertStatus(403);
  338. }
  339. public function test_guest_cannot_correct_inventory(): void
  340. {
  341. $sparePartOrder = SparePartOrder::factory()->create();
  342. $response = $this->post(route('spare_part_orders.correct', $sparePartOrder), [
  343. 'new_quantity' => 5,
  344. 'reason' => 'Test',
  345. ]);
  346. $response->assertRedirect(route('login'));
  347. }
  348. }