'arlista.xlsx', 'supplier_id' => Supplier::factory()->create()->id, 'available_date' => now()->addWeek(), 'status' => PricelistFileStatusEnum::waiting_for_approval, 'workflow_steps' => [ ['name' => PricelistWorkflowStep::Preprocessing->value, 'label' => 'Előfeldolgozás', 'status' => 'completed'], ['name' => PricelistWorkflowStep::Validation->value, 'label' => 'Validálás', 'status' => 'completed'], ['name' => PricelistWorkflowStep::Approval->value, 'label' => 'Jóváhagyás', 'status' => 'inprogress'], ['name' => PricelistWorkflowStep::Execution->value, 'label' => 'Végrehajtás', 'status' => 'pending'], ], ], $attributes)); } function addLine(PricelistFile $file, PricelistFileLineStatusEnum $status, int $rowNumber = 5): PricelistFileLine { return PricelistFileLine::create([ 'pricelist_file_id' => $file->id, 'row_number' => $rowNumber, 'status' => $status, 'payload' => [], ]); } function developerUser(): User { $user = User::factory()->create(); $user->addRole(Role::create(['name' => 'developer', 'display_name' => 'Developer'])); return $user; } function definePricelistExecutionFlag(array $attributes = []): void { FeatureFlag::create(array_merge([ 'name' => 'PricelistExecution', 'label' => 'Árlista végrehajtás', 'enabled' => true, 'stages' => null, 'roles' => ['developer'], ], $attributes)); app(FeatureFlagRegistrar::class)->registerAll(); } test('a hibátlan, jóváhagyásra váró fájl jóváhagyható', function () { $file = approvableFile(); addLine($file, PricelistFileLineStatusEnum::new_product); addLine($file, PricelistFileLineStatusEnum::updated, 6); expect($file->canBeApproved())->toBeTrue(); }); test('hibás sor esetén nem hagyható jóvá', function () { $file = approvableFile(); addLine($file, PricelistFileLineStatusEnum::ok); addLine($file, PricelistFileLineStatusEnum::error, 6); expect($file->canBeApproved())->toBeFalse(); }); test('nem jóváhagyásra váró státuszban nem hagyható jóvá', function () { $file = approvableFile(['status' => PricelistFileStatusEnum::inprogress]); expect($file->canBeApproved())->toBeFalse(); }); test('befejezetlen validálás esetén nem hagyható jóvá', function () { $file = approvableFile([ 'workflow_steps' => [ ['name' => PricelistWorkflowStep::Validation->value, 'label' => 'Validálás', 'status' => 'failed'], ['name' => PricelistWorkflowStep::Approval->value, 'label' => 'Jóváhagyás', 'status' => 'pending'], ], ]); expect($file->canBeApproved())->toBeFalse(); }); test('a jóváhagyás elindítja a végrehajtást és azonnal átbillenti a státuszt', function () { Queue::fake(); $file = approvableFile(); addLine($file, PricelistFileLineStatusEnum::new_product); expect(app(PricelistFileProcessService::class)->approve($file))->toBeTrue(); Queue::assertPushed(PricelistExecutionJob::class); $file->refresh(); // A státusz még a dispatch előtt átbillen, különben a queue-latency alatt a // Jóváhagyás gomb látszana, és egy második kattintás párhuzamos importot indítana. expect($file->status)->toBe(PricelistFileStatusEnum::inprogress) ->and($file->stepStatus(PricelistWorkflowStep::Approval))->toBe('completed') ->and($file->stepStatus(PricelistWorkflowStep::Execution))->toBe('inprogress') ->and($file->file_meta['approval']['decision'])->toBe('approved'); }); test('a már nem jóváhagyható fájlra a jóváhagyás nem indít végrehajtást', function () { Queue::fake(); $file = approvableFile(); addLine($file, PricelistFileLineStatusEnum::error); expect(app(PricelistFileProcessService::class)->approve($file))->toBeFalse(); Queue::assertNothingPushed(); expect($file->refresh()->status)->toBe(PricelistFileStatusEnum::waiting_for_approval); }); test('az elutasítás lezárja a fájlt indoklással, végrehajtás nélkül', function () { Queue::fake(); $file = approvableFile(); addLine($file, PricelistFileLineStatusEnum::updated); expect(app(PricelistFileProcessService::class)->reject($file, 'Rossz árlistát töltöttek fel.'))->toBeTrue(); Queue::assertNothingPushed(); $file->refresh(); expect($file->status)->toBe(PricelistFileStatusEnum::closed) ->and($file->stepStatus(PricelistWorkflowStep::Approval))->toBe('rejected') ->and($file->file_meta['approval']['reason'])->toBe('Rossz árlistát töltöttek fel.'); }); test('a végrehajtás elindulása után a fájl nem tölthető újra', function () { $file = approvableFile(); expect($file->hasExecutionStarted())->toBeFalse(); app(PricelistFileProcessService::class)->updateStepStatus($file, PricelistWorkflowStep::Execution, 'failed', 'Hiba.'); expect($file->refresh()->hasExecutionStarted())->toBeTrue(); }); test('a döntési gombok csak aktív flaggel látszanak', function () { definePricelistExecutionFlag(); $file = approvableFile(); addLine($file, PricelistFileLineStatusEnum::new_product); $this->actingAs(developerUser()); Livewire::test(ViewPricelistFile::class, ['record' => $file->id]) ->assertActionVisible('approve') ->assertActionVisible('reject'); }); test('a döntési gombok rejtve maradnak a flag nélküli felhasználónak', function () { definePricelistExecutionFlag(); $file = approvableFile(); addLine($file, PricelistFileLineStatusEnum::new_product); $user = User::factory()->create(); $user->addRole(Role::create(['name' => 'admin', 'display_name' => 'Admin'])); $this->actingAs($user); Livewire::test(ViewPricelistFile::class, ['record' => $file->id]) ->assertActionHidden('approve') ->assertActionHidden('reject'); }); test('hibás sor esetén a döntési gombok rejtve maradnak flaggel is', function () { definePricelistExecutionFlag(); $file = approvableFile(); addLine($file, PricelistFileLineStatusEnum::error); $this->actingAs(developerUser()); Livewire::test(ViewPricelistFile::class, ['record' => $file->id]) ->assertActionHidden('approve') ->assertActionHidden('reject'); });