| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046 |
- <?php
- namespace Tests\Feature;
- use App\Jobs\GenerateInstallationPack;
- use App\Jobs\GenerateTtnPack;
- use App\Models\Dictionary\Area;
- use App\Models\Dictionary\District;
- use App\Models\File;
- use App\Models\MafOrder;
- use App\Models\ObjectType;
- use App\Models\Order;
- use App\Models\OrderStatus;
- use App\Models\Product;
- use App\Models\ProductSKU;
- use App\Models\Role;
- use App\Models\Setting;
- use App\Models\Ttn;
- use App\Models\User;
- use Illuminate\Foundation\Testing\RefreshDatabase;
- use Illuminate\Http\UploadedFile;
- use Illuminate\Support\Facades\Bus;
- use Illuminate\Support\Facades\Storage;
- use Tests\TestCase;
- class OrderControllerTest extends TestCase
- {
- use RefreshDatabase;
- protected $seed = true;
- private User $adminUser;
- private User $managerUser;
- private User $brigadierUser;
- protected function setUp(): void
- {
- parent::setUp();
- $this->adminUser = User::factory()->create(['role' => Role::ADMIN]);
- $this->managerUser = User::factory()->create(['role' => Role::MANAGER]);
- $this->brigadierUser = User::factory()->create(['role' => Role::BRIGADIER]);
- }
- // ==================== Authentication ====================
- public function test_guest_cannot_access_orders_index(): void
- {
- $response = $this->get(route('order.index'));
- $response->assertRedirect(route('login'));
- }
- public function test_authenticated_user_can_access_orders_index(): void
- {
- $response = $this->actingAs($this->managerUser)
- ->get(route('order.index'));
- $response->assertStatus(200);
- $response->assertViewIs('orders.index');
- }
- // ==================== Index ====================
- public function test_orders_index_displays_orders(): void
- {
- $order = Order::factory()->create([
- 'object_address' => 'ул. Проверочная, д. 101',
- ]);
- $response = $this->actingAs($this->managerUser)
- ->get(route('order.index'));
- $response->assertStatus(200);
- $response->assertSee($order->object_address);
- }
- public function test_orders_index_displays_products_total_column_value(): void
- {
- $order = Order::factory()->create([
- 'object_address' => 'ул. Колоночная, д. 10',
- ]);
- $product = Product::factory()->create();
- ProductSKU::factory()->count(3)->create([
- 'order_id' => $order->id,
- 'product_id' => $product->id,
- ]);
- $response = $this->actingAs($this->managerUser)
- ->get(route('order.index'));
- $response->assertOk();
- $response->assertSee('Всего МАФ');
- $response->assertSee('ул. Колоночная, д. 10');
- $response->assertViewHas('orders', static function ($orders) use ($order) {
- return (int) $orders->firstWhere('id', $order->id)?->products_total === 3;
- });
- }
- public function test_orders_index_displays_filtered_footer_stats(): void
- {
- $district = District::factory()->create(['shortname' => 'ЦАО']);
- $area = Area::factory()->create(['district_id' => $district->id, 'name' => 'Тверской']);
- $matchingOrder = Order::factory()->create([
- 'district_id' => $district->id,
- 'area_id' => $area->id,
- 'object_address' => 'ул. Итоговая, д. 1',
- ]);
- $otherOrder = Order::factory()->create([
- 'object_address' => 'ул. Неучтенная, д. 2',
- ]);
- $product = Product::factory()->create();
- ProductSKU::factory()->count(2)->create([
- 'order_id' => $matchingOrder->id,
- 'product_id' => $product->id,
- ]);
- ProductSKU::factory()->count(5)->create([
- 'order_id' => $otherOrder->id,
- 'product_id' => $product->id,
- ]);
- $response = $this->actingAs($this->managerUser)
- ->get(route('order.index', ['s' => 'Итоговая']));
- $response->assertOk();
- $response->assertSee('Адресов: 1', false);
- $response->assertSee('МАФ: 2', false);
- $response->assertDontSee('МАФ: 7', false);
- }
- public function test_brigadier_sees_only_assigned_orders(): void
- {
- $assignedOrder = Order::factory()->create([
- 'brigadier_id' => $this->brigadierUser->id,
- 'object_address' => 'ул. Бригадирская, д. 10',
- ]);
- $otherOrder = Order::factory()->create([
- 'brigadier_id' => User::factory()->create(['role' => Role::BRIGADIER])->id,
- 'object_address' => 'ул. Чужая, д. 20',
- ]);
- $response = $this->actingAs($this->brigadierUser)
- ->get(route('order.index'));
- $response->assertStatus(200);
- $response->assertSee($assignedOrder->object_address);
- $response->assertDontSee($otherOrder->object_address);
- }
- public function test_brigadier_does_not_see_handed_over_orders_in_index(): void
- {
- $visibleOrder = Order::factory()->create([
- 'brigadier_id' => $this->brigadierUser->id,
- 'order_status_id' => Order::STATUS_IN_MOUNT,
- 'object_address' => 'ул. Видимая, д. 11',
- ]);
- $hiddenOrder = Order::factory()->create([
- 'brigadier_id' => $this->brigadierUser->id,
- 'order_status_id' => Order::STATUS_HANDED_OVER,
- 'object_address' => 'ул. Сданная, д. 12',
- ]);
- $response = $this->actingAs($this->brigadierUser)
- ->get(route('order.index'));
- $response->assertStatus(200);
- $response->assertSee($visibleOrder->object_address);
- $response->assertDontSee($hiddenOrder->object_address);
- }
- // ==================== Create ====================
- public function test_can_view_create_order_form(): void
- {
- $response = $this->actingAs($this->adminUser)
- ->get(route('order.create'));
- $response->assertStatus(200);
- $response->assertViewIs('orders.edit');
- }
- // ==================== Store ====================
- public function test_can_create_new_order(): void
- {
- $district = District::factory()->create();
- $area = Area::factory()->create();
- $objectType = ObjectType::factory()->create();
- $response = $this->actingAs($this->managerUser)
- ->post(route('order.store'), [
- 'name' => 'Тестовый заказ',
- 'user_id' => $this->managerUser->id,
- 'district_id' => $district->id,
- 'area_id' => $area->id,
- 'object_address' => 'ул. Тестовая, д. 1',
- 'object_type_id' => $objectType->id,
- 'comment' => 'Тестовый комментарий',
- ]);
- $response->assertRedirect();
- $this->assertDatabaseHas('orders', [
- 'object_address' => 'ул. Тестовая, д. 1',
- 'order_status_id' => Order::STATUS_NEW,
- ]);
- }
- public function test_creating_order_sets_tg_group_name(): void
- {
- $district = District::factory()->create(['shortname' => 'ЦАО']);
- $area = Area::factory()->create(['name' => 'Тверской']);
- $objectType = ObjectType::factory()->create();
- $this->actingAs($this->managerUser)
- ->post(route('order.store'), [
- 'name' => 'Тестовый заказ',
- 'user_id' => $this->managerUser->id,
- 'district_id' => $district->id,
- 'area_id' => $area->id,
- 'object_address' => 'ул. Пушкина',
- 'object_type_id' => $objectType->id,
- ]);
- $order = Order::where('object_address', 'ул. Пушкина')->first();
- $this->assertStringContainsString('ЦАО', $order->tg_group_name);
- $this->assertStringContainsString('Тверской', $order->tg_group_name);
- }
- public function test_can_update_existing_order(): void
- {
- $order = Order::factory()->create([
- 'object_address' => 'Старый адрес',
- ]);
- $response = $this->actingAs($this->managerUser)
- ->post(route('order.store'), [
- 'id' => $order->id,
- 'name' => $order->name,
- 'user_id' => $order->user_id,
- 'district_id' => $order->district_id,
- 'area_id' => $order->area_id,
- 'object_address' => 'Новый адрес',
- 'object_type_id' => $order->object_type_id,
- ]);
- $response->assertRedirect();
- $this->assertDatabaseHas('orders', [
- 'id' => $order->id,
- 'object_address' => 'Новый адрес',
- ]);
- }
- public function test_cannot_set_in_mount_status_when_order_has_unlinked_maf(): void
- {
- $product = Product::factory()->create();
- $order = Order::factory()->readyToMount()->withBrigadier($this->brigadierUser)->create([
- 'installation_date' => '2026-04-25',
- ]);
- ProductSKU::factory()->create([
- 'order_id' => $order->id,
- 'product_id' => $product->id,
- 'maf_order_id' => null,
- ]);
- $response = $this->actingAs($this->adminUser)
- ->post(route('order.update'), [
- 'id' => $order->id,
- 'order_status_id' => Order::STATUS_IN_MOUNT,
- ]);
- $response->assertRedirect();
- $response->assertSessionHas('danger', function ($messages) {
- return in_array('МАФ не привязан к заказу', (array) $messages, true);
- });
- $this->assertDatabaseHas('orders', [
- 'id' => $order->id,
- 'order_status_id' => Order::STATUS_READY_TO_MOUNT,
- ]);
- }
- public function test_ajax_cannot_set_in_mount_status_when_maf_order_number_is_empty(): void
- {
- $product = Product::factory()->create();
- $order = Order::factory()->readyToMount()->withBrigadier($this->brigadierUser)->create([
- 'installation_date' => '2026-04-25',
- ]);
- $mafOrder = MafOrder::factory()->create([
- 'product_id' => $product->id,
- 'order_number' => '',
- ]);
- ProductSKU::factory()->create([
- 'order_id' => $order->id,
- 'product_id' => $product->id,
- 'maf_order_id' => $mafOrder->id,
- ]);
- $response = $this->actingAs($this->adminUser)
- ->withHeader('X-Requested-With', 'XMLHttpRequest')
- ->post(route('order.update'), [
- 'id' => $order->id,
- 'order_status_id' => Order::STATUS_IN_MOUNT,
- ]);
- $response->assertStatus(422);
- $response->assertJson([
- 'message' => 'МАФ не привязан к заказу',
- ]);
- $this->assertDatabaseHas('orders', [
- 'id' => $order->id,
- 'order_status_id' => Order::STATUS_READY_TO_MOUNT,
- ]);
- }
- public function test_can_set_in_mount_status_when_all_mafs_are_linked_to_order_numbers(): void
- {
- $product = Product::factory()->create();
- $order = Order::factory()->readyToMount()->withBrigadier($this->brigadierUser)->create([
- 'installation_date' => '2026-04-25',
- ]);
- $mafOrder = MafOrder::factory()->create([
- 'product_id' => $product->id,
- 'order_number' => 'MO-7788',
- ]);
- ProductSKU::factory()->create([
- 'order_id' => $order->id,
- 'product_id' => $product->id,
- 'maf_order_id' => $mafOrder->id,
- ]);
- $response = $this->actingAs($this->adminUser)
- ->post(route('order.update'), [
- 'id' => $order->id,
- 'order_status_id' => Order::STATUS_IN_MOUNT,
- ]);
- $response->assertRedirect();
- $this->assertDatabaseHas('orders', [
- 'id' => $order->id,
- 'order_status_id' => Order::STATUS_IN_MOUNT,
- ]);
- }
- public function test_manager_can_update_ready_date(): void
- {
- $order = Order::factory()->create([
- 'ready_date' => '2026-04-01',
- ]);
- $response = $this->actingAs($this->managerUser)
- ->post(route('order.store'), [
- 'id' => $order->id,
- 'ready_date' => '2026-04-25',
- ]);
- $response->assertRedirect();
- $this->assertDatabaseHas('orders', [
- 'id' => $order->id,
- 'ready_date' => '2026-04-25',
- ]);
- }
- // ==================== Show ====================
- public function test_can_view_order_details(): void
- {
- $order = Order::factory()->create([
- 'object_address' => 'ул. Детальная, д. 5',
- ]);
- $response = $this->actingAs($this->managerUser)
- ->get(route('order.show', $order));
- $response->assertStatus(200);
- $response->assertViewIs('orders.show');
- $response->assertSee($order->object_address);
- }
- public function test_order_details_show_area_and_district(): void
- {
- $district = District::factory()->create(['name' => 'Центральный округ']);
- $area = Area::factory()->create([
- 'district_id' => $district->id,
- 'name' => 'Тверской район',
- ]);
- $order = Order::factory()->create([
- 'district_id' => $district->id,
- 'area_id' => $area->id,
- ]);
- $response = $this->actingAs($this->managerUser)
- ->get(route('order.show', $order));
- $response->assertOk();
- $response->assertSee('Центральный округ');
- $response->assertSee('Тверской район');
- }
- public function test_brigadier_cannot_view_handed_over_order_details(): void
- {
- $order = Order::factory()->create([
- 'brigadier_id' => $this->brigadierUser->id,
- 'order_status_id' => Order::STATUS_HANDED_OVER,
- ]);
- $response = $this->actingAs($this->brigadierUser)
- ->get(route('order.show', $order));
- $response->assertStatus(403);
- }
- // ==================== Edit ====================
- public function test_can_view_edit_order_form(): void
- {
- $order = Order::factory()->create();
- $response = $this->actingAs($this->managerUser)
- ->get(route('order.edit', $order));
- $response->assertStatus(200);
- $response->assertViewIs('orders.edit');
- }
- public function test_manager_can_edit_ready_date_in_order_edit_form(): void
- {
- $order = Order::factory()->create();
- $response = $this->actingAs($this->managerUser)
- ->get(route('order.edit', $order));
- $response->assertOk();
- $this->assertMatchesRegularExpression(
- '/<input[^>]*name="ready_date"(?:(?!disabled).)*>/s',
- $response->getContent()
- );
- }
- // ==================== Destroy ====================
- public function test_can_delete_order(): void
- {
- $order = Order::factory()->create();
- $orderId = $order->id;
- $response = $this->actingAs($this->adminUser)
- ->delete(route('order.destroy', $order));
- $response->assertRedirect(route('order.index'));
- // Order uses SoftDeletes, so check deleted_at is set
- $this->assertSoftDeleted('orders', ['id' => $orderId]);
- }
- public function test_deleting_order_removes_related_product_skus(): void
- {
- $order = Order::factory()->create();
- $product = Product::factory()->create();
- $sku = ProductSKU::factory()->create([
- 'order_id' => $order->id,
- 'product_id' => $product->id,
- ]);
- $this->actingAs($this->adminUser)
- ->delete(route('order.destroy', $order));
- // ProductSKU uses SoftDeletes, so check deleted_at is set
- $this->assertSoftDeleted('products_sku', ['id' => $sku->id]);
- }
- // ==================== Search ====================
- public function test_search_returns_matching_orders(): void
- {
- $order = Order::factory()->create([
- 'object_address' => 'ул. Уникальная Тестовая, д. 999',
- ]);
- $otherOrder = Order::factory()->create([
- 'object_address' => 'ул. Другая, д. 1',
- ]);
- $response = $this->actingAs($this->managerUser)
- ->get(route('order.index', ['s' => 'Уникальная Тестовая']));
- $response->assertStatus(200);
- $response->assertSee($order->object_address);
- $response->assertDontSee($otherOrder->object_address);
- }
- public function test_order_search_route_can_search_by_manager_name(): void
- {
- $manager = User::factory()->create([
- 'role' => Role::MANAGER,
- 'name' => 'Менеджер Поиска',
- ]);
- $matchedOrder = Order::factory()->create([
- 'user_id' => $manager->id,
- 'object_address' => 'ул. Найденная, д. 7',
- ]);
- $otherOrder = Order::factory()->create([
- 'object_address' => 'ул. Не должна попасть, д. 8',
- ]);
- $response = $this->actingAs($this->adminUser)
- ->getJson(route('order.search', ['s' => 'Менеджер Поиска']));
- $response->assertOk();
- $response->assertJsonPath((string) $matchedOrder->id, $matchedOrder->move_maf_name);
- $response->assertJsonMissing([$otherOrder->id => $otherOrder->move_maf_name]);
- }
- public function test_order_search_route_includes_site_name_and_excludes_current_order(): void
- {
- $district = District::factory()->create(['name' => 'Северный округ']);
- $area = Area::factory()->create([
- 'district_id' => $district->id,
- 'name' => 'Левобережный',
- ]);
- $currentOrder = Order::factory()->create([
- 'district_id' => $district->id,
- 'area_id' => $area->id,
- 'name' => 'Текущая площадка',
- 'object_address' => 'ул. Проверочная, д. 1',
- ]);
- $targetOrder = Order::factory()->create([
- 'district_id' => $district->id,
- 'area_id' => $area->id,
- 'name' => 'Площадка назначения',
- 'object_address' => 'ул. Проверочная, д. 2',
- ]);
- $response = $this->actingAs($this->adminUser)
- ->getJson(route('order.search', [
- 's' => 'Проверочная',
- 'current_order_id' => $currentOrder->id,
- ]));
- $response->assertOk();
- $response->assertJsonPath((string) $targetOrder->id, $targetOrder->move_maf_name);
- $response->assertJsonMissing([(string) $currentOrder->id => $currentOrder->move_maf_name]);
- }
- // ==================== MAF Operations ====================
- public function test_get_maf_to_order_assigns_available_maf(): void
- {
- $product = Product::factory()->create();
- $order = Order::factory()->create();
- $productSku = ProductSKU::factory()->create([
- 'order_id' => $order->id,
- 'product_id' => $product->id,
- 'maf_order_id' => null,
- 'status' => 'требуется',
- ]);
- $mafOrder = MafOrder::factory()->create([
- 'product_id' => $product->id,
- 'in_stock' => 5,
- ]);
- // This route requires admin role
- $response = $this->actingAs($this->adminUser)
- ->get(route('order.get-maf', $order));
- $response->assertRedirect(route('order.show', $order));
- $response->assertSessionHas('success', function ($messages) {
- return str_contains(implode(' ', (array) $messages), 'Привязано МАФ: 1.');
- });
- $productSku->refresh();
- $this->assertEquals($mafOrder->id, $productSku->maf_order_id);
- $this->assertEquals('отгружен', $productSku->status);
- $mafOrder->refresh();
- $this->assertEquals(4, $mafOrder->in_stock);
- }
- public function test_get_maf_to_order_shows_error_when_not_enough_stock(): void
- {
- $product = Product::factory()->create();
- $order = Order::factory()->create();
- $productSku = ProductSKU::factory()->create([
- 'order_id' => $order->id,
- 'product_id' => $product->id,
- 'maf_order_id' => null,
- 'status' => 'требуется',
- ]);
- $response = $this->actingAs($this->adminUser)
- ->get(route('order.get-maf', $order));
- $response->assertRedirect(route('order.show', $order));
- $response->assertSessionHas('danger', function ($messages) {
- return str_contains(implode(' ', (array) $messages), 'Не удалось привязать 1 шт. МАФ');
- });
- $productSku->refresh();
- $this->assertNull($productSku->maf_order_id);
- $this->assertEquals('требуется', $productSku->status);
- }
- public function test_revert_maf_returns_maf_to_stock(): void
- {
- $product = Product::factory()->create();
- $order = Order::factory()->create();
- $mafOrder = MafOrder::factory()->create([
- 'product_id' => $product->id,
- 'in_stock' => 3,
- ]);
- $productSku = ProductSKU::factory()->create([
- 'order_id' => $order->id,
- 'product_id' => $product->id,
- 'maf_order_id' => $mafOrder->id,
- 'status' => 'отгружен',
- ]);
- // This route requires admin role
- $response = $this->actingAs($this->adminUser)
- ->get(route('order.revert-maf', $order));
- $response->assertRedirect(route('order.show', $order));
- $response->assertSessionHas('success', function ($messages) {
- return str_contains(implode(' ', (array) $messages), 'Отвязано МАФ: 1.');
- });
- $productSku->refresh();
- $this->assertNull($productSku->maf_order_id);
- $this->assertEquals('требуется', $productSku->status);
- $mafOrder->refresh();
- $this->assertEquals(4, $mafOrder->in_stock);
- }
- public function test_revert_maf_shows_error_when_nothing_is_attached(): void
- {
- $order = Order::factory()->create();
- ProductSKU::factory()->create([
- 'order_id' => $order->id,
- 'maf_order_id' => null,
- 'status' => 'требуется',
- ]);
- $response = $this->actingAs($this->adminUser)
- ->get(route('order.revert-maf', $order));
- $response->assertRedirect(route('order.show', $order));
- $response->assertSessionHas('danger', function ($messages) {
- return str_contains(implode(' ', (array) $messages), 'Нет МАФ для отвязки');
- });
- }
- public function test_store_order_cannot_remove_attached_maf_from_order_list(): void
- {
- $attachedProduct = Product::factory()->create();
- $otherProduct = Product::factory()->create();
- $order = Order::factory()->create([
- 'order_status_id' => Order::STATUS_NEW,
- ]);
- $mafOrder = MafOrder::factory()->create([
- 'product_id' => $attachedProduct->id,
- 'in_stock' => 1,
- ]);
- $attachedSku = ProductSKU::factory()->create([
- 'order_id' => $order->id,
- 'product_id' => $attachedProduct->id,
- 'maf_order_id' => $mafOrder->id,
- 'status' => 'отгружен',
- ]);
- ProductSKU::factory()->create([
- 'order_id' => $order->id,
- 'product_id' => $otherProduct->id,
- 'maf_order_id' => null,
- 'status' => 'требуется',
- ]);
- $response = $this->actingAs($this->adminUser)
- ->post(route('order.store'), [
- 'id' => $order->id,
- 'products' => [$otherProduct->id],
- 'quantity' => [1],
- ]);
- $response->assertRedirect();
- $response->assertSessionHas('danger', function ($messages) {
- return str_contains(implode(' ', (array) $messages), 'Нельзя удалить привязанные МАФ');
- });
- $this->assertDatabaseHas('products_sku', [
- 'id' => $attachedSku->id,
- 'maf_order_id' => $mafOrder->id,
- 'deleted_at' => null,
- ]);
- }
- public function test_store_order_cannot_add_new_positions_when_has_attached_maf(): void
- {
- $attachedProduct = Product::factory()->create();
- $existingProduct = Product::factory()->create();
- $newProduct = Product::factory()->create();
- $order = Order::factory()->create([
- 'order_status_id' => Order::STATUS_NEW,
- ]);
- $mafOrder = MafOrder::factory()->create([
- 'product_id' => $attachedProduct->id,
- 'in_stock' => 1,
- ]);
- ProductSKU::factory()->create([
- 'order_id' => $order->id,
- 'product_id' => $attachedProduct->id,
- 'maf_order_id' => $mafOrder->id,
- 'status' => 'отгружен',
- ]);
- ProductSKU::factory()->create([
- 'order_id' => $order->id,
- 'product_id' => $existingProduct->id,
- 'maf_order_id' => null,
- 'status' => 'требуется',
- ]);
- $response = $this->actingAs($this->adminUser)
- ->post(route('order.store'), [
- 'id' => $order->id,
- 'products' => [$attachedProduct->id, $existingProduct->id, $newProduct->id],
- 'quantity' => [1, 1, 1],
- ]);
- $response->assertRedirect();
- $response->assertSessionHas('danger', function ($messages) {
- return str_contains(implode(' ', (array) $messages), 'Нельзя добавлять новые позиции МАФ');
- });
- $this->assertDatabaseMissing('products_sku', [
- 'order_id' => $order->id,
- 'product_id' => $newProduct->id,
- 'deleted_at' => null,
- ]);
- }
- public function test_move_maf_transfers_sku_to_another_order(): void
- {
- $product = Product::factory()->create();
- $order1 = Order::factory()->create();
- $order2 = Order::factory()->create();
- $productSku = ProductSKU::factory()->create([
- 'order_id' => $order1->id,
- 'product_id' => $product->id,
- ]);
- // This route requires admin role
- $response = $this->actingAs($this->adminUser)
- ->post(route('order.move-maf'), [
- 'new_order_id' => $order2->id,
- 'ids' => json_encode([$productSku->id]),
- ]);
- $response->assertStatus(200);
- $response->assertJson(['success' => true]);
- $productSku->refresh();
- $this->assertEquals($order2->id, $productSku->order_id);
- }
- // ==================== Photo Management ====================
- public function test_can_upload_photo_to_order(): void
- {
- Storage::fake('public');
- $order = Order::factory()->create();
- // Use create() instead of image() to avoid GD extension requirement
- $photo = UploadedFile::fake()->create('photo.jpg', 100, 'image/jpeg');
- $response = $this->actingAs($this->managerUser)
- ->post(route('order.upload-photo', $order), [
- 'photo' => [$photo],
- ]);
- $response->assertRedirect(route('order.show', $order));
- $this->assertCount(1, $order->fresh()->photos);
- }
- public function test_can_delete_photo_from_order(): void
- {
- Storage::fake('public');
- $order = Order::factory()->create();
- $file = File::factory()->create();
- $order->photos()->attach($file);
- // This route requires admin role
- $response = $this->actingAs($this->adminUser)
- ->delete(route('order.delete-photo', [$order, $file]));
- $response->assertRedirect(route('order.show', $order));
- $this->assertCount(0, $order->fresh()->photos);
- }
- public function test_can_delete_all_photos_from_order(): void
- {
- Storage::fake('public');
- $order = Order::factory()->create();
- $files = File::factory()->count(3)->create();
- $order->photos()->attach($files->pluck('id'));
- // This route requires admin role
- $response = $this->actingAs($this->adminUser)
- ->delete(route('order.delete-all-photos', $order));
- $response->assertRedirect(route('order.show', $order));
- $this->assertCount(0, $order->fresh()->photos);
- }
- // ==================== Document Management ====================
- public function test_can_upload_document_to_order(): void
- {
- Storage::fake('public');
- $order = Order::factory()->create();
- $document = UploadedFile::fake()->create('document.pdf', 100);
- $response = $this->actingAs($this->managerUser)
- ->post(route('order.upload-document', $order), [
- 'document' => [$document],
- ]);
- $response->assertRedirect(route('order.show', $order));
- $this->assertCount(1, $order->fresh()->documents);
- }
- public function test_upload_document_preserves_unicode_and_quotes_filename(): void
- {
- Storage::fake('public');
- $order = Order::factory()->create();
- $filename = "Документ «смета» 'финал' \"v2\".pdf";
- $document = UploadedFile::fake()->create($filename, 100, 'application/pdf');
- $response = $this->actingAs($this->managerUser)
- ->post(route('order.upload-document', $order), [
- 'document' => [$document],
- ]);
- $response->assertRedirect(route('order.show', $order));
- $saved = $order->fresh()->documents->first();
- $this->assertNotNull($saved);
- $this->assertEquals($filename, $saved->original_name);
- $this->assertEquals('orders/' . $order->id . '/document/' . $filename, $saved->path);
- Storage::disk('public')->assertExists($saved->path);
- }
- public function test_upload_statement_preserves_unicode_and_quotes_filename(): void
- {
- Storage::fake('public');
- $order = Order::factory()->create();
- $filename = "Акт «приемки» 'этап 1' \"финал\".pdf";
- $statement = UploadedFile::fake()->create($filename, 100, 'application/pdf');
- $response = $this->actingAs($this->managerUser)
- ->post(route('order.upload-statement', $order), [
- 'statement' => [$statement],
- ]);
- $response->assertRedirect(route('order.show', $order));
- $saved = $order->fresh()->statements->first();
- $this->assertNotNull($saved);
- $this->assertEquals($filename, $saved->original_name);
- $this->assertEquals('orders/' . $order->id . '/statement/' . $filename, $saved->path);
- Storage::disk('public')->assertExists($saved->path);
- }
- public function test_upload_document_limits_to_5_files(): void
- {
- Storage::fake('public');
- $order = Order::factory()->create();
- $documents = [];
- for ($i = 0; $i < 7; $i++) {
- $documents[] = UploadedFile::fake()->create("document{$i}.pdf", 100);
- }
- $this->actingAs($this->managerUser)
- ->post(route('order.upload-document', $order), [
- 'document' => $documents,
- ]);
- $this->assertCount(5, $order->fresh()->documents);
- }
- public function test_can_upload_webp_photo(): void
- {
- Storage::fake('public');
- $order = Order::factory()->create();
- $photo = UploadedFile::fake()->create('photo.webp', 100, 'image/webp');
- $response = $this->actingAs($this->managerUser)
- ->post(route('order.upload-photo', $order), [
- 'photo' => [$photo],
- ]);
- $response->assertRedirect();
- $saved = $order->fresh()->photos->first();
- $this->assertNotNull($saved);
- $this->assertSame('photo.webp', $saved->original_name);
- }
- // ==================== Generation ====================
- public function test_generate_installation_pack_is_allowed_for_any_status_when_data_is_valid(): void
- {
- Bus::fake();
- $product = Product::factory()->create();
- $mafOrder = MafOrder::factory()->create(['product_id' => $product->id]);
- $order = Order::factory()->create([
- 'order_status_id' => Order::STATUS_NEW,
- ]);
- ProductSKU::factory()->create([
- 'order_id' => $order->id,
- 'product_id' => $product->id,
- 'maf_order_id' => $mafOrder->id,
- ]);
- $response = $this->actingAs($this->managerUser)
- ->get(route('order.generate-installation-pack', $order));
- $response->assertRedirect(route('order.show', $order));
- $response->assertSessionHas('success');
- Bus::assertDispatched(GenerateInstallationPack::class);
- }
- public function test_generate_installation_pack_still_requires_connected_maf(): void
- {
- Bus::fake();
- $product = Product::factory()->create();
- $order = Order::factory()->create([
- 'order_status_id' => Order::STATUS_IN_MOUNT,
- ]);
- ProductSKU::factory()->create([
- 'order_id' => $order->id,
- 'product_id' => $product->id,
- 'maf_order_id' => null,
- ]);
- $response = $this->actingAs($this->managerUser)
- ->get(route('order.generate-installation-pack', $order));
- $response->assertRedirect(route('order.show', $order));
- $response->assertSessionHas('danger');
- Bus::assertNotDispatched(GenerateInstallationPack::class);
- }
- public function test_admin_can_create_ttn_with_departure_date_and_increment_counter(): void
- {
- Bus::fake();
- Setting::set(Setting::KEY_TTN_NEXT_NUMBER, 10);
- $product = Product::factory()->create();
- $order = Order::factory()->create([
- 'installation_date' => '2026-04-15',
- ]);
- $sku = ProductSKU::factory()->create([
- 'order_id' => $order->id,
- 'product_id' => $product->id,
- ]);
- $response = $this->actingAs($this->adminUser)
- ->post(route('order.create-ttn'), [
- 'order_number' => 'З-100',
- 'order_date' => '2026-04-07',
- 'departure_date' => '2026-04-08',
- 'order_sum' => '150000',
- 'skus' => [$sku->id],
- ]);
- $response->assertRedirect();
- $response->assertSessionHas('success');
- $this->assertDatabaseHas('ttns', [
- 'ttn_number' => 10,
- 'order_number' => 'З-100',
- 'order_date' => '2026-04-07',
- 'departure_date' => '2026-04-08',
- 'order_sum' => '150000',
- ]);
- $this->assertSame(11, Setting::getInt(Setting::KEY_TTN_NEXT_NUMBER));
- $ttn = Ttn::query()->where('ttn_number', 10)->firstOrFail();
- $this->assertSame([(string) $sku->id], array_map('strval', json_decode($ttn->skus, true)));
- Bus::assertDispatched(GenerateTtnPack::class);
- }
- // ==================== Export ====================
- public function test_can_export_orders(): void
- {
- Order::factory()->count(3)->create();
- // This route requires admin role
- $response = $this->actingAs($this->adminUser)
- ->post(route('order.export'));
- $response->assertRedirect(route('order.index'));
- $response->assertSessionHas('success');
- }
- public function test_can_export_single_order(): void
- {
- $order = Order::factory()->create();
- // This route requires admin role
- $response = $this->actingAs($this->adminUser)
- ->post(route('order.export-one', $order));
- $response->assertRedirect(route('order.show', $order));
- $response->assertSessionHas('success');
- }
- }
|