From 68ee0795299674b037825410862f0ff00dd82c24 Mon Sep 17 00:00:00 2001 From: dhanabalan Date: Mon, 25 May 2026 17:10:45 +0530 Subject: [PATCH 1/2] Updated alignments on resource --- app/Filament/Resources/InvoiceValidationResource.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Filament/Resources/InvoiceValidationResource.php b/app/Filament/Resources/InvoiceValidationResource.php index 97a23a5..cb053de 100644 --- a/app/Filament/Resources/InvoiceValidationResource.php +++ b/app/Filament/Resources/InvoiceValidationResource.php @@ -305,8 +305,8 @@ class InvoiceValidationResource extends Resource ->alignCenter(), Tables\Columns\TextColumn::make('panel_box_code')// stickerMaster.panel_box_code ->label('Panel Box Code') - ->alignCenter() - ->sortable(), // ->searchable() + ->alignCenter() // ->searchable() + ->sortable(), // ->toggleable(isToggledHiddenByDefault: true), Tables\Columns\TextColumn::make('panel_box_supplier') ->label('Panel Box Supplier') From 4378f3adbdb7f8ba11c4d0c6b871f52eeea20e5b Mon Sep 17 00:00:00 2001 From: dhanabalan Date: Tue, 26 May 2026 09:01:52 +0530 Subject: [PATCH 2/2] Added material type 4 (Bundle Individual) on livewire, blade, exporter, resource and create page --- .../Exports/InvoiceValidationExporter.php | 9 + .../Resources/InvoiceValidationResource.php | 1846 +++++++++-------- .../Pages/CreateInvoiceValidation.php | 4 +- app/Livewire/InvoiceDataTable.php | 3 + .../livewire/invoice-data-table.blade.php | 2 +- 5 files changed, 944 insertions(+), 920 deletions(-) diff --git a/app/Filament/Exports/InvoiceValidationExporter.php b/app/Filament/Exports/InvoiceValidationExporter.php index 78efff3..f6fc513 100644 --- a/app/Filament/Exports/InvoiceValidationExporter.php +++ b/app/Filament/Exports/InvoiceValidationExporter.php @@ -36,6 +36,15 @@ class InvoiceValidationExporter extends Exporter ->label('ITEM DESCRIPTION'), ExportColumn::make('stickerMaster.item.uom') ->label('UNIT OF MEASURE'), + ExportColumn::make('stickerMaster.material_type') + ->label('MATERIAL TYPE') + ->formatStateUsing(fn ($state) => match ($state) { + 1 => 'Individual', + 2 => 'Bundle', + 3 => 'Quantity', + 4 => 'Bundle Individual', + default => '-', + }), ExportColumn::make('motor_scanned_status') ->label('MOTOR SCANNED STATUS'), ExportColumn::make('pump_scanned_status') diff --git a/app/Filament/Resources/InvoiceValidationResource.php b/app/Filament/Resources/InvoiceValidationResource.php index cb053de..3e4d41d 100644 --- a/app/Filament/Resources/InvoiceValidationResource.php +++ b/app/Filament/Resources/InvoiceValidationResource.php @@ -284,6 +284,18 @@ class InvoiceValidationResource extends Resource ->alignCenter() ->sortable() // ->searchable() ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('stickerMaster.material_type') + ->label('Material Type') + ->alignCenter() // ->searchable() + ->sortable() + ->formatStateUsing(fn ($state) => match ($state) { + 1 => 'Individual', + 2 => 'Bundle', + 3 => 'Quantity', + 4 => 'Bundle Individual', + default => '-', + }) + ->toggleable(isToggledHiddenByDefault: true), Tables\Columns\TextColumn::make('serial_number') ->label('Serial Number') ->alignCenter() @@ -366,123 +378,101 @@ class InvoiceValidationResource extends Resource ->headerActions([ - // Action::make('plant_id') - // ->label('Select Plant') - // ->form([ - // Select::make('plant_id') - // ->options(Plant::pluck('name', 'id')->toArray()) // Fetch plant names and IDs - // ->label('Select Plant') - // ->required() - // ->reactive() - // ]), + // Action::make('plant_id') + // ->label('Select Plant') + // ->form([ + // Select::make('plant_id') + // ->options(Plant::pluck('name', 'id')->toArray()) // Fetch plant names and IDs + // ->label('Select Plant') + // ->required() + // ->reactive() + // ]), - Tables\Actions\Action::make('Import Serial Number') - ->label('Import Serial Invoice') - ->form([ - Select::make('plant_id') - // ->options(Plant::pluck('name', 'id')->toArray()) // Fetch plant names and IDs - ->options(function (callable $get) { - $userHas = Filament::auth()->user()->plant_id; + Tables\Actions\Action::make('Import Serial Number') + ->label('Import Serial Invoice') + ->form([ + Select::make('plant_id') + // ->options(Plant::pluck('name', 'id')->toArray()) // Fetch plant names and IDs + ->options(function (callable $get) { + $userHas = Filament::auth()->user()->plant_id; - return ($userHas && strlen($userHas) > 0) ? Plant::where('id', $userHas)->pluck('name', 'id')->toArray() : Plant::orderBy('code')->pluck('name', 'id')->toArray(); - }) - ->searchable() - ->label('Search by Plant Name') - ->required() - ->default(function () { - return optional(InvoiceValidation::latest()->first())->plant_id; - }) - ->afterStateUpdated(function ($state, callable $set, callable $get) { - $set('invoice_serial_number', null); - $plantId = $get('plant_id'); - if (! $plantId) { + return ($userHas && strlen($userHas) > 0) ? Plant::where('id', $userHas)->pluck('name', 'id')->toArray() : Plant::orderBy('code')->pluck('name', 'id')->toArray(); + }) + ->searchable() + ->label('Search by Plant Name') + ->required() + ->default(function () { + return optional(InvoiceValidation::latest()->first())->plant_id; + }) + ->afterStateUpdated(function ($state, callable $set, callable $get) { $set('invoice_serial_number', null); + $plantId = $get('plant_id'); + if (! $plantId) { + $set('invoice_serial_number', null); - return; - } - $plantCode = Plant::find($plantId)?->code ?? null; + return; + } + $plantCode = Plant::find($plantId)?->code ?? null; - $directory = "uploads/temp/{$plantCode}"; - if ($plantId && ! Storage::disk('local')->exists($directory)) { - Storage::disk('local')->makeDirectory($directory); - } - }) - ->reactive(), + $directory = "uploads/temp/{$plantCode}"; + if ($plantId && ! Storage::disk('local')->exists($directory)) { + Storage::disk('local')->makeDirectory($directory); + } + }) + ->reactive(), - FileUpload::make('invoice_serial_number') - ->label('Choose Serial Invoice') - ->required() - ->acceptedFileTypes([ - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - // 'application/vnd.ms-excel', // Legacy .xls fallback if needed - ]) - ->rules(['mimes:xlsx', 'max:50']) // Laravel validation: extension check - ->maxSize(50) - ->preserveFilenames() // <- this keeps the original filename - ->reactive() - ->storeFiles(false) // prevent auto-storing, we will store manually - ->disk('local') // 'local' refers to the local storage disk defined in config/filesystems.php, typically pointing to storage/app. - ->visible(fn (Get $get) => ! empty($get('plant_id'))) - ->directory(function (callable $get) { - $plant = Plant::find($get('plant_id')); - $plantCode = $plant?->code ?? null; + FileUpload::make('invoice_serial_number') + ->label('Choose Serial Invoice') + ->required() + ->acceptedFileTypes([ + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + // 'application/vnd.ms-excel', // Legacy .xls fallback if needed + ]) + ->rules(['mimes:xlsx', 'max:50']) // Laravel validation: extension check + ->maxSize(50) + ->preserveFilenames() // <- this keeps the original filename + ->reactive() + ->storeFiles(false) // prevent auto-storing, we will store manually + ->disk('local') // 'local' refers to the local storage disk defined in config/filesystems.php, typically pointing to storage/app. + ->visible(fn (Get $get) => ! empty($get('plant_id'))) + ->directory(function (callable $get) { + $plant = Plant::find($get('plant_id')); + $plantCode = $plant?->code ?? null; - return "uploads/temp/{$plantCode}"; - }) - ->uploadingMessage('Uploading...') - ->helperText('Only .xlsx files are allowed (Excel files).'), - ]) - ->action(function (array $data) { - $uploadedFile = $data['invoice_serial_number']; + return "uploads/temp/{$plantCode}"; + }) + ->uploadingMessage('Uploading...') + ->helperText('Only .xlsx files are allowed (Excel files).'), + ]) + ->action(function (array $data) { + $uploadedFile = $data['invoice_serial_number']; - $disk = Storage::disk('local'); + $disk = Storage::disk('local'); - $plantId = $data['plant_id']; + $plantId = $data['plant_id']; - $plant = Plant::find($plantId); - $plantCode = $plant?->code ?? null; + $plant = Plant::find($plantId); + $plantCode = $plant?->code ?? null; - // Get original filename - $originalName = $uploadedFile->getClientOriginalName(); // e.g. 3RA0018732.xlsx + // Get original filename + $originalName = $uploadedFile->getClientOriginalName(); // e.g. 3RA0018732.xlsx - $extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION)); - if ($extension != 'xlsx') { - throw new \Exception('Only .xlsx files allowed.'); - } - - $originalNameOnly = strtoupper(pathinfo($originalName, PATHINFO_FILENAME)); - - $originalName = "{$originalNameOnly}.xlsx"; - - // Store manually using storeAs to keep original name - $path = $uploadedFile->storeAs("uploads/temp/{$plantCode}", $originalName, 'local'); // returns relative path - // uploads/temp/{$plantCode}/3RA0018735.xlsx - - if (strlen($originalNameOnly) < 8 || ! ctype_alnum($originalNameOnly)) { - Notification::make() - ->title("Serial invoice number : '$originalNameOnly' should contain minimum 8 digit alpha numeric values!") - ->danger() - ->send(); - - if ($disk->exists($path)) { - $disk->delete($path); + $extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION)); + if ($extension != 'xlsx') { + throw new \Exception('Only .xlsx files allowed.'); } - return; - } + $originalNameOnly = strtoupper(pathinfo($originalName, PATHINFO_FILENAME)); - $fullPath = Storage::disk('local')->path($path); - // /home/iot-dev/projects/pds/storage/app/private/uploads/temp/{$plantCode}/3RA0018735.xlsx + $originalName = "{$originalNameOnly}.xlsx"; - $totQuan = InvoiceValidation::where('invoice_number', $originalNameOnly)->count(); - if ($totQuan > 0) { - $scanSQuan = InvoiceValidation::where('invoice_number', $originalNameOnly)->where('scanned_status', 'Scanned')->count(); - if ($totQuan == $scanSQuan) { - $invoiceFirst = InvoiceValidation::with('plant')->where('invoice_number', $originalNameOnly)->first(); - $plantName = $invoiceFirst ? (string) $invoiceFirst->plant->name : null; + // Store manually using storeAs to keep original name + $path = $uploadedFile->storeAs("uploads/temp/{$plantCode}", $originalName, 'local'); // returns relative path + // uploads/temp/{$plantCode}/3RA0018735.xlsx + if (strlen($originalNameOnly) < 8 || ! ctype_alnum($originalNameOnly)) { Notification::make() - ->title("Serial invoice number : '$originalNameOnly' already completed the scanning process for plant : '$plantName'.") + ->title("Serial invoice number : '$originalNameOnly' should contain minimum 8 digit alpha numeric values!") ->danger() ->send(); @@ -491,14 +481,71 @@ class InvoiceValidationResource extends Resource } return; - } else { - $invoiceFirst = InvoiceValidation::with('plant')->where('invoice_number', $originalNameOnly)->first(); - // $plantCode = $invoiceFirst ? (String)$invoiceFirst->plant->code : null; - $plantName = $invoiceFirst ? (string) $invoiceFirst->plant->name : null; - $invoicePlantId = $invoiceFirst->plant_id; - if ($plantId != $invoicePlantId) { + } + + $fullPath = Storage::disk('local')->path($path); + // /home/iot-dev/projects/pds/storage/app/private/uploads/temp/{$plantCode}/3RA0018735.xlsx + + $totQuan = InvoiceValidation::where('invoice_number', $originalNameOnly)->count(); + if ($totQuan > 0) { + $scanSQuan = InvoiceValidation::where('invoice_number', $originalNameOnly)->where('scanned_status', 'Scanned')->count(); + if ($totQuan == $scanSQuan) { + $invoiceFirst = InvoiceValidation::with('plant')->where('invoice_number', $originalNameOnly)->first(); + $plantName = $invoiceFirst ? (string) $invoiceFirst->plant->name : null; + Notification::make() - ->title("Serial invoice number : '$originalNameOnly' already exists for plant : '$plantName'.
Choose the valid 'Plant' to proceed!") + ->title("Serial invoice number : '$originalNameOnly' already completed the scanning process for plant : '$plantName'.") + ->danger() + ->send(); + + if ($disk->exists($path)) { + $disk->delete($path); + } + + return; + } else { + $invoiceFirst = InvoiceValidation::with('plant')->where('invoice_number', $originalNameOnly)->first(); + // $plantCode = $invoiceFirst ? (String)$invoiceFirst->plant->code : null; + $plantName = $invoiceFirst ? (string) $invoiceFirst->plant->name : null; + $invoicePlantId = $invoiceFirst->plant_id; + if ($plantId != $invoicePlantId) { + Notification::make() + ->title("Serial invoice number : '$originalNameOnly' already exists for plant : '$plantName'.
Choose the valid 'Plant' to proceed!") + ->danger() + ->send(); + + if ($disk->exists($path)) { + $disk->delete($path); + } + + return; + } + } + } + + $totQuan = InvoiceValidation::where('invoice_number', $originalNameOnly)->where('plant_id', $plantId)->count(); + $scanSQuan = InvoiceValidation::where('invoice_number', $originalNameOnly)->where('scanned_status', 'Scanned')->where('plant_id', $plantId)->count(); + + if ($totQuan > 0 && $totQuan == $scanSQuan) { + Notification::make() + ->title('Serial invoice already completed the scanning process for selected plant.') + ->danger() + ->send(); + + if ($disk->exists($path)) { + $disk->delete($path); + } + + return; + } + + if ($fullPath && file_exists($fullPath)) { + $rows = Excel::toArray(null, $fullPath)[0]; + + if ((count($rows) - 1) <= 0) { + Notification::make() + ->title('Records Not Found') + ->body("Import the valid 'Serial Invoice' file to proceed..!") ->danger() ->send(); @@ -508,32 +555,273 @@ class InvoiceValidationResource extends Resource return; } + + $invalidMatCodes = []; + $invalidSerialCodes = []; + $materialCodes = []; + $missingSerials = []; + $duplicateSerials = []; + $seenSerialNumbers = []; + $validRowsFound = false; + + foreach ($rows as $index => $row) { + if ($index == 0) { + continue; + } // Skip header + + $materialCode = trim($row[0]); + $serialNumber = trim($row[1]); + + if (empty($materialCode) && empty($serialNumber)) { + continue; + } + + if (! empty($materialCode)) { + if (Str::length($materialCode) < 6 || ! ctype_alnum($materialCode)) { + $invalidMatCodes[] = $materialCode; + } else { + if (empty($serialNumber)) { + $missingSerials[] = $materialCode; + + } elseif (Str::length($serialNumber) < 9 || ! ctype_alnum($serialNumber)) { + $invalidSerialCodes[] = $serialNumber; + } else { + if (in_array($serialNumber, $seenSerialNumbers)) { + $duplicateSerials[] = $serialNumber; + } else { + $seenSerialNumbers[] = $serialNumber; + $materialCodes[] = $materialCode; + $validRowsFound = true; + } + } + } + } else { + continue; + } + } + + $uniqueInvalidCodes = array_unique($invalidMatCodes); + + $uniqueMissingSerials = array_unique($missingSerials); + + $uniqueSerialCodes = array_unique($invalidSerialCodes); + + $duplicateSerialCodes = array_unique($duplicateSerials); + + if (! empty($uniqueInvalidCodes)) { + Notification::make() + ->title('Invalid Item Codes') + ->body('The following item codes should contain minimum 6 digit alpha numeric values:
'.implode(', ', $uniqueInvalidCodes)) + ->danger() + ->send(); + if ($disk->exists($path)) { + $disk->delete($path); + } + + return; + } elseif (! empty($uniqueMissingSerials)) { + Notification::make() + ->title('Missing Serial Numbers') + ->body("The following item codes doesn't have valid serial number:
".implode(', ', $uniqueMissingSerials)) + ->danger() + ->send(); + if ($disk->exists($path)) { + $disk->delete($path); + } + + return; + } elseif (! empty($uniqueSerialCodes)) { + Notification::make() + ->title('Invalid Serial Number') + ->body('The following serial numbers should contain minimum 9 digit alpha numeric values:
'.implode(', ', $uniqueSerialCodes)) + ->danger() + ->send(); + if ($disk->exists($path)) { + $disk->delete($path); + } + + return; + } elseif (! empty($duplicateSerialCodes)) { + Notification::make() + ->title('Duplicate Serial Numbers') + ->body('The following serial numbers are already exist in imported excel:
'.implode(', ', $duplicateSerialCodes)) + ->danger() + ->send(); + if ($disk->exists($path)) { + $disk->delete($path); + } + + return; + } elseif (! $validRowsFound) { + Notification::make() + ->title('Invalid Serial Invoice') + ->danger() // This makes the notification red to indicate an error + ->body('Uploaded Excel sheet is empty or
contains no valid data.') + ->send(); + if ($disk->exists($path)) { + $disk->delete($path); + } + + return; + } + + $uniqueCodes = array_unique($materialCodes); + + $matchedItems = StickerMaster::with('item')->where('plant_id', $plantId) + ->whereHas('item', function ($query) use ($uniqueCodes, $plantId) { + $query->whereIn('code', $uniqueCodes)->where('plant_id', $plantId); + }) + ->get(); + + $matchedCodes = $matchedItems->pluck('item.code')->toArray(); + + $missingCodes = array_diff($uniqueCodes, $matchedCodes); + + if (! empty($missingCodes)) { + $missingCount = count($missingCodes); + + $message = $missingCount > 10 ? "'$missingCount' item codes are not found in sticker master." : 'The following item codes are not found in sticker master:
'.implode(', ', $missingCodes); + + Notification::make() + ->title('Unknown Item Codes') + ->body($message) + ->danger() + ->send(); + + if ($disk->exists($path)) { + $disk->delete($path); + } + + return; + } + + // Check which codes have a material_type set (not null) + $invalidCodes = $matchedItems + ->filter(fn ($sticker) => ! empty($sticker->material_type)) // filter invalid + ->pluck('item.code') + ->toArray(); + + if (count($invalidCodes) > 10) { + Notification::make() + ->title('Invalid item codes found') + ->body(''.count($invalidCodes).' item codes have material type.') + ->danger() + ->send(); + + if ($disk->exists($path)) { + $disk->delete($path); + } + + return; + } elseif (count($invalidCodes) > 0) { + Notification::make() + ->title('Invalid item codes found') + ->body('Material invoice Item Codes found : '.implode(', ', $invalidCodes)) + ->danger() + ->send(); + + if ($disk->exists($path)) { + $disk->delete($path); + } + + return; + } else { + // Save full file path to session + session(['uploaded_invoice_path' => $fullPath]); + Notification::make() + ->title('Serial invoice imported successfully.') + ->success() + ->send(); + } } - } + }) + ->visible(function () { + return Filament::auth()->user()->can('view import serial invoice'); + }), + Tables\Actions\Action::make('Import Invoice Material') + ->label('Import Material Invoice') + ->form([ - $totQuan = InvoiceValidation::where('invoice_number', $originalNameOnly)->where('plant_id', $plantId)->count(); - $scanSQuan = InvoiceValidation::where('invoice_number', $originalNameOnly)->where('scanned_status', 'Scanned')->where('plant_id', $plantId)->count(); + Select::make('plant_id') + // ->options(Plant::pluck('name', 'id')->toArray()) // Fetch plant names and IDs + ->options(function (callable $get) { + $userHas = Filament::auth()->user()->plant_id; - if ($totQuan > 0 && $totQuan == $scanSQuan) { - Notification::make() - ->title('Serial invoice already completed the scanning process for selected plant.') - ->danger() - ->send(); + return ($userHas && strlen($userHas) > 0) ? Plant::where('id', $userHas)->pluck('name', 'id')->toArray() : Plant::orderBy('code')->pluck('name', 'id')->toArray(); + }) + ->searchable() + ->label('Search by Plant Name') + ->required() + ->default(function () { + return optional(InvoiceValidation::latest()->first())->plant_id; + }) + ->afterStateUpdated(function ($state, callable $set, callable $get) { + $set('invoice_material', null); + $plantId = $get('plant_id'); + if (! $plantId) { + $set('invoice_material', null); - if ($disk->exists($path)) { - $disk->delete($path); + return; + } + $plantCode = Plant::find($plantId)?->code ?? null; + + $directory = "uploads/temp/{$plantCode}"; + if ($plantId && ! Storage::disk('local')->exists($directory)) { + Storage::disk('local')->makeDirectory($directory); + } + }) + ->reactive(), + + FileUpload::make('invoice_material') + ->label('Choose Material Invoice') + ->required() + ->acceptedFileTypes([ + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + // 'application/vnd.ms-excel', // Legacy .xls fallback if needed + ]) + ->rules(['mimes:xlsx', 'max:50']) // Laravel validation: extension check + ->maxSize(50) + ->preserveFilenames() // <- this keeps the original filename + ->reactive() + ->storeFiles(false) // prevent auto-storing, we will store manually + ->disk('local') // 'local' refers to the local storage disk defined in config/filesystems.php, typically pointing to storage/app. + ->visible(fn (Get $get) => ! empty($get('plant_id'))) + ->directory(function (callable $get) { + $plant = Plant::find($get('plant_id')); + $plantCode = $plant?->code ?? null; + + return "uploads/temp/{$plantCode}"; + }) + ->uploadingMessage('Uploading...') + ->helperText('Only .xlsx files are allowed (Excel files).'), + ]) + ->action(function (array $data) { + $uploadedFile = $data['invoice_material']; + + $plantId = $data['plant_id']; // Access the selected plant_id + + $plant = Plant::find($plantId); + $plantCode = $plant?->code ?? null; + + $disk = Storage::disk('local'); + + // Get original filename + $originalName = $uploadedFile->getClientOriginalName(); + + $extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION)); + if ($extension != 'xlsx') { + throw new \Exception('Only .xlsx files allowed.'); } - return; - } + $originalNameOnly = strtoupper(pathinfo($originalName, PATHINFO_FILENAME)); - if ($fullPath && file_exists($fullPath)) { - $rows = Excel::toArray(null, $fullPath)[0]; + $originalName = "{$originalNameOnly}.xlsx"; - if ((count($rows) - 1) <= 0) { + $path = $uploadedFile->storeAs("uploads/temp/{$plantCode}", $originalName, 'local'); + + if (strlen($originalNameOnly) < 8 || ! ctype_alnum($originalNameOnly)) { Notification::make() - ->title('Records Not Found') - ->body("Import the valid 'Serial Invoice' file to proceed..!") + ->title("Material invoice number : '$originalNameOnly' should contain minimum 8 digit alpha numeric values!") ->danger() ->send(); @@ -544,309 +832,203 @@ class InvoiceValidationResource extends Resource return; } - $invalidMatCodes = []; - $invalidSerialCodes = []; - $materialCodes = []; - $missingSerials = []; - $duplicateSerials = []; - $seenSerialNumbers = []; - $validRowsFound = false; + $fullPath = Storage::disk('local')->path($path); - foreach ($rows as $index => $row) { - if ($index == 0) { - continue; - } // Skip header + $totQuan = InvoiceValidation::where('invoice_number', $originalNameOnly)->count(); + if ($totQuan > 0) { + $scanMQuan = InvoiceValidation::where('invoice_number', $originalNameOnly)->whereNotNull('serial_number')->where('serial_number', '!=', '')->count(); + if ($totQuan == $scanMQuan) { + $invoiceFirst = InvoiceValidation::with('plant')->where('invoice_number', $originalNameOnly)->first(); + $plantName = $invoiceFirst ? (string) $invoiceFirst->plant->name : null; - $materialCode = trim($row[0]); - $serialNumber = trim($row[1]); + Notification::make() + ->title("Material invoice number : '$originalNameOnly' already completed the scanning process for plant : '$plantName'.") + ->danger() + ->send(); - if (empty($materialCode) && empty($serialNumber)) { - continue; + if ($disk->exists($path)) { + $disk->delete($path); + } + + return; + } else { + $invoiceFirst = InvoiceValidation::with('plant')->where('invoice_number', $originalNameOnly)->first(); + // $plantCode = $invoiceFirst ? (String)$invoiceFirst->plant->code : null; + $plantName = $invoiceFirst ? (string) $invoiceFirst->plant->name : null; + $invoicePlantId = $invoiceFirst->plant_id; + if ($plantId != $invoicePlantId) { + Notification::make() + ->title("Material invoice number : '$originalNameOnly' already exists for plant : '$plantName'.
Choose the valid 'Plant' to proceed!") + ->danger() + ->send(); + + if ($disk->exists($path)) { + $disk->delete($path); + } + + return; + } + } + } + + $totQuan = InvoiceValidation::where('invoice_number', $originalNameOnly)->where('plant_id', $plantId)->count(); + $scanMQuan = InvoiceValidation::where('invoice_number', $originalNameOnly)->whereNotNull('serial_number')->where('serial_number', '!=', '')->where('plant_id', $plantId)->count(); + + if ($totQuan > 0 && $totQuan == $scanMQuan) { + Notification::make() + ->title('Material invoice already completed the scanning process for selected plant.') + ->danger() + ->send(); + if ($disk->exists($path)) { + $disk->delete($path); } - if (! empty($materialCode)) { - if (Str::length($materialCode) < 6 || ! ctype_alnum($materialCode)) { - $invalidMatCodes[] = $materialCode; - } else { - if (empty($serialNumber)) { - $missingSerials[] = $materialCode; + return; + } - } elseif (Str::length($serialNumber) < 9 || ! ctype_alnum($serialNumber)) { - $invalidSerialCodes[] = $serialNumber; + if ($fullPath && file_exists($fullPath)) { + $rows = Excel::toArray(null, $fullPath)[0]; + + if ((count($rows) - 1) <= 0) { + Notification::make() + ->title('Records Not Found') + ->body("Import the valid 'Material Invoice' file to proceed..!") + ->danger() + ->send(); + + if ($disk->exists($path)) { + $disk->delete($path); + } + + return; + } + + $invalidMatCodes = []; + $materialCodes = []; + $missingQuantities = []; + $invalidMatQuan = []; + $invalidMaterialQuan = []; + $validRowsFound = false; + + foreach ($rows as $index => $row) { + if ($index == 0) { + continue; + } // Skip header + + $materialCode = trim($row[0]); + $materialQuantity = trim($row[1]); + + if (empty($materialCode) && empty($materialQuantity)) { + continue; + } + + if (! empty($materialCode)) { + if (Str::length($materialCode) < 6 || ! ctype_alnum($materialCode)) { + $invalidMatCodes[] = $materialCode; } else { - if (in_array($serialNumber, $seenSerialNumbers)) { - $duplicateSerials[] = $serialNumber; + if ($materialQuantity == 0) { + $invalidMaterialQuan[] = $materialCode; + } elseif (empty($materialQuantity)) { + $missingQuantities[] = $materialCode; + } elseif (! is_numeric($materialQuantity)) { + $invalidMatQuan[] = $materialCode; } else { - $seenSerialNumbers[] = $serialNumber; $materialCodes[] = $materialCode; $validRowsFound = true; } } + } else { + continue; } - } else { - continue; - } - } - - $uniqueInvalidCodes = array_unique($invalidMatCodes); - - $uniqueMissingSerials = array_unique($missingSerials); - - $uniqueSerialCodes = array_unique($invalidSerialCodes); - - $duplicateSerialCodes = array_unique($duplicateSerials); - - if (! empty($uniqueInvalidCodes)) { - Notification::make() - ->title('Invalid Item Codes') - ->body('The following item codes should contain minimum 6 digit alpha numeric values:
'.implode(', ', $uniqueInvalidCodes)) - ->danger() - ->send(); - if ($disk->exists($path)) { - $disk->delete($path); } - return; - } elseif (! empty($uniqueMissingSerials)) { - Notification::make() - ->title('Missing Serial Numbers') - ->body("The following item codes doesn't have valid serial number:
".implode(', ', $uniqueMissingSerials)) - ->danger() - ->send(); - if ($disk->exists($path)) { - $disk->delete($path); - } + $uniqueInvalidCodes = array_unique($invalidMatCodes); + $uniqueaplhaMat = array_unique($invalidMatQuan); + $uniqueZeroMat = array_unique($invalidMaterialQuan); + $uniqueEmptyMat = array_unique($missingQuantities); - return; - } elseif (! empty($uniqueSerialCodes)) { - Notification::make() - ->title('Invalid Serial Number') - ->body('The following serial numbers should contain minimum 9 digit alpha numeric values:
'.implode(', ', $uniqueSerialCodes)) - ->danger() - ->send(); - if ($disk->exists($path)) { - $disk->delete($path); - } + if (! empty($uniqueInvalidCodes)) { + Notification::make() + ->title('Invalid Item Codes') + ->body('The following item codes should contain minimum 6 digit alpha numeric values:
'.implode(', ', $uniqueInvalidCodes)) + ->danger() + ->send(); - return; - } elseif (! empty($duplicateSerialCodes)) { - Notification::make() - ->title('Duplicate Serial Numbers') - ->body('The following serial numbers are already exist in imported excel:
'.implode(', ', $duplicateSerialCodes)) - ->danger() - ->send(); - if ($disk->exists($path)) { - $disk->delete($path); - } + if ($disk->exists($path)) { + $disk->delete($path); + } - return; - } elseif (! $validRowsFound) { - Notification::make() - ->title('Invalid Serial Invoice') - ->danger() // This makes the notification red to indicate an error - ->body('Uploaded Excel sheet is empty or
contains no valid data.') - ->send(); - if ($disk->exists($path)) { - $disk->delete($path); - } + return; + } elseif (! empty($uniqueaplhaMat)) { + Notification::make() + ->title('Invalid Material Quantity') + ->body('The following item codes material quantity must be a numeric values :
'.implode(', ', $uniqueaplhaMat)) + ->danger() + ->send(); - return; - } + if ($disk->exists($path)) { + $disk->delete($path); + } - $uniqueCodes = array_unique($materialCodes); + return; + } elseif (! empty($uniqueZeroMat)) { + Notification::make() + ->title('Invalid Material Quantity') + ->body('The following item codes material quantity should be greater than 0:
'.implode(', ', $uniqueZeroMat)) + ->danger() + ->send(); - $matchedItems = StickerMaster::with('item')->where('plant_id', $plantId) - ->whereHas('item', function ($query) use ($uniqueCodes, $plantId) { - $query->whereIn('code', $uniqueCodes)->where('plant_id', $plantId); - }) - ->get(); + if ($disk->exists($path)) { + $disk->delete($path); + } - $matchedCodes = $matchedItems->pluck('item.code')->toArray(); + return; + } elseif (! empty($uniqueEmptyMat)) { + Notification::make() + ->title('Missing Material Quantity') + ->body("The following item codes doesn't have valid material quantity:
".implode(', ', $uniqueEmptyMat)) + ->danger() + ->send(); - $missingCodes = array_diff($uniqueCodes, $matchedCodes); + if ($disk->exists($path)) { + $disk->delete($path); + } - if (! empty($missingCodes)) { - $missingCount = count($missingCodes); + return; + } elseif (! $validRowsFound) { + Notification::make() + ->title('Invalid Material Invoice') + ->danger() // This makes the notification red to indicate an error + ->body('Uploaded Excel sheet is empty or
contains no valid data.') + ->send(); - $message = $missingCount > 10 ? "'$missingCount' item codes are not found in sticker master." : 'The following item codes are not found in sticker master:
'.implode(', ', $missingCodes); - - Notification::make() - ->title('Unknown Item Codes') - ->body($message) - ->danger() - ->send(); - - if ($disk->exists($path)) { - $disk->delete($path); - } - - return; - } - - // Check which codes have a material_type set (not null) - $invalidCodes = $matchedItems - ->filter(fn ($sticker) => ! empty($sticker->material_type)) // filter invalid - ->pluck('item.code') - ->toArray(); - - if (count($invalidCodes) > 10) { - Notification::make() - ->title('Invalid item codes found') - ->body(''.count($invalidCodes).' item codes have material type.') - ->danger() - ->send(); - - if ($disk->exists($path)) { - $disk->delete($path); - } - - return; - } elseif (count($invalidCodes) > 0) { - Notification::make() - ->title('Invalid item codes found') - ->body('Material invoice Item Codes found : '.implode(', ', $invalidCodes)) - ->danger() - ->send(); - - if ($disk->exists($path)) { - $disk->delete($path); - } - - return; - } else { - // Save full file path to session - session(['uploaded_invoice_path' => $fullPath]); - Notification::make() - ->title('Serial invoice imported successfully.') - ->success() - ->send(); - } - } - }) - ->visible(function () { - return Filament::auth()->user()->can('view import serial invoice'); - }), - Tables\Actions\Action::make('Import Invoice Material') - ->label('Import Material Invoice') - ->form([ - - Select::make('plant_id') - // ->options(Plant::pluck('name', 'id')->toArray()) // Fetch plant names and IDs - ->options(function (callable $get) { - $userHas = Filament::auth()->user()->plant_id; - - return ($userHas && strlen($userHas) > 0) ? Plant::where('id', $userHas)->pluck('name', 'id')->toArray() : Plant::orderBy('code')->pluck('name', 'id')->toArray(); - }) - ->searchable() - ->label('Search by Plant Name') - ->required() - ->default(function () { - return optional(InvoiceValidation::latest()->first())->plant_id; - }) - ->afterStateUpdated(function ($state, callable $set, callable $get) { - $set('invoice_material', null); - $plantId = $get('plant_id'); - if (! $plantId) { - $set('invoice_material', null); + if ($disk->exists($path)) { + $disk->delete($path); + } return; } - $plantCode = Plant::find($plantId)?->code ?? null; - $directory = "uploads/temp/{$plantCode}"; - if ($plantId && ! Storage::disk('local')->exists($directory)) { - Storage::disk('local')->makeDirectory($directory); - } - }) - ->reactive(), + $uniqueCodes = array_unique($materialCodes); - FileUpload::make('invoice_material') - ->label('Choose Material Invoice') - ->required() - ->acceptedFileTypes([ - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - // 'application/vnd.ms-excel', // Legacy .xls fallback if needed - ]) - ->rules(['mimes:xlsx', 'max:50']) // Laravel validation: extension check - ->maxSize(50) - ->preserveFilenames() // <- this keeps the original filename - ->reactive() - ->storeFiles(false) // prevent auto-storing, we will store manually - ->disk('local') // 'local' refers to the local storage disk defined in config/filesystems.php, typically pointing to storage/app. - ->visible(fn (Get $get) => ! empty($get('plant_id'))) - ->directory(function (callable $get) { - $plant = Plant::find($get('plant_id')); - $plantCode = $plant?->code ?? null; + $matchedItems = StickerMaster::with('item')->where('plant_id', $plantId) + ->whereHas('item', function ($query) use ($uniqueCodes, $plantId) { + $query->whereIn('code', $uniqueCodes)->where('plant_id', $plantId); + }) + ->get(); - return "uploads/temp/{$plantCode}"; - }) - ->uploadingMessage('Uploading...') - ->helperText('Only .xlsx files are allowed (Excel files).'), - ]) - ->action(function (array $data) { - $uploadedFile = $data['invoice_material']; + $matchedCodes = $matchedItems->pluck('item.code')->toArray(); - $plantId = $data['plant_id']; // Access the selected plant_id + $missingCodes = array_diff($uniqueCodes, $matchedCodes); - $plant = Plant::find($plantId); - $plantCode = $plant?->code ?? null; + if (! empty($missingCodes)) { + $missingCount = count($missingCodes); - $disk = Storage::disk('local'); + $message = $missingCount > 10 ? "'$missingCount' item codes are not found in sticker master." : 'The following item codes are not found in sticker master:
'.implode(', ', $missingCodes); - // Get original filename - $originalName = $uploadedFile->getClientOriginalName(); - - $extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION)); - if ($extension != 'xlsx') { - throw new \Exception('Only .xlsx files allowed.'); - } - - $originalNameOnly = strtoupper(pathinfo($originalName, PATHINFO_FILENAME)); - - $originalName = "{$originalNameOnly}.xlsx"; - - $path = $uploadedFile->storeAs("uploads/temp/{$plantCode}", $originalName, 'local'); - - if (strlen($originalNameOnly) < 8 || ! ctype_alnum($originalNameOnly)) { - Notification::make() - ->title("Material invoice number : '$originalNameOnly' should contain minimum 8 digit alpha numeric values!") - ->danger() - ->send(); - - if ($disk->exists($path)) { - $disk->delete($path); - } - - return; - } - - $fullPath = Storage::disk('local')->path($path); - - $totQuan = InvoiceValidation::where('invoice_number', $originalNameOnly)->count(); - if ($totQuan > 0) { - $scanMQuan = InvoiceValidation::where('invoice_number', $originalNameOnly)->whereNotNull('serial_number')->where('serial_number', '!=', '')->count(); - if ($totQuan == $scanMQuan) { - $invoiceFirst = InvoiceValidation::with('plant')->where('invoice_number', $originalNameOnly)->first(); - $plantName = $invoiceFirst ? (string) $invoiceFirst->plant->name : null; - - Notification::make() - ->title("Material invoice number : '$originalNameOnly' already completed the scanning process for plant : '$plantName'.") - ->danger() - ->send(); - - if ($disk->exists($path)) { - $disk->delete($path); - } - - return; - } else { - $invoiceFirst = InvoiceValidation::with('plant')->where('invoice_number', $originalNameOnly)->first(); - // $plantCode = $invoiceFirst ? (String)$invoiceFirst->plant->code : null; - $plantName = $invoiceFirst ? (string) $invoiceFirst->plant->name : null; - $invoicePlantId = $invoiceFirst->plant_id; - if ($plantId != $invoicePlantId) { Notification::make() - ->title("Material invoice number : '$originalNameOnly' already exists for plant : '$plantName'.
Choose the valid 'Plant' to proceed!") + ->title('Unknown Item Codes') + ->body($message) ->danger() ->send(); @@ -856,570 +1038,400 @@ class InvoiceValidationResource extends Resource return; } - } - } - $totQuan = InvoiceValidation::where('invoice_number', $originalNameOnly)->where('plant_id', $plantId)->count(); - $scanMQuan = InvoiceValidation::where('invoice_number', $originalNameOnly)->whereNotNull('serial_number')->where('serial_number', '!=', '')->where('plant_id', $plantId)->count(); + $invalidCodes = $matchedItems + ->filter(fn ($sticker) => empty($sticker->material_type)) // filter invalid + ->pluck('item.code') + ->toArray(); - if ($totQuan > 0 && $totQuan == $scanMQuan) { - Notification::make() - ->title('Material invoice already completed the scanning process for selected plant.') - ->danger() - ->send(); - if ($disk->exists($path)) { - $disk->delete($path); - } + if (count($invalidCodes) > 10) { + $invalidCodes = array_unique($invalidCodes); + Notification::make() + ->title('Invalid item codes found') + ->body(''.count($invalidCodes).' item codes have serial type.') + ->danger() + ->send(); - return; - } - - if ($fullPath && file_exists($fullPath)) { - $rows = Excel::toArray(null, $fullPath)[0]; - - if ((count($rows) - 1) <= 0) { - Notification::make() - ->title('Records Not Found') - ->body("Import the valid 'Material Invoice' file to proceed..!") - ->danger() - ->send(); - - if ($disk->exists($path)) { - $disk->delete($path); - } - - return; - } - - $invalidMatCodes = []; - $materialCodes = []; - $missingQuantities = []; - $invalidMatQuan = []; - $invalidMaterialQuan = []; - $validRowsFound = false; - - foreach ($rows as $index => $row) { - if ($index == 0) { - continue; - } // Skip header - - $materialCode = trim($row[0]); - $materialQuantity = trim($row[1]); - - if (empty($materialCode) && empty($materialQuantity)) { - continue; - } - - if (! empty($materialCode)) { - if (Str::length($materialCode) < 6 || ! ctype_alnum($materialCode)) { - $invalidMatCodes[] = $materialCode; - } else { - if ($materialQuantity == 0) { - $invalidMaterialQuan[] = $materialCode; - } elseif (empty($materialQuantity)) { - $missingQuantities[] = $materialCode; - } elseif (! is_numeric($materialQuantity)) { - $invalidMatQuan[] = $materialCode; - } else { - $materialCodes[] = $materialCode; - $validRowsFound = true; - } - } - } else { - continue; - } - } - - $uniqueInvalidCodes = array_unique($invalidMatCodes); - $uniqueaplhaMat = array_unique($invalidMatQuan); - $uniqueZeroMat = array_unique($invalidMaterialQuan); - $uniqueEmptyMat = array_unique($missingQuantities); - - if (! empty($uniqueInvalidCodes)) { - Notification::make() - ->title('Invalid Item Codes') - ->body('The following item codes should contain minimum 6 digit alpha numeric values:
'.implode(', ', $uniqueInvalidCodes)) - ->danger() - ->send(); - - if ($disk->exists($path)) { - $disk->delete($path); - } - - return; - } elseif (! empty($uniqueaplhaMat)) { - Notification::make() - ->title('Invalid Material Quantity') - ->body('The following item codes material quantity must be a numeric values :
'.implode(', ', $uniqueaplhaMat)) - ->danger() - ->send(); - - if ($disk->exists($path)) { - $disk->delete($path); - } - - return; - } elseif (! empty($uniqueZeroMat)) { - Notification::make() - ->title('Invalid Material Quantity') - ->body('The following item codes material quantity should be greater than 0:
'.implode(', ', $uniqueZeroMat)) - ->danger() - ->send(); - - if ($disk->exists($path)) { - $disk->delete($path); - } - - return; - } elseif (! empty($uniqueEmptyMat)) { - Notification::make() - ->title('Missing Material Quantity') - ->body("The following item codes doesn't have valid material quantity:
".implode(', ', $uniqueEmptyMat)) - ->danger() - ->send(); - - if ($disk->exists($path)) { - $disk->delete($path); - } - - return; - } elseif (! $validRowsFound) { - Notification::make() - ->title('Invalid Material Invoice') - ->danger() // This makes the notification red to indicate an error - ->body('Uploaded Excel sheet is empty or
contains no valid data.') - ->send(); - - if ($disk->exists($path)) { - $disk->delete($path); - } - - return; - } - - $uniqueCodes = array_unique($materialCodes); - - $matchedItems = StickerMaster::with('item')->where('plant_id', $plantId) - ->whereHas('item', function ($query) use ($uniqueCodes, $plantId) { - $query->whereIn('code', $uniqueCodes)->where('plant_id', $plantId); - }) - ->get(); - - $matchedCodes = $matchedItems->pluck('item.code')->toArray(); - - $missingCodes = array_diff($uniqueCodes, $matchedCodes); - - if (! empty($missingCodes)) { - $missingCount = count($missingCodes); - - $message = $missingCount > 10 ? "'$missingCount' item codes are not found in sticker master." : 'The following item codes are not found in sticker master:
'.implode(', ', $missingCodes); - - Notification::make() - ->title('Unknown Item Codes') - ->body($message) - ->danger() - ->send(); - - if ($disk->exists($path)) { - $disk->delete($path); - } - - return; - } - - $invalidCodes = $matchedItems - ->filter(fn ($sticker) => empty($sticker->material_type)) // filter invalid - ->pluck('item.code') - ->toArray(); - - if (count($invalidCodes) > 10) { - $invalidCodes = array_unique($invalidCodes); - Notification::make() - ->title('Invalid item codes found') - ->body(''.count($invalidCodes).' item codes have serial type.') - ->danger() - ->send(); - - if ($disk->exists($path)) { - $disk->delete($path); - } - - return; - } elseif (count($invalidCodes) > 0) { - $invalidCodes = array_unique($invalidCodes); - Notification::make() - ->title('Invalid item codes found') - ->body('Serial invoice Item Codes found : '.implode(', ', $invalidCodes)) - ->danger() - ->send(); - - if ($disk->exists($path)) { - $disk->delete($path); - } - - return; - } - - $nonNumericQtyCodes = []; - $zeroQtyCodes = []; - $notDivisibleCodes = []; - - foreach ($matchedItems as $sticker) { - $code = $sticker->item->code; - $materialType = $sticker->material_type; - - if ($materialType == 2) { - $bundleQty = $sticker->bundle_quantity ?? 0; - $totalExcelQty = 0; - - foreach ($rows as $index => $row) { - if ($index == 0) { - continue; - } // Skip header - - $excelCode = trim($row[0]); - $excelMatQty = trim($row[1]); - - if ($excelCode == $code && is_numeric($excelMatQty)) { - $totalExcelQty += $excelMatQty; // Sum up the quantities - } + if ($disk->exists($path)) { + $disk->delete($path); } - if ($totalExcelQty == 0) { - $zeroQtyCodes[] = $code; - } elseif (! is_numeric($totalExcelQty)) { - $nonNumericQtyCodes[] = $code; // Here you may want to check divisibility condition too - } elseif ($bundleQty != 0 && $totalExcelQty % $bundleQty != 0) { - $notDivisibleCodes[] = $code; - } - } - } + return; + } elseif (count($invalidCodes) > 0) { + $invalidCodes = array_unique($invalidCodes); + Notification::make() + ->title('Invalid item codes found') + ->body('Serial invoice Item Codes found : '.implode(', ', $invalidCodes)) + ->danger() + ->send(); + + if ($disk->exists($path)) { + $disk->delete($path); + } - $showValidationNotification = function (array $codes, string $message) { - if (count($codes) == 0) { return; } - $uniqueCodes = array_unique($codes); - $codeList = implode(', ', $uniqueCodes); + $nonNumericQtyCodes = []; + $zeroQtyCodes = []; + $notDivisibleCodes = []; - Notification::make() - ->title('Invalid Bundle Quantity') - ->body("$message
$codeList") - ->danger() - ->send(); - }; + foreach ($matchedItems as $sticker) { + $code = $sticker->item->code; + $materialType = $sticker->material_type; - $nonNumericQtyCodes = array_unique($nonNumericQtyCodes); - $zeroQtyCodes = array_unique($zeroQtyCodes); - $notDivisibleCodes = array_unique($notDivisibleCodes); + if ($materialType == 2) { + $bundleQty = $sticker->bundle_quantity ?? 0; + $totalExcelQty = 0; - $showValidationNotification($nonNumericQtyCodes, 'The following item codes contains invalid bundle quantity:'); - $showValidationNotification($zeroQtyCodes, "The following item codes quantity should be greater than '0':"); - $showValidationNotification($notDivisibleCodes, 'The following item codes quantity is not divisible by bundle quantity.'); + foreach ($rows as $index => $row) { + if ($index == 0) { + continue; + } // Skip header - if ($nonNumericQtyCodes || $zeroQtyCodes || $notDivisibleCodes) { - if ($disk->exists($path)) { - $disk->delete($path); + $excelCode = trim($row[0]); + $excelMatQty = trim($row[1]); + + if ($excelCode == $code && is_numeric($excelMatQty)) { + $totalExcelQty += $excelMatQty; // Sum up the quantities + } + } + + if ($totalExcelQty == 0) { + $zeroQtyCodes[] = $code; + } elseif (! is_numeric($totalExcelQty)) { + $nonNumericQtyCodes[] = $code; // Here you may want to check divisibility condition too + } elseif ($bundleQty != 0 && $totalExcelQty % $bundleQty != 0) { + $notDivisibleCodes[] = $code; + } + } } - return; - } else { - // Save full file path to session - session(['uploaded_material_invoice' => $fullPath]); - Notification::make() - ->title('Material invoice imported successfully.') - ->success() - ->send(); - } + $showValidationNotification = function (array $codes, string $message) { + if (count($codes) == 0) { + return; + } - } - }) - ->visible(function () { - return Filament::auth()->user()->can('view import material invoice'); - }), - ImportAction::make() - ->label('Import Invoices') - ->color('warning') - ->importer(InvoiceValidationImporter::class) - ->visible(function () { - return Filament::auth()->user()->can('view import invoice'); - }), - ExportAction::make('invoice_export') - ->label('Export Invoices') - ->color('warning') - ->exporter(InvoiceValidationExporter::class) - ->visible(function () { - return Filament::auth()->user()->can('view export invoice'); - }), - ExportAction::make('axn_export') - ->label('Export Asn') - ->color('warning') - ->exporter(AxnExporter::class) - ->visible(function () { - return Filament::auth()->user()->can('view export asn invoice'); - }), + $uniqueCodes = array_unique($codes); + $codeList = implode(', ', $uniqueCodes); + + Notification::make() + ->title('Invalid Bundle Quantity') + ->body("$message
$codeList") + ->danger() + ->send(); + }; + + $nonNumericQtyCodes = array_unique($nonNumericQtyCodes); + $zeroQtyCodes = array_unique($zeroQtyCodes); + $notDivisibleCodes = array_unique($notDivisibleCodes); + + $showValidationNotification($nonNumericQtyCodes, 'The following item codes contains invalid bundle quantity:'); + $showValidationNotification($zeroQtyCodes, "The following item codes quantity should be greater than '0':"); + $showValidationNotification($notDivisibleCodes, 'The following item codes quantity is not divisible by bundle quantity.'); + + if ($nonNumericQtyCodes || $zeroQtyCodes || $notDivisibleCodes) { + if ($disk->exists($path)) { + $disk->delete($path); + } + + return; + } else { + // Save full file path to session + session(['uploaded_material_invoice' => $fullPath]); + Notification::make() + ->title('Material invoice imported successfully.') + ->success() + ->send(); + } + + } + }) + ->visible(function () { + return Filament::auth()->user()->can('view import material invoice'); + }), + ImportAction::make() + ->label('Import Invoices') + ->color('warning') + ->importer(InvoiceValidationImporter::class) + ->visible(function () { + return Filament::auth()->user()->can('view import invoice'); + }), + ExportAction::make('invoice_export') + ->label('Export Invoices') + ->color('warning') + ->exporter(InvoiceValidationExporter::class) + ->visible(function () { + return Filament::auth()->user()->can('view export invoice'); + }), + ExportAction::make('axn_export') + ->label('Export Asn') + ->color('warning') + ->exporter(AxnExporter::class) + ->visible(function () { + return Filament::auth()->user()->can('view export asn invoice'); + }), ]) ->filters([ Tables\Filters\TrashedFilter::make(), Filter::make('advanced_filters') - ->label('Advanced Filters') - ->form([ - Radio::make('invoice_type') - ->label('Type ?') - ->boolean() - // ->options([ - // 'Serial' => 'Serial', - // 'Material' => 'Material', - // ]) - ->options(function () { - $userRights = Filament::auth()->user(); - $hasBoth = ($userRights->hasRole(['Super Admin', 'Sales Employee', 'Sales Supervisor Hub', 'Sales Supervisor Hub Upload', 'Sales Manager']) ?? null); + ->label('Advanced Filters') + ->form([ + Radio::make('invoice_type') + ->label('Type ?') + ->boolean() + // ->options([ + // 'Serial' => 'Serial', + // 'Material' => 'Material', + // ]) + ->options(function () { + $userRights = Filament::auth()->user(); + $hasBoth = ($userRights->hasRole(['Super Admin', 'Sales Employee', 'Sales Supervisor Hub', 'Sales Supervisor Hub Upload', 'Sales Manager']) ?? null); - $hasSerial = ($userRights->hasRole(['Sales Employee 1', 'Sales Supervisor 1', 'Sales Supervisor Upload 1']) ?? null); // , 'Sales Supervisor Inventory', 'Sales Supervisor Inventory Upload', 'Sales Employee Inventory' + $hasSerial = ($userRights->hasRole(['Sales Employee 1', 'Sales Supervisor 1', 'Sales Supervisor Upload 1']) ?? null); // , 'Sales Supervisor Inventory', 'Sales Supervisor Inventory Upload', 'Sales Employee Inventory' - $hasMaterial = ($userRights->hasRole(['Sales Employee 2', 'Sales Supervisor 2', 'Sales Supervisor Upload 2']) ?? null); + $hasMaterial = ($userRights->hasRole(['Sales Employee 2', 'Sales Supervisor 2', 'Sales Supervisor Upload 2']) ?? null); - $retRes = [ - 'Serial' => 'Serial', - 'Material' => 'Material', - ]; + $retRes = [ + 'Serial' => 'Serial', + 'Material' => 'Material', + ]; - if ($hasBoth) { - return $retRes; - } elseif ($hasSerial) { - return ['Serial' => 'Serial']; - } elseif ($hasMaterial) { - return ['Material' => 'Material']; - } else { - return ['Serial' => 'Serial']; // return []; + if ($hasBoth) { + return $retRes; + } elseif ($hasSerial) { + return ['Serial' => 'Serial']; + } elseif ($hasMaterial) { + return ['Material' => 'Material']; + } else { + return ['Serial' => 'Serial']; // return []; + } + }) + // ->default('Serial') + ->default(function () { + $userRights = Filament::auth()->user(); + $hasBoth = ($userRights->hasRole(['Super Admin', 'Sales Employee', 'Sales Supervisor Hub', 'Sales Supervisor Hub Upload', 'Sales Manager']) ?? null); + + $hasSerial = ($userRights->hasRole(['Sales Employee 1', 'Sales Supervisor 1', 'Sales Supervisor Upload 1']) ?? null); // , 'Sales Supervisor Inventory', 'Sales Supervisor Inventory Upload', 'Sales Employee Inventory' + + $hasMaterial = ($userRights->hasRole(['Sales Employee 2', 'Sales Supervisor 2', 'Sales Supervisor Upload 2']) ?? null); + + if ($hasBoth || $hasSerial) { + return 'Serial'; + } elseif ($hasMaterial) { + return 'Material'; + } else { + return 'Serial'; // return []; + } + }) + ->inlineLabel(false) + ->inline(), + Select::make('Plant') + ->label('Search by Plant Name') + ->nullable() + ->searchable() + ->reactive() + ->options(function (callable $get) { + $userHas = Filament::auth()->user()->plant_id; + + if ($userHas && strlen($userHas) > 0) { + return Plant::where('id', $userHas)->pluck('name', 'id')->toArray(); + } else { + return Plant::whereHas('invoiceValidations', function ($query) { + $query->whereNotNull('id'); + })->orderBy('code')->pluck('name', 'id'); + } + + // return ($userHas && strlen($userHas) > 0) ? Plant::where('id', $userHas)->pluck('name', 'id')->toArray() : Plant::orderBy('code')->pluck('name', 'id')->toArray(); + }) + ->afterStateUpdated(function ($state, callable $set, callable $get): void { + $set('sticker_master_id', null); + $set('operator_id', null); + }), + TextInput::make('invoice_number') + ->label('Invoice Number') + ->placeholder(placeholder: 'Enter Invoice Number'), + TextInput::make('serial_number') + ->label('Serial Number') + ->placeholder(placeholder: 'Enter Serial Number'), + Select::make('sticker_master_id') + ->label('Search by Item Code') + ->nullable() + ->searchable() + ->reactive() + ->options(function (callable $get) { + $pId = $get('Plant'); + + // if (empty($pId)) { + // return []; + // } + return Item::whereHas('stickerMasters', function ($query) use ($pId) { + if ($pId) { + $query->where('plant_id', $pId); + } + $query->whereHas('invoiceValidations'); + })->pluck('code', 'id'); + }), + Select::make('scanned_status') + ->label('Scanned Status') + ->nullable() + ->options([ + 'Scanned' => 'Scanned', + 'Pending' => 'Pending', + ]) + ->searchable() + ->reactive(), + Select::make('operator_id') + ->label('Created By') + ->nullable() + ->options(function (callable $get) { + $plantId = $get('Plant'); + if (! $plantId) { + return InvoiceValidation::whereNotNull('operator_id')->select('operator_id')->distinct()->pluck('operator_id', 'operator_id'); + } else { + return InvoiceValidation::where('plant_id', $plantId)->whereNotNull('operator_id')->select('operator_id')->distinct()->pluck('operator_id', 'operator_id'); + } + }) + ->searchable() + ->reactive(), + DateTimePicker::make(name: 'created_from') + ->label('Created From') + ->placeholder(placeholder: 'Select From DateTime') + ->reactive() + ->native(false), + DateTimePicker::make('created_to') + ->label('Created To') + ->placeholder(placeholder: 'Select To DateTime') + ->reactive() + ->native(false), + ]) + ->query(function ($query, array $data) { + // Hide all records initially if no filters are applied + if (empty($data['invoice_type']) || (empty($data['Plant']) && empty($data['invoice_number']) && empty($data['serial_number']) && empty($data['created_from']) && empty($data['created_to']) && empty($data['operator_id']) && empty($data['scanned_status']) && empty($data['sticker_master_id']))) { + if (empty($data['invoice_type'])) { + Notification::make() + ->title('Please, choose invoice type to filter.') + ->danger() + ->send(); } - }) - // ->default('Serial') - ->default(function () { - $userRights = Filament::auth()->user(); - $hasBoth = ($userRights->hasRole(['Super Admin', 'Sales Employee', 'Sales Supervisor Hub', 'Sales Supervisor Hub Upload', 'Sales Manager']) ?? null); - $hasSerial = ($userRights->hasRole(['Sales Employee 1', 'Sales Supervisor 1', 'Sales Supervisor Upload 1']) ?? null); // , 'Sales Supervisor Inventory', 'Sales Supervisor Inventory Upload', 'Sales Employee Inventory' + return $query->whereRaw('1 = 0'); + } - $hasMaterial = ($userRights->hasRole(['Sales Employee 2', 'Sales Supervisor 2', 'Sales Supervisor Upload 2']) ?? null); + if ($data['invoice_type'] == 'Serial') { + $query->whereNull('quantity'); - if ($hasBoth || $hasSerial) { - return 'Serial'; - } elseif ($hasMaterial) { - return 'Material'; - } else { - return 'Serial'; // return []; + if (! empty($data['scanned_status'])) { + if ($data['scanned_status'] == 'Scanned') { + $query->whereNotNull('scanned_status')->where('scanned_status', '!=', ''); + } elseif ($data['scanned_status'] == 'Pending') { + // $query->whereNull('scanned_status')->orWhere('scanned_status', ''); + $query->where(function ($query) { + // if (empty($data['scanned_status']) || $data['scanned_status'] == 'Pending') { + $query->whereNull('scanned_status')->orWhere('scanned_status', '!=', 'Scanned'); + // } + }); + } } - }) - ->inlineLabel(false) - ->inline(), - Select::make('Plant') - ->label('Search by Plant Name') - ->nullable() - ->searchable() - ->reactive() - ->options(function (callable $get) { + } elseif ($data['invoice_type'] == 'Material') { + $query->whereNotNull('quantity'); // ->where('quantity', '>', 0) + + if (! empty($data['scanned_status'])) { + if ($data['scanned_status'] == 'Scanned') { + $query->whereNotNull('serial_number')->where('serial_number', '!=', ''); + } elseif ($data['scanned_status'] == 'Pending') { + $query->where(function ($query) { + $query->whereNull('serial_number')->orWhere('serial_number', '=', ''); + }); + } + } + } + + if (! empty($data['Plant'])) { // $plant = $data['Plant'] ?? null + $query->where('plant_id', $data['Plant']); + } else { $userHas = Filament::auth()->user()->plant_id; if ($userHas && strlen($userHas) > 0) { - return Plant::where('id', $userHas)->pluck('name', 'id')->toArray(); - } else { - return Plant::whereHas('invoiceValidations', function ($query) { - $query->whereNotNull('id'); - })->orderBy('code')->pluck('name', 'id'); + return $query->whereRaw('1 = 0'); } - - // return ($userHas && strlen($userHas) > 0) ? Plant::where('id', $userHas)->pluck('name', 'id')->toArray() : Plant::orderBy('code')->pluck('name', 'id')->toArray(); - }) - ->afterStateUpdated(function ($state, callable $set, callable $get): void { - $set('sticker_master_id', null); - $set('operator_id', null); - }), - TextInput::make('invoice_number') - ->label('Invoice Number') - ->placeholder(placeholder: 'Enter Invoice Number'), - TextInput::make('serial_number') - ->label('Serial Number') - ->placeholder(placeholder: 'Enter Serial Number'), - Select::make('sticker_master_id') - ->label('Search by Item Code') - ->nullable() - ->searchable() - ->reactive() - ->options(function (callable $get) { - $pId = $get('Plant'); - - // if (empty($pId)) { - // return []; - // } - return Item::whereHas('stickerMasters', function ($query) use ($pId) { - if ($pId) { - $query->where('plant_id', $pId); - } - $query->whereHas('invoiceValidations'); - })->pluck('code', 'id'); - }), - Select::make('scanned_status') - ->label('Scanned Status') - ->nullable() - ->options([ - 'Scanned' => 'Scanned', - 'Pending' => 'Pending', - ]) - ->searchable() - ->reactive(), - Select::make('operator_id') - ->label('Created By') - ->nullable() - ->options(function (callable $get) { - $plantId = $get('Plant'); - if (! $plantId) { - return InvoiceValidation::whereNotNull('operator_id')->select('operator_id')->distinct()->pluck('operator_id', 'operator_id'); - } else { - return InvoiceValidation::where('plant_id', $plantId)->whereNotNull('operator_id')->select('operator_id')->distinct()->pluck('operator_id', 'operator_id'); - } - }) - ->searchable() - ->reactive(), - DateTimePicker::make(name: 'created_from') - ->label('Created From') - ->placeholder(placeholder: 'Select From DateTime') - ->reactive() - ->native(false), - DateTimePicker::make('created_to') - ->label('Created To') - ->placeholder(placeholder: 'Select To DateTime') - ->reactive() - ->native(false), - ]) - ->query(function ($query, array $data) { - // Hide all records initially if no filters are applied - if (empty($data['invoice_type']) || (empty($data['Plant']) && empty($data['invoice_number']) && empty($data['serial_number']) && empty($data['created_from']) && empty($data['created_to']) && empty($data['operator_id']) && empty($data['scanned_status']) && empty($data['sticker_master_id']))) { - if (empty($data['invoice_type'])) { - Notification::make() - ->title('Please, choose invoice type to filter.') - ->danger() - ->send(); } - return $query->whereRaw('1 = 0'); - } + if (! empty($data['invoice_number'])) { + $query->where('invoice_number', 'like', '%'.$data['invoice_number'].'%'); + } - if ($data['invoice_type'] == 'Serial') { - $query->whereNull('quantity'); + if (! empty($data['serial_number'])) { + $query->where('serial_number', 'like', '%'.$data['serial_number'].'%'); + } + + if (! empty($data['created_from'])) { + $query->where('created_at', '>=', $data['created_from']); + } + + if (! empty($data['created_to'])) { + $query->where('created_at', '<=', $data['created_to']); + } + + if (! empty($data['operator_id'])) { + $query->where('operator_id', $data['operator_id']); + } + + if (! empty($data['sticker_master_id'])) { + $stickerMasterIds = StickerMaster::where('item_id', $data['sticker_master_id']) + ->pluck('id') + ->toArray(); + + if (! empty($stickerMasterIds)) { + $query->whereIn('sticker_master_id', $stickerMasterIds); + } + } + }) + ->indicateUsing(function (array $data) { + $indicators = []; + + if (! empty($data['Plant'])) { + $indicators[] = 'Plant Name: '.Plant::where('id', $data['Plant'])->value('name'); + } else { + $userHas = Filament::auth()->user()->plant_id; + + if ($userHas && strlen($userHas) > 0) { + return 'Plant: Choose plant to filter records.'; + } + } + + if (! empty($data['invoice_number'])) { + $indicators[] = 'Invoice Number: '.$data['invoice_number']; + } + + if (! empty($data['serial_number'])) { + $indicators[] = 'Serial Number: '.$data['serial_number']; + } + + if (! empty($data['sticker_master_id'])) { + $itemCode = Item::find($data['sticker_master_id'])->code ?? 'Unknown'; + $indicators[] = 'Item Code: '.$itemCode; + } + + if (! empty($data['operator_id'])) { + $indicators[] = 'Created By: '.$data['operator_id']; + } + + if (! empty($data['created_from'])) { + $indicators[] = 'From: '.$data['created_from']; + } + + if (! empty($data['created_to'])) { + $indicators[] = 'To: '.$data['created_to']; + } if (! empty($data['scanned_status'])) { - if ($data['scanned_status'] == 'Scanned') { - $query->whereNotNull('scanned_status')->where('scanned_status', '!=', ''); - } elseif ($data['scanned_status'] == 'Pending') { - // $query->whereNull('scanned_status')->orWhere('scanned_status', ''); - $query->where(function ($query) { - // if (empty($data['scanned_status']) || $data['scanned_status'] == 'Pending') { - $query->whereNull('scanned_status')->orWhere('scanned_status', '!=', 'Scanned'); - // } - }); - } + $indicators[] = 'Scanned Status: '.$data['scanned_status']; } - } elseif ($data['invoice_type'] == 'Material') { - $query->whereNotNull('quantity'); // ->where('quantity', '>', 0) - if (! empty($data['scanned_status'])) { - if ($data['scanned_status'] == 'Scanned') { - $query->whereNotNull('serial_number')->where('serial_number', '!=', ''); - } elseif ($data['scanned_status'] == 'Pending') { - $query->where(function ($query) { - $query->whereNull('serial_number')->orWhere('serial_number', '=', ''); - }); - } - } - } - - if (! empty($data['Plant'])) { // $plant = $data['Plant'] ?? null - $query->where('plant_id', $data['Plant']); - } else { - $userHas = Filament::auth()->user()->plant_id; - - if ($userHas && strlen($userHas) > 0) { - return $query->whereRaw('1 = 0'); - } - } - - if (! empty($data['invoice_number'])) { - $query->where('invoice_number', 'like', '%'.$data['invoice_number'].'%'); - } - - if (! empty($data['serial_number'])) { - $query->where('serial_number', 'like', '%'.$data['serial_number'].'%'); - } - - if (! empty($data['created_from'])) { - $query->where('created_at', '>=', $data['created_from']); - } - - if (! empty($data['created_to'])) { - $query->where('created_at', '<=', $data['created_to']); - } - - if (! empty($data['operator_id'])) { - $query->where('operator_id', $data['operator_id']); - } - - if (! empty($data['sticker_master_id'])) { - $stickerMasterIds = StickerMaster::where('item_id', $data['sticker_master_id']) - ->pluck('id') - ->toArray(); - - if (! empty($stickerMasterIds)) { - $query->whereIn('sticker_master_id', $stickerMasterIds); - } - } - }) - ->indicateUsing(function (array $data) { - $indicators = []; - - if (! empty($data['Plant'])) { - $indicators[] = 'Plant Name: '.Plant::where('id', $data['Plant'])->value('name'); - } else { - $userHas = Filament::auth()->user()->plant_id; - - if ($userHas && strlen($userHas) > 0) { - return 'Plant: Choose plant to filter records.'; - } - } - - if (! empty($data['invoice_number'])) { - $indicators[] = 'Invoice Number: '.$data['invoice_number']; - } - - if (! empty($data['serial_number'])) { - $indicators[] = 'Serial Number: '.$data['serial_number']; - } - - if (! empty($data['sticker_master_id'])) { - $itemCode = Item::find($data['sticker_master_id'])->code ?? 'Unknown'; - $indicators[] = 'Item Code: '.$itemCode; - } - - if (! empty($data['operator_id'])) { - $indicators[] = 'Created By: '.$data['operator_id']; - } - - if (! empty($data['created_from'])) { - $indicators[] = 'From: '.$data['created_from']; - } - - if (! empty($data['created_to'])) { - $indicators[] = 'To: '.$data['created_to']; - } - - if (! empty($data['scanned_status'])) { - $indicators[] = 'Scanned Status: '.$data['scanned_status']; - } - - return $indicators; - }), + return $indicators; + }), ]) ->filtersFormMaxHeight('280px') ->actions([ @@ -1428,10 +1440,10 @@ class InvoiceValidationResource extends Resource ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ - Tables\Actions\DeleteBulkAction::make(), - Tables\Actions\ForceDeleteBulkAction::make(), - Tables\Actions\RestoreBulkAction::make(), - FilamentExportBulkAction::make('export'), + Tables\Actions\DeleteBulkAction::make(), + Tables\Actions\ForceDeleteBulkAction::make(), + Tables\Actions\RestoreBulkAction::make(), + FilamentExportBulkAction::make('export'), ]), ]); } diff --git a/app/Filament/Resources/InvoiceValidationResource/Pages/CreateInvoiceValidation.php b/app/Filament/Resources/InvoiceValidationResource/Pages/CreateInvoiceValidation.php index 13a4139..3c4eee6 100644 --- a/app/Filament/Resources/InvoiceValidationResource/Pages/CreateInvoiceValidation.php +++ b/app/Filament/Resources/InvoiceValidationResource/Pages/CreateInvoiceValidation.php @@ -581,7 +581,7 @@ class CreateInvoiceValidation extends CreateRecord $materialType = $sticker->material_type; // $sticker = StickerMaster::where('plant_id', $plantId)->whereHas('item', function ($query) use ($code) { $query->where('plant_id', $this->plantId)->where('code', $code); })->first(); - if ($materialType == 1) { + if ($materialType == 1 || $materialType == 4) { $totalExcelQty = 0; foreach ($rows as $index => $row) { @@ -1765,7 +1765,7 @@ class CreateInvoiceValidation extends CreateRecord $materialType = $sticker->material_type; // $sticker = StickerMaster::where('plant_id', $plantId)->whereHas('item', function ($query) use ($code) { $query->where('plant_id', $this->plantId)->where('code', $code); })->first(); - if ($materialType == 1) { + if ($materialType == 1 || $materialType == 4) { $totalExcelQty = 0; foreach ($rows as $index => $row) { diff --git a/app/Livewire/InvoiceDataTable.php b/app/Livewire/InvoiceDataTable.php index 71307b1..fbe7183 100644 --- a/app/Livewire/InvoiceDataTable.php +++ b/app/Livewire/InvoiceDataTable.php @@ -50,6 +50,7 @@ class InvoiceDataTable extends Component 'refreshEmptyInvoice' => 'loadEmptyData', 'refreshInvoiceData' => 'loadData', 'refreshMaterialInvoiceData' => 'loadMaterialData', + 'refreshMaterialInvoiceData1' => 'loadMaterialData1', 'openCapacitorModal' => 'showCapacitorInputBox', ]; @@ -602,6 +603,8 @@ class InvoiceDataTable extends Component $row['material_type'] = 'Bundle'; } elseif ($matType === 3) { $row['material_type'] = 'Quantity'; + } elseif ($matType === 4) { + $row['material_type'] = 'Bundle Individual'; } else { $row['material_type'] = 'N/A'; } diff --git a/resources/views/livewire/invoice-data-table.blade.php b/resources/views/livewire/invoice-data-table.blade.php index ffb73e1..b29619f 100644 --- a/resources/views/livewire/invoice-data-table.blade.php +++ b/resources/views/livewire/invoice-data-table.blade.php @@ -243,7 +243,7 @@ {{ $row['code'] ?? 'N/A' }} {{ $row['material_type'] ?? 'N/A' }} - @if(($row['material_type'] ?? '') === 'Individual' || ($row['material_type'] ?? '') === 'Bundle') + @if(($row['material_type'] ?? '') == 'Individual' || ($row['material_type'] ?? '') == 'Bundle' || ($row['material_type'] ?? '') == 'Bundle Individual') {{ number_format((float)($row['quantity'] ?? 0), 0) }} @else {{ $row['quantity'] ?? 'N/A' }}