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>
264 lines
18 KiB
PHP
264 lines
18 KiB
PHP
<?php
|
|
|
|
namespace App\Filament\Resources\PricelistFiles\Schemas;
|
|
|
|
use App\Enums\PricelistFileStatusEnum;
|
|
use App\Models\PricelistFile;
|
|
use App\Services\PricelistGuard;
|
|
use Filament\Schemas\Components\Grid;
|
|
use Filament\Schemas\Components\Section;
|
|
use Filament\Schemas\Components\Text;
|
|
use Filament\Infolists\Components\RepeatableEntry;
|
|
use Filament\Infolists\Components\TextEntry;
|
|
use Filament\Schemas\Schema;
|
|
use Filament\Support\Enums\FontWeight;
|
|
use Filament\Support\Enums\IconPosition;
|
|
use Filament\Support\Enums\Width;
|
|
|
|
class PricelistFileInfolist
|
|
{
|
|
public static function configure(Schema $schema): Schema
|
|
{
|
|
return $schema
|
|
->components([
|
|
// Döntéstámogató panel: megszakadt végrehajtás után a felhasználónak a
|
|
// Folytatás és a Visszavonás között kell választania. Enélkül vakon
|
|
// döntene - itt látja, mi történt már meg az importból.
|
|
Section::make('Megszakadt végrehajtás')
|
|
->description('A végrehajtás nem fejeződött be. Az árlista nem élesedett, tehát a felhasználók felé nem került ki ár. Döntsd el, folytatod-e, vagy visszavonod a már végrehajtott részt.')
|
|
->icon('heroicon-o-exclamation-triangle')
|
|
->columnSpanFull()
|
|
->maxWidth(Width::Full)
|
|
->visible(fn ($record) => $record->needsExecutionDecision())
|
|
->schema([
|
|
TextEntry::make('execution_error')
|
|
->label('Hiba')
|
|
->color('danger')
|
|
->weight(FontWeight::Bold)
|
|
->columnSpanFull()
|
|
->getStateUsing(fn ($record) => $record->isExecutionStuck()
|
|
? 'A végrehajtás ' . PricelistFile::STUCK_AFTER_MINUTES . ' percnél régebben nem jelzett haladást (elakadt folyamat).'
|
|
: ($record->processing_current_step ?: 'Ismeretlen hiba.')),
|
|
Grid::make(4)->schema([
|
|
TextEntry::make('created_products')
|
|
->label('Létrehozott termék')
|
|
->icon('heroicon-o-plus-circle')
|
|
->getStateUsing(fn ($record) => $record->executionSummary()['created_products']),
|
|
TextEntry::make('updated_products')
|
|
->label('Frissített termék')
|
|
->icon('heroicon-o-pencil-square')
|
|
->getStateUsing(fn ($record) => $record->executionSummary()['updated_products']),
|
|
TextEntry::make('priced_lines')
|
|
->label('Rögzített ár')
|
|
->icon('heroicon-o-currency-dollar')
|
|
->getStateUsing(fn ($record) => $record->executionSummary()['priced_lines']),
|
|
TextEntry::make('pending_lines')
|
|
->label('Hátralévő sor')
|
|
->icon('heroicon-o-clock')
|
|
->getStateUsing(fn ($record) => $record->executionSummary()['pending_lines']),
|
|
]),
|
|
TextEntry::make('revert_error')
|
|
->label('Utolsó visszaállítási kísérlet hibája')
|
|
->color('danger')
|
|
->columnSpanFull()
|
|
->visible(fn ($record) => ($record->file_meta['revert']['last_status'] ?? null) === 'failed')
|
|
->getStateUsing(fn ($record) => $record->file_meta['revert']['last_error'] ?? null),
|
|
]),
|
|
|
|
// A jóváhagyás gombjai némán tűnnének el, ha ugyanahhoz a beszállítóhoz
|
|
// egy másik fájl végrehajtása van nyitva - ez megmondja, miért.
|
|
Section::make('Blokkolt beszállító')
|
|
->icon('heroicon-o-no-symbol')
|
|
->columnSpanFull()
|
|
->maxWidth(Width::Full)
|
|
->visible(fn ($record) => $record->status === PricelistFileStatusEnum::waiting_for_approval
|
|
&& app(PricelistGuard::class)->isBlocked($record->supplier_id, $record->id))
|
|
->schema([
|
|
TextEntry::make('blocking_file')
|
|
->hiddenLabel()
|
|
->color('danger')
|
|
->columnSpanFull()
|
|
->getStateUsing(function ($record) {
|
|
$guard = app(PricelistGuard::class);
|
|
$blockingFile = $guard->blockingFileFor($record->supplier_id, $record->id);
|
|
|
|
return $blockingFile ? $guard->blockingMessage($blockingFile) : null;
|
|
}),
|
|
]),
|
|
|
|
Section::make(null)
|
|
->poll('5s')
|
|
->columnSpanFull()
|
|
->maxWidth(Width::Full)
|
|
->schema([
|
|
Grid::make(2)
|
|
->columnSpanFull()
|
|
->maxWidth(Width::Full)
|
|
->schema([
|
|
// BAL OSZLOP
|
|
Grid::make(1)
|
|
->columnSpan(1)
|
|
->schema([
|
|
Section::make('Fájl információk')
|
|
->schema([
|
|
Grid::make(3)
|
|
->schema([
|
|
TextEntry::make('filename')
|
|
->label('Fájlnév')
|
|
->icon('heroicon-o-document-text')
|
|
->color('primary'),
|
|
TextEntry::make('supplier.name')
|
|
->label('Beszállító')
|
|
->icon('heroicon-o-user'),
|
|
TextEntry::make('status')
|
|
->label('Státusz')
|
|
->badge()
|
|
->formatStateUsing(fn (PricelistFileStatusEnum $state): string => $state->label())
|
|
->color(fn (PricelistFileStatusEnum $state): string => $state->color()),
|
|
]),
|
|
Grid::make(3)
|
|
->schema([
|
|
TextEntry::make('available_date')
|
|
->label('Életbelépés')
|
|
->date('Y.m.d'),
|
|
TextEntry::make('created_at')
|
|
->label('Feltöltés')
|
|
->dateTime('Y.m.d H:i'),
|
|
]),
|
|
// SÁRGA NÉGYZET JAVÍTÁSA: Teljes szélességben alul, csak hiba esetén
|
|
TextEntry::make('processing_current_step')
|
|
->label('Validációs hiba')
|
|
->weight('bold')
|
|
->color('danger')
|
|
->columnSpanFull()
|
|
->visible(fn ($record) => $record->status === PricelistFileStatusEnum::fail)
|
|
->formatStateUsing(fn ($state, $record) => str_contains((string)$state, 'hibás') ? $state : "Hiba történt a folyamat során: $state"),
|
|
|
|
// Folyamatban lévő állapot kijelzése (nem hiba, de hasznos)
|
|
TextEntry::make('processing_current_step_inprogress')
|
|
->label('Aktuális folyamat')
|
|
->getStateUsing(fn ($record) => $record->processing_current_step)
|
|
->weight('bold')
|
|
->color('info')
|
|
->columnSpanFull()
|
|
->visible(fn ($record) => $record->status === PricelistFileStatusEnum::inprogress)
|
|
->formatStateUsing(fn ($state, $record) => "$state ({$record->processing_current_step_percentage}%)"),
|
|
|
|
TextEntry::make('note')
|
|
->label('Megjegyzés')
|
|
->placeholder('Nincs megjegyzés')
|
|
->columnSpanFull(),
|
|
]),
|
|
]),
|
|
|
|
// JOBB OSZLOP
|
|
Grid::make(1)
|
|
->columnSpan(1)
|
|
->schema([
|
|
Section::make('Fájl statisztika') // KÉK NÉGYZETEK JAVÍTÁSA: Összevont szekció
|
|
->schema([
|
|
Grid::make(5)->schema([
|
|
TextEntry::make('lines_count')
|
|
->label('Sorok')
|
|
->icon('heroicon-o-list-bullet')
|
|
->getStateUsing(fn ($record) => $record->lines()->count()),
|
|
TextEntry::make('error_lines_count')
|
|
->label('Hibás')
|
|
->icon('heroicon-o-x-circle')
|
|
->color('danger')
|
|
->getStateUsing(fn ($record) => $record->lines()->where('status', 'error')->count()),
|
|
TextEntry::make('warning_lines_count')
|
|
->label('Figyelm.')
|
|
->icon('heroicon-o-exclamation-triangle')
|
|
->color('warning')
|
|
->getStateUsing(fn ($record) => $record->lines()->where('status', 'warning')->count()),
|
|
TextEntry::make('new_lines_count')
|
|
->label('Új')
|
|
->icon('heroicon-o-sparkles')
|
|
->color('info')
|
|
->getStateUsing(fn ($record) => $record->lines()->where('status', 'new_product')->count()),
|
|
TextEntry::make('updated_lines_count')
|
|
->label('Módosult')
|
|
->icon('heroicon-o-pencil-square')
|
|
->color('primary')
|
|
->getStateUsing(fn ($record) => $record->lines()->where('status', 'updated')->count()),
|
|
]),
|
|
Grid::make(4)->schema([
|
|
TextEntry::make('file_meta.preprocess.statistics.data_rows')
|
|
->label('Adatsorok')
|
|
->icon('heroicon-o-table-cells'),
|
|
TextEntry::make('file_meta.preprocess.statistics.column_count')
|
|
->label('Oszlopok'),
|
|
TextEntry::make('file_meta.preprocess.file_info.file_size_human')
|
|
->label('Fájlméret'),
|
|
TextEntry::make('file_meta.preprocess.duration_seconds')
|
|
->label('Idő')
|
|
->suffix(' mp'),
|
|
])
|
|
->extraAttributes(['class' => 'mt-4 border-t pt-4']),
|
|
])
|
|
->visible(fn ($record) => $record->lines()->exists() || !empty($record->file_meta['preprocess'] ?? [])),
|
|
|
|
Section::make('Feldolgozási folyamat (Workflow)')
|
|
->collapsible()
|
|
->collapsed(fn ($record) => $record->status !== PricelistFileStatusEnum::inprogress)
|
|
->schema([
|
|
RepeatableEntry::make('workflow_steps')
|
|
->label(false)
|
|
->schema([
|
|
TextEntry::make('label')
|
|
->label('Lépés')
|
|
->weight('bold')
|
|
->inlineLabel(),
|
|
TextEntry::make('status')
|
|
->label('Állapot')
|
|
->badge()
|
|
->inlineLabel()
|
|
->formatStateUsing(fn (string $state): string => match ($state) {
|
|
'pending' => 'várakozik',
|
|
'inprogress' => 'folyamatban',
|
|
'completed' => 'kész',
|
|
'failed' => 'hiba',
|
|
default => $state,
|
|
})
|
|
->color(fn (string $state): string => match ($state) {
|
|
'pending' => 'gray',
|
|
'inprogress' => 'info',
|
|
'completed' => 'success',
|
|
'failed' => 'danger',
|
|
default => 'gray',
|
|
})
|
|
->icon(fn (string $state): string => match ($state) {
|
|
'pending' => 'heroicon-o-clock',
|
|
'inprogress' => 'heroicon-o-arrow-path',
|
|
'completed' => 'heroicon-o-check-circle',
|
|
'failed' => 'heroicon-o-x-circle',
|
|
default => 'heroicon-o-question-mark-circle',
|
|
}),
|
|
])
|
|
->columns(2)
|
|
->columnSpanFull(),
|
|
]),
|
|
|
|
RepeatableEntry::make('file_meta.preprocess.warnings')
|
|
->label('Előfeldolgozási figyelmeztetések')
|
|
->schema([
|
|
TextEntry::make('message')->label('Leírás')->color('warning'),
|
|
TextEntry::make('severity')->label('Súlyosság')->badge()->color('warning'),
|
|
])
|
|
->visible(fn ($record) => !empty($record->file_meta['preprocess']['warnings'] ?? [])),
|
|
|
|
RepeatableEntry::make('file_meta.preprocess.errors')
|
|
->label('Előfeldolgozási hibák')
|
|
->schema([
|
|
TextEntry::make('message')->label('Leírás')->color('danger'),
|
|
TextEntry::make('severity')->label('Súlyosság')->badge()->color('danger'),
|
|
])
|
|
->visible(fn ($record) => !empty($record->file_meta['preprocess']['errors'] ?? [])),
|
|
]),
|
|
]),
|
|
]),
|
|
]);
|
|
}
|
|
}
|