ADD EV3-357 Árlista feldolgozás phase5/1 PricelistExecution feature flag + migrációk
- új PricelistExecution Pennant flag (enabled + roles=[developer]) a jóváhagyás, végrehajtás és visszavonás rollout-kapujaként - új execution_failed fájlstátusz: a validálásig tartó szakasz fail státuszától szándékosan elkülönítve, mert itt már történhettek termék- és árírások - PricelistFileStatusEnum::color(): a badge-színek eddig két helyen, kimerítő match-ben voltak duplikálva, amit az új case eltört volna - pricelist_file_lines.applied_snapshot + executed_at a chunkolt, idempotens végrehajtáshoz és a kompenzáló visszaállításhoz Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
cbda830c5b
commit
5bf2f83092
@ -10,6 +10,13 @@ enum PricelistFileStatusEnum: string
|
||||
case done = 'done';
|
||||
case waiting_for_approval = 'waiting_for_approval';
|
||||
case closed = 'closed';
|
||||
/**
|
||||
* A Végrehajtás (Execution) lépés futott hibára. Szándékosan külön áll a `fail`-től:
|
||||
* a `fail` a validálásig tartó szakasz hibája, ahonnan a fájl újratöltésével (Edit)
|
||||
* biztonságosan újraindítható a lánc - itt viszont már történhettek termék- és
|
||||
* árírások, ezért csak a Folytatás vagy a Visszavonás akció indítható.
|
||||
*/
|
||||
case execution_failed = 'execution_failed';
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
@ -20,6 +27,24 @@ public function label(): string
|
||||
self::done => 'elkészült',
|
||||
self::waiting_for_approval => 'jóváhagyásra vár',
|
||||
self::closed => 'lezárva',
|
||||
self::execution_failed => 'végrehajtási hiba',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Badge szín a felülethez (a PricelistFileLineStatusEnum mintáját követve, hogy a
|
||||
* színek egy helyen legyenek, és egy új státusz ne törjön el minden match-et).
|
||||
*/
|
||||
public function color(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::todo => 'gray',
|
||||
self::inprogress => 'info',
|
||||
self::done => 'success',
|
||||
self::fail => 'danger',
|
||||
self::waiting_for_approval => 'primary',
|
||||
self::closed => 'warning',
|
||||
self::execution_failed => 'danger',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,14 +47,7 @@ public static function configure(Schema $schema): Schema
|
||||
->label('Státusz')
|
||||
->badge()
|
||||
->formatStateUsing(fn (PricelistFileStatusEnum $state): string => $state->label())
|
||||
->color(fn (PricelistFileStatusEnum $state): string => match ($state) {
|
||||
PricelistFileStatusEnum::todo => 'gray',
|
||||
PricelistFileStatusEnum::inprogress => 'info',
|
||||
PricelistFileStatusEnum::done => 'success',
|
||||
PricelistFileStatusEnum::fail => 'danger',
|
||||
PricelistFileStatusEnum::waiting_for_approval => 'primary',
|
||||
PricelistFileStatusEnum::closed => 'warning',
|
||||
}),
|
||||
->color(fn (PricelistFileStatusEnum $state): string => $state->color()),
|
||||
]),
|
||||
Grid::make(3)
|
||||
->schema([
|
||||
|
||||
@ -32,14 +32,7 @@ public static function configure(Table $table): Table
|
||||
->label('Státusz')
|
||||
->badge()
|
||||
->formatStateUsing(fn (PricelistFileStatusEnum $state): string => $state->label())
|
||||
->color(fn (PricelistFileStatusEnum $state): string => match ($state) {
|
||||
PricelistFileStatusEnum::todo => 'gray',
|
||||
PricelistFileStatusEnum::inprogress => 'info',
|
||||
PricelistFileStatusEnum::done => 'success',
|
||||
PricelistFileStatusEnum::fail => 'danger',
|
||||
PricelistFileStatusEnum::waiting_for_approval => 'primary',
|
||||
PricelistFileStatusEnum::closed => 'warning',
|
||||
})
|
||||
->color(fn (PricelistFileStatusEnum $state): string => $state->color())
|
||||
->sortable(),
|
||||
TextColumn::make('processing_current_step')
|
||||
->label('Aktuális lépés')
|
||||
|
||||
@ -16,6 +16,8 @@ class PricelistFileLine extends BaseAuditable
|
||||
'payload' => 'array',
|
||||
'validation_messages' => 'array',
|
||||
'diff' => 'array',
|
||||
'applied_snapshot' => 'array',
|
||||
'executed_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function pricelistFile(): BelongsTo
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\PricelistFileStatusEnum;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* A `pricelist_files.status` valódi DB enum oszlop, ezért egy új PHP enum case
|
||||
* (execution_failed) önmagában nem elég - az oszlop definícióját is bővíteni kell.
|
||||
*
|
||||
* A 2026_03_11_153652_update_enums_in_pricelist_tables mintáját követjük: az enum
|
||||
* értékkészletet a PHP enumból generáljuk, így nem csúszhat el a kettő egymástól.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
$statuses = array_map(fn ($case) => "'{$case->value}'", PricelistFileStatusEnum::cases());
|
||||
$statusesStr = implode(', ', $statuses);
|
||||
|
||||
DB::statement("ALTER TABLE pricelist_files MODIFY COLUMN status ENUM({$statusesStr}) DEFAULT 'todo' NOT NULL");
|
||||
}
|
||||
|
||||
/**
|
||||
* Visszagörgetéskor az execution_failed státuszú rekordokat előbb `fail`-re állítjuk,
|
||||
* különben a szűkített enum miatt az ALTER hibára futna (vagy némán ürítené a mezőt).
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
DB::table('pricelist_files')
|
||||
->where('status', PricelistFileStatusEnum::execution_failed->value)
|
||||
->update(['status' => PricelistFileStatusEnum::fail->value]);
|
||||
|
||||
$statuses = array_map(
|
||||
fn ($case) => "'{$case->value}'",
|
||||
array_filter(
|
||||
PricelistFileStatusEnum::cases(),
|
||||
fn ($case) => $case !== PricelistFileStatusEnum::execution_failed,
|
||||
),
|
||||
);
|
||||
$statusesStr = implode(', ', $statuses);
|
||||
|
||||
DB::statement("ALTER TABLE pricelist_files MODIFY COLUMN status ENUM({$statusesStr}) DEFAULT 'todo' NOT NULL");
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* A chunkolt végrehajtás két dolgot igényel soronként:
|
||||
*
|
||||
* - `applied_snapshot`: a termék TÉNYLEGES, írás előtti DB-állapota, ugyanabban a
|
||||
* chunk-tranzakcióban rögzítve, mint maga az írás. Ebből áll vissza a termék a
|
||||
* Visszavonás akcióban. Szándékosan NEM a validáláskor számolt `diff['old']`-ot
|
||||
* használjuk erre: az a jóváhagyás pillanatában készült, és egy időközbeni kézi
|
||||
* módosítást felülírna. A snapshotban a termék `updated_at`-jét is eltároljuk,
|
||||
* hogy a visszavonás ki tudja hagyni (és jelenteni tudja) a közben módosított
|
||||
* termékeket.
|
||||
* - `executed_at`: a sor feldolgozottságának jelölése. Ez teszi a végrehajtást
|
||||
* idempotenssé, azaz a megszakadt futás a kurzortól folytathatóvá - a már
|
||||
* megjelölt sorok kimaradnak, nem jön létre duplikált termék vagy ár.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('pricelist_file_lines', function (Blueprint $table) {
|
||||
$table->json('applied_snapshot')->nullable()->after('diff');
|
||||
$table->timestamp('executed_at')->nullable()->after('applied_snapshot');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('pricelist_file_lines', function (Blueprint $table) {
|
||||
$table->dropColumn(['applied_snapshot', 'executed_at']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -46,5 +46,19 @@ public function run(): void
|
||||
'roles' => ['developer'],
|
||||
],
|
||||
);
|
||||
|
||||
FeatureFlag::updateOrCreate(
|
||||
['name' => 'PricelistExecution'],
|
||||
[
|
||||
'label' => 'Árlista jóváhagyás és végrehajtás',
|
||||
'description' => 'Az árlista feldolgozó Jóváhagyás/Elutasítás, Végrehajtás folytatása és Visszavonás akcióit, valamint a tényleges árlista-importot szabályozza a rollout alatt. Átmeneti kapcsoló, a funkció élesítése után eltávolítható - a kényszerlezárás jogosultsági korlátozása viszont a flag megszűnése után is maradjon.',
|
||||
// enabled=true + roles=['developer']: a developer felhasználók automatikusan
|
||||
// megkapják, rajtuk kívül senki. Lásd a fenti magyarázatot arról, miért nem
|
||||
// enabled=false-szal fejezzük ki a "csak developer lássa" állapotot.
|
||||
'enabled' => true,
|
||||
'stages' => null,
|
||||
'roles' => ['developer'],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user