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 done = 'done';
|
||||||
case waiting_for_approval = 'waiting_for_approval';
|
case waiting_for_approval = 'waiting_for_approval';
|
||||||
case closed = 'closed';
|
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
|
public function label(): string
|
||||||
{
|
{
|
||||||
@ -20,6 +27,24 @@ public function label(): string
|
|||||||
self::done => 'elkészült',
|
self::done => 'elkészült',
|
||||||
self::waiting_for_approval => 'jóváhagyásra vár',
|
self::waiting_for_approval => 'jóváhagyásra vár',
|
||||||
self::closed => 'lezárva',
|
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')
|
->label('Státusz')
|
||||||
->badge()
|
->badge()
|
||||||
->formatStateUsing(fn (PricelistFileStatusEnum $state): string => $state->label())
|
->formatStateUsing(fn (PricelistFileStatusEnum $state): string => $state->label())
|
||||||
->color(fn (PricelistFileStatusEnum $state): string => match ($state) {
|
->color(fn (PricelistFileStatusEnum $state): string => $state->color()),
|
||||||
PricelistFileStatusEnum::todo => 'gray',
|
|
||||||
PricelistFileStatusEnum::inprogress => 'info',
|
|
||||||
PricelistFileStatusEnum::done => 'success',
|
|
||||||
PricelistFileStatusEnum::fail => 'danger',
|
|
||||||
PricelistFileStatusEnum::waiting_for_approval => 'primary',
|
|
||||||
PricelistFileStatusEnum::closed => 'warning',
|
|
||||||
}),
|
|
||||||
]),
|
]),
|
||||||
Grid::make(3)
|
Grid::make(3)
|
||||||
->schema([
|
->schema([
|
||||||
|
|||||||
@ -32,14 +32,7 @@ public static function configure(Table $table): Table
|
|||||||
->label('Státusz')
|
->label('Státusz')
|
||||||
->badge()
|
->badge()
|
||||||
->formatStateUsing(fn (PricelistFileStatusEnum $state): string => $state->label())
|
->formatStateUsing(fn (PricelistFileStatusEnum $state): string => $state->label())
|
||||||
->color(fn (PricelistFileStatusEnum $state): string => match ($state) {
|
->color(fn (PricelistFileStatusEnum $state): string => $state->color())
|
||||||
PricelistFileStatusEnum::todo => 'gray',
|
|
||||||
PricelistFileStatusEnum::inprogress => 'info',
|
|
||||||
PricelistFileStatusEnum::done => 'success',
|
|
||||||
PricelistFileStatusEnum::fail => 'danger',
|
|
||||||
PricelistFileStatusEnum::waiting_for_approval => 'primary',
|
|
||||||
PricelistFileStatusEnum::closed => 'warning',
|
|
||||||
})
|
|
||||||
->sortable(),
|
->sortable(),
|
||||||
TextColumn::make('processing_current_step')
|
TextColumn::make('processing_current_step')
|
||||||
->label('Aktuális lépés')
|
->label('Aktuális lépés')
|
||||||
|
|||||||
@ -16,6 +16,8 @@ class PricelistFileLine extends BaseAuditable
|
|||||||
'payload' => 'array',
|
'payload' => 'array',
|
||||||
'validation_messages' => 'array',
|
'validation_messages' => 'array',
|
||||||
'diff' => 'array',
|
'diff' => 'array',
|
||||||
|
'applied_snapshot' => 'array',
|
||||||
|
'executed_at' => 'datetime',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function pricelistFile(): BelongsTo
|
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'],
|
'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