| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- <?php
- declare(strict_types=1);
- namespace Tests\Feature;
- use App\Jobs\GenerateProductionOrderFileJob;
- use App\Models\CommonCatalogItem;
- use App\Models\File;
- use App\Models\ProductionOrder;
- use App\Models\ProductionOrderDelivery;
- use App\Models\ProductionOrderInstallation;
- use App\Models\ProductionOrderItem;
- use App\Models\User;
- use App\Services\ProductionOrderDocumentService;
- use Illuminate\Foundation\Testing\RefreshDatabase;
- use Illuminate\Support\Facades\Bus;
- use Illuminate\Support\Facades\Storage;
- use PhpOffice\PhpSpreadsheet\IOFactory;
- use Tests\TestCase;
- use ZipArchive;
- class ProductionOrderDocumentControllerTest extends TestCase
- {
- use RefreshDatabase;
- protected bool $seed = true;
- private User $admin;
- private User $manager;
- private User $driver;
- private User $brigadier;
- private ProductionOrder $order;
- private ProductionOrderItem $item;
- protected function setUp(): void
- {
- parent::setUp();
- Storage::fake('local');
- Storage::fake('public');
- $this->admin = User::factory()->admin()->create();
- $this->manager = User::factory()->manager()->create(['phone' => '+7 900 123-45-67']);
- $this->driver = User::factory()->driver()->create();
- $this->brigadier = User::factory()->brigadier()->create();
- $this->order = ProductionOrder::factory()->create([
- 'order_number' => 'ДОК-001',
- 'customer_name' => 'ООО Заказчик',
- 'object_address' => 'г. Москва, ул. Доставки, 1',
- 'invoice_number' => 'СЧ-55',
- 'invoice_date' => '2026-09-01',
- 'contract_number' => 'Д-77',
- 'contract_date' => '2026-08-20',
- 'manager_id' => $this->manager->id,
- ]);
- $catalogItem = CommonCatalogItem::factory()->create([
- 'article' => 'ART-100',
- 'calculator_name' => 'Качели',
- ]);
- $this->item = ProductionOrderItem::factory()->create([
- 'production_order_id' => $this->order->id,
- 'common_catalog_item_id' => $catalogItem->id,
- 'order_item_number' => 'МАФ-100',
- 'factory_number' => 'ЗН-100',
- 'manufacture_date' => '2026-09-02',
- ]);
- ProductionOrderItem::factory()->create([
- 'production_order_id' => $this->order->id,
- 'common_catalog_item_id' => $catalogItem->id,
- 'order_item_number' => 'МАФ-101',
- ]);
- }
- public function test_export_items_contains_order_header_and_each_item(): void
- {
- $file = app(ProductionOrderDocumentService::class)->generate(
- ProductionOrderDocumentService::TYPE_ITEMS,
- $this->order->id,
- $this->admin->id,
- );
- Storage::disk('local')->assertExists($file->path);
- $spreadsheet = IOFactory::load(Storage::disk('local')->path($file->path));
- $sheet = $spreadsheet->getActiveSheet();
- $this->assertSame('Оборудование заказа №ДОК-001', $sheet->getCell('A1')->getValue());
- $this->assertStringContainsString('ООО Заказчик', (string) $sheet->getCell('A2')->getValue());
- $this->assertSame('ART-100', $sheet->getCell('A5')->getValue());
- $this->assertSame('МАФ-101', $sheet->getCell('C6')->getValue());
- }
- public function test_delivery_request_fills_provided_template_and_preserves_print_layout(): void
- {
- $delivery = ProductionOrderDelivery::factory()->create([
- 'production_order_id' => $this->order->id,
- 'driver_id' => $this->driver->id,
- 'delivery_date' => '2026-09-15',
- 'request_number' => 'ЗД-15',
- 'request_date' => '2026-09-10',
- 'contact_name' => 'Иван Иванов',
- 'contact_phone' => '+7 999 000-00-00',
- 'has_passports_and_certificates' => true,
- 'note' => 'Позвонить за час',
- ]);
- $file = app(ProductionOrderDocumentService::class)->generate(
- ProductionOrderDocumentService::TYPE_DELIVERY,
- $this->order->id,
- $this->admin->id,
- $delivery->id,
- );
- $spreadsheet = IOFactory::load(Storage::disk('local')->path($file->path));
- $sheet = $spreadsheet->getActiveSheet();
- $this->assertSame('B1:AG94', $sheet->getPageSetup()->getPrintArea());
- $this->assertCount(2, $sheet->getDrawingCollection());
- $this->assertSame('СЧ-55', $sheet->getCell('G4')->getValue());
- $this->assertSame('ДОК-001', $sheet->getCell('G6')->getValue());
- $this->assertSame('Иван Иванов', $sheet->getCell('L13')->getValue());
- $this->assertStringContainsString('ART-100 — Качели — 2 шт.', (string) $sheet->getCell('G25')->getValue());
- }
- public function test_installation_and_technical_archives_include_catalog_documents(): void
- {
- $document = File::factory()->pdf()->create([
- 'user_id' => $this->admin->id,
- 'path' => 'catalog/ART-100/manual.pdf',
- 'original_name' => 'manual.pdf',
- ]);
- Storage::disk('public')->put($document->path, 'pdf-content');
- $this->item->catalogItem->documents()->attach($document->id);
- $installation = ProductionOrderInstallation::factory()->create([
- 'production_order_id' => $this->order->id,
- 'brigadier_id' => $this->brigadier->id,
- 'installation_date' => '2026-09-20',
- ]);
- $technicalFile = app(ProductionOrderDocumentService::class)->generate(
- ProductionOrderDocumentService::TYPE_TECHNICAL_DOCUMENTS,
- $this->order->id,
- $this->admin->id,
- itemIds: [$this->item->id],
- );
- $installationFile = app(ProductionOrderDocumentService::class)->generate(
- ProductionOrderDocumentService::TYPE_INSTALLATION,
- $this->order->id,
- $this->admin->id,
- $installation->id,
- );
- $technicalEntries = $this->zipEntries(Storage::disk('local')->path($technicalFile->path));
- $installationEntries = $this->zipEntries(Storage::disk('local')->path($installationFile->path));
- $this->assertContains('Техдокументация/ART-100/manual.pdf', $technicalEntries);
- $this->assertContains('Техдокументация/ART-100/manual.pdf', $installationEntries);
- $this->assertTrue(collect($installationEntries)->contains(fn (string $name): bool => str_ends_with($name, '.xlsx')));
- }
- public function test_document_actions_are_queued_and_related_records_are_scoped_to_order(): void
- {
- Bus::fake();
- $delivery = ProductionOrderDelivery::factory()->create([
- 'production_order_id' => $this->order->id,
- 'driver_id' => $this->driver->id,
- ]);
- $otherOrder = ProductionOrder::factory()->create(['manager_id' => $this->manager->id]);
- $otherDelivery = ProductionOrderDelivery::factory()->create([
- 'production_order_id' => $otherOrder->id,
- 'driver_id' => $this->driver->id,
- ]);
- $this->actingAs($this->admin)
- ->post(route('schedule.orders.export-items', $this->order))
- ->assertRedirect();
- $this->actingAs($this->admin)
- ->post(route('schedule.orders.deliveries.document', [$this->order, $delivery]))
- ->assertRedirect();
- $this->actingAs($this->admin)
- ->post(route('schedule.orders.technical-documents', $this->order), ['item_ids' => [$this->item->id]])
- ->assertRedirect();
- Bus::assertDispatchedTimes(GenerateProductionOrderFileJob::class, 3);
- $this->actingAs($this->admin)
- ->post(route('schedule.orders.deliveries.document', [$this->order, $otherDelivery]))
- ->assertNotFound();
- $this->actingAs($this->manager)
- ->post(route('schedule.orders.export-items', $this->order))
- ->assertForbidden();
- }
- public function test_generated_file_is_private_to_owner_and_admin(): void
- {
- $otherManager = User::factory()->manager()->create();
- $file = app(ProductionOrderDocumentService::class)->generate(
- ProductionOrderDocumentService::TYPE_ITEMS,
- $this->order->id,
- $this->manager->id,
- );
- $this->actingAs($otherManager)
- ->get(route('schedule.orders.files.download', $file))
- ->assertForbidden();
- $this->actingAs($this->manager)
- ->get(route('schedule.orders.files.download', $file))
- ->assertOk()
- ->assertHeader('content-disposition');
- $this->actingAs($this->admin)
- ->get(route('schedule.orders.files.download', $file))
- ->assertOk()
- ->assertHeader('content-disposition');
- }
- /** @return list<string> */
- private function zipEntries(string $path): array
- {
- $zip = new ZipArchive;
- $this->assertTrue($zip->open($path) === true);
- $entries = [];
- for ($index = 0; $index < $zip->numFiles; $index++) {
- $entries[] = $zip->getNameIndex($index);
- }
- $zip->close();
- return $entries;
- }
- }
|