diff --git a/app/Exports/CurrentPriceExport.php b/app/Exports/CurrentPriceExport.php new file mode 100644 index 0000000..cb23e6f --- /dev/null +++ b/app/Exports/CurrentPriceExport.php @@ -0,0 +1,48 @@ + $rows + */ + public function __construct(private Collection $rows) {} + + public function collection(): Collection + { + return $this->rows; + } + + public function headings(): array + { + return [ + 'Beszállító', + 'Gyártó', + 'Termék Főcsoport', + 'Termék alcsoport 1', + 'Termék alcsoport 2', + 'Termék megnevezése', + 'Aktuális ár', + ]; + } + + public function map($row): array + { + return [ + $row['supplier'] ?? '', + $row['producer'] ?? '', + $row['mainGroup'] ?? '', + $row['subGroup1'] ?? '', + $row['subGroup2'] ?? '', + $row['name'], + $row['price'], + ]; + } +} diff --git a/app/Filament/Pages/CurrentPrice.php b/app/Filament/Pages/CurrentPrice.php index d351ca3..b1ef2cf 100644 --- a/app/Filament/Pages/CurrentPrice.php +++ b/app/Filament/Pages/CurrentPrice.php @@ -2,6 +2,7 @@ namespace App\Filament\Pages; +use App\Exports\CurrentPriceExport; use App\Models\Producer; use App\Models\Product; use App\Models\ProductGroup; @@ -13,12 +14,18 @@ use Filament\Schemas\Schema; use Filament\Pages\Page; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; +use Maatwebsite\Excel\Facades\Excel; +use Symfony\Component\HttpFoundation\BinaryFileResponse; class CurrentPrice extends Page implements HasForms { use InteractsWithForms; + /** @var int[] */ + public const PER_PAGE_OPTIONS = [10, 25, 50, 100]; + protected static string | \BackedEnum | null $navigationIcon = 'heroicon-o-currency-dollar'; protected static ?string $title = 'Aktuális ár'; @@ -41,6 +48,17 @@ public function getBreadcrumbs(): array public ?array $data = []; + public bool $hasSearched = false; + + public int $perPage = 10; + + public int $page = 1; + + public int $totalResults = 0; + + /** @var array */ + public array $results = []; + public function mount(): void { $this->form->fill(); @@ -157,9 +175,151 @@ public function form(Schema $form): Schema public function submit(): void { - $data = $this->form->getState(); - // Itt lehet majd feldolgozni a szűrést - // dd($data); + $this->data = $this->form->getState(); + $this->page = 1; + $this->runSearch(); + } + + public function updatedPerPage(): void + { + $this->page = 1; + + if ($this->hasSearched) { + $this->runSearch(); + } + } + + public function goToPage(int $page): void + { + $this->page = max(1, min($page, $this->lastPage())); + $this->runSearch(); + } + + public function lastPage(): int + { + return max(1, (int) ceil($this->totalResults / $this->perPage)); + } + + public function exportExcel(): BinaryFileResponse + { + $products = $this->filteredResultsQuery()->orderBy('name')->get(); + $rows = $this->mapProductsToRows($products); + + return Excel::download(new CurrentPriceExport($rows), 'aktualis-ar-'.now()->format('Y-m-d_His').'.xlsx'); + } + + private function runSearch(): void + { + $query = $this->filteredResultsQuery(); + + $this->totalResults = (clone $query)->count(); + $this->page = min($this->page, $this->lastPage()); + + $products = $query->orderBy('name') + ->skip(($this->page - 1) * $this->perPage) + ->take($this->perPage) + ->get(); + + $this->results = $this->mapProductsToRows($products)->toArray(); + + $this->hasSearched = true; + } + + /** + * A szűrő űrlap aktuális állapota alapján felépített, még nem lapozott/rendezett Product query. + */ + private function filteredResultsQuery(): Builder + { + $data = $this->data; + + $query = Product::query()->with(['Supplier', 'Producer']); + + if (filled($data['supplier'] ?? [])) { + $query->whereIn('supplier_id', $data['supplier']); + } + + if (filled($data['productGroup'] ?? [])) { + $query->whereIn('product_group_id', $data['productGroup']); + } + + if (filled($data['producer'] ?? [])) { + $query->whereIn('producer_id', $data['producer']); + } + + if (filled($data['product'] ?? [])) { + $query->whereIn('name', $data['product']); + } + + if (filled($data['hooreycaName'] ?? [])) { + $query->whereIn('buyerProductName', $data['hooreycaName']); + } + + return $query; + } + + /** + * Termékekhez tartozó aktuális ár és termékcsoport-hierarchia feloldása, és a táblázat/export + * soraivá alakítása. Egyetlen extra lekérdezéssel dolgozza fel a kapott terméklistát (nincs N+1). + * + * @return Collection + */ + private function mapProductsToRows(Collection $products): Collection + { + $today = now()->toDateString(); + + $currentPrices = DB::table('price_list_prices as plp') + ->join('price_lists as pl', 'pl.id', '=', 'plp.price_list_id') + ->whereIn('plp.product_id', $products->pluck('id')) + ->where('pl.available', '<=', $today) + ->orderBy('pl.available', 'desc') + ->orderBy('pl.id', 'desc') + ->get(['plp.product_id', 'plp.price']) + ->groupBy('product_id') + ->map(fn ($rows) => (float) $rows->first()->price); + + $groupPaths = $this->getProductGroupPaths($products->pluck('product_group_id')); + + return $products->map(function (Product $product) use ($currentPrices, $groupPaths) { + $path = $groupPaths->get($product->product_group_id, []); + + return [ + 'supplier' => $product->Supplier?->name, + 'producer' => $product->Producer?->name, + 'mainGroup' => $path[0] ?? '', + 'subGroup1' => $path[1] ?? '', + 'subGroup2' => $path[2] ?? '', + 'name' => $product->name, + 'price' => $currentPrices->get($product->id), + ]; + })->values(); + } + + /** + * A termékcsoport-hierarchia első 3 szintje (Termék Főcsoport / alcsoport 1 / alcsoport 2) + * termékcsoport id-nként, ugyanaz a logika, mint a legacy Árváltozás statisztikában + * (ProductGroup::allWithPath() — a technikai gyökér csomópont nélkül). + * + * @param \Illuminate\Support\Collection $productGroupIds + * @return \Illuminate\Support\Collection> + */ + private function getProductGroupPaths($productGroupIds): \Illuminate\Support\Collection + { + $ids = $productGroupIds->filter()->unique()->values(); + + if ($ids->isEmpty()) { + return collect(); + } + + return ProductGroup::whereIn('id', $ids) + ->with('ancestors') + ->get() + ->mapWithKeys(function (ProductGroup $group) { + $names = $group->ancestors->pluck('name')->all(); + array_shift($names); // technikai gyökér ("Főcsoportok") elhagyása + $names[] = $group->name; + + return [$group->id => $names]; + }); } /** diff --git a/resources/views/filament/pages/current-price.blade.php b/resources/views/filament/pages/current-price.blade.php index 281c0ce..17ffbff 100644 --- a/resources/views/filament/pages/current-price.blade.php +++ b/resources/views/filament/pages/current-price.blade.php @@ -78,6 +78,89 @@ color: #453821; } + /* Aktuális ár találati táblázat */ + .result-table-wrapper { + overflow-x: auto; + } + .result-table { + width: 100%; + border-collapse: collapse; + } + .result-table th, + .result-table td { + padding: 8px 12px; + border: 1px solid #dee2e6; + text-align: left; + } + .result-table thead th { + background-color: #f8f9fa; + font-weight: 600; + color: #333; + } + .result-table tbody tr:nth-child(even) { + background-color: #f8f9fa; + } + .result-table td.price-cell { + text-align: right; + white-space: nowrap; + } + .result-table-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 10px; + color: #333; + } + .per-page-select { + display: flex; + align-items: center; + gap: 6px; + } + .per-page-select select { + padding: 4px 8px; + border: 1px solid #dee2e6; + border-radius: 4px; + background-color: #fff; + } + .result-pager { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + margin-top: 15px; + } + .result-pager button { + padding: 6px 12px; + border: 1px solid #dee2e6; + border-radius: 4px; + background-color: #fff; + color: #333; + cursor: pointer; + } + .result-pager button:hover:not(:disabled) { + background-color: #f8f9fa; + } + .result-pager button:disabled { + opacity: 0.5; + cursor: not-allowed; + } + .result-pager-info { + font-weight: 600; + color: #333; + } + .excel-export-button { + padding: 6px 14px; + border: 1px solid #1e7e34; + border-radius: 4px; + background-color: #28a745; + color: #fff; + font-weight: 600; + cursor: pointer; + } + .excel-export-button:hover { + background-color: #218838; + } + /* Aktív menüpont stílusa a sidebar-ban */ .sidebar-menu ul li.active > a { color: #b8bfce; @@ -128,11 +211,74 @@
-
- Aktuális ár statisztika +
+ Aktuális ár statisztika + @if ($hasSearched && ! empty($results)) + + @endif
-

Ez a statisztikai elem még fejlesztés alatt áll.

+ @if (! $hasSearched) +

Válassza ki a szűrési feltételeket, majd nyomja meg a "Szűrés alkalmazása" gombot.

+ @elseif (empty($results)) +

Nincs a szűrésnek megfelelő találat.

+ @else +
+
+ Összesen {{ $totalResults }} találat +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + @foreach ($results as $row) + + + + + + + + + + @endforeach + +
BeszállítóGyártóTermék FőcsoportTermék alcsoport 1Termék alcsoport 2Termék megnevezéseAktuális ár
{{ $row['supplier'] ?? '-' }}{{ $row['producer'] ?? '-' }}{{ $row['mainGroup'] ?: '-' }}{{ $row['subGroup1'] ?: '-' }}{{ $row['subGroup2'] ?: '-' }}{{ $row['name'] }}{{ $row['price'] !== null ? number_format($row['price'], 0, ',', ' ') . ' Ft' : '-' }}
+
+ + @php $lastPage = $this->lastPage(); @endphp + @if ($lastPage > 1) +
+ + + {{ $page }}. / {{ $lastPage }} oldal + + +
+ @endif + @endif