d2d.emegrendeles.hu/app/Enums/PricelistWorkflowStep.php
E98Developer e338f68ee8 ADD EV3-357 Árlista feldolgozás phase5/4 visszavonás, elakadás-észlelés, kényszerlezárás
- PricelistExecutionJob + új PricelistRevertJob: $tries=1, $timeout, failed()
  hook. A service catch ága csak PHP exceptiont fog el; worker timeout vagy
  memórialimit esetén a fájl inprogress-ben ragadna, döntési gomb nélkül.
- heartbeat backstop a kill -9 esetére, ahol a failed() sem fut: minden chunk ír
  a rekordba, így az updated_at a szívverés (isExecutionStuck). Ezzel garantált,
  hogy minden kísérlet véges időn belül terminális állapotba jut - enélkül a
  kényszerlezárás sem nyílna ki soha.
- kompenzáló visszaállítás R1-R4: árak törlése, termékek visszaállítása a
  snapshotból, létrehozott termékek kivonása (soft delete, NEM fizikai törlés,
  mert lehet rájuk hivatkozás), árlista + fájl lezárása
- konfliktuskezelés: a végrehajtás óta kézzel módosított terméket kihagyjuk és
  jelentjük, nem írjuk felül vakon
- FIX phase5/3: a snapshot csak az írás ELŐTTI updated_at-et tárolta, amihez
  képest a termék a végrehajtás után mindig eltér - a konfliktus-ellenőrzés így
  minden sort kihagyott volna. Most a saját írásunk utáni updated_at is bekerül.
- kényszerlezárás: csak legalább egy terminális hibába futott visszaállítási
  kísérlet után, flag + developer szerepkör mögött, kötelező indoklással
- döntéstámogató panel: mi történt már meg az importból, hogy a felhasználó ne
  vakon válasszon a Folytatás és a Visszavonás között

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 06:36:21 +02:00

79 lines
2.2 KiB
PHP

<?php
namespace App\Enums;
use App\Jobs\PricelistPreProcessJob;
use App\Jobs\PricelistValidationJob;
use App\Jobs\PricelistExecutionJob;
enum PricelistWorkflowStep: string
{
case Preprocessing = 'preprocess';
case Validation = 'validation';
case Approval = 'approval';
case Execution = 'execution';
/**
* Magyar megnevezés (UI megjelenítéshez)
*/
public function label(): string
{
return match($this) {
self::Preprocessing => 'Előfeldolgozás',
self::Validation => 'Validálás',
self::Approval => 'Jóváhagyás',
self::Execution => 'Végrehajtás',
};
}
/**
* A lépéshez tartozó Job osztály neve
*/
public function jobClass(): ?string
{
return match($this) {
self::Preprocessing => PricelistPreProcessJob::class,
self::Validation => PricelistValidationJob::class,
self::Execution => PricelistExecutionJob::class,
self::Approval => null, // Manuális lépésnél nincs automatikus job
};
}
/**
* A lépéshez tartozó Job példány visszaadása
*/
public function getJob(\App\Models\PricelistFile $file): ?object
{
$jobClass = $this->jobClass();
return $jobClass ? new $jobClass($file) : null;
}
/**
* Státusz színek (UI megjelenítéshez)
*/
public function color(string $status): string
{
return match ($status) {
'pending' => 'gray',
'inprogress' => 'primary',
'completed' => 'success',
'failed' => 'danger',
'rejected' => 'warning', // manuális elutasítás - nem hiba, hanem szabályos lezárás
'reverted' => 'warning', // a végrehajtás kompenzáló visszaállítással visszavonva
default => 'gray',
};
}
/**
* Segédfüggvény az alapértelmezett workflow struktúra legenerálásához
*/
public static function defaultSteps(): array
{
return array_map(fn($step) => [
'name' => $step->value,
'label' => $step->label(),
'status' => 'pending',
], self::cases());
}
}