d2d.emegrendeles.hu/app/Filament/Pages/PriceListProcessor.php
E98Developer 8bcfeba0f9 ADD EV3-357 Árlista feldolgozás phase5/5 beszállítói blokkolás (PricelistGuard)
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>
2026-08-15 06:46:40 +02:00

118 lines
3.9 KiB
PHP

<?php
namespace App\Filament\Pages;
use Filament\Pages\Page;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\FileUpload;
use Filament\Schemas\Schema;
use Filament\Forms\Contracts\HasForms;
use Filament\Forms\Concerns\InteractsWithForms;
use App\Models\Supplier;
use App\Models\PricelistFile;
use App\Filament\Resources\PricelistFiles\PricelistFileResource;
use App\Jobs\PricelistPreProcessJob;
use Filament\Notifications\Notification;
use Filament\Actions\Action;
use Illuminate\Contracts\Support\Htmlable;
class PriceListProcessor extends Page implements HasForms
{
use InteractsWithForms;
protected static string | \BackedEnum | null $navigationIcon = 'heroicon-o-document-text';
protected string $view = 'filament.pages.price-list-processor';
protected static ?string $title = 'Árlista feldolgozó';
protected static ?string $navigationLabel = 'Árlista feldolgozó';
protected static bool $shouldRegisterNavigation = false;
public ?array $data = [];
public function mount(): void
{
$this->form->fill();
}
public function form(Schema $form): Schema
{
return $form
->schema([
Select::make('supplier_id')
->label('Beszállító')
->options(Supplier::query()->pluck('name', 'id'))
->required()
->searchable(),
DatePicker::make('available_date')
->label('Életbelépés dátuma')
->required()
->native(false)
->displayFormat('Y.m.d'),
Textarea::make('note')
->label('Megjegyzés')
->rows(3),
FileUpload::make('filename')
->label('Fájl kiválasztása')
->required()
->acceptedFileTypes(['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'text/csv'])
->disk('public')
->directory('pricelist-uploads')
->preserveFilenames(),
])
->statePath('data');
}
protected function getFormActions(): array
{
return [
Action::make('save')
->label('Feltöltés és feldolgozás indítása')
->submit('save')
->color('primary'),
];
}
public function save()
{
$data = $this->form->getState();
// Ugyanaz a védelem, mint a modern felületen: enélkül ez az oldal megkerülné a
// beszállítói blokkolást, hiszen ugyanúgy PricelistFile-t hoz létre és láncot indít.
if ($blockingFile = app(\App\Services\PricelistGuard::class)->blockingFileFor($data['supplier_id'] ?? null)) {
Notification::make()
->title('A beszállítóhoz jelenleg nem vihető fel árlista')
->body(app(\App\Services\PricelistGuard::class)->blockingMessage($blockingFile))
->danger()
->persistent()
->send();
return;
}
$pricelistFile = PricelistFile::create([
'filename' => $data['filename'],
'supplier_id' => $data['supplier_id'],
'available_date' => $data['available_date'],
'note' => $data['note'],
]);
app(\App\Services\PricelistFileProcessService::class)->dispatchInitialChain($pricelistFile);
Notification::make()
->title('Sikeres feltöltés')
->body('Az árlista fájl rögzítve lett és a feldolgozás elindult.')
->success()
->send();
$this->form->fill();
return redirect()->to(PricelistFileResource::getUrl('view', ['record' => $pricelistFile]));
}
}