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() ->sortable() ->toggleable(isToggledHiddenByDefault: true), 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 = []; foreach ($rows as $index => $row) { if ($index == 0) continue; // Skip header $qrCode = trim($row[1]); $plantCode = trim($row[2]); $scannedAt = trim($row[3]); $scannedby = trim($row[4]); //$createdBy = 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 (empty($createdBy)) $invalidUser[] = "Row {$index}"; if (strlen($plantCode) < 4) { $invalidPlantCode[] = $plantCode; } else if(!Plant::where('code', $plantCode)->first()) { $invalidPlaCoFound[] = $plantCode; } // else if(!User::where('name', $createdBy)->first()) // { // $userNotFound[] = $createdBy; // } } 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) . '
'; //if (!empty($invalidUser)) $errorMsg .= 'Missing User in rows: '.implode(', ', $invalidUser) . '
'; 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; } // $successCount = 0; // $failCount = 0; // $lastErrorMessage = null; // 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]); // try // { // $plant = Plant::where('code', $plantCode)->first(); // $formattedDate = null; // if (!empty($scannedAt)) { // try { // $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}"); // } // } // $inserted = InvoiceOutValidation::create([ // 'plant_id' => $plant->id, // 'qr_code' => $qrcode, // 'scanned_at' => $formattedDate, // 'scanned_by' => $scannedBy, // 'created_by' => $operatorName, // ]); // if ($inserted) { // $successCount++; // } else { // $failCount++; // } // } catch (\Exception $e) { // //$failCount++; // $lastErrorMessage = $e->getMessage(); // } // } // if($successCount > 0) // { // Notification::make() // ->title('Import Success') // ->body("Successfully inserted: {$successCount} records") // ->success() // ->send(); // } // else { // $errorText = $lastErrorMessage // ? "Failed to insert any records. Error: {$lastErrorMessage}" // : "Failed to insert any records. Unknown error."; // Notification::make() // ->title('Import Failed') // ->body($errorText) // ->danger() // ->send(); // } $successCount = 0; $failedRecords = []; DB::beginTransaction(); try { foreach ($rows as $index => $row) { if ($index == 0) continue; // skip header $rowNumber = $index + 1; // For Excel-style numbering try { $qrcode = trim($row[1]); $plantCode = trim($row[2]); $scannedAt = trim($row[3]); $scannedBy = trim($row[4]); if (empty($qrcode)) { throw new \Exception("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}"); } } $inserted = InvoiceOutValidation::create([ 'plant_id' => $plant->id, 'qr_code' => $qrcode, 'scanned_at' => $formattedDate, 'scanned_by' => $scannedBy, 'created_by' => $operatorName, ]); if (!$inserted) { throw new \Exception("Insert failed for QR: {$qrcode}"); } $successCount++; } catch (\Exception $e) { $failedRecords[] = [ 'row' => $rowNumber, 'qrcode' => $qrcode ?? null, 'error' => $e->getMessage(), ]; } } DB::commit(); // ✅ After loop, show result summary 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 inserted: {$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, ]); } }