From 54e0760f94833a48b91304bec5eeced3a2f2914d Mon Sep 17 00:00:00 2001 From: dhanabalan Date: Tue, 4 Nov 2025 10:41:56 +0530 Subject: [PATCH] Enhance invoice validation by adding plant type checks and improving error handling for imports --- .../InvoiceDataValidationResource.php | 91 +++++--- .../InvoiceOutValidationResource.php | 200 +++++++++++++----- 2 files changed, 217 insertions(+), 74 deletions(-) diff --git a/app/Filament/Resources/InvoiceDataValidationResource.php b/app/Filament/Resources/InvoiceDataValidationResource.php index 266e37e..d819c2b 100644 --- a/app/Filament/Resources/InvoiceDataValidationResource.php +++ b/app/Filament/Resources/InvoiceDataValidationResource.php @@ -203,30 +203,35 @@ class InvoiceDataValidationResource extends Resource $invalidCusLocation = []; $invalidUser = []; $userNotFound = []; + $invalidPlantType = []; foreach ($rows as $index => $row) { if ($index == 0) continue; // Skip header - $plantCode = trim($row[0]); - $DisChaDesc = trim($row[1]); - $CustomerCode = trim($row[2]); - $DocNo = trim($row[3]); - $DocDate = trim($row[4]); - $CusTradeName = trim($row[5]); - $CusLocation = trim($row[6]); - if (empty($plantCode)) $invalidPlantCode[] = "Row {$index}"; - //if (empty($DisChaDesc)) $invalidDisChaDesc[] = "Row {$index}"; - if (empty($CustomerCode)) $invalidCustomerCode[] = "Row {$index}"; + $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 (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; @@ -241,7 +246,7 @@ class InvoiceDataValidationResource extends Resource //!empty($invalidDisChaDesc) || - if (!empty($invalidCustomerCode) || !empty($invalidDocNo) || !empty($invalidDocDate) || !empty($invalidCusTradeName) || !empty($invalidCusLocation) || !empty($invalidUser)) + if (!empty($invalidCustomerCode) || !empty($invalidDocNo) || !empty($invalidDocDate) || !empty($invalidCusTradeName) || !empty($invalidCusLocation)) { $errorMsg = ''; @@ -251,7 +256,6 @@ class InvoiceDataValidationResource extends Resource 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) . '
'; - if (!empty($invalidUser)) $errorMsg .= 'Missing User in rows: ' . implode(', ', $invalidUser) . '
'; Notification::make() ->title('Missing Mandatory Fields') @@ -265,6 +269,30 @@ class InvoiceDataValidationResource extends Resource return; } + // if (!empty($invalidDocNo) || !empty($invalidUser)) + // { + // $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) . '
'; + // //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() @@ -277,6 +305,19 @@ class InvoiceDataValidationResource extends Resource } 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; + } if (!empty($invalidPlaCoFound)) { $invalidPlaCoFound = array_unique($invalidPlaCoFound); Notification::make() @@ -308,13 +349,13 @@ class InvoiceDataValidationResource extends Resource foreach ($rows as $index => $row) { if ($index == 0) continue; - $plantCode = trim($row[0]); - $DisChaDesc = trim($row[1]); - $CustomerCode = trim($row[2]); - $DocNo = trim($row[3]); - $DocDate = trim($row[4]); - $CusTradeName = trim($row[5]); - $CusLocation = trim($row[6]); + $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]); try { @@ -375,9 +416,9 @@ class InvoiceDataValidationResource extends Resource return Filament::auth()->user()->can('view import invoice data validation'); }), ExportAction::make() - ->exporter(InvoiceDataValidationExporter::class) - ->visible(function() { - return Filament::auth()->user()->can('view export invoice data validation'); + ->exporter(InvoiceDataValidationExporter::class) + ->visible(function() { + return Filament::auth()->user()->can('view export invoice data validation'); }), ]); } diff --git a/app/Filament/Resources/InvoiceOutValidationResource.php b/app/Filament/Resources/InvoiceOutValidationResource.php index cdd7c9c..617aefb 100644 --- a/app/Filament/Resources/InvoiceOutValidationResource.php +++ b/app/Filament/Resources/InvoiceOutValidationResource.php @@ -9,6 +9,7 @@ use App\Models\InvoiceOutValidation; use App\Models\Plant; use App\Models\User; use Carbon\Carbon; +use DB; use Filament\Forms; use Filament\Forms\Form; use Filament\Resources\Resource; @@ -23,6 +24,7 @@ use Filament\Notifications\Notification; use Maatwebsite\Excel\Facades\Excel; use Storage; use Filament\Tables\Actions\ExportAction; +use PhpOffice\PhpSpreadsheet\Shared\Date as ExcelDate; class InvoiceOutValidationResource extends Resource { @@ -183,10 +185,10 @@ class InvoiceOutValidationResource extends Resource { if ($index == 0) continue; // Skip header - $plantCode = trim($row[0]); $qrCode = trim($row[1]); - $scannedAt = trim($row[2]); - $scannedby = trim($row[3]); + $plantCode = trim($row[2]); + $scannedAt = trim($row[3]); + $scannedby = trim($row[4]); //$createdBy = trim($row[4]); if (empty($plantCode)) $invalidPlantCode[] = "Row {$index}"; @@ -267,67 +269,167 @@ class InvoiceOutValidationResource extends Resource 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; - $failCount = 0; + $failedRecords = []; - $lastErrorMessage = null; + DB::beginTransaction(); - foreach ($rows as $index => $row) { - if ($index == 0) continue; + try { + 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]); + $rowNumber = $index + 1; // For Excel-style numbering - try - { - $plant = Plant::where('code', $plantCode)->first(); + try { + $qrcode = trim($row[1]); + $plantCode = trim($row[2]); + $scannedAt = trim($row[3]); + $scannedBy = trim($row[4]); - $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}"); + if (empty($qrcode)) { + throw new \Exception("Missing QR Code"); } - } - $inserted = InvoiceOutValidation::create([ - 'plant_id' => $plant->id, - 'qr_code' => $qrcode, - 'scanned_at' => $formattedDate, - 'scanned_by' => $scannedBy, - 'created_by' => $operatorName, - ]); + $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}"); + } - if ($inserted) { $successCount++; - } else { - $failCount++; + + } catch (\Exception $e) { + $failedRecords[] = [ + 'row' => $rowNumber, + 'qrcode' => $qrcode ?? null, + 'error' => $e->getMessage(), + ]; } - } 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."; + + 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($errorText) + ->body("No records were inserted. Error: {$e->getMessage()}") ->danger() ->send(); }