From 68c43b69e9514cb83123228e0800687b9fc3efac Mon Sep 17 00:00:00 2001 From: dhanabalan Date: Fri, 19 Jun 2026 17:34:47 +0530 Subject: [PATCH] Added SAP Invoice Importer Page with validation --- .../Imports/SapInvoiceValidationImporter.php | 308 +++ .../Resources/InvoiceValidationResource.php | 1981 +++++++++-------- 2 files changed, 1302 insertions(+), 987 deletions(-) create mode 100644 app/Filament/Imports/SapInvoiceValidationImporter.php diff --git a/app/Filament/Imports/SapInvoiceValidationImporter.php b/app/Filament/Imports/SapInvoiceValidationImporter.php new file mode 100644 index 0000000..ee28127 --- /dev/null +++ b/app/Filament/Imports/SapInvoiceValidationImporter.php @@ -0,0 +1,308 @@ +requiredMapping() + ->exampleHeader('PLANT CODE') + ->example('2040') + ->label('PLANT CODE') + ->relationship(resolveUsing: 'code') + ->rules(['required']), + ImportColumn::make('invoice_number') + ->requiredMapping() + ->exampleHeader('INVOICE NUMBER') + ->example('INV000001') + ->label('INVOICE NUMBER') + ->rules(['required']), + ImportColumn::make('item_reference') // stickerMaster + ->requiredMapping() + ->exampleHeader('ITEM CODE') + ->example('123456') + ->label('ITEM CODE') + // ->relationship() // resolveUsing: 'items.code' + ->rules(['required']), + ImportColumn::make('serial_number') + ->requiredMapping() + ->exampleHeader('SERIAL NUMBER') + ->example('12345678901234') + ->label('SERIAL NUMBER'), + ImportColumn::make('scanned_status_set') + ->requiredMapping() + ->exampleHeader('PUMPSET SCANNED STATUS') + ->example('1') + ->label('PUMPSET SCANNED STATUS'), + ImportColumn::make('created_at') + ->requiredMapping() + ->exampleHeader('CREATED AT') + ->example('19-06-2026 15:00:00') + ->label('CREATED AT') + ->rules(['required']), + ImportColumn::make('operator_id') + ->requiredMapping() + ->exampleHeader('OPERATOR ID') + ->example('USER00001') + ->label('OPERATOR ID') + ->rules(['required']), + ]; + } + + public function resolveRecord(): ?InvoiceValidation + { + // return InvoiceValidation::firstOrNew([ + // // Update existing records, matching them by `$this->data['column_name']` + // 'email' => $this->data['email'], + // ]); + + $warnMsg = []; + $plantId = null; + $stickId = null; + + $plantCod = $this->data['plant']; + $invoiceNumber = strtoupper(trim($this->data['invoice_number'])) ?? null; + $iCode = strtoupper(trim($this->data['item_reference'])) ?? null; + $serialNumber = trim($this->data['serial_number']) ?? null; + $curPumpSetQr = trim($this->data['scanned_status_set']) ?? null; + $curScanStatus = null; + $loadRate = 0; + $operatorId = trim($this->data['operator_id']); + $createdAt = $this->data['created_at']; + $createdBy = Filament::auth()->user()?->name; + $updatedBy = $createdBy; + + $packCnt = 0; + $scanCnt = 0; + $hasPumpSetQr = null; + $hadPumpSetQr = null; + + if ($plantCod == null || $plantCod == '') { + $warnMsg[] = "Plant code can't be empty!"; + } elseif ($invoiceNumber == null || $invoiceNumber == '') { + $warnMsg[] = "Invoice number can't be empty!"; + } elseif ($iCode == null || $iCode == '') { + $warnMsg[] = "Item code can't be empty!"; + } elseif ($serialNumber == null || $serialNumber == '') { + $warnMsg[] = "Serial number can't be empty!"; + } elseif ($curPumpSetQr != null && $curPumpSetQr != '' && $curPumpSetQr != '1' && $curPumpSetQr != 1) { + $warnMsg[] = 'PumpSet scanned status is invalid!'; + } elseif ($operatorId == null || $operatorId == '') { + $warnMsg[] = "Operator ID can't be empty!"; + } elseif ($createdAt == null || $createdAt == '') { + $warnMsg[] = "Created at timestamp can't be empty!"; + } + + if (Str::length($plantCod) > 0) { + if (Str::length($plantCod) < 4 || ! is_numeric($plantCod) || ! preg_match('/^[1-9]\d{3,}$/', $plantCod)) { + $warnMsg[] = 'Invalid plant code found!'; + } elseif ($plantCod == '2040') { // 2040 + $plant = Plant::where('code', $plantCod)->first(); + if (! $plant) { + $warnMsg[] = 'Plant code not found!'; + } else { + $plantId = $plant->id; + } + } else { + $warnMsg[] = "Unknown plant code '$plantCod' found!"; + } + } + + if (Str::length($invoiceNumber) > 0 && ! ctype_alnum($invoiceNumber)) { + $warnMsg[] = "Invalid invoice number '$invoiceNumber' found!"; + } elseif (Str::length($iCode) > 0 && (Str::length($iCode) < 6 || ! ctype_alnum($iCode))) { + $warnMsg[] = "Invalid item code '$iCode' found!"; + } elseif ($plantId) { + $itemCode = Item::where('code', $iCode)->first(); + if (! $itemCode) { + $warnMsg[] = 'Item code not found in item master!'; + } else { + $itemCode = Item::where('code', $iCode)->where('plant_id', $plantId)->first(); + if (! $itemCode) { + $warnMsg[] = 'Item code not found in item master for the given plant!'; + } else { + $itemId = $itemCode->id; + $itemCode = StickerMaster::where('item_id', $itemId)->first(); + if (! $itemCode) { + $warnMsg[] = 'Item code not found in sticker master!'; + } else { + if ($plantId) { + $itemCode = StickerMaster::where('item_id', $itemId)->where('plant_id', $plantId)->first(); + if (! $itemCode) { + $warnMsg[] = 'Item code not found in sticker master for the given plant!'; + } elseif ($itemCode->material_type != '' && $itemCode->material_type != null) { + $stickId = null; + $warnMsg[] = 'Material invoice item code found!'; + } else { + $stickId = $itemCode->id; + $loadRate = $itemCode->load_rate ?? 0; + $invalidPackage = false; + + $hasMotorQr = $itemCode->tube_sticker_motor ?? null; + $hasPumpQr = $itemCode->tube_sticker_pump ?? null; + $hasPumpSetQr = $itemCode->tube_sticker_pumpset ?? null; + $hasCapacitorQr = $itemCode->panel_box_code ?? null; + + if (! $hasMotorQr && ! $hasPumpQr && ! $hasPumpSetQr) {// && ! $hasCapacitorQr + $hasMotorQr = $itemCode->pack_slip_motor ?? null; + $hasPumpQr = $itemCode->pack_slip_pump ?? null; + $hasPumpSetQr = $itemCode->pack_slip_pumpset ?? null; + } else { + if (! $hasPumpSetQr && ! $hasPumpQr) { + $hasPumpQr = $itemCode->pack_slip_pump ?? null; + } + + $hasTubeMotorQr = $itemCode->tube_sticker_motor ?? null; + $hasPackMotorQr = $itemCode->pack_slip_motor ?? null; + $hasTubePumpSetQr = $itemCode->tube_sticker_pumpset ?? null; + $hasPackPumpSetQr = $itemCode->pack_slip_pumpset ?? null; + if ($hasTubeMotorQr != $hasPackMotorQr || $hasTubePumpSetQr != $hasPackPumpSetQr) { + $invalidPackage = true; + } + } + + if ($hasMotorQr || $hasPumpQr || ! $hasPumpSetQr || $hasCapacitorQr || $invalidPackage) { + $stickId = null; + $warnMsg[] = "Item code doesn't have valid package type to proceed!"; + } else { + $packCnt++; + } + } + } + } + } + } + } + + if ($stickId) { + $record = InvoiceValidation::where('serial_number', $serialNumber)->where('plant_id', $plantId)->first(); + + if ($record) { + if ($record->sticker_master_id != $stickId) { + $stickId = null; + $warnMsg[] = 'Item code mismatch with existing record!'; + } else { + $record = InvoiceValidation::where('serial_number', $serialNumber)->where('plant_id', $plantId) + ->whereHas('stickerMasterRelation.item', function ($query) use ($plantId, $iCode) { + $query->where('plant_id', $plantId)->where('code', $iCode); + }) + ->first(); + + if ($record) { + $hadPumpSetQr = $record->scanned_status_set ?? null; + + if ($hadPumpSetQr && $hasPumpSetQr) { + $curPumpSetQr = $hadPumpSetQr; + } + + $warnMsg[] = 'Invoice Record Item ID : '.$record->sticker_master_id.' Master Item ID : '.$stickId; + if ($record->invoice_number != $invoiceNumber) { + $stickId = null; + $warnMsg[] = 'Invoice number mismatch with existing record!'; + } elseif ($record->scanned_status == 'Scanned') { + $stickId = null; + + return null; + } else { + if ($hasPumpSetQr) { + $scanCnt = $curPumpSetQr ? $scanCnt + 1 : $scanCnt; + $record->scanned_status_set = $curPumpSetQr; + if ($packCnt == $scanCnt) { + $record->scanned_status = 'Scanned'; + } + $record->upload_status = 'Y'; + $record->updated_by = $updatedBy; + $record->save(); + + return null; + } + } + } + } + } + } + + if ($stickId) { + $formats = ['d-m-Y H:i', 'd-m-Y H:i:s']; // '07-05-2025 08:00' or '07-05-2025 08:00:00' + $cDateTime = null; + + foreach ($formats as $format) { + try { + $cDateTime = Carbon::createFromFormat($format, $createdAt); + break; + } catch (\Exception $e) { + // $warnMsg[] = "Date format mismatch with format: $format"; + } + } + + if (! isset($cDateTime)) { + $warnMsg[] = "Invalid 'Created DateTime' format. Expected DD-MM-YYYY HH:MM:SS"; + } + } + + if (! empty($warnMsg)) { + throw new RowImportFailedException(implode(', ', $warnMsg)); + } + + if ($stickId) { + if ($hasPumpSetQr) { + $scanCnt = $curPumpSetQr ? $scanCnt + 1 : $scanCnt; + + if ($packCnt == $scanCnt) { + $curScanStatus = 'Scanned'; + } else { + $curScanStatus = null; + } + } + // $curScanStatus + + InvoiceValidation::updateOrCreate([ + 'plant_id' => $plantId, + 'sticker_master_id' => $stickId, + 'serial_number' => $serialNumber, + ], + [ + 'invoice_number' => $invoiceNumber, + 'scanned_status_set' => $curPumpSetQr, + 'scanned_status' => $curScanStatus, + 'load_rate' => $loadRate, + 'upload_status' => 'Y', + 'operator_id' => $operatorId, + 'created_by' => $createdBy, + 'created_at' => $cDateTime->format('Y-m-d H:i:s'), + 'updated_by' => $updatedBy, + ]); + } + + return null; + // return new InvoiceValidation; + } + + public static function getCompletedNotificationBody(Import $import): string + { + $body = 'Your sap invoice validation import has completed and '.number_format($import->successful_rows).' '.str('row')->plural($import->successful_rows).' imported.'; + + if ($failedRowsCount = $import->getFailedRowsCount()) { + $body .= ' '.number_format($failedRowsCount).' '.str('row')->plural($failedRowsCount).' failed to import.'; + } + + return $body; + } +} diff --git a/app/Filament/Resources/InvoiceValidationResource.php b/app/Filament/Resources/InvoiceValidationResource.php index 3e4d41d..8dbffa4 100644 --- a/app/Filament/Resources/InvoiceValidationResource.php +++ b/app/Filament/Resources/InvoiceValidationResource.php @@ -6,6 +6,7 @@ use AlperenErsoy\FilamentExport\Actions\FilamentExportBulkAction; use App\Filament\Exports\AxnExporter; use App\Filament\Exports\InvoiceValidationExporter; use App\Filament\Imports\InvoiceValidationImporter; +use App\Filament\Imports\SapInvoiceValidationImporter; use App\Filament\Resources\InvoiceValidationResource\Pages; use App\Mail\InvoiceNotification; use App\Models\InvoiceValidation; @@ -377,451 +378,174 @@ 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() - // ]), - - 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) { - $set('invoice_serial_number', 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(), - - 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']; - - $disk = Storage::disk('local'); - - $plantId = $data['plant_id']; - - $plant = Plant::find($plantId); - $plantCode = $plant?->code ?? null; - - // 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); - } - - return; - } - - $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 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(); - - if ($disk->exists($path)) { - $disk->delete($path); - } - - 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([ - - Select::make('plant_id') + // 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; + ->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); + 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_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; + 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_material']; + return "uploads/temp/{$plantCode}"; + }) + ->uploadingMessage('Uploading...') + ->helperText('Only .xlsx files are allowed (Excel files).'), + ]) + ->action(function (array $data) { + $uploadedFile = $data['invoice_serial_number']; - $plantId = $data['plant_id']; // Access the selected plant_id + $disk = Storage::disk('local'); - $plant = Plant::find($plantId); - $plantCode = $plant?->code ?? null; + $plantId = $data['plant_id']; - $disk = Storage::disk('local'); + $plant = Plant::find($plantId); + $plantCode = $plant?->code ?? null; - // Get original filename - $originalName = $uploadedFile->getClientOriginalName(); + // 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.'); + $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); } - $originalNameOnly = strtoupper(pathinfo($originalName, PATHINFO_FILENAME)); + return; + } - $originalName = "{$originalNameOnly}.xlsx"; + $fullPath = Storage::disk('local')->path($path); + // /home/iot-dev/projects/pds/storage/app/private/uploads/temp/{$plantCode}/3RA0018735.xlsx - $path = $uploadedFile->storeAs("uploads/temp/{$plantCode}", $originalName, 'local'); + $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; - if (strlen($originalNameOnly) < 8 || ! ctype_alnum($originalNameOnly)) { Notification::make() - ->title("Material invoice number : '$originalNameOnly' should contain minimum 8 digit alpha numeric values!") + ->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(); @@ -832,606 +556,889 @@ class InvoiceValidationResource extends Resource return; } - $fullPath = Storage::disk('local')->path($path); + $invalidMatCodes = []; + $invalidSerialCodes = []; + $materialCodes = []; + $missingSerials = []; + $duplicateSerials = []; + $seenSerialNumbers = []; + $validRowsFound = false; - $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; + foreach ($rows as $index => $row) { + if ($index == 0) { + continue; + } // Skip header - Notification::make() - ->title("Material invoice number : '$originalNameOnly' already completed the scanning process for plant : '$plantName'.") - ->danger() - ->send(); + $materialCode = trim($row[0]); + $serialNumber = trim($row[1]); - 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) && empty($serialNumber)) { + continue; } - return; - } + if (! empty($materialCode)) { + if (Str::length($materialCode) < 6 || ! ctype_alnum($materialCode)) { + $invalidMatCodes[] = $materialCode; + } else { + if (empty($serialNumber)) { + $missingSerials[] = $materialCode; - 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; + } elseif (Str::length($serialNumber) < 9 || ! ctype_alnum($serialNumber)) { + $invalidSerialCodes[] = $serialNumber; } else { - if ($materialQuantity == 0) { - $invalidMaterialQuan[] = $materialCode; - } elseif (empty($materialQuantity)) { - $missingQuantities[] = $materialCode; - } elseif (! is_numeric($materialQuantity)) { - $invalidMatQuan[] = $materialCode; + if (in_array($serialNumber, $seenSerialNumbers)) { + $duplicateSerials[] = $serialNumber; } else { + $seenSerialNumbers[] = $serialNumber; $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 ($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; - } - } - } - - $showValidationNotification = function (array $codes, string $message) { - if (count($codes) == 0) { - return; - } - - $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(); + 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; } - }) - ->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($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([ + + 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); + + 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.'); + } + + $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!") + ->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); + } + + 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 ($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; + } + } + } + + $showValidationNotification = function (array $codes, string $message) { + if (count($codes) == 0) { + return; + } + + $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('invoice_import') + ->label('Import Invoices') + ->color('warning') + ->importer(InvoiceValidationImporter::class) + ->visible(function () { + return Filament::auth()->user()->can('view import invoice'); + }), + ImportAction::make('sap_invoice_import') + ->label('Import SAP Invoices') + ->color('warning') + ->importer(SapInvoiceValidationImporter::class) + ->visible(function () { + return Filament::auth()->user()->can('view import sap 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 []; - } - }) - // ->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(); + 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); - return $query->whereRaw('1 = 0'); - } + $hasSerial = ($userRights->hasRole(['Sales Employee 1', 'Sales Supervisor 1', 'Sales Supervisor Upload 1']) ?? null); // , 'Sales Supervisor Inventory', 'Sales Supervisor Inventory Upload', 'Sales Employee Inventory' - if ($data['invoice_type'] == 'Serial') { - $query->whereNull('quantity'); + $hasMaterial = ($userRights->hasRole(['Sales Employee 2', 'Sales Supervisor 2', 'Sales Supervisor Upload 2']) ?? null); - 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'); - // } - }); - } + if ($hasBoth || $hasSerial) { + return 'Serial'; + } elseif ($hasMaterial) { + return 'Material'; + } else { + return 'Serial'; // return []; } - } 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 { + }) + ->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 $query->whereRaw('1 = 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'); } - } - if (! empty($data['invoice_number'])) { - $query->where('invoice_number', 'like', '%'.$data['invoice_number'].'%'); - } + // 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($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($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(); } - if (! empty($data['invoice_number'])) { - $indicators[] = 'Invoice Number: '.$data['invoice_number']; - } + return $query->whereRaw('1 = 0'); + } - 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 ($data['invoice_type'] == 'Serial') { + $query->whereNull('quantity'); if (! empty($data['scanned_status'])) { - $indicators[] = 'Scanned Status: '.$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'); + // } + }); + } } + } elseif ($data['invoice_type'] == 'Material') { + $query->whereNotNull('quantity'); // ->where('quantity', '>', 0) - return $indicators; - }), + 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; + }), ]) ->filtersFormMaxHeight('280px') ->actions([ @@ -1440,10 +1447,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'), ]), ]); }