Added update invoice toggle button and import invoice validations
This commit is contained in:
@@ -3,13 +3,21 @@
|
|||||||
namespace App\Filament\Resources;
|
namespace App\Filament\Resources;
|
||||||
|
|
||||||
use App\Filament\Resources\InvoiceValidationResource\Pages;
|
use App\Filament\Resources\InvoiceValidationResource\Pages;
|
||||||
|
use App\Imports\ExcelImport;
|
||||||
use App\Models\InvoiceValidation;
|
use App\Models\InvoiceValidation;
|
||||||
|
use App\Models\Plant;
|
||||||
use App\Models\StickerMaster;
|
use App\Models\StickerMaster;
|
||||||
|
use Filament\Actions\Action as FilamentActionsAction;
|
||||||
use Filament\Actions\CreateAction;
|
use Filament\Actions\CreateAction;
|
||||||
use Filament\Forms;
|
use Filament\Forms;
|
||||||
|
use Filament\Forms\Components\Actions\Action as ActionsAction;
|
||||||
use Filament\Forms\Components\FileUpload;
|
use Filament\Forms\Components\FileUpload;
|
||||||
use Filament\Forms\Components\Section;
|
use Filament\Forms\Components\Section;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Forms\Components\ToggleButtons;
|
||||||
use Filament\Forms\Form;
|
use Filament\Forms\Form;
|
||||||
|
use Filament\Forms\Get;
|
||||||
use Filament\Resources\Resource;
|
use Filament\Resources\Resource;
|
||||||
use Filament\Tables;
|
use Filament\Tables;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
@@ -20,7 +28,11 @@ use Filament\Notifications\Notification;
|
|||||||
use Filament\Tables\Actions\Action;
|
use Filament\Tables\Actions\Action;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Maatwebsite\Excel\Facades\Excel;
|
use Maatwebsite\Excel\Facades\Excel;
|
||||||
use Livewire\Livewire; // Ensure this is imported
|
use Livewire\Livewire;
|
||||||
|
use Str;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class InvoiceValidationResource extends Resource
|
class InvoiceValidationResource extends Resource
|
||||||
{
|
{
|
||||||
@@ -45,117 +57,842 @@ class InvoiceValidationResource extends Resource
|
|||||||
Section::make('')
|
Section::make('')
|
||||||
->schema([
|
->schema([
|
||||||
|
|
||||||
Forms\Components\Select::make('plant_id')
|
Forms\Components\Select::make('plant_id')
|
||||||
->relationship('plant', 'name')
|
->relationship('plant', 'name')
|
||||||
->required(),
|
|
||||||
|
|
||||||
Forms\Components\TextInput::make('invoice_number')
|
|
||||||
->required()
|
->required()
|
||||||
->label('Invoice Number')
|
// ->preload()
|
||||||
|
// ->nullable(),
|
||||||
|
->reactive()
|
||||||
->columnSpan(1)
|
->columnSpan(1)
|
||||||
|
->default(function () {
|
||||||
|
return optional(InvoiceValidation::latest()->first())->plant_id;
|
||||||
|
})
|
||||||
|
->disabled(fn (Get $get) => !empty($get('id')))
|
||||||
|
// ->afterStateUpdated(fn ($set) => $set('block_id', null) & $set('name', null) & $set('start_time', null) & $set('duration', null) & $set('end_time', null))
|
||||||
|
->afterStateUpdated(function ($state, callable $set, callable $get) {
|
||||||
|
$plantId = $get('plant_id');
|
||||||
|
$set('update_invoice', null);
|
||||||
|
// Ensure `linestop_id` is not cleared
|
||||||
|
if (!$plantId) {
|
||||||
|
$set('ivPlantError', 'Please select a plant first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$set('ivPlantError', null);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
->extraAttributes(fn ($get) => [
|
||||||
|
'class' => $get('ivPlantError') ? 'border-red-500' : '',
|
||||||
|
])
|
||||||
|
->hint(fn ($get) => $get('ivPlantError') ? $get('ivPlantError') : null)
|
||||||
|
->hintColor('danger'),
|
||||||
|
|
||||||
|
Forms\Components\TextInput::make('invoice_number')
|
||||||
|
->label('Invoice Number')
|
||||||
|
->required()
|
||||||
|
->reactive()
|
||||||
|
->columnSpan(1)
|
||||||
|
->readOnly(fn (callable $get) => !empty($get('serial_number')))
|
||||||
|
//->disabled(fn (Get $get) => !empty($get('serial_number')))
|
||||||
->extraAttributes([
|
->extraAttributes([
|
||||||
'x-data' => '{ value: "" }',
|
'x-data' => '{ value: "" }',
|
||||||
'x-model' => 'value',
|
'x-model' => 'value',
|
||||||
'x-on:keydown.enter.prevent' => '$wire.processInvoice(value)',
|
'x-on:keydown.enter.prevent' => '$wire.processInvoice(value)',
|
||||||
]),
|
])
|
||||||
|
// ->afterStateHydrated(function (TextInput $component, string $state) {
|
||||||
|
// $component->state(ucwords($state));
|
||||||
|
// })
|
||||||
|
->afterStateUpdated(function ($state, callable $set, callable $get) {
|
||||||
|
$invNo = $get('invoice_number');
|
||||||
|
$set('serial_number', null);
|
||||||
|
$set('update_invoice', null);
|
||||||
|
// if (!$invNo) { return; } else { }
|
||||||
|
}),
|
||||||
|
|
||||||
Forms\Components\TextInput::make('serial_number')
|
Forms\Components\TextInput::make('serial_number')
|
||||||
|
->label('Serial Number')
|
||||||
|
->reactive()
|
||||||
|
->readOnly(fn (callable $get) => empty($get('invoice_number')))
|
||||||
|
//->disabled(fn (Get $get) => empty($get('invoice_number')))
|
||||||
->extraAttributes([
|
->extraAttributes([
|
||||||
'x-data' => '{ value: "" }',
|
'x-data' => '{ value: "" }',
|
||||||
'x-model' => 'value',
|
'x-model' => 'value',
|
||||||
'wire:keydown.enter.prevent' => 'processSerialNumber(value)', // Using wire:keydown
|
'wire:keydown.enter.prevent' => 'processSerialNumber(value)', // Using wire:keydown
|
||||||
])
|
])
|
||||||
|
|
||||||
->columnSpan(1),
|
->columnSpan(1),
|
||||||
|
|
||||||
Forms\Components\TextInput::make('total_quantity')
|
Forms\Components\TextInput::make('total_quantity')
|
||||||
->label('Total Quantity')
|
->label('Total Quantity')
|
||||||
|
->readOnly(true)
|
||||||
->columnSpan(1),
|
->columnSpan(1),
|
||||||
Forms\Components\TextInput::make('scanned_quantity')
|
Forms\Components\TextInput::make('scanned_quantity')
|
||||||
->label('Scanned Quantity')
|
->label('Scanned Quantity')
|
||||||
|
->readOnly(true)
|
||||||
->columnSpan(1),
|
->columnSpan(1),
|
||||||
|
ToggleButtons::make('update_invoice')
|
||||||
|
->label('Update Invoice?')
|
||||||
|
->boolean()
|
||||||
|
->grouped()
|
||||||
|
->reactive()
|
||||||
|
->hidden(fn (callable $get) => ($get('invoice_number') == null || $get('update_invoice') === '0') || !empty($get('serial_number')))
|
||||||
|
->afterStateUpdated(function ($state, callable $set, callable $get) {
|
||||||
|
if(!$get('plant_id'))
|
||||||
|
{
|
||||||
|
$set('update_invoice', null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($get('update_invoice') === "1")
|
||||||
|
{
|
||||||
|
$totQuan = InvoiceValidation::where('invoice_number', $get('invoice_number'))->where('plant_id', $get('plant_id'))->count();
|
||||||
|
if($totQuan <= 0)
|
||||||
|
{
|
||||||
|
$set('update_invoice', null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$totMQuan = InvoiceValidation::where('invoice_number', $get('invoice_number'))->whereNotNull('quantity')->where('plant_id', $get('plant_id'))->count();
|
||||||
|
$scanMQuan = InvoiceValidation::where('invoice_number', $get('invoice_number'))->whereNotNull('serial_number')->where('serial_number', '!=', '')->where('plant_id', $get('plant_id'))->count();
|
||||||
|
$scanSQuan = InvoiceValidation::where('invoice_number', $get('invoice_number'))->where('scanned_status', 'Scanned')->where('plant_id', $get('plant_id'))->count();
|
||||||
|
|
||||||
|
if($totMQuan > 0)
|
||||||
|
{
|
||||||
|
if ($totQuan === $scanMQuan)
|
||||||
|
{
|
||||||
|
$set('update_invoice', null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ($totQuan === $scanSQuan)
|
||||||
|
{
|
||||||
|
$set('update_invoice', null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
->disabled(fn (Get $get) => empty($get('invoice_number'))),
|
||||||
|
Forms\Components\TextInput::make('id')
|
||||||
|
->hidden()
|
||||||
|
->readOnly(true),
|
||||||
])
|
])
|
||||||
->columns(5),
|
->columns(5),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static function table(Table $table): Table
|
public static function table(Table $table): Table
|
||||||
{
|
{
|
||||||
|
|
||||||
return $table
|
return $table
|
||||||
->columns([
|
->columns([
|
||||||
Tables\Columns\TextColumn::make('id')
|
Tables\Columns\TextColumn::make('id')
|
||||||
->label('ID')
|
->label('ID')
|
||||||
->numeric()
|
->numeric()
|
||||||
->sortable(),
|
->sortable(),
|
||||||
Tables\Columns\TextColumn::make('stickerMaster.id')
|
Tables\Columns\TextColumn::make('invoice_number')
|
||||||
->numeric()
|
->label('Invoice Number')
|
||||||
->sortable(),
|
->sortable(),
|
||||||
Tables\Columns\TextColumn::make('plant.name')
|
Tables\Columns\TextColumn::make('stickerMaster.item.code')
|
||||||
->numeric()
|
->label('Material Code')
|
||||||
|
->sortable(),
|
||||||
|
Tables\Columns\TextColumn::make('serial_number')
|
||||||
|
->label('Serial Number')
|
||||||
|
->sortable(),
|
||||||
|
Tables\Columns\TextColumn::make('motor_scanned_status')
|
||||||
|
->label('Motor Scanned Status')
|
||||||
|
->sortable(),
|
||||||
|
Tables\Columns\TextColumn::make('pump_scanned_status')
|
||||||
|
->label('Pump Scanned Status')
|
||||||
|
->sortable(),
|
||||||
|
Tables\Columns\TextColumn::make('scanned_status_set')
|
||||||
|
->label('Pump Set Scanned Status')
|
||||||
|
->sortable(),
|
||||||
|
Tables\Columns\TextColumn::make('capacitor_scanned_status')
|
||||||
|
->label('Capacitor Scanned Status')
|
||||||
|
->sortable(),
|
||||||
|
Tables\Columns\TextColumn::make('scanned_status')
|
||||||
|
->label('Scanned Status')
|
||||||
|
->sortable(),
|
||||||
|
Tables\Columns\TextColumn::make('panel_box_supplier')
|
||||||
|
->label('Panel Box Supplier')
|
||||||
|
->sortable(),
|
||||||
|
Tables\Columns\TextColumn::make('panel_box_serial_number')
|
||||||
|
->label('Panel Box Serial Number')
|
||||||
->sortable(),
|
->sortable(),
|
||||||
Tables\Columns\TextColumn::make('load_rate')
|
Tables\Columns\TextColumn::make('load_rate')
|
||||||
|
->label('Load Rate')
|
||||||
->numeric()
|
->numeric()
|
||||||
->sortable(),
|
->sortable(),
|
||||||
|
Tables\Columns\TextColumn::make('upload_status')
|
||||||
|
->label('Upload Status')
|
||||||
|
->sortable(),
|
||||||
|
Tables\Columns\TextColumn::make('batch_number')
|
||||||
|
->label('Batch Number')
|
||||||
|
->sortable(),
|
||||||
|
Tables\Columns\TextColumn::make('quantity')
|
||||||
|
->label('Quantity')
|
||||||
|
->numeric()
|
||||||
|
->sortable(),
|
||||||
|
Tables\Columns\TextColumn::make('operator_id')
|
||||||
|
->label('Operator ID')
|
||||||
|
->sortable(),
|
||||||
|
Tables\Columns\TextColumn::make('plant.name')
|
||||||
|
->label('Plant')
|
||||||
|
->sortable(),
|
||||||
|
Tables\Columns\TextColumn::make('created_at')
|
||||||
|
->label('Created At')
|
||||||
|
->dateTime()
|
||||||
|
->sortable(),
|
||||||
|
Tables\Columns\TextColumn::make('updated_at')
|
||||||
|
->label('Updated At')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
Tables\Columns\TextColumn::make('deleted_at')
|
||||||
|
->label('Deleted At')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
->headerActions([
|
->headerActions([
|
||||||
|
|
||||||
|
// Action::make('plant_id')
|
||||||
|
// ->label('Select Plant')
|
||||||
|
// ->form([
|
||||||
|
// Select::make('plant_id')
|
||||||
|
// ->options(Plant::pluck('name', 'id')->toArray()) // Fetch plant names and IDs
|
||||||
|
// ->label('Select Plant')
|
||||||
|
// ->required()
|
||||||
|
// ->reactive()
|
||||||
|
// ]),
|
||||||
|
|
||||||
Tables\Actions\Action::make('Import Serial Number')
|
Tables\Actions\Action::make('Import Serial Number')
|
||||||
->label('Import Serial Number')
|
->label('Import Serial Invoice')
|
||||||
->form([
|
->form([
|
||||||
|
Select::make('plant_id')
|
||||||
|
->options(Plant::pluck('name', 'id')->toArray()) // Fetch plant names and IDs
|
||||||
|
->label('Select Plant')
|
||||||
|
->required()
|
||||||
|
->default(function () {
|
||||||
|
return optional(InvoiceValidation::latest()->first())->plant_id;
|
||||||
|
})
|
||||||
|
->afterStateUpdated(function ($state, callable $set, callable $get) {
|
||||||
|
$set('invoice_serial_number', null);
|
||||||
|
})
|
||||||
|
->reactive(),
|
||||||
|
|
||||||
FileUpload::make('invoice_serial_number')
|
FileUpload::make('invoice_serial_number')
|
||||||
->label('Invoice Serial Number')
|
->label('Invoice Serial Number')
|
||||||
|
// ->required()
|
||||||
->preserveFilenames() // <- this keeps the original filename
|
->preserveFilenames() // <- this keeps the original filename
|
||||||
->storeFiles(false) // prevent auto-storing, we will store manually
|
->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.
|
->disk('local') //'local' refers to the local storage disk defined in config/filesystems.php, typically pointing to storage/app.
|
||||||
->directory('uploads/temp'), //storage/app/uploads/temp
|
->visible(fn (Get $get) => !empty($get('plant_id')))
|
||||||
|
->directory('uploads/temp'),
|
||||||
])
|
])
|
||||||
|
|
||||||
->action(function (array $data) {
|
->action(function (array $data) {
|
||||||
$uploadedFile = $data['invoice_serial_number'];
|
$uploadedFile = $data['invoice_serial_number'];
|
||||||
|
|
||||||
|
$disk = Storage::disk('local');
|
||||||
|
|
||||||
|
$plantId = $data['plant_id'];
|
||||||
|
|
||||||
// Get original filename
|
// Get original filename
|
||||||
$originalName = $uploadedFile->getClientOriginalName(); // e.g. 3RA0018732.xlsx
|
$originalName = $uploadedFile->getClientOriginalName(); // e.g. 3RA0018732.xlsx
|
||||||
|
|
||||||
// Store manually using storeAs to keep original name
|
// Store manually using storeAs to keep original name
|
||||||
$path = $uploadedFile->storeAs('uploads/temp', $originalName, 'local'); // returns relative path
|
$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 = InvoiceValidation::where('invoice_number', $originalName)->where('plant_id', $plantId)->count();
|
||||||
|
$scanSQuan = InvoiceValidation::where('invoice_number', $originalName)->where('scanned_status', 'Scanned')->where('plant_id', $plantId)->count();
|
||||||
|
|
||||||
|
if($totQuan == $scanSQuan && $totQuan > 0)
|
||||||
|
{
|
||||||
|
Notification::make()
|
||||||
|
->title('Serial invoice already completed the scanning process 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 'Serial Invoice' file to proceed..!")
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
if ($disk->exists($path)) {
|
||||||
|
$disk->delete($path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$invalidMatCodes = [];
|
||||||
|
$invalidSerialCodes=[];
|
||||||
|
$materialCodes = [];
|
||||||
|
$missingSerials = [];
|
||||||
|
$duplicateSerials = [];
|
||||||
|
$seenSerialNumbers = [];
|
||||||
|
$validRowsFound = false;
|
||||||
|
|
||||||
|
foreach ($rows as $index => $row)
|
||||||
|
{
|
||||||
|
if ($index === 0) continue; // Skip header
|
||||||
|
|
||||||
|
$materialCode = trim($row[0]);
|
||||||
|
$serialNumber = trim($row[1]);
|
||||||
|
|
||||||
|
if (empty($materialCode) && empty($serialNumber)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($materialCode))
|
||||||
|
{
|
||||||
|
if(Str::length($materialCode) < 6 || !ctype_alnum($materialCode))
|
||||||
|
{
|
||||||
|
$invalidMatCodes[] = $materialCode;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
if (empty($serialNumber)) {
|
||||||
|
$missingSerials[] = $materialCode;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if(Str::length($serialNumber) < 9 || !ctype_alnum($serialNumber))
|
||||||
|
{
|
||||||
|
$invalidSerialCodes[] = $serialNumber;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (in_array($serialNumber, $seenSerialNumbers)) {
|
||||||
|
$duplicateSerials[] = $serialNumber;
|
||||||
|
} else {
|
||||||
|
$seenSerialNumbers[] = $serialNumber;
|
||||||
|
$materialCodes[] = $materialCode;
|
||||||
|
$validRowsFound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$uniqueInvalidCodes = array_unique($invalidMatCodes);
|
||||||
|
|
||||||
|
$uniqueMissingSerials = array_unique($missingSerials);
|
||||||
|
|
||||||
|
$uniqueSerialCodes = array_unique($invalidSerialCodes);
|
||||||
|
|
||||||
|
$duplicateSerialCodes = array_unique($duplicateSerials);
|
||||||
|
|
||||||
|
|
||||||
|
if (!empty($uniqueInvalidCodes)) {
|
||||||
|
Notification::make()
|
||||||
|
->title('Invalid Item Codes')
|
||||||
|
->body('The following item codes should contain minimum 6 digit alpha numeric values:<br>' . implode(', ', $uniqueInvalidCodes))
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
if ($disk->exists($path)) {
|
||||||
|
$disk->delete($path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (!empty($uniqueMissingSerials)) {
|
||||||
|
Notification::make()
|
||||||
|
->title('Missing Serial Numbers')
|
||||||
|
->body("The following item codes doesn't have valid serial number:<br>" . implode(', ', $uniqueMissingSerials))
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
if ($disk->exists($path)) {
|
||||||
|
$disk->delete($path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (!empty($uniqueSerialCodes)) {
|
||||||
|
Notification::make()
|
||||||
|
->title('Invalid Serial Number')
|
||||||
|
->body('The following serial numbers should contain minimum 9 digit alpha numeric values:<br>' . implode(', ', $uniqueSerialCodes))
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
if ($disk->exists($path)) {
|
||||||
|
$disk->delete($path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (!empty($duplicateSerialCodes)) {
|
||||||
|
Notification::make()
|
||||||
|
->title('Duplicate Serial Numbers')
|
||||||
|
->body('The following serial numbers are already exist in database:<br>' . implode(', ', $duplicateSerialCodes))
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
if ($disk->exists($path)) {
|
||||||
|
$disk->delete($path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$validRowsFound) {
|
||||||
|
Notification::make()
|
||||||
|
->title('Invalid Serial Invoice')
|
||||||
|
->danger() // This makes the notification red to indicate an error
|
||||||
|
->body('Uploaded Excel sheet is empty or<br>contains no valid data.')
|
||||||
|
->send();
|
||||||
|
if ($disk->exists($path)) {
|
||||||
|
$disk->delete($path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$uniqueCodes = array_unique($materialCodes);
|
||||||
|
|
||||||
|
$matchedItems = StickerMaster::with('item')
|
||||||
|
->whereHas('item', function ($query) use ($uniqueCodes) {
|
||||||
|
$query->whereIn('code', $uniqueCodes);
|
||||||
|
})
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$matchedCodes = $matchedItems->pluck('item.code')->toArray();
|
||||||
|
|
||||||
|
$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:<br>' . implode(', ', $missingCodes);
|
||||||
|
|
||||||
|
Notification::make()
|
||||||
|
->title('Unknown Item Codes')
|
||||||
|
->body($message)
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
if ($disk->exists($path)) {
|
||||||
|
$disk->delete($path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check which codes have a material_type set (not null)
|
||||||
|
$invalidCodes = $matchedItems
|
||||||
|
->filter(fn ($sticker) => !empty($sticker->material_type)) //filter invalid
|
||||||
|
->pluck('item.code')
|
||||||
|
->toArray();
|
||||||
|
|
||||||
|
if (count($invalidCodes) > 10)
|
||||||
|
{
|
||||||
|
Notification::make()
|
||||||
|
->title('Invalid item codes found')
|
||||||
|
->body('' . count($invalidCodes) . 'item codes found have material type.')
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
if ($disk->exists($path)) {
|
||||||
|
$disk->delete($path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(count($invalidCodes) > 0)
|
||||||
|
{
|
||||||
|
Notification::make()
|
||||||
|
->title('Invalid item codes found')
|
||||||
|
->body('Material invoice Item Codes found : ' . implode(', ', $invalidCodes))
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
if ($disk->exists($path)) {
|
||||||
|
$disk->delete($path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Save full file path to session
|
||||||
|
session(['uploaded_invoice_path' => $fullPath]);
|
||||||
|
Notification::make()
|
||||||
|
->title('Serial invoice imported successfully.')
|
||||||
|
->success()
|
||||||
|
->send();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
Tables\Actions\Action::make('Import Invoice Material')
|
||||||
|
->label('Import Material Invoice')
|
||||||
|
->form([
|
||||||
|
|
||||||
|
Select::make('plant_id')
|
||||||
|
->options(Plant::pluck('name', 'id')->toArray()) // Fetch plant names and IDs
|
||||||
|
->label('Select Plant')
|
||||||
|
->required()
|
||||||
|
->reactive()
|
||||||
|
->default(function () {
|
||||||
|
return optional(InvoiceValidation::latest()->first())->plant_id;
|
||||||
|
})
|
||||||
|
->afterStateUpdated(function ($state, callable $set, callable $get) {
|
||||||
|
$set('invoice_material', null);
|
||||||
|
}),
|
||||||
|
|
||||||
|
|
||||||
|
FileUpload::make('invoice_material')
|
||||||
|
->label('Invoice Material')
|
||||||
|
->required()
|
||||||
|
->preserveFilenames()
|
||||||
|
->reactive() // <- this keeps the original filename
|
||||||
|
->storeFiles(false) // prevent auto-storing
|
||||||
|
->disk('local')
|
||||||
|
->visible(fn (Get $get) => !empty($get('plant_id')))
|
||||||
|
->directory('uploads/temp')
|
||||||
|
|
||||||
|
])
|
||||||
|
|
||||||
|
->action(function (array $data) {
|
||||||
|
$uploadedFile = $data['invoice_material'];
|
||||||
|
|
||||||
|
$plantId = $data['plant_id']; // Access the selected plant_id
|
||||||
|
|
||||||
|
$disk = Storage::disk('local');
|
||||||
|
|
||||||
|
// Get original filename
|
||||||
|
$originalName = $uploadedFile->getClientOriginalName();
|
||||||
|
|
||||||
|
$path = $uploadedFile->storeAs('uploads/temp', $originalName, 'local');
|
||||||
|
|
||||||
$fullPath = Storage::disk('local')->path($path);
|
$fullPath = Storage::disk('local')->path($path);
|
||||||
|
|
||||||
// Save full file path to session
|
$totQuan = InvoiceValidation::where('invoice_number', $originalName)->where('plant_id', $plantId)->count();
|
||||||
session(['uploaded_invoice_path' => $fullPath]);
|
$scanMQuan = InvoiceValidation::where('invoice_number', $originalName)->whereNotNull('serial_number')->where('serial_number', '!=', '')->where('plant_id', $plantId)->count();
|
||||||
|
|
||||||
// session()->flash('just_uploaded_invoice', true); // 👈 add this
|
if($totQuan == $scanMQuan && $totQuan > 0)
|
||||||
|
{
|
||||||
|
Notification::make()
|
||||||
|
->title('Material invoice already completed the scanning process for selected plant.')
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
if ($disk->exists($path)) {
|
||||||
|
$disk->delete($path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Notification::make()
|
if ($fullPath && file_exists($fullPath))
|
||||||
->title('File Uploaded. Continue in Create Page.')
|
{
|
||||||
->success()
|
$rows = Excel::toArray(null, $fullPath)[0];
|
||||||
->send();
|
|
||||||
|
if((count($rows) - 1) <= 0)
|
||||||
|
{
|
||||||
|
Notification::make()
|
||||||
|
->title('Records Not Found')
|
||||||
|
->body("Import the valid 'Material Invoice' file to proceed..!")
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
if ($disk->exists($path)) {
|
||||||
|
$disk->delete($path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$invalidMatCodes = [];
|
||||||
|
$materialCodes = [];
|
||||||
|
$missingQuantities = [];
|
||||||
|
$invalidMatQuan = [];
|
||||||
|
$invalidMaterialQuan = [];
|
||||||
|
$validRowsFound = false;
|
||||||
|
|
||||||
|
foreach ($rows as $index => $row)
|
||||||
|
{
|
||||||
|
if ($index === 0) continue; // Skip header
|
||||||
|
|
||||||
|
$materialCode = trim($row[0]);
|
||||||
|
$materialQuantity = trim($row[1]);
|
||||||
|
|
||||||
|
if (empty($materialCode) && empty($materialQuantity)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($materialCode)) {
|
||||||
|
if(Str::length($materialCode) < 6 || !ctype_alnum($materialCode))
|
||||||
|
{
|
||||||
|
$invalidMatCodes[] = $materialCode;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if($materialQuantity == 0)
|
||||||
|
{
|
||||||
|
$invalidMaterialQuan[] = $materialCode;
|
||||||
|
}
|
||||||
|
else if (empty($materialQuantity))
|
||||||
|
{
|
||||||
|
$missingQuantities[] = $materialCode;
|
||||||
|
}
|
||||||
|
else if(!is_numeric($materialQuantity))
|
||||||
|
{
|
||||||
|
$invalidMatQuan[] = $materialCode;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$materialCodes[] = $materialCode;
|
||||||
|
$validRowsFound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$uniqueInvalidCodes = array_unique($invalidMatCodes);
|
||||||
|
$uniqueaplhaMat = array_unique($invalidMatQuan);
|
||||||
|
$uniqueZeroMat = array_unique($invalidMaterialQuan);
|
||||||
|
$uniqueEmptyMat = array_unique($missingQuantities);
|
||||||
|
|
||||||
|
if (!empty($uniqueInvalidCodes)) {
|
||||||
|
Notification::make()
|
||||||
|
->title('Invalid Item Codes')
|
||||||
|
->body('The following item codes should contain minimum 6 digit alpha numeric values:<br>' . implode(', ', $uniqueInvalidCodes))
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
if ($disk->exists($path)) {
|
||||||
|
$disk->delete($path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!empty($uniqueaplhaMat)) {
|
||||||
|
Notification::make()
|
||||||
|
->title('Invalid Material Quantity')
|
||||||
|
->body("The following item codes material quantity must be a numeric values :<br>" . implode(', ', $uniqueaplhaMat))
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
if ($disk->exists($path)) {
|
||||||
|
$disk->delete($path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!empty($uniqueZeroMat)) {
|
||||||
|
Notification::make()
|
||||||
|
->title('Invalid Material Quantity')
|
||||||
|
->body("The following item codes material quantity should be greater than 0:<br>" . implode(', ', $uniqueZeroMat))
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
if ($disk->exists($path)) {
|
||||||
|
$disk->delete($path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($uniqueEmptyMat)) {
|
||||||
|
Notification::make()
|
||||||
|
->title('Missing Material Quantity')
|
||||||
|
->body("The following item codes doesn't have valid material quantity:<br>" . implode(', ', $uniqueEmptyMat))
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
if ($disk->exists($path)) {
|
||||||
|
$disk->delete($path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$validRowsFound) {
|
||||||
|
Notification::make()
|
||||||
|
->title('Invalid Material Invoice')
|
||||||
|
->danger() // This makes the notification red to indicate an error
|
||||||
|
->body('Uploaded Excel sheet is empty or<br>contains no valid data.')
|
||||||
|
->send();
|
||||||
|
if ($disk->exists($path)) {
|
||||||
|
$disk->delete($path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$uniqueCodes = array_unique($materialCodes);
|
||||||
|
|
||||||
|
$matchedItems = StickerMaster::with('item')
|
||||||
|
->whereHas('item', function ($query) use ($uniqueCodes) {
|
||||||
|
$query->whereIn('code', $uniqueCodes);
|
||||||
|
})
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$matchedCodes = $matchedItems->pluck('item.code')->toArray();
|
||||||
|
|
||||||
|
$missingCodes = array_diff($uniqueCodes, $matchedCodes);
|
||||||
|
|
||||||
|
if (!empty($missingCodes))
|
||||||
|
{
|
||||||
|
$missingCount = count($missingCodes);
|
||||||
|
|
||||||
|
$message = $missingCount > 10
|
||||||
|
? "'$missingCount' Item Codes are not found in sticker master."
|
||||||
|
: 'Item Codes are not found in sticker master:<br>' . implode(', ', $missingCodes);
|
||||||
|
|
||||||
|
Notification::make()
|
||||||
|
->title('Unknown Item Codes')
|
||||||
|
->body($message)
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
if ($disk->exists($path)) {
|
||||||
|
$disk->delete($path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$invalidCodes = $matchedItems
|
||||||
|
->filter(fn ($sticker) => empty($sticker->material_type)) //filter invalid
|
||||||
|
->pluck('item.code')
|
||||||
|
->toArray();
|
||||||
|
|
||||||
|
if (count($invalidCodes) > 10)
|
||||||
|
{
|
||||||
|
Notification::make()
|
||||||
|
->title('Invalid item codes found')
|
||||||
|
->body('' . count($invalidCodes) . 'invalid item codes found have serial number.')
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
if ($disk->exists($path)) {
|
||||||
|
$disk->delete($path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(count($invalidCodes) > 0)
|
||||||
|
{
|
||||||
|
Notification::make()
|
||||||
|
->title('Invalid item codes found')
|
||||||
|
->body('Serial invoice Item Codes found : ' . implode(', ', $invalidCodes))
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
if ($disk->exists($path)) {
|
||||||
|
$disk->delete($path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nonNumericQtyCodes = [];
|
||||||
|
$zeroQtyCodes = [];
|
||||||
|
$notDivisibleCodes = [];
|
||||||
|
|
||||||
|
foreach ($matchedItems as $sticker)
|
||||||
|
{
|
||||||
|
$code = $sticker->item->code;
|
||||||
|
$materialType = $sticker->material_type;
|
||||||
|
|
||||||
|
if ($materialType == 2)
|
||||||
|
{
|
||||||
|
$bundleQty = $sticker->bundle_quantity ?? 0;
|
||||||
|
$totalExcelQty = 0;
|
||||||
|
|
||||||
|
foreach ($rows as $index => $row)
|
||||||
|
{
|
||||||
|
if ($index === 0) continue; // Skip header
|
||||||
|
|
||||||
|
$excelCode = trim($row[0]);
|
||||||
|
$excelMatQty = trim($row[1]);
|
||||||
|
|
||||||
|
|
||||||
|
if ($excelCode === $code && is_numeric($excelMatQty)) {
|
||||||
|
$totalExcelQty += $excelMatQty; // Sum up the quantities
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($totalExcelQty === 0) {
|
||||||
|
$zeroQtyCodes[] = $code;
|
||||||
|
} elseif (!is_numeric($totalExcelQty)) {
|
||||||
|
$nonNumericQtyCodes[] = $code; // Here you may want to check divisibility condition too
|
||||||
|
} elseif ($bundleQty != 0 && $totalExcelQty % $bundleQty !== 0) {
|
||||||
|
$notDivisibleCodes[] = $code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$showValidationNotification = function(array $codes, string $message) {
|
||||||
|
if (count($codes) === 0) return;
|
||||||
|
|
||||||
|
$uniqueCodes = array_unique($codes);
|
||||||
|
$codeList = implode(', ', $uniqueCodes);
|
||||||
|
|
||||||
|
Notification::make()
|
||||||
|
->title('Invalid Bundle Quantity')
|
||||||
|
->body("$message <br>$codeList")
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
};
|
||||||
|
|
||||||
|
$showValidationNotification($nonNumericQtyCodes, "The following item codes contains invalid bundle quantity:");
|
||||||
|
$showValidationNotification($zeroQtyCodes, "The following item codes quantity should be greater than '0':");
|
||||||
|
$showValidationNotification($notDivisibleCodes, "The following item codes quantity is not divisible by bundle quantity.");
|
||||||
|
|
||||||
|
if ($nonNumericQtyCodes || $zeroQtyCodes || $notDivisibleCodes) {
|
||||||
|
if ($disk->exists($path))
|
||||||
|
{
|
||||||
|
$disk->delete($path);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Save full file path to session
|
||||||
|
session(['uploaded_material_invoice' => $fullPath]);
|
||||||
|
Notification::make()
|
||||||
|
->title('Material invoice imported successfully.')
|
||||||
|
->success()
|
||||||
|
->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
Tables\Actions\Action::make('Import Invoice Material')
|
->filters([
|
||||||
->label('Import Invoice Material')
|
Tables\Filters\TrashedFilter::make(),
|
||||||
->form([
|
])
|
||||||
FileUpload::make('invoice_material')
|
->actions([
|
||||||
->label('Invoice Material')
|
Tables\Actions\ViewAction::make(),
|
||||||
->required(),
|
Tables\Actions\EditAction::make(),
|
||||||
])
|
])
|
||||||
|
->bulkActions([
|
||||||
|
Tables\Actions\BulkActionGroup::make([
|
||||||
|
Tables\Actions\DeleteBulkAction::make(),
|
||||||
|
Tables\Actions\ForceDeleteBulkAction::make(),
|
||||||
|
Tables\Actions\RestoreBulkAction::make(),
|
||||||
|
]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// ->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(),
|
|
||||||
// ]),
|
|
||||||
// ]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getRelations(): array
|
public static function getRelations(): array
|
||||||
|
|||||||
Reference in New Issue
Block a user