diff --git a/app/Filament/Resources/WeightValidationResource.php b/app/Filament/Resources/WeightValidationResource.php
index efcf10b..3a6f7ac 100644
--- a/app/Filament/Resources/WeightValidationResource.php
+++ b/app/Filament/Resources/WeightValidationResource.php
@@ -2,16 +2,32 @@
namespace App\Filament\Resources;
+use App\Filament\Exports\WeightValidationExporter;
+use App\Filament\Imports\WeightValidationImporter;
use App\Filament\Resources\WeightValidationResource\Pages;
use App\Filament\Resources\WeightValidationResource\RelationManagers;
+use App\Models\Item;
+use App\Models\Plant;
use App\Models\WeightValidation;
+use Filament\Actions\Action;
+use Filament\Facades\Filament;
use Filament\Forms;
+use Filament\Forms\Components\FileUpload;
+use Filament\Forms\Components\Select;
use Filament\Forms\Form;
+use Filament\Forms\Get;
+use Filament\Notifications\Notification;
use Filament\Resources\Resource;
use Filament\Tables;
+use Filament\Tables\Actions\ExportAction;
+use Filament\Tables\Actions\ImportAction;
use Filament\Tables\Table;
+use Filament\Widgets\StatsOverviewWidget\Stat;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
+use Illuminate\Support\Facades\Storage;
+use Maatwebsite\Excel\Facades\Excel;
+use Str;
class WeightValidationResource extends Resource
{
@@ -63,26 +79,72 @@ class WeightValidationResource extends Resource
{
return $table
->columns([
- Tables\Columns\TextColumn::make('id')
- ->label('ID')
- ->numeric()
- ->sortable(),
- Tables\Columns\TextColumn::make('item.id')
- ->numeric()
- ->sortable(),
+ // Tables\Columns\TextColumn::make('id')
+ // ->label('ID')
+ // ->numeric()
+ // ->sortable(),
+ 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.name')
- ->numeric()
- ->sortable(),
- Tables\Columns\TextColumn::make('created_at')
- ->dateTime()
+ ->label('Plant')
+ ->alignCenter()
->sortable()
- ->toggleable(isToggledHiddenByDefault: true),
- Tables\Columns\TextColumn::make('updated_at')
+ ->searchable(),
+ Tables\Columns\TextColumn::make('item.code')
+ ->label('Item Code')
+ ->alignCenter()
+ ->searchable(),
+ Tables\Columns\TextColumn::make('obd_number')
+ ->label('OBD Number')
+ ->alignCenter()
+ ->searchable(),
+ Tables\Columns\TextColumn::make('line_number')
+ ->label('Line Number')
+ ->alignCenter(),
+ Tables\Columns\TextColumn::make('batch_number')
+ ->label('Batch Number')
+ ->alignCenter(),
+ Tables\Columns\TextColumn::make('heat_number')
+ ->label('Heat Number')
+ ->alignCenter(),
+ Tables\Columns\TextColumn::make('obd_weight')
+ ->label('Actual Weight')
+ ->alignCenter(),
+ Tables\Columns\TextColumn::make('vehicle_number')
+ ->label('Vehicle Number')
+ ->alignCenter()
+ ->searchable(),
+ Tables\Columns\TextColumn::make('bundle_number')
+ ->label('Bundle Number')
+ ->alignCenter(),
+ Tables\Columns\TextColumn::make('picked_weight')
+ ->label('Picked Weight')
+ ->alignCenter(),
+ Tables\Columns\TextColumn::make('scanned_by')
+ ->label('Scanned By')
+ ->alignCenter()
+ ->searchable(),
+ Tables\Columns\TextColumn::make('created_at')
+ ->label('Create At')
->dateTime()
+ ->alignCenter()
+ ->sortable(),
+ Tables\Columns\TextColumn::make('updated_at')
+ ->label('Updated At')
+ ->dateTime()
+ ->alignCenter()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('deleted_at')
+ ->label('Deleted At')
->dateTime()
+ ->alignCenter()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
@@ -99,6 +161,561 @@ class WeightValidationResource extends Resource
Tables\Actions\ForceDeleteBulkAction::make(),
Tables\Actions\RestoreBulkAction::make(),
]),
+ ])
+ ->headerActions([
+ Tables\Actions\Action::make('Import OBD Number')
+ ->label('Import OBD Weight Invoice')
+ ->form([
+ Select::make('plant_id')
+ ->options(Plant::pluck('name', 'id')->toArray())
+ ->label('Select Plant')
+ ->required()
+ ->default(function () {
+ return optional(WeightValidation::latest()->first())->plant_id;
+ })
+ ->afterStateUpdated(function ($state, callable $set, callable $get) {
+ $set('invoice_obd_number', null);
+ })
+ ->reactive(),
+ FileUpload::make('invoice_obd_number')
+ ->label('Import OBD Weight Invoice')
+ // ->required()
+ ->preserveFilenames() // <- this keeps the original filename
+ ->storeFiles(false) // prevent auto-storing, we will store manually
+ ->reactive()
+ ->required()
+ ->disk('local') //'local' refers to the local storage disk defined in config/filesystems.php, typically pointing to storage/app.
+ ->visible(fn (Get $get) => !empty($get('plant_id')))
+ ->directory('uploads/temp'),
+ ])
+ ->action(function (array $data) {
+ $uploadedFile = $data['invoice_obd_number'];
+
+ $disk = Storage::disk('local');
+
+ $plantId = $data['plant_id'];
+
+ // 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
+ // uploads/temp/3RA0018735.xlsx
+
+ $fullPath = Storage::disk('local')->path($path);
+
+ // /home/iot-dev/projects/pds/storage/app/private/uploads/temp/3RA0018735.xlsx
+
+ $totQuan = WeightValidation::where('obd_number',$originalNameOnly)->where('plant_id', $plantId)->count();
+ $scanSQuan = WeightValidation::where('obd_number',$originalNameOnly)->where('plant_id', $plantId)->whereNotNull('vehicle_number')->where('vehicle_number', '!=', '')->count();
+
+ if($totQuan == $scanSQuan && $totQuan > 0) {
+ Notification::make()
+ ->title('Completed: OBD invoice')
+ ->body('OBD invoice already completed the scanning process for selected plant.')
+ ->danger()
+ ->send();
+
+ if ($disk->exists($path))
+ {
+ $disk->delete($path);
+ }
+ return;
+ }
+
+ // if($totQuan > 0) {
+ // Notification::make()
+ // ->title('OBD invoice already exist in database for selected plant.')
+ // ->danger()
+ // ->send();
+
+ // if ($disk->exists($path))
+ // {
+ // $disk->delete($path);
+ // }
+ // return;
+ // }
+
+ 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 'OBD Invoice' file to proceed..!")
+ ->danger()
+ ->seconds(2)
+ ->send();
+
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ return;
+ }
+
+ $invalidMatCodes = [];
+ $invalidLines = [];
+ $duplicateLines = [];
+ $invalidBatch = [];
+ $invalidHeat = [];
+ $invalidWeight = [];
+ $materialCodes = [];
+ $lineNumbers = [];
+ $validRowsFound = false;
+
+ foreach ($rows as $index => $row)
+ {
+ if ($index === 0) continue; // Skip header
+
+ $materialCode = trim($row[0]);
+ $lineNumber = trim($row[1]);
+ $batchNumber = trim($row[2]);
+ $heatNumber = trim($row[3]);
+ $actualWeight = trim($row[4]);
+
+ if (empty($materialCode)) {
+ continue;
+ }
+
+ if (!empty($materialCode)) {
+ if(Str::length($materialCode) < 6 || !ctype_alnum($materialCode))
+ {
+ $invalidMatCodes[] = $materialCode;
+ continue;
+ }
+ else
+ {
+ $materialCodes[] = $materialCode;
+
+ $validData = true;
+ if(Str::length($lineNumber) < 1 || !is_numeric($lineNumber))
+ {
+ $validData = false;
+ $invalidLines[] = $materialCode;
+ }
+ else if (in_array($lineNumber, $lineNumbers))
+ {
+ $duplicateLines[] = $materialCode;
+ }
+ else
+ {
+ $lineNumbers[] = $lineNumber;
+ }
+ if(Str::length($batchNumber) < 8 || !is_numeric($batchNumber))//ctype_alnum
+ {
+ $validData = false;
+ $invalidBatch[] = $materialCode;
+ }
+ if(Str::length($heatNumber) < 4)
+ {
+ $validData = false;
+ $invalidHeat[] = $materialCode;
+ }
+ if(Str::length($actualWeight) < 1 || !is_numeric($actualWeight))//ctype_alnum
+ {
+ $validData = false;
+ $invalidWeight[] = $materialCode;
+ }
+
+ if ($validData)
+ {
+ $validRowsFound = true;
+ }
+ }
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ $uniqueInvalidCodes = array_unique($invalidMatCodes);
+ if (!empty($uniqueInvalidCodes)) {
+ Notification::make()
+ ->title('Invalid: Item Codes')
+ ->body('The following item codes should contain minimum 6 digit alpha numeric values:
' . implode(', ', $uniqueInvalidCodes))
+ ->danger()
+ ->seconds(2)
+ ->send();
+
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ return;
+ }
+
+ $uniqueInvalidLines = array_unique($invalidLines);
+ if (!empty($uniqueInvalidLines)) {
+ Notification::make()
+ ->title('Invalid: Line Numbers')
+ ->body('Line number should contain minimum 1 digit numeric values!
Following item codes has invalid line number:
' . implode(', ', $uniqueInvalidLines))
+ ->danger()
+ ->seconds(2)
+ ->send();
+
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ return;
+ }
+
+ $uniqueDupLines = array_unique($duplicateLines);
+ if (!empty($uniqueDupLines)) {
+ Notification::make()
+ ->title('Duplicate: Line Numbers')
+ ->body('The following item codes contains duplicate line numbers in invoice excel:
' . implode(', ', $uniqueDupLines))
+ ->danger()
+ ->seconds(2)
+ ->send();
+
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ return;
+ }
+
+ $uniqueInvalidBatch = array_unique($invalidBatch);
+ if (!empty($uniqueInvalidBatch)) {
+ Notification::make()
+ ->title('Invalid: Batch Numbers')
+ ->body('Batch number should contain minimum 8 digit numeric values!
Following item codes has invalid batch number:
' . implode(', ', $uniqueInvalidBatch))
+ ->danger()
+ ->seconds(2)
+ ->send();
+
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ return;
+ }
+
+ $uniqueInvalidHeat = array_unique($invalidHeat);
+ if (!empty($uniqueInvalidHeat)) {
+ Notification::make()
+ ->title('Invalid: Heat Numbers')
+ ->body('Heat number should contain minimum 4 characters!
Following item codes has invalid heat number:
' . implode(', ', $uniqueInvalidHeat))
+ ->danger()
+ ->seconds(2)
+ ->send();
+
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ return;
+ }
+
+ $uniqueInvalidWeight = array_unique($invalidWeight);
+ if (!empty($uniqueInvalidWeight)) {
+ Notification::make()
+ ->title('Invalid: Actual Weights')
+ ->body('Actual weight should contain minimum 1 digit numeric value!
Following item codes has invalid actual weight:
' . implode(', ', $uniqueInvalidWeight))
+ ->danger()
+ ->seconds(2)
+ ->send();
+
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ return;
+ }
+
+ $uniqueCodes = array_unique($materialCodes);
+
+ //itemNotFound.
+ $matchedItems = Item::whereIn('code', $uniqueCodes)->get();
+
+ // // Get all codes that exist in the database for the given plant_id
+ // $existingCodes = StickerMaster::where('plant_id', $plantId)
+ // ->whereHas('item', function ($query) use ($uniqueCodes) {
+ // $query->whereIn('code', $uniqueCodes);
+ // })
+ // ->with('item') // Eager load for performance
+ // ->get()
+ // ->pluck('item.code')
+ // ->toArray();
+
+ $matchedCodes = $matchedItems->pluck('code')->toArray(); //item.code
+ $missingCodes = array_diff($uniqueCodes, $matchedCodes);
+
+ if (!empty($missingCodes)) {
+ $missingCount = count($missingCodes);
+
+ $message = $missingCount > 10 ? "'$missingCount' item codes are not found in database." : 'The following item codes are not found in database:
' . implode(', ', $missingCodes);
+
+ Notification::make()
+ ->title('Unknown: Item Codes')
+ ->body($message)
+ ->danger()
+ ->seconds(2)
+ ->send();
+
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ return;
+ }
+
+ //plantNotFound
+ $matchedItems = Item::whereIn('code', $uniqueCodes)->where('plant_id', $plantId)->get();
+
+ $matchedCodes = $matchedItems->pluck('code')->toArray();
+ $missingCodes = array_diff($uniqueCodes, $matchedCodes);
+
+ if (!empty($missingCodes)) {
+ $missingCount = count($missingCodes);
+
+ $message = $missingCount > 10 ? "'$missingCount' item codes are not found in database for choosed plant." : 'The following item codes are not found in database for choosed plant:
' . implode(', ', $missingCodes);
+
+ Notification::make()
+ ->title('Unknown: Item Codes')
+ ->body($message)
+ ->danger()
+ ->seconds(2)
+ ->send();
+
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ return;
+ }
+
+ if (!$validRowsFound) {
+ Notification::make()
+ ->title('Invalid OBD Invoice')
+ ->danger() // This makes the notification red to indicate an error
+ ->body('Uploaded excel sheet is empty or
contains no valid data.')
+ ->seconds(2)
+ ->send();
+
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ return;
+ }
+
+ $updateInv = WeightValidation::where('plant_id', $plantId)->where('obd_number', $originalNameOnly)->first()?->exists();
+
+ $availLines = WeightValidation::where('plant_id', $plantId)->where('obd_number', $originalNameOnly)->where(function($query) {
+ $query->whereNull('vehicle_number')->orWhere('vehicle_number', '');
+ })->pluck('line_number')->toArray();
+
+ $updated = WeightValidation::where('plant_id', $plantId)->where('obd_number', $originalNameOnly)->where(function($query) {
+ $query->whereNull('vehicle_number')->orWhere('vehicle_number', '');
+ })->count();
+
+ WeightValidation::where('plant_id', $plantId)->where('obd_number', $originalNameOnly)->where(function($query) {
+ $query->whereNull('vehicle_number')->orWhere('vehicle_number', '');
+ })
+ ->forceDelete();
+
+ $inserted = 0;
+ // $updated = 0;
+ foreach ($rows as $index => $row)
+ {
+ // Skip header
+ if ($index === 0) { continue; }
+
+ $materialCode = trim($row[0]);
+ $lineNumber = trim($row[1]);
+ $batchNumber = trim($row[2]);
+ $heatNumber = trim($row[3]);
+ $actualWeight = trim($row[4]);
+
+ if (empty($materialCode) || Str::length($materialCode) < 6) {
+ continue;
+ }
+
+ if (!empty($materialCode)) {
+ $recordExist = WeightValidation::where('plant_id', $plantId)->where('obd_number', $originalNameOnly)->where('line_number', $lineNumber)->first();
+
+ $masItem = Item::where('plant_id', $plantId)->where('code', $materialCode)->first();
+ if($recordExist) {
+ $skipUpd = $recordExist->vehicle_number ?? null;
+ if($skipUpd)
+ {
+ continue;
+ }
+ else
+ {
+ if ($masItem) {
+ $recordExist->update([
+ 'item_id' => $masItem->id,
+ 'batch_number' => $batchNumber,
+ 'heat_number' => $heatNumber,
+ 'obd_weight' => $actualWeight,
+ 'updated_at' => now(),
+ ]);
+ $updated++;
+ }
+ }
+ }
+ else {
+ if ($masItem) {
+ WeightValidation::create([
+ 'item_id' => $masItem->id,
+ 'plant_id' => $plantId,
+ 'obd_number' => $originalNameOnly,
+ 'line_number' => $lineNumber,
+ 'batch_number' => $batchNumber,
+ 'heat_number' => $heatNumber,
+ 'obd_weight' => $actualWeight,
+ ]);
+ if (in_array($lineNumber, $availLines))
+ {
+ continue;
+ }
+ else
+ {
+ $inserted++;
+ }
+ }
+ }
+ }
+ }
+
+ if($updateInv) {
+ if ($updated > 0) {
+ Notification::make()
+ ->title("Start the scanning process!")
+ ->body("'$updated' OBD lines were updated and
'$inserted' OBD lines were inserted for
imported OBD Invoice : '$originalNameOnly'.")
+ ->info()
+ // ->success()
+ ->seconds(1)
+ ->send();
+
+ // Update total quantity in the form
+ $totalQuantity = WeightValidation::where('obd_number', $originalNameOnly)->where('plant_id', $plantId)->count();
+ $scannedQuantity = WeightValidation::where('obd_number', $originalNameOnly)->where('plant_id', $plantId)->whereNotNull('vehicle_number')->where('vehicle_number', '!=', '')->count();
+
+ if ($totalQuantity === $scannedQuantity)
+ {
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ // $this->dispatch('refreshCompletedInvoice', invoiceNumber: $originalNameOnly, plantId: $plantId);
+ }
+ else
+ {
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ // $this->dispatch('refreshInvoiceData', invoiceNumber: $originalNameOnly, plantId: $plantId);
+ }
+ }
+ else if ($inserted > 0) {
+ Notification::make()
+ ->title("Start the scanning process!")
+ ->body("'$inserted' OBD lines were inserted for imported OBD Invoice : '$originalNameOnly'.")
+ ->info()
+ // ->success()
+ ->seconds(1)
+ ->send();
+
+ // Update total quantity in the form
+ $totalQuantity = WeightValidation::where('obd_number', $originalNameOnly)->where('plant_id', $plantId)->count();
+ $scannedQuantity = WeightValidation::where('obd_number', $originalNameOnly)->where('plant_id', $plantId)->whereNotNull('vehicle_number')->where('vehicle_number', '!=', '')->count();
+
+ if ($totalQuantity === $scannedQuantity)
+ {
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ // $this->dispatch('refreshCompletedInvoice', invoiceNumber: $originalNameOnly, plantId: $plantId);
+ }
+ else
+ {
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ // $this->dispatch('refreshInvoiceData', invoiceNumber: $originalNameOnly, plantId: $plantId);
+ }
+ }
+ else {
+ Notification::make()
+ ->title("Import Failed: OBD Invoice")
+ ->body("No exist records were updated for imported OBD Invoice : '$originalNameOnly'.")
+ ->danger()
+ ->seconds(2)
+ ->send();
+
+ $totalQuantity = WeightValidation::where('obd_number', $originalNameOnly)->where('plant_id', $plantId)->count();
+ $scannedQuantity = WeightValidation::where('obd_number', $originalNameOnly)->where('plant_id', $plantId)->whereNotNull('vehicle_number')->where('vehicle_number', '!=', '')->count();
+
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ // $this->dispatch('refreshEmptyInvoice', invoiceNumber: $originalNameOnly, plantId: $plantId);
+ return;
+ }
+ }
+ else {
+ if ($inserted > 0) {
+ Notification::make()
+ ->title("Start the scanning process!")
+ ->body("'$inserted' OBD lines were inserted for imported OBD Invoice : '$originalNameOnly'.")
+ ->info()
+ // ->success()
+ ->seconds(1)
+ ->send();
+
+ // Update total quantity in the form
+ $totalQuantity = WeightValidation::where('obd_number', $originalNameOnly)->where('plant_id', $plantId)->count();
+ $scannedQuantity = WeightValidation::where('obd_number', $originalNameOnly)->where('plant_id', $plantId)->whereNotNull('vehicle_number')->where('vehicle_number', '!=', '')->count();
+
+ if ($totalQuantity === $scannedQuantity)
+ {
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ // $this->dispatch('refreshCompletedInvoice', invoiceNumber: $originalNameOnly, plantId: $plantId);
+ }
+ else
+ {
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ // $this->dispatch('refreshInvoiceData', invoiceNumber: $originalNameOnly, plantId: $plantId);
+ }
+ }
+ else {
+ Notification::make()
+ ->title("Import Failed: OBD Invoice")
+ ->body("No new records were inserted for imported OBD Invoice : '$originalNameOnly'.")
+ ->danger()
+ ->seconds(2)
+ ->send();
+
+ $totalQuantity = WeightValidation::where('obd_number', $originalNameOnly)->where('plant_id', $plantId)->count();
+ $scannedQuantity = WeightValidation::where('obd_number', $originalNameOnly)->where('plant_id', $plantId)->whereNotNull('vehicle_number')->where('vehicle_number', '!=', '')->count();
+
+ if ($disk->exists($path)) {
+ $disk->delete($path);
+ }
+ // $this->dispatch('refreshEmptyInvoice', invoiceNumber: $originalNameOnly, plantId: $plantId);
+ return;
+ }
+ }
+ }
+ })
+ ->visible(function() {
+ return Filament::auth()->user()->can('view import obd number validations');
+ }),
+ ImportAction::make()
+ ->label('Import OBD Invoice')
+ ->color('warning')
+ ->importer(WeightValidationImporter::class)
+ ->visible(function() {
+ return Filament::auth()->user()->can('view import weight validation');
+ }),
+ ExportAction::make()
+ ->label('Export OBD Invoices')
+ ->color('warning')
+ ->exporter(WeightValidationExporter::class)
+ ->visible(function() {
+ return Filament::auth()->user()->can('view export weight validation');
+ }),
]);
}