Egy beszállítóhoz egyszerre legfeljebb egy nyitott végrehajtás tartozhat: egy execution_failed fájl után a termékek félig frissített állapotban vannak, egy új import erre a kevert alapra rétegződne rá (és a validálás is ehhez számolná a diffeket); két párhuzamos végrehajtás pedig nem determinisztikus eredményt adna. - új PricelistGuard service: egyetlen igazságforrás a blokkoláshoz - mind a NÉGY belépési pont véd: modern Filament create, régebbi PriceListProcessor oldal, legacy Admin\PriceListController import, konzol parancs. A legacy import a legfontosabb - az közvetlenül ír a products táblába, megkerülve a modult, és a beszállítókat ma még nagyrészt ott kezelik. - az `inprogress` önmagában nem blokkol: az előfeldolgozás és a validálás egyetlen terméket sem ír, csak a már elindult végrehajtás számít - a beszállító nem tűnik el a select listából, hanem konkrét magyarázatot kap a felhasználó arról, melyik fájl blokkol és miért - blokkolt beszállítónál másik fájl jóváhagyása sem indítható Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
79 lines
3.4 KiB
PHP
79 lines
3.4 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Enums\PricelistFileStatusEnum;
|
|
use App\Models\PricelistFile;
|
|
|
|
/**
|
|
* Egy beszállítóhoz egyszerre legfeljebb egy "nyitott" végrehajtás tartozhat.
|
|
*
|
|
* Amíg egy fájl végrehajtása hibára futott (`execution_failed`), a beszállító
|
|
* termékei félig frissített állapotban vannak: egy részük már az új árlista adatait
|
|
* hordozza, más részük még a régit. Ha erre az állapotra új árlistát vinnénk fel, a
|
|
* validálás ehhez a kevert állapothoz számolná a diffeket - egyes sorok tévesen
|
|
* "rendben"-nek látszanának, mások fölöslegesen "módosítás"-nak -, az import pedig
|
|
* egy inkonzisztens alapra rétegződne rá.
|
|
*
|
|
* A futó (`inprogress`) végrehajtás blokkolása még közvetlenebb: két párhuzamos
|
|
* import ugyanarra a termékhalmazra nem determinisztikus végeredményt adna.
|
|
*
|
|
* FONTOS: ezt a védelmet MINDEN belépési ponton alkalmazni kell (modern Filament
|
|
* felület, régebbi PriceListProcessor oldal, legacy admin import, konzol parancs) -
|
|
* ha csak az űrlapot védjük, a blokkolás látszatvédelem marad, mert a beszállítókat
|
|
* ma még nagyrészt a legacy felületen kezelik.
|
|
*/
|
|
class PricelistGuard
|
|
{
|
|
/**
|
|
* A beszállítót blokkoló árlista fájl, ha van ilyen.
|
|
*
|
|
* @param int|null $exceptFileId ezt a fájlt hagyjuk figyelmen kívül (önmagát ne blokkolja)
|
|
*/
|
|
public function blockingFileFor(?int $supplierId, ?int $exceptFileId = null): ?PricelistFile
|
|
{
|
|
if (! $supplierId) {
|
|
return null;
|
|
}
|
|
|
|
return PricelistFile::query()
|
|
->where('supplier_id', $supplierId)
|
|
->when($exceptFileId, fn ($query) => $query->whereKeyNot($exceptFileId))
|
|
->whereIn('status', [
|
|
PricelistFileStatusEnum::execution_failed->value,
|
|
PricelistFileStatusEnum::inprogress->value,
|
|
])
|
|
->latest('id')
|
|
->get()
|
|
// Az `inprogress` önmagában nem blokkol: az előfeldolgozás és a validálás
|
|
// egyetlen terméket sem ír, ezért közben nyugodtan feltölthető másik fájl.
|
|
// Csak az számít, ahol a végrehajtás már elindult - ezt a workflow_steps
|
|
// JSON tartalma dönti el, amit SQL-ben nem szűrnénk tisztán, a beszállítónkénti
|
|
// jelöltek száma viszont elenyésző.
|
|
->first(fn (PricelistFile $file) => $file->status === PricelistFileStatusEnum::execution_failed
|
|
|| $file->hasExecutionStarted());
|
|
}
|
|
|
|
public function isBlocked(?int $supplierId, ?int $exceptFileId = null): bool
|
|
{
|
|
return $this->blockingFileFor($supplierId, $exceptFileId) !== null;
|
|
}
|
|
|
|
/**
|
|
* Felhasználónak szóló magyarázat: melyik fájl blokkol és miért.
|
|
*/
|
|
public function blockingMessage(PricelistFile $blockingFile): string
|
|
{
|
|
$state = $blockingFile->status === PricelistFileStatusEnum::execution_failed
|
|
? 'végrehajtása hibára futott'
|
|
: 'végrehajtása jelenleg fut';
|
|
|
|
return sprintf(
|
|
'A beszállítóhoz tartozó "%s" árlista (#%d) %s. Amíg ez nincs lezárva (folytatás vagy visszavonás), nem indítható újabb árlista-feldolgozás, mert a termékadatok félig frissített állapotban lehetnek.',
|
|
$blockingFile->filename,
|
|
$blockingFile->id,
|
|
$state,
|
|
);
|
|
}
|
|
}
|