- 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>
111 lines
3.4 KiB
PHP
111 lines
3.4 KiB
PHP
<?php
|
|
|
|
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;
|
|
|
|
class PricelistFile extends BaseAuditable
|
|
{
|
|
use SoftDeletes;
|
|
|
|
protected $casts = [
|
|
'status' => PricelistFileStatusEnum::class,
|
|
'processing_current_step_percentage' => 'integer',
|
|
'available_date' => 'date',
|
|
'workflow_steps' => 'array',
|
|
'file_meta' => 'array',
|
|
];
|
|
|
|
protected static function booted()
|
|
{
|
|
static::creating(function ($pricelistFile) {
|
|
if (empty($pricelistFile->workflow_steps)) {
|
|
$pricelistFile->workflow_steps = \App\Enums\PricelistWorkflowStep::defaultSteps();
|
|
}
|
|
});
|
|
}
|
|
|
|
public function supplier(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Supplier::class);
|
|
}
|
|
|
|
public function priceList(): BelongsTo
|
|
{
|
|
return $this->belongsTo(PriceList::class);
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|