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'),
]),
]);
}