schema([ Forms\Components\Select::make('plant_id') ->label('Plant') ->relationship('plant', 'name') ->required(), Forms\Components\TextInput::make('distribution_channel_desc') ->label('Distribution Channel Description') ->required(), Forms\Components\TextInput::make('customer_code') ->label('Customer Code') ->required(), Forms\Components\TextInput::make('document_number') ->label('Document Number') ->required(), Forms\Components\DatePicker::make('document_date') ->label('Document Date') ->required(), Forms\Components\TextInput::make('customer_trade_name') ->label('Customer Trade Name') ->required(), Forms\Components\TextInput::make('customer_location') ->label('Customer Location') ->required(), 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), ]); } 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() ->searchable() ->sortable(), Tables\Columns\TextColumn::make('distribution_channel_desc') ->label('Distribution Channel Description') ->alignCenter() ->searchable() ->sortable(), Tables\Columns\TextColumn::make('customer_code') ->label('Customer Code') ->alignCenter() ->searchable() ->sortable(), Tables\Columns\TextColumn::make('document_number') ->label('Document Number') ->searchable() ->alignCenter() ->sortable(), Tables\Columns\TextColumn::make('document_date') ->label('Document Date') ->alignCenter() ->searchable() ->date() ->sortable(), Tables\Columns\TextColumn::make('customer_trade_name') ->label('Customer Trade Name') ->alignCenter() ->searchable() ->sortable(), Tables\Columns\TextColumn::make('customer_location') ->label('Customer Location') ->alignCenter() ->searchable() ->sortable(), Tables\Columns\TextColumn::make('created_at') ->label('Created At') ->alignCenter() ->dateTime() ->sortable() ->toggleable(isToggledHiddenByDefault: true), Tables\Columns\TextColumn::make('updated_at') ->label('Updated At') ->alignCenter() ->dateTime() ->sortable() ->toggleable(isToggledHiddenByDefault: true), Tables\Columns\TextColumn::make('deleted_at') ->label('Deleted At') ->alignCenter() ->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 Data') ->label('Import Invoice Data') ->form([ FileUpload::make('invoice_data_file') ->label('Invoice Data File') // ->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 = []; $invalidDisChaDesc = []; $invalidCustomerCode = []; $invalidDocNo = []; $invalidDocDate = []; $invalidCusTradeName = []; $invalidCusLocation = []; $invalidUser = []; $userNotFound = []; $invalidPlantType = []; $seenPlantDoc = []; //$duplicateEntries = []; $duplicateEntriesExcel = []; foreach ($rows as $index => $row) { if ($index == 0) continue; // Skip header $DisChaDesc = trim($row[3]); $plantCode = trim($row[4]); $CustomerCode = trim($row[5]); $DocNo = trim($row[6]); $DocDate = trim($row[7]); $CusTradeName = trim($row[9]); $CusLocation = trim($row[10]); // if (empty($plantCode)) $invalidPlantCode[] = "Row {$index}"; if (empty($DisChaDesc)){ $invalidDisChaDesc[] = "Row {$index}"; } if (empty($CustomerCode)){ $invalidCustomerCode[] = "Row {$index}"; } if (empty($DocNo)) { $invalidDocNo[] = "Row {$index}"; } if (empty($CusTradeName)) { $invalidCusTradeName[] = "Row {$index}"; } if (empty($CusLocation)) { $invalidCusLocation[] = "Row {$index}"; } // if (empty($createdBy)) $invalidUser[] = "Row {$index}"; if (strlen($plantCode) < 4) { $invalidPlantCode[] = $plantCode; } if (!is_numeric($plantCode)) { $invalidPlantType[] = $plantCode; } else if (!Plant::where('code', $plantCode)->first()) { $invalidPlaCoFound[] = $plantCode; } // --- Find Plant by code --- $plant = Plant::where('code', $plantCode)->first(); // //Duplicate Check in DB --- // $exists = InvoiceDataValidation::where('plant_id', $plant->id) // ->where('document_number', $DocNo) // ->first(); // if ($exists) // { // $duplicateEntries[] = "Duplicate record: Document Number '{$DocNo}' already exists for Plant '{$plant->name}'"; // } //Also check duplicates within the same file --- $uniqueKey = $plantCode . '_' . $DocNo; if (in_array($uniqueKey, $seenPlantDoc)) { $duplicateEntriesExcel[] = "Duplicate in file at Row {$index}: Document Number '{$DocNo}' already exists for Plant '{$plant->name}'"; } $seenPlantDoc[] = $uniqueKey; } if (!empty($invalidCustomerCode) || !empty($invalidDocNo) || !empty($invalidDocDate) || !empty($invalidCusTradeName) || !empty($invalidCusLocation)) { $errorMsg = ''; //if (!empty($invalidDisChaDesc)) $errorMsg .= 'Missing Distribution Channel Description in rows: ' . implode(', ', $invalidDisChaDesc) . '
'; if (!empty($invalidCustomerCode)) $errorMsg .= 'Missing Customer Code in rows: ' . implode(', ', $invalidCustomerCode) . '
'; if (!empty($invalidDocNo)) $errorMsg .= 'Missing Document Number in rows: ' . implode(', ', $invalidDocNo) . '
'; if (!empty($invalidDocDate)) $errorMsg .= 'Missing Document Date in rows: ' . implode(', ', $invalidDocDate) . '
'; if (!empty($invalidCusTradeName)) $errorMsg .= 'Missing Customer Trade Name in rows: ' . implode(', ', $invalidCusTradeName) . '
'; if (!empty($invalidCusLocation)) $errorMsg .= 'Missing Customer Location in rows: ' . implode(', ', $invalidCusLocation) . '
'; Notification::make() ->title('Missing Mandatory Fields') ->body($errorMsg) ->danger() ->send(); if ($disk->exists($path)) { $disk->delete($path); } return; } else 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; } else if (!empty($invalidPlantType)) { $invalidPlantType = array_unique($invalidPlantType); Notification::make() ->title('Invalid Plant Codes') ->body('The following plant codes should contain numeric values:
' . implode(', ', $invalidPlantType)) ->danger() ->send(); if ($disk->exists($path)) { $disk->delete($path); } return; } else 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($duplicateEntries)) // { // $duplicateGroupedByPlant = []; // foreach ($duplicateEntries as $message) // { // if (preg_match("/Document Number '([^']+)' already exists for Plant '([^']+)'/", $message, $matches)) { // $docNo = $matches[1]; // $plantName = trim($matches[2]); // $duplicateGroupedByPlant[$plantName][] = $docNo; // } // } // $errorMsg = 'Duplicate Document Number found in Database :
'; // foreach ($duplicateGroupedByPlant as $plant => $docNumbers) { // $count = count($docNumbers); // if ($count > 10) // { // $errorMsg .= "Duplicate record(s) for Plant {$plant} : {$count} document numbers already exist in DB
"; // } // else // { // $errorMsg .= "Duplicate record(s) for Plant {$plant} : " // . implode(', ', $docNumbers) // . " already exist
"; // } // } // Notification::make() // //->title('Duplicate Entries in Database') // ->body($errorMsg) // ->danger() // ->send(); // if ($disk->exists($path)) { // $disk->delete($path); // } // return; // } if (!empty($duplicateEntriesExcel)) { $duplicateGroupedByPlantExcel = []; foreach ($duplicateEntriesExcel as $message) {//"/Document Number '([^']+)' already exist(?:s)?(?: for Plant (.+))?/" if (preg_match("/Document Number '([^']+)' already exists for Plant '([^']+)'/", $message, $matches)) { $docNo = $matches[1]; $plantName = $matches[2] ?? 'Unknown'; $duplicateGroupedByPlantExcel[$plantName][] = $docNo; } } $errorMsg = 'Duplicate Document Number found in Uploaded File :
'; foreach ($duplicateGroupedByPlantExcel as $plant => $docNumbers) { // Remove duplicate document numbers for each plant $uniqueDocNumbers = array_unique($docNumbers); $count = count($uniqueDocNumbers); if ($count > 10) { $errorMsg .= "Duplicate Document Numbers for Plant {$plant} : {$count} Document Numbers already exist in uploaded file
"; } else { $errorMsg .= "Duplicate Document Numbers for Plant {$plant} : '" . implode(', ', $uniqueDocNumbers) . "' 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($duplicateEntriesExcel)) // { // //$errorMsg = 'Duplicate Document Number found in the uploaded file:
' . implode('
', $duplicateEntriesExcel); // $errorMsg = buildDuplicateMessage($duplicateEntriesExcel, 'Duplicate Document Number found in Uploaded File'); // Notification::make() // ->title('Duplicate Document Number in Uploaded File') // ->body($errorMsg) $successCount = 0; $failedRecords = []; DB::beginTransaction(); try { foreach ($rows as $index => $row) { if ($index == 0) continue; $rowNumber = $index + 1; try { $DisChaDesc = trim($row[3]); $plantCode = trim($row[4]); $CustomerCode = trim($row[5]); $DocNo = trim($row[6]); $DocDate = trim($row[7]); $CusTradeName = trim($row[9]); $CusLocation = trim($row[10]); if (empty($DocNo)) { throw new \Exception("Row '{$rowNumber}' Missing QR Code"); } $plant = Plant::where('code', $plantCode)->first(); if (!$plant) { throw new \Exception("Invalid plant code : '{$plantCode}'"); } if (!empty($DocDate)) { if (preg_match('/^\d{2}[-\/]\d{2}[-\/]\d{4}$/', $DocDate)) { [$day, $month, $year] = preg_split('/[-\/]/', $DocDate); $formattedDate = "{$year}-{$month}-{$day}"; } elseif (is_numeric($DocDate)) { $formattedDate = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($DocDate)->format('Y-m-d'); } else { $formattedDate = date('Y-m-d', strtotime($DocDate)); } } else { $formattedDate = null; } $record = InvoiceDataValidation::where('plant_id', $plant->id) ->where('document_number', $DocNo) ->first(); $curStat = $record ? 'Updation' : 'Insertion'; if ($record) { $record->update([ 'distribution_channel_desc' => $DisChaDesc, 'customer_code' => $CustomerCode, 'document_date' => $formattedDate, 'customer_trade_name' => $CusTradeName, 'customer_location' => $CusLocation, 'updated_by' => $operatorName ]); $inserted = $record; } else { // Record does not exist, create with 'created_by' $inserted = InvoiceDataValidation::create([ 'plant_id' => $plant->id, 'document_number' => $DocNo, 'distribution_channel_desc' => $DisChaDesc, 'customer_code' => $CustomerCode, 'document_date' => $formattedDate, 'customer_trade_name' => $CusTradeName, 'customer_location' => $CusLocation, 'created_by' => $operatorName ]); } // $inserted = InvoiceDataValidation::create([ // 'plant_id' => $plant->id, // 'document_number' => $DocNo, // 'distribution_channel_desc' => $DisChaDesc, // 'customer_code' => $CustomerCode, // 'document_date' => $formattedDate, // 'customer_trade_name' => $CusTradeName, // 'customer_location' => $CusLocation, // 'created_by' => $operatorName // ]); if (!$inserted) { throw new \Exception("{$curStat} failed for Document Number : {$DocNo}"); } $successCount++; } catch (\Exception $e) { $failedRecords[] = [ 'row' => $rowNumber, 'document_number' => $DocNo ?? null, 'error' => $e->getMessage() ]; } } DB::commit(); if (count($failedRecords) > 0) { $failedSummary = collect($failedRecords) ->map(fn($f) => "Row {$f['row']} ({$f['document_number']}) : {$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 data validation'); }), ExportAction::make() ->label('Export Invoice Data') ->color('warning') ->exporter(InvoiceDataValidationExporter::class) ->visible(function() { return Filament::auth()->user()->can('view export invoice data validation'); }), ]); } public static function getRelations(): array { return [ // ]; } public static function getPages(): array { return [ 'index' => Pages\ListInvoiceDataValidations::route('/'), 'create' => Pages\CreateInvoiceDataValidation::route('/create'), 'view' => Pages\ViewInvoiceDataValidation::route('/{record}'), 'edit' => Pages\EditInvoiceDataValidation::route('/{record}/edit'), ]; } public static function getEloquentQuery(): Builder { return parent::getEloquentQuery() ->withoutGlobalScopes([ SoftDeletingScope::class, ]); } }