diff --git a/app/Filament/Resources/InvoiceOutValidationResource.php b/app/Filament/Resources/InvoiceOutValidationResource.php new file mode 100644 index 0000000..97d58e3 --- /dev/null +++ b/app/Filament/Resources/InvoiceOutValidationResource.php @@ -0,0 +1,368 @@ +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') + ->alignCenter() + ->dateTime() + ->sortable(), + Tables\Columns\TextColumn::make('scanned_by') + ->label('Scanned By') + ->alignCenter() + ->sortable(), + Tables\Columns\TextColumn::make('created_at') + ->dateTime() + ->sortable(), + //->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('created_by') + ->label('Created By') + ->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 + + $plantCode = trim($row[0]); + $qrCode = trim($row[1]); + $scannedAt = trim($row[2]); + $scannedby = trim($row[3]); + //$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)) { + 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)) { + 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)) { + 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; + + $plantCode = trim($row[0]); + $qrcode = trim($row[1]); + $scannedAt = trim($row[2]); + $scannedBy = trim($row[3]); + + 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(); + } + } + }) + ->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, + ]); + } +} diff --git a/app/Filament/Resources/InvoiceOutValidationResource/Pages/CreateInvoiceOutValidation.php b/app/Filament/Resources/InvoiceOutValidationResource/Pages/CreateInvoiceOutValidation.php new file mode 100644 index 0000000..12345ed --- /dev/null +++ b/app/Filament/Resources/InvoiceOutValidationResource/Pages/CreateInvoiceOutValidation.php @@ -0,0 +1,12 @@ +