schema([ Section::make('') ->schema([ Forms\Components\Select::make('plant_id') ->label('Plant') ->relationship('plant', 'name') ->required(), Forms\Components\TextInput::make('qr_code') ->label('QR Code'), Forms\Components\DateTimePicker::make('scanned_at') ->label('Scanned At'), Forms\Components\TextInput::make('scanned_by') ->label('Scanned By'), Forms\Components\Hidden::make('created_by') ->label('Created By') ->default(Filament::auth()->user()?->name), Forms\Components\Hidden::make('updated_by') ->default(Filament::auth()->user()?->name), ]) ->columns(4), ]); } public static function table(Table $table): Table { return $table ->columns([ Tables\Columns\TextColumn::make('No.') ->label('No.') ->getStateUsing(function ($record, $livewire, $column, $rowLoop) { $paginator = $livewire->getTableRecords(); $perPage = method_exists($paginator, 'perPage') ? $paginator->perPage() : 10; $currentPage = method_exists($paginator, 'currentPage') ? $paginator->currentPage() : 1; return ($currentPage - 1) * $perPage + $rowLoop->iteration; }), Tables\Columns\TextColumn::make('plant.code') ->label('Plant') ->alignCenter() ->sortable(), Tables\Columns\TextColumn::make('qr_code') ->label('QR Code') ->alignCenter() ->searchable() ->sortable(), Tables\Columns\TextColumn::make('scanned_at') ->label('Scanned At') ->searchable() ->alignCenter() ->dateTime() ->sortable(), Tables\Columns\TextColumn::make('scanned_by') ->label('Scanned By') ->searchable() ->alignCenter() ->sortable(), Tables\Columns\TextColumn::make('created_at') ->searchable() ->dateTime() ->sortable(), //->toggleable(isToggledHiddenByDefault: true), Tables\Columns\TextColumn::make('created_by') ->label('Created By') ->searchable() ->alignCenter() ->sortable(), Tables\Columns\TextColumn::make('updated_at') ->dateTime() ->searchable() ->sortable(), // ->toggleable(isToggledHiddenByDefault: true), Tables\Columns\TextColumn::make('updated_by') ->label('Updated By') ->searchable() ->alignCenter() ->sortable(), Tables\Columns\TextColumn::make('deleted_at') ->dateTime() ->sortable() ->toggleable(isToggledHiddenByDefault: true), ]) ->filters([ Tables\Filters\TrashedFilter::make(), ]) ->actions([ Tables\Actions\ViewAction::make(), Tables\Actions\EditAction::make(), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make(), Tables\Actions\ForceDeleteBulkAction::make(), Tables\Actions\RestoreBulkAction::make(), ]), ]) ->headerActions([ Tables\Actions\Action::make('Import Invoice Out Data') ->label('Import Invoice Out Data') ->form([ FileUpload::make('invoice_data_file') ->label('Invoice Out Data') // ->required() ->preserveFilenames() ->storeFiles(false) ->reactive() ->required() ->disk('local') //->visible(fn (Get $get) => !empty($get('plant_id'))) ->directory('uploads/temp'), ]) ->action(function (array $data) { $uploadedFile = $data['invoice_data_file']; $disk = Storage::disk('local'); $user = Filament::auth()->user(); $operatorName = $user->name; // Get original filename $originalName = $uploadedFile->getClientOriginalName(); // e.g. 3RA0018732.xlsx $originalNameOnly = pathinfo($originalName, PATHINFO_FILENAME); // Store manually using storeAs to keep original name $path = $uploadedFile->storeAs('uploads/temp', $originalName, 'local'); // returns relative path $fullPath = Storage::disk('local')->path($path); 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 'Invoice Data' file to proceed..!") ->danger() ->send(); if ($disk->exists($path)) { $disk->delete($path); } return; } $invalidPlantCode = []; $invalidPlaCoFound = []; $invalidqrCode = []; $invalidScannedAt = []; $invalidScannedBy = []; $invalidUser = []; $userNotFound = []; $seenPlantQr = []; $duplicateQrExcel = []; //$duplicateQrDb = []; foreach ($rows as $index => $row) { if ($index == 0) continue; $qrCode = trim($row[1]); $plantCode = trim($row[2]); $scannedAt = trim($row[3]); $scannedby = trim($row[4]); if (empty($plantCode)) $invalidPlantCode[] = "Row {$index}"; if (empty($qrCode)) $invalidqrCode[] = "Row {$index}"; if (empty($scannedAt)) $invalidScannedAt[] = "Row {$index}"; if (empty($scannedby)) $invalidScannedBy[] = "Row {$index}"; if (strlen($plantCode) < 4) { $invalidPlantCode[] = $plantCode; } else if(!Plant::where('code', $plantCode)->first()) { $invalidPlaCoFound[] = $plantCode; } $plant = Plant::where('code', $plantCode)->first(); $plantId = $plant->id; $uniqueKey = $plantCode . '_' . $qrCode; if (in_array($uniqueKey, $seenPlantQr)) { $duplicateQrExcel[] = "Duplicate in file at Row {$index}: Document Number '{$qrCode}' already exists for Plant '{$plant->name}'"; } $seenPlantQr[] = $uniqueKey; // $existsInDb = InvoiceOutValidation::where('plant_id', $plantId) // ->where('qr_code', $qrCode) // ->first(); // if ($existsInDb) { // $duplicateQrDb[] = "Document Numbers '{$qrCode}' already exists in DB for Plant '{$plant->name}'"; // } } if (!empty($invalidqrCode) || !empty($invalidScannedAt) || !empty($invalidScannedBy) || !empty($invalidUser)) { $errorMsg = ''; if (!empty($invalidqrCode)) $errorMsg .= 'Missing Qr code in rows: '.implode(', ', $invalidqrCode) . '
'; if (!empty($invalidScannedAt)) $errorMsg .= 'Missing Scanned At in rows: '.implode(', ', $invalidScannedAt) . '
'; if (!empty($invalidScannedBy)) $errorMsg .= 'Missing Scanned By in rows: '.implode(', ', $invalidScannedBy) . '
'; Notification::make() ->title('Missing Mandatory Fields') ->body($errorMsg) ->danger() ->send(); if ($disk->exists($path)) { $disk->delete($path); } return; } if (!empty($invalidPlantCode)) { $invalidPlantCode = array_unique($invalidPlantCode); Notification::make() ->title('Invalid Plant Codes') ->body('The following plant codes should contain minimum 4 digits:
' . implode(', ', $invalidPlantCode)) ->danger() ->send(); if ($disk->exists($path)) { $disk->delete($path); } return; } if (!empty($invalidPlaCoFound)) { $invalidPlaCoFound = array_unique($invalidPlaCoFound); Notification::make() ->title('Invalid Plant Codes') ->body('The following plant codes not found in plants:
' . implode(', ', $invalidPlaCoFound)) ->danger() ->send(); if ($disk->exists($path)) { $disk->delete($path); } return; } if (!empty($userNotFound)) { $userNotFound = array_unique($userNotFound); Notification::make() ->title('Invalid User') ->body('The following user not found:
' . implode(', ', $userNotFound)) ->danger() ->send(); if ($disk->exists($path)) { $disk->delete($path); } return; } if (!empty($duplicateQrExcel)) { $duplicateGroupedByPlantQr = []; foreach ($duplicateQrExcel as $message) {//"/Document Numbers '([^']+)' already exists for Plant Code (\S+)/" if (preg_match("/Document Number '([^']+)' already exists for Plant '([^']+)'/", $message, $matches)) { $qrCode = $matches[1]; $plantCode = $matches[2]; $duplicateGroupedByPlantQr[$plantCode][] = $qrCode; } } $errorMsg = 'Duplicate Document Number found in Uploaded File :
'; foreach ($duplicateGroupedByPlantQr as $plantCode => $qrCodes) { $uniqueQrCodes = array_unique($qrCodes); $count = count($uniqueQrCodes); if ($count > 10) { $errorMsg .= "Duplicate Document Numbers for Plant {$plantCode} : {$count} Document Numbers already exist in uploaded file
"; } else { $errorMsg .= "Duplicate Document Numbers for Plant {$plantCode} : '" . implode(', ', $uniqueQrCodes) . "' already exist in uploaded file
"; } } Notification::make() //->title('Duplicate Document Number in Uploaded File') ->body($errorMsg) ->danger() ->send(); if ($disk->exists($path)) { $disk->delete($path); } return; } // if (!empty($duplicateQrDb)) { // $duplicateGroupedByPlantDb = []; // foreach ($duplicateQrDb as $message) { // if (preg_match("/Document Numbers '([^']+)' already exists in DB for Plant '([^']+)'/", $message, $matches)) { // $qrCode = $matches[1]; // $plantCode = $matches[2]; // $duplicateGroupedByPlantDb[$plantCode][] = $qrCode; // } // } // $errorMsg = 'Duplicate Document Numbers found in Database:
'; // foreach ($duplicateGroupedByPlantDb as $plantCode => $qrCodes) { // $uniqueQrCodes = array_unique($qrCodes); // $count = count($uniqueQrCodes); // if ($count > 10) { // $errorMsg .= "Duplicate Document Numbers for Plant {$plantCode}: {$count} Document Numbers already exist in DB
"; // } else { // $errorMsg .= "Duplicate Document Numbers for Plant {$plantCode}: " // . implode(', ', $uniqueQrCodes) // . " already exist in DB
"; // } // } // Notification::make() // // ->title('Duplicate Document Numbers in Database') // ->body($errorMsg) // ->danger() // ->send(); // if ($disk->exists($path)) { // $disk->delete($path); // } // return; // } $successCount = 0; $failedRecords = []; DB::beginTransaction(); try { foreach ($rows as $index => $row) { if ($index == 0) continue; $rowNumber = $index + 1; try { $qrcode = trim($row[1]); $plantCode = trim($row[2]); $scannedAt = trim($row[3]); $scannedBy = trim($row[4]); if (empty($qrcode)) { throw new \Exception("Row '{$rowNumber}' Missing QR Code"); } $plant = Plant::where('code', $plantCode)->first(); if (!$plant) { throw new \Exception("Invalid plant code : '{$plantCode}'"); } $formattedDate = null; if (!empty($scannedAt)) { try { // $formattedDate = Carbon::createFromFormat('d-m-Y H:i:s', $scannedAt) // ->format('Y-m-d H:i:s'); if (is_numeric($scannedAt)) { $formattedDate = ExcelDate::excelToDateTimeObject($scannedAt) ->format('Y-m-d H:i:s'); } else { // Or handle as manual string date (d-m-Y H:i:s) $formattedDate = Carbon::createFromFormat('d-m-Y H:i:s', $scannedAt) ->format('Y-m-d H:i:s'); } } catch (\Exception $e) { throw new \Exception("Invalid date format : '{$scannedAt}'"); } } $record = InvoiceOutValidation::where('plant_id', $plant->id) ->where('qr_code', $qrcode) ->first(); $curStat = $record ? 'Updation' : 'Insertion'; if ($record) { $record->update([ 'scanned_at' => $formattedDate, 'scanned_by' => $scannedBy, 'updated_by' => $operatorName ]); $inserted = $record; } else { // Record does not exist, create with 'created_by' $inserted = InvoiceOutValidation::create([ 'plant_id' => $plant->id, 'qr_code' => $qrcode, 'scanned_at' => $formattedDate, 'scanned_by' => $scannedBy, 'created_by' => $operatorName ]); } // $inserted = InvoiceOutValidation::create([ // 'plant_id' => $plant->id, // 'qr_code' => $qrcode, // 'scanned_at' => $formattedDate, // 'scanned_by' => $scannedBy, // 'created_by' => $operatorName // ]); if (!$inserted) { throw new \Exception("{$curStat} failed for QR : {$qrcode}"); } $successCount++; } catch (\Exception $e) { $failedRecords[] = [ 'row' => $rowNumber, 'qrcode' => $qrcode ?? null, 'error' => $e->getMessage() ]; } } DB::commit(); if (count($failedRecords) > 0) { $failedSummary = collect($failedRecords) ->map(fn($f) => "Row {$f['row']} ({$f['qrcode']}) : {$f['error']}") ->take(5) // limit preview to first 5 errors ->implode("\n"); Notification::make() ->title('Partial Import Warning') ->body("'{$successCount}' records inserted. " . count($failedRecords) . " failed.\n\n{$failedSummary}") ->warning() ->send(); } else { Notification::make() ->title('Import Success') ->body("Successfully imported '{$successCount}' records.") ->success() ->send(); } } catch (\Exception $e) { DB::rollBack(); Notification::make() ->title('Import Failed') ->body("No records were inserted. Error : {$e->getMessage()}") ->danger() ->send(); } } }) ->visible(function() { return Filament::auth()->user()->can('view import invoice out validation'); }), ExportAction::make() ->exporter(InvoiceOutValidationExporter::class) ->visible(function() { return Filament::auth()->user()->can('view export invoice out validation'); }), ]); } public static function getRelations(): array { return [ // ]; } public static function getPages(): array { return [ 'index' => Pages\ListInvoiceOutValidations::route('/'), 'create' => Pages\CreateInvoiceOutValidation::route('/create'), 'view' => Pages\ViewInvoiceOutValidation::route('/{record}'), 'edit' => Pages\EditInvoiceOutValidation::route('/{record}/edit'), ]; } public static function getEloquentQuery(): Builder { return parent::getEloquentQuery() ->withoutGlobalScopes([ SoftDeletingScope::class, ]); } }