ADD EV3-357 Árlista feldolgozás phase5/2 jóváhagyás/elutasítás akció
- PricelistFile::canBeApproved() / hasExecutionStarted(): a jóváhagyás feltételei egy helyen, hogy a felületi gomb és a service ugyanazt ellenőrizze - approve(): a státusz MÉG A DISPATCH ELŐTT billen át, különben a queue-latency alatt a gomb látszana és egy második kattintás párhuzamos importot indítana - reject(): új 'rejected' lépésstátusz - nem 'failed', mert azt az updateStepStatus fail fájlstátuszra fordítaná, az elutasítás viszont closed - a döntés (ki, mikor, milyen indokkal) a file_meta.approval-ba kerül, mert a BaseAuditable a nevével ellentétben nem naplóz - Edit gomb szűkítése hasExecutionStarted()-tel három helyen, köztük az EditPricelistFile::afterSave()-ben, ami a teljes láncot újraindítja Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
5bf2f83092
commit
eee27aa659
@ -58,6 +58,7 @@ public function color(string $status): string
|
||||
'inprogress' => 'primary',
|
||||
'completed' => 'success',
|
||||
'failed' => 'danger',
|
||||
'rejected' => 'warning', // manuális elutasítás - nem hiba, hanem szabályos lezárás
|
||||
default => 'gray',
|
||||
};
|
||||
}
|
||||
|
||||
@ -27,7 +27,10 @@ protected function getHeaderActions(): array
|
||||
|
||||
protected function afterSave(): void
|
||||
{
|
||||
if ($this->record->status === PricelistFileStatusEnum::fail) {
|
||||
// A hasExecutionStarted() védelem itt a lényegi: ez a metódus indítja újra a
|
||||
// TELJES feldolgozási láncot. Ha a végrehajtás egyszer már futott, egy
|
||||
// nulláról induló újrafuttatás duplikált termékeket és árakat hozna létre.
|
||||
if ($this->record->status === PricelistFileStatusEnum::fail && ! $this->record->hasExecutionStarted()) {
|
||||
app(PricelistFileProcessService::class)->dispatchInitialChain($this->record);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,10 +2,17 @@
|
||||
|
||||
namespace App\Filament\Resources\PricelistFiles\Pages;
|
||||
|
||||
use App\Filament\Resources\PricelistFiles\PricelistFileResource;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
use App\Enums\PricelistFileLineStatusEnum;
|
||||
use App\Enums\PricelistFileStatusEnum;
|
||||
use App\Filament\Resources\PricelistFiles\PricelistFileResource;
|
||||
use App\Models\PricelistFile;
|
||||
use App\Services\PricelistFileProcessService;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
use Laravel\Pennant\Feature;
|
||||
|
||||
class ViewPricelistFile extends ViewRecord
|
||||
{
|
||||
@ -14,11 +21,128 @@ class ViewPricelistFile extends ViewRecord
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
$this->approveAction(),
|
||||
$this->rejectAction(),
|
||||
EditAction::make()
|
||||
->visible(fn ($record) => $record->status === PricelistFileStatusEnum::fail),
|
||||
// A `fail` a validálásig tartó szakasz hibája, onnan a fájl újratöltése
|
||||
// biztonságosan újraindítja a láncot. A hasExecutionStarted() extra
|
||||
// feltétel a maradék kockázatot zárja ki: ha a végrehajtás egyszer már
|
||||
// elindult, a nulláról induló újrafuttatás duplikált termékeket és
|
||||
// árakat hozna létre.
|
||||
->visible(fn (PricelistFile $record) => $record->status === PricelistFileStatusEnum::fail
|
||||
&& ! $record->hasExecutionStarted()),
|
||||
];
|
||||
}
|
||||
|
||||
protected function approveAction(): Action
|
||||
{
|
||||
return Action::make('approve')
|
||||
->label('Jóváhagyás')
|
||||
->icon('heroicon-o-check-circle')
|
||||
->color('success')
|
||||
->visible(fn (PricelistFile $record) => $this->canDecide($record))
|
||||
->requiresConfirmation()
|
||||
->modalHeading('Árlista jóváhagyása')
|
||||
->modalDescription(fn (PricelistFile $record) => 'A jóváhagyás elindítja a tényleges importot: '
|
||||
. $this->summarizeLines($record)
|
||||
. ' A művelet a termékadatokat is módosítja.')
|
||||
->modalSubmitActionLabel('Jóváhagyás és végrehajtás')
|
||||
->action(function (PricelistFile $record) {
|
||||
if (! app(PricelistFileProcessService::class)->approve($record)) {
|
||||
$this->notifyStateChanged();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Notification::make()
|
||||
->title('Végrehajtás elindítva')
|
||||
->body('Az árlista importja elindult, a haladás ezen az oldalon követhető.')
|
||||
->success()
|
||||
->send();
|
||||
});
|
||||
}
|
||||
|
||||
protected function rejectAction(): Action
|
||||
{
|
||||
return Action::make('reject')
|
||||
->label('Elutasítás')
|
||||
->icon('heroicon-o-x-circle')
|
||||
->color('danger')
|
||||
->visible(fn (PricelistFile $record) => $this->canDecide($record))
|
||||
->requiresConfirmation()
|
||||
->modalHeading('Árlista elutasítása')
|
||||
->modalDescription('A fájl lezárva státuszba kerül, import nem indul. A művelet nem vonható vissza.')
|
||||
->modalSubmitActionLabel('Elutasítás')
|
||||
->schema([
|
||||
Textarea::make('reason')
|
||||
->label('Elutasítás indoka')
|
||||
->rows(3)
|
||||
->maxLength(1000),
|
||||
])
|
||||
->action(function (PricelistFile $record, array $data) {
|
||||
if (! app(PricelistFileProcessService::class)->reject($record, $data['reason'] ?? null)) {
|
||||
$this->notifyStateChanged();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Notification::make()
|
||||
->title('Árlista elutasítva')
|
||||
->body('A fájl lezárva státuszba került.')
|
||||
->success()
|
||||
->send();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A döntési gombok közös feltétele: a rollout flag és a fájl jóváhagyhatósága.
|
||||
* A canBeApproved() a modellben él, mert ugyanezt a service is ellenőrzi a
|
||||
* művelet végrehajtásakor - a felület csak elrejti a gombot, a védelmet a
|
||||
* service adja.
|
||||
*/
|
||||
protected function canDecide(PricelistFile $record): bool
|
||||
{
|
||||
return Feature::for(auth()->user())->active('PricelistExecution')
|
||||
&& $record->canBeApproved();
|
||||
}
|
||||
|
||||
/**
|
||||
* A jóváhagyás előtti összegzés: mit fog csinálni az import.
|
||||
*/
|
||||
protected function summarizeLines(PricelistFile $record): string
|
||||
{
|
||||
$counts = $record->lineStatusCounts();
|
||||
|
||||
$parts = [];
|
||||
foreach ([
|
||||
PricelistFileLineStatusEnum::new_product,
|
||||
PricelistFileLineStatusEnum::updated,
|
||||
PricelistFileLineStatusEnum::ok,
|
||||
PricelistFileLineStatusEnum::warning,
|
||||
] as $status) {
|
||||
$count = (int) ($counts[$status->value] ?? 0);
|
||||
if ($count > 0) {
|
||||
$parts[] = $count . ' ' . $status->label();
|
||||
}
|
||||
}
|
||||
|
||||
return $parts === [] ? 'nincs feldolgozandó sor.' : implode(', ', $parts) . '.';
|
||||
}
|
||||
|
||||
/**
|
||||
* A felület 5 mp-enként pollozik, így a gomb megjelenítése és a kattintás között
|
||||
* változhat az állapot (pl. más felhasználó közben jóváhagyta). Ilyenkor a service
|
||||
* elutasítja a műveletet, a felhasználónak pedig ezt meg kell mondani.
|
||||
*/
|
||||
protected function notifyStateChanged(): void
|
||||
{
|
||||
Notification::make()
|
||||
->title('A művelet nem hajtható végre')
|
||||
->body('A fájl állapota időközben megváltozott. Frissítsd az oldalt a jelenlegi állapotért.')
|
||||
->warning()
|
||||
->send();
|
||||
}
|
||||
|
||||
protected function getHeaderWidgets(): array
|
||||
{
|
||||
return [
|
||||
|
||||
@ -52,7 +52,10 @@ public static function configure(Table $table): Table
|
||||
->recordActions([
|
||||
ViewAction::make(),
|
||||
EditAction::make()
|
||||
->visible(fn ($record) => $record->status === PricelistFileStatusEnum::fail),
|
||||
// Ha a végrehajtás már elindult, az újratöltés (és a vele járó
|
||||
// teljes lánc-újraindítás) duplikált termékeket/árakat okozna.
|
||||
->visible(fn ($record) => $record->status === PricelistFileStatusEnum::fail
|
||||
&& ! $record->hasExecutionStarted()),
|
||||
])
|
||||
->toolbarActions([
|
||||
BulkActionGroup::make([
|
||||
|
||||
@ -2,7 +2,9 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\PricelistFileLineStatusEnum;
|
||||
use App\Enums\PricelistFileStatusEnum;
|
||||
use App\Enums\PricelistWorkflowStep;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
@ -42,4 +44,67 @@ public function lines(): HasMany
|
||||
{
|
||||
return $this->hasMany(PricelistFileLine::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Egy workflow lépés aktuális státusza a `workflow_steps` tömbből
|
||||
* ('pending' / 'inprogress' / 'completed' / 'failed'), vagy null, ha a lépés
|
||||
* nem szerepel a rekordban (régi fájloknál előfordulhat).
|
||||
*/
|
||||
public function stepStatus(PricelistWorkflowStep $step): ?string
|
||||
{
|
||||
foreach ($this->workflow_steps ?? [] as $workflowStep) {
|
||||
if (($workflowStep['name'] ?? null) === $step->value) {
|
||||
return $workflowStep['status'] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Van-e a fájlban blokkoló (hibás) sor. Ez a jóváhagyás egyik feltétele:
|
||||
* hibás sorral nem indítható végrehajtás.
|
||||
*/
|
||||
public function hasErrorLines(): bool
|
||||
{
|
||||
return $this->lines()
|
||||
->where('status', PricelistFileLineStatusEnum::error->value)
|
||||
->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Jóváhagyható-e a fájl. Egyetlen igazságforrás: ezt nézi a felületi gomb
|
||||
* láthatósága ÉS a végrehajtási job is induláskor - a felület 5 mp-enként
|
||||
* pollozik, így a gomb megjelenítése és a kattintás között változhat az állapot.
|
||||
*/
|
||||
public function canBeApproved(): bool
|
||||
{
|
||||
return $this->status === PricelistFileStatusEnum::waiting_for_approval
|
||||
&& $this->stepStatus(PricelistWorkflowStep::Validation) === 'completed'
|
||||
&& ! $this->hasErrorLines();
|
||||
}
|
||||
|
||||
/**
|
||||
* Elindult-e már a végrehajtás. Ha igen, a fájl újratöltése (Edit -> a teljes
|
||||
* lánc újraindítása) TILOS: addigra már létrejöhettek termékek és árak, egy
|
||||
* nulláról induló újrafuttatás duplikálna. Helyette a Folytatás vagy a
|
||||
* Visszavonás akció használható.
|
||||
*/
|
||||
public function hasExecutionStarted(): bool
|
||||
{
|
||||
return ! in_array($this->stepStatus(PricelistWorkflowStep::Execution), ['pending', null], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Soronkénti státuszok darabszáma (státusz => darab), a jóváhagyás előtti
|
||||
* összegző modalhoz.
|
||||
*/
|
||||
public function lineStatusCounts(): array
|
||||
{
|
||||
return $this->lines()
|
||||
->selectRaw('status, COUNT(*) as total')
|
||||
->groupBy('status')
|
||||
->pluck('total', 'status')
|
||||
->all();
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,6 +106,84 @@ public function dispatchExecutionJob(PricelistFile $pricelistFile): void
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Jóváhagyás: lezárja a manuális Jóváhagyás lépést és elindítja a végrehajtást.
|
||||
*
|
||||
* @return bool false, ha a fájl közben már nem jóváhagyható állapotba került
|
||||
*/
|
||||
public function approve(PricelistFile $pricelistFile): bool
|
||||
{
|
||||
// Újraellenőrzés a friss rekordon: a felület 5 mp-enként pollozik, tehát a
|
||||
// gomb megjelenítése és a kattintás között változhatott az állapot.
|
||||
$pricelistFile->refresh();
|
||||
|
||||
if (! $pricelistFile->canBeApproved()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$meta = $pricelistFile->file_meta ?? [];
|
||||
$meta['approval'] = [
|
||||
'decision' => 'approved',
|
||||
'user_id' => auth()->id(),
|
||||
'at' => now()->toDateTimeString(),
|
||||
];
|
||||
$pricelistFile->update(['file_meta' => $meta]);
|
||||
|
||||
$this->updateStepStatus($pricelistFile, PricelistWorkflowStep::Approval, 'completed', 'Jóváhagyva.', 100);
|
||||
|
||||
// A státuszt MÉG A DISPATCH ELŐTT billentjük át (az Execution lépés
|
||||
// 'inprogress'-re állítása a fájlt is inprogress-re teszi). Ha ezt a jobra
|
||||
// bíznánk, a queue-latency alatt a fájl waiting_for_approval maradna, a
|
||||
// Jóváhagyás gomb továbbra is látszana, és egy második kattintás párhuzamos
|
||||
// végrehajtást indítana ugyanarra a termékhalmazra.
|
||||
$this->updateStepStatus($pricelistFile, PricelistWorkflowStep::Execution, 'inprogress', 'Végrehajtás előkészítése...', 0);
|
||||
|
||||
$this->dispatchExecutionJob($pricelistFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Elutasítás: a fájl lezárva (`closed`) státuszba kerül, végrehajtás nem indul.
|
||||
*
|
||||
* @return bool false, ha a fájl közben már nem elutasítható állapotba került
|
||||
*/
|
||||
public function reject(PricelistFile $pricelistFile, ?string $reason = null): bool
|
||||
{
|
||||
$pricelistFile->refresh();
|
||||
|
||||
if (! $pricelistFile->canBeApproved()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->updateStepStatus(
|
||||
$pricelistFile,
|
||||
PricelistWorkflowStep::Approval,
|
||||
'rejected',
|
||||
$reason ? 'Elutasítva: ' . $reason : 'Elutasítva.',
|
||||
);
|
||||
|
||||
$meta = $pricelistFile->file_meta ?? [];
|
||||
$meta['approval'] = [
|
||||
'decision' => 'rejected',
|
||||
'reason' => $reason,
|
||||
'user_id' => auth()->id(),
|
||||
'at' => now()->toDateTimeString(),
|
||||
];
|
||||
|
||||
// A 'rejected' lépésstátusz szándékosan nem 'failed': az updateStepStatus a
|
||||
// 'failed'-et fail fájlstátuszra fordítaná, az elutasítás viszont nem hiba,
|
||||
// hanem szabályos lezárás.
|
||||
$pricelistFile->update([
|
||||
'status' => PricelistFileStatusEnum::closed,
|
||||
'file_meta' => $meta,
|
||||
'processing_current_step' => null,
|
||||
'processing_current_step_percentage' => null,
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function updateStepStatus(
|
||||
PricelistFile $pricelistFile,
|
||||
PricelistWorkflowStep $stepEnum,
|
||||
|
||||
205
tests/Feature/PricelistApprovalTest.php
Normal file
205
tests/Feature/PricelistApprovalTest.php
Normal file
@ -0,0 +1,205 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\PricelistFileLineStatusEnum;
|
||||
use App\Enums\PricelistFileStatusEnum;
|
||||
use App\Enums\PricelistWorkflowStep;
|
||||
use App\Filament\Resources\PricelistFiles\Pages\ViewPricelistFile;
|
||||
use App\Jobs\PricelistExecutionJob;
|
||||
use App\Models\FeatureFlag;
|
||||
use App\Models\PricelistFile;
|
||||
use App\Models\PricelistFileLine;
|
||||
use App\Models\Role;
|
||||
use App\Models\Supplier;
|
||||
use App\Models\User;
|
||||
use App\Services\FeatureFlagRegistrar;
|
||||
use App\Services\PricelistFileProcessService;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
use Livewire\Livewire;
|
||||
use Tests\TestCase;
|
||||
|
||||
uses(TestCase::class, RefreshDatabase::class);
|
||||
|
||||
/**
|
||||
* Jóváhagyásra váró fájl: a validálás lezárult, a jóváhagyás a soron következő lépés.
|
||||
*/
|
||||
function approvableFile(array $attributes = []): PricelistFile
|
||||
{
|
||||
return PricelistFile::create(array_merge([
|
||||
'filename' => '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');
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user