From c1231df86e33b1f7d26062e77d5e08279023c098 Mon Sep 17 00:00:00 2001 From: dhanabalan Date: Sun, 20 Sep 2026 10:59:21 +0530 Subject: [PATCH] Added process order resource page --- .../Resources/ProcessOrderResource.php | 1057 +++++++++++++++-- 1 file changed, 936 insertions(+), 121 deletions(-) diff --git a/app/Filament/Resources/ProcessOrderResource.php b/app/Filament/Resources/ProcessOrderResource.php index 00be833..51402f8 100644 --- a/app/Filament/Resources/ProcessOrderResource.php +++ b/app/Filament/Resources/ProcessOrderResource.php @@ -5,11 +5,18 @@ namespace App\Filament\Resources; use App\Filament\Exports\ProcessOrderExporter; use App\Filament\Imports\ProcessOrderImporter; use App\Filament\Resources\ProcessOrderResource\Pages; +use App\Models\Item; +use App\Models\Line; use App\Models\Plant; use App\Models\ProcessOrder; +use App\Models\ProductCharacteristicsMaster; +use Closure; use Filament\Facades\Filament; use Filament\Forms; use Filament\Forms\Components\Actions\Action; +use Filament\Forms\Components\DateTimePicker; +use Filament\Forms\Components\Select; +use Filament\Forms\Components\TextInput; use Filament\Forms\Form; use Filament\Forms\Get; use Filament\Forms\Set; @@ -18,12 +25,18 @@ use Filament\Resources\Resource; use Filament\Tables; use Filament\Tables\Actions\ExportAction; use Filament\Tables\Actions\ImportAction; +use Filament\Tables\Filters\Filter; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; +use Illuminate\Validation\Rule; use Livewire\Features\SupportFileUploads\TemporaryUploadedFile; use Smalot\PdfParser\Parser; use Storage; +use Str; + +// use App\Models\PalletValidation; +// use Dom\Text; class ProcessOrderResource extends Resource { @@ -38,24 +51,43 @@ class ProcessOrderResource extends Resource return $form ->schema([ Forms\Components\Select::make('plant_id') - ->label('Plant') - ->reactive() + ->label('Plant Name') ->relationship('plant', 'name') + ->searchable() ->options(function (callable $get) { $userHas = Filament::auth()->user()->plant_id; - return ($userHas && strlen($userHas) > 0) ? Plant::where('id', $userHas)->pluck('name', 'id')->toArray() : Plant::pluck('name', 'id')->toArray(); + return ($userHas && strlen($userHas) > 0) ? Plant::where('id', $userHas)->pluck('name', 'id')->toArray() : Plant::orderBy('code')->pluck('name', 'id')->toArray(); }) + ->disabled(fn (Get $get) => ! empty($get('id'))) + ->default(function () { + $userHas = Filament::auth()->user()->plant_id; + + return ($userHas && strlen($userHas) > 0) ? $userHas : optional(ProcessOrder::latest()->first())->plant_id; + }) + ->reactive() ->afterStateUpdated(function ($state, $set, callable $get, $livewire) { $plantId = $get('plant_id'); - $set('coil_number', null); + $set('item_id', null); + $set('line_id', null); + $set('item_description', null); + $set('item_uom', null); + $set('process_order', null); + $set('coil_number', '0'); + $set('order_quantity', '0'); + $set('updated_order_quantity', '0'); + $set('received_quantity', '0'); + $set('scrap_quantity', '0'); $set('sfg_number', null); $set('machine_name', null); if (! $plantId) { $set('poPlantError', 'Please select a plant first.'); $set('coilNumberError', null); $set('sfgNumberError', null); + } else { + $set('poPlantError', null); } + $set('updated_by', Filament::auth()->user()?->name); }) ->extraAttributes(fn ($get) => [ 'class' => $get('poPlantError') ? 'border-red-500' : '', @@ -63,10 +95,8 @@ class ProcessOrderResource extends Resource ->hint(fn ($get) => $get('poPlantError') ? $get('poPlantError') : null) ->hintColor('danger') ->required(), - Forms\Components\Select::make('item_id') - ->label('Item Code') - // ->relationship('item', 'id') - // ->required(), + Forms\Components\Select::make('line_id') + ->label('Line Name') ->reactive() ->searchable() ->options(function (callable $get) { @@ -75,125 +105,504 @@ class ProcessOrderResource extends Resource return []; } - return \App\Models\Item::where('plant_id', $plantId)->pluck('code', 'id'); + return Line::where('plant_id', $plantId)->pluck('name', 'id'); }) + ->disabled(fn (Get $get) => ! empty($get('id'))) + ->afterStateUpdated(function (callable $set, callable $get, ?string $state) { + $set('item_id', null); + $set('item_description', null); + $set('item_uom', null); + $set('process_order', null); + $set('coil_number', '0'); + $set('order_quantity', '0'); + $set('updated_order_quantity', '0'); + $set('received_quantity', '0'); + $set('scrap_quantity', '0'); + $set('sfg_number', null); + $set('machine_name', null); + $set('updated_by', Filament::auth()->user()?->name); + }) + ->required(), + Forms\Components\Select::make('item_id') + ->label('Item Code') + // ->relationship('item', 'id') + // ->required(), + ->searchable() + ->reactive() + ->options(function (callable $get) { + $plantId = $get('plant_id'); + // $lineId = $get('line_id'); + if (empty($plantId)) {// || empty($lineId) + return []; + } + + // $availItems = ProductCharacteristicsMaster::where('plant_id', $plantId)->where('line_id', $lineId)->pluck('item_id', 'item_id')->toArray(); + + // return Item::where('plant_id', $plantId)->whereIn('id', $availItems)->pluck('code', 'id'); + return Item::where('plant_id', $plantId)->pluck('code', 'id'); + }) + ->disabled(fn (Get $get) => ! empty($get('id'))) ->afterStateUpdated(function (callable $set, callable $get, ?string $state) { $plantId = $get('plant_id'); $itemId = $get('item_id'); - // dd($plantId); + $set('process_order', null); + $set('coil_number', '0'); + $set('order_quantity', '0'); + $set('updated_order_quantity', '0'); + $set('received_quantity', '0'); + $set('scrap_quantity', '0'); + $set('sfg_number', null); + $set('machine_name', null); if ($plantId && $itemId) { // Get the item code using item_id - $itemCode = \App\Models\Item::where('id', $itemId)->value('code'); + $itemCode = Item::where('id', $itemId)->value('code'); if ($itemCode) { // Now get the item description using plant_id + code - $item = \App\Models\Item::where('plant_id', $plantId) + $item = Item::where('plant_id', $plantId) ->where('code', $itemCode) ->first(); $set('item_description', $item?->description); + $set('item_uom', $item?->uom); } else { $set('item_description', null); + $set('item_uom', null); } } else { $set('item_description', null); + $set('item_uom', null); } + $set('updated_by', Filament::auth()->user()?->name); }) ->required(), - Forms\Components\TextInput::make('item_description') - ->label('Description') + ->label('Item Description') + ->readOnly() ->required() ->reactive() ->afterStateHydrated(function ($component, $state, Get $get, Set $set) { $itemId = $get('item_id'); if ($get('id')) { - - $item = \App\Models\Item::where('id', $itemId)->first()?->description; + $item = Item::where('id', $itemId)->first()?->description; if ($item) { $set('item_description', $item); } else { $set('item_description', null); } } + }) + ->afterStateUpdated(function (callable $set, callable $get, ?string $state) { + $set('updated_by', Filament::auth()->user()?->name); + }), + Forms\Components\TextInput::make('item_uom') + ->label('Item UOM') + ->readOnly() + ->required() + ->reactive() + ->afterStateHydrated(function ($component, $state, Get $get, Set $set) { + $itemId = $get('item_id'); + if ($get('id')) { + $item = Item::where('id', $itemId)->first()?->uom; + if ($item) { + $set('item_uom', $item); + } else { + $set('item_uom', null); + } + } + }) + ->afterStateUpdated(function (callable $set, callable $get, ?string $state) { + $set('updated_by', Filament::auth()->user()?->name); }), - - Forms\Components\TextInput::make('id') - ->hidden() - ->readOnly(), - // ->readOnly(true), - Forms\Components\TextInput::make('process_order') ->label('Process Order') ->reactive() + ->numeric() + ->length(12) + ->readOnly(fn ($get) => ($get('plant_id') == null || $get('item_id') == null || $get('line_id') == null)) ->afterStateUpdated(function ($state, $set, callable $get, $livewire) { $plantId = $get('plant_id'); - $set('coil_number', null); + $set('coil_number', '0'); + $set('order_quantity', '0'); + $set('updated_order_quantity', '0'); + $set('received_quantity', '0'); + $set('scrap_quantity', '0'); $set('sfg_number', null); $set('machine_name', null); - if (! $plantId) { - $set('poPlantError', 'Please select a plant first.'); - $set('process_order', null); - $set('coilNumberError', null); - $set('sfgNumberError', null); + // if (! $plantId) { + // $set('poPlantError', 'Please select a plant first.'); + // $set('process_order', null); + // $set('coilNumberError', null); + // $set('sfgNumberError', null); + + // return; + // } + + $itemId = $get('item_id'); + $processOrder = trim($get('process_order')); + if (! $itemId || ! $processOrder) { + return; } + + $orderExist = ProcessOrder::where('plant_id', $plantId) + ->where('process_order', $processOrder) + ->where('item_id', $itemId)->latest()->first(); + // ->value('order_quantity') ?? 0; + + if ($orderExist) { + $set('order_quantity', $orderExist->order_quantity ?? 0); + } + $set('updated_by', Filament::auth()->user()?->name); + }) + ->rule(function (callable $get) { + return function (string $attribute, $value, Closure $fail) use ($get) { + + $plantId = $get('plant_id'); + $itemId = $get('item_id'); + $processOrder = $value; + // $currentId = $get('id'); // current editing record id + + if (! $plantId || ! $processOrder) { + return; + } + + $existing = ProcessOrder::where('plant_id', $plantId) + ->where('process_order', $processOrder) + ->where('item_id', '!=', $itemId) + ->first(); + + if ($existing) { + // Notification::make() + // ->title('Duplicate Process Order!') + // ->body("Process Order '{$value}' is already exist with item code '{$existing->item->code}'.") + // ->danger() + // ->send(); + $fail("process order already exists for this plant and item code '{$existing->item->code}'."); + } + }; }) ->required(), Forms\Components\TextInput::make('coil_number') ->label('Coil Number') ->default('0') + ->required() ->reactive() + ->numeric() + ->readOnly(fn ($get) => (trim($get('process_order')) == null || trim($get('process_order')) == '')) ->afterStateUpdated(function ($state, $set, callable $get, $livewire) { $plantId = $get('plant_id'); - $processOrder = $get('process_order'); - $coilNo = $get('coil_number'); + $processOrder = trim($get('process_order')); + // $coilNo = $get('coil_number'); if (! $plantId) { $set('poPlantError', 'Please select a plant first.'); - $set('coil_number', null); - $set('sfg_number', null); - $set('machine_name', null); - $set('coilNumberError', null); - $set('sfgNumberError', null); + $set('coil_number', '0'); } elseif (! $processOrder) { - $set('coil_number', null); - $set('sfg_number', null); - $set('machine_name', null); $set('poPlantError', null); - $set('coilNumberError', null); - $set('sfgNumberError', null); - } elseif ($coilNo || $coilNo == '0') { - $existing = ProcessOrder::where('plant_id', $plantId) - ->where('process_order', $processOrder) - ->where('coil_number', $coilNo) - ->first(); - - if ($existing) { - $set('poPlantError', null); - $set('coil_number', null); - $set('coilNumberError', "Duplicate Coil : '{$coilNo}' found!"); - } else { - $set('poPlantError', null); - $set('coilNumberError', null); - } + $set('coil_number', '0'); } + $set('received_quantity', '0'); + $set('scrap_quantity', '0'); + $set('sfg_number', null); + $set('machine_name', null); + $set('coilNumberError', null); + $set('sfgNumberError', null); + // elseif ($coilNo || $coilNo == 0) { + // $existing = ProcessOrder::where('plant_id', $plantId) + // ->where('process_order', $processOrder) + // ->where('coil_number', $coilNo) + // ->first(); + + // if ($existing) { + // $set('poPlantError', null); + // $set('coil_number', '0'); + // $set('coilNumberError', "Duplicate Coil : '{$coilNo}' found!"); + // } else { + // $set('poPlantError', null); + // $set('coilNumberError', null); + // } + // } + $set('updated_by', Filament::auth()->user()?->name); }) + ->rules([ + function (callable $get) { + return Rule::unique('process_orders', 'coil_number') + ->where('plant_id', $get('plant_id')) + ->where('process_order', trim($get('process_order'))) + ->ignore($get('id')); + }, + function (callable $get): Closure { + return function (string $attribute, $value, Closure $fail) use ($get) { + $rework = $get('rework_status'); + $plantId = $get('plant_id'); + $processOrder = trim($get('process_order')); + + if ($value && Str::contains($value, '.')) { + if ($rework == 0) { + $fail("Rework status should be 'Yes' for rework coil number '{$value}'!"); + } + } elseif ($value > 1) { + $prevCoilNo = (string) ($value - 1); + $existPrevCoil = ProcessOrder::where('plant_id', $plantId) + ->where('process_order', $processOrder) + ->where('coil_number', $prevCoilNo) + ->first(); + + if (! $existPrevCoil) { + $fail("Previous coil number '{$prevCoilNo}' not exist against plant and process order!"); + } + } + }; + }, + ]) + // ->rule(function (callable $get) { + // return Rule::unique('process_orders', 'coil_number') + // ->where('plant_id', $get('plant_id')) + // ->where('process_order', trim($get('process_order'))) + // ->ignore($get('id')); + // }) ->extraAttributes(fn ($get) => [ 'class' => $get('coilNumberError') ? 'border-red-500' : '', ]) ->hint(fn ($get) => $get('coilNumberError') ? $get('coilNumberError') : null) - ->hintColor('danger') - ->required(), + ->hintColor('danger'), Forms\Components\TextInput::make('order_quantity') ->label('Order Quantity') - ->required(), + ->default('1.000') + ->required() + ->reactive() + ->numeric() + ->readOnly(fn ($get) => (trim($get('process_order')) == null || trim($get('process_order')) == '')) + ->afterStateUpdated(function (callable $set, callable $get, ?string $state) { + $plantId = $get('plant_id'); + $itemId = $get('item_id'); + $processOrder = trim($get('process_order')); + $set('received_quantity', '0'); + // $set('updated_order_quantity', $state); + $set('scrap_quantity', '0'); + if (! $plantId || ! $itemId || ! $processOrder) { + $set('order_quantity', '0'); + $set('updated_order_quantity', '0'); + } + + $query = ProcessOrder::where('plant_id', $plantId) + ->where('process_order', $processOrder) + ->where('item_id', $itemId); + + $orderExist = $query->latest()->first(); // Latest record (reuse base query) + + if ($query->count() == 1 && $orderExist && ! $get('id')) { + $set('order_quantity', $orderExist->order_quantity ?? 0); + } elseif ($query->count() > 1 && $orderExist) { + $set('order_quantity', $orderExist->order_quantity ?? 0); + } + $set('updated_by', Filament::auth()->user()?->name); + }), + Forms\Components\TextInput::make('updated_order_quantity') + ->label('Updated Order Quantity') + ->required() + ->readOnly(fn ($get) => (trim($get('process_order')) == null || trim($get('process_order')) == '' || trim($get('order_quantity')) == '' || trim($get('order_quantity')) == null)) + ->reactive() + ->rules([ + function (callable $get): Closure { + return function (string $attribute, $value, Closure $fail) use ($get) { + $plantId = $get('plant_id'); + $processOrder = trim($get('process_order')); + $receivedQty = $get('received_quantity'); + + $currentId = $get('id'); + + $orderQty = (float) $get('order_quantity'); + $updatedQty = (float) $value; + + $allowedVariance = $orderQty * 0.10; + + $maxAllowed = $orderQty + $allowedVariance; + $minAllowed = $orderQty - $allowedVariance; + + if ($updatedQty > $maxAllowed || $updatedQty < $minAllowed) { + $fail('Quantity can vary by ±10% of the order quantity.'); + } + + $otherReceivedQty = ProcessOrder::where('plant_id', $plantId) + ->where('process_order', $processOrder) + ->when($currentId, fn ($q) => $q->where('id', '!=', $currentId)) + ->sum('received_quantity'); + + $currentReceivedQty = (float) $get('received_quantity'); + + $totalReceivedQty = $otherReceivedQty + $currentReceivedQty; + + if ($updatedQty < $totalReceivedQty) { + $fail("You cannot set quantity below received quantity ({$totalReceivedQty})."); + } + }; + }, + ]), Forms\Components\TextInput::make('received_quantity') ->label('Received Quantity') - ->default('0') - ->required(), + ->default('0.000') + ->required() + ->reactive() + ->numeric() + ->maxValue(fn (Get $get) => $get('order_quantity') ?? PHP_INT_MAX) + ->readOnly(fn ($get) => (trim($get('process_order')) == null || trim($get('process_order')) == '')) + ->rule(function (callable $get) { + return function (string $attribute, $value, Closure $fail) use ($get) { + $rework = $get('rework_status'); + $plantId = $get('plant_id'); + $lineId = $get('line_id'); + $itemId = $get('item_id'); + $processOrder = trim($get('process_order')); + $coilNo = trim($get('coil_number')); + + $orderExist = ProcessOrder::where('plant_id', $plantId) + ->where('process_order', $processOrder) + ->where('item_id', $itemId)->latest()->first(); + + if ($orderExist) { + // $orderQty = $orderExist->order_quantity ?? 0; + $orderQty = $orderExist->updated_order_quantity ?? 0; + + $alreadyReceived = ProcessOrder::where('plant_id', $plantId) + ->where('process_order', $processOrder) + ->where('item_id', $itemId) + ->sum('received_quantity'); + + $total = $alreadyReceived + $value; + + if ($rework == 1) { + if ($coilNo && Str::contains($coilNo, '.')) { + $coilMain = explode('.', $coilNo)[0]; + + $existingCoil = ProcessOrder::where('plant_id', $plantId) + ->where('process_order', $processOrder) + ->where('line_id', $lineId) + ->where('coil_number', $coilMain) + ->first(); + + $existPrevCoil = ProcessOrder::where('plant_id', $plantId) + ->where('process_order', $processOrder) + ->where('item_id', $itemId) + ->where('coil_number', $coilNo) + ->first(); + + if ($existPrevCoil && $existPrevCoil->received_quantity < $value) { + $fail('Rework received quantity should not exceed exist received quantity!'); + + return; + } + + if ($existingCoil && $existingCoil->rework_status == 1 && $total > $orderQty) { + $fail("Rework received quantity should not exceed exist order quantity! Already Exist Total Received Qty = '{$alreadyReceived}'."); + } + } else { + if ($coilNo > 1) { + $prevCoilNo = (string) ($coilNo - 1); + $existPrevCoil = ProcessOrder::where('plant_id', $plantId) + ->where('process_order', $processOrder) + ->where('coil_number', $prevCoilNo) + ->first(); + + if (! $existPrevCoil) { + return; + } + } + + $existingCoil = ProcessOrder::where('plant_id', $plantId) + ->where('process_order', $processOrder) + ->where('line_id', $lineId) + ->where('coil_number', $coilNo) + ->first(); + + if (! $existingCoil) { + return; + } + + $existPrevCoil = ProcessOrder::where('plant_id', $plantId) + ->where('process_order', $processOrder) + ->where('item_id', $itemId) + ->where('coil_number', $coilNo) + ->first(); + + if ($existPrevCoil && $existPrevCoil->received_quantity < $value) { + $fail('Rework received quantity should not exceed exist received quantity!'); + + return; + } + } + } else { + if ($coilNo && Str::contains($coilNo, '.')) { + return; + } + + $existing = ProcessOrder::where('plant_id', $plantId) + ->where('process_order', $processOrder) + ->where('coil_number', $coilNo) + ->first(); + if ($existing) { + if ($existing->received_quantity < $value) { + $fail('Received quantity should not exceed exist received quantity!'); // Exist Received Qty = '{$existing->received_quantity}'. + } + + return; + } + + if ($coilNo > 1) { + $prevCoilNo = (string) ($coilNo - 1); + $existPrevCoil = ProcessOrder::where('plant_id', $plantId) + ->where('process_order', $processOrder) + ->where('coil_number', $prevCoilNo) + ->first(); + + if (! $existPrevCoil) { + return; + } + } + + if ($total > $orderQty) { + $fail("Received quantity should not exceed exist order quantity! Already Exist Total Received Qty = '{$alreadyReceived}'."); + } + } + } + }; + }) + ->afterStateUpdated(function (callable $set, callable $get, ?string $state) { + $set('scrap_quantity', '0'); + $set('updated_by', Filament::auth()->user()?->name); + }), + Forms\Components\TextInput::make('scrap_quantity') + ->label('Scrap Quantity') + ->default('0.000') + ->required() + ->readOnly(fn ($get) => ($get('rework_status') == 0 || trim($get('process_order')) == null || trim($get('process_order')) == '')) + ->reactive() + ->numeric() + ->afterStateUpdated(function (callable $set, callable $get, ?string $state) { + $rework = $get('rework_status'); + if ($rework == 0) { + $set('scrap_quantity', '0'); + } + $set('updated_by', Filament::auth()->user()?->name); + }) + ->rule(function (callable $get) { + return function (string $attribute, $value, Closure $fail) use ($get) { + + $rework = $get('rework_status'); + $scrapQty = $value; + // $currentId = $get('id'); // current editing record id + + if ($rework == 0 && $scrapQty != 0) { + $fail("Scrap Quanity value should be '0'!"); + + return; + } + }; + }), Forms\Components\TextInput::make('sfg_number') ->label('SFG Number') ->reactive() + ->readOnly(fn ($get) => (trim($get('process_order')) == null || trim($get('process_order')) == '')) ->afterStateUpdated(function ($state, $set, callable $get, $livewire) { $plantId = $get('plant_id'); $sfgNo = $get('sfg_number'); @@ -217,6 +626,7 @@ class ProcessOrderResource extends Resource $set('sfgNumberError', null); } } + $set('updated_by', Filament::auth()->user()?->name); }) ->extraAttributes(fn ($get) => [ 'class' => $get('sfgNumberError') ? 'border-red-500' : '', @@ -224,7 +634,73 @@ class ProcessOrderResource extends Resource ->hint(fn ($get) => $get('sfgNumberError') ? $get('sfgNumberError') : null) ->hintColor('danger'), Forms\Components\TextInput::make('machine_name') - ->label('Machine ID'), + ->label('Machine Name') + ->reactive() + ->maxLength(18) + ->readOnly(fn ($get) => (trim($get('process_order')) == null || trim($get('process_order')) == '')) + ->afterStateUpdated(function (callable $set, callable $get, ?string $state) { + $set('updated_by', Filament::auth()->user()?->name); + }), + Forms\Components\Select::make('rework_status') + ->label('Rework Status') + ->default(0) + ->reactive() + ->options([0 => 'No', 1 => 'Yes']) + ->reactive() + ->afterStateUpdated(function (callable $set, callable $get, ?string $state) { + if ($state == 0) { + $set('scrap_quantity', '0'); + } + $set('updated_by', Filament::auth()->user()?->name); + }) + ->rule(function (callable $get, callable $set) { + return function (string $attribute, $value, Closure $fail) use ($get, $set) { + $plantId = $get('plant_id'); + $lineId = $get('line_id'); + $processOrder = trim($get('process_order')); + $coilNo = trim($get('coil_number')); + $status = $value; + // $currentId = $get('id'); // current editing record id + + if (! $plantId || ! $lineId || ! $processOrder || Str::length($coilNo) <= 0) { + $set('rework_status', 0); + + return; + } elseif ($status == 0) { + $existingCoil = ProcessOrder::where('plant_id', $plantId) + ->where('line_id', $lineId) + ->where('process_order', $processOrder) + ->where('coil_number', $coilNo) + ->first(); + + if ($existingCoil && $existingCoil->rework_status == 1 && $get('id')) { + $fail("Rework coil number '{$coilNo}' exist against plant and process order!"); + + return; + } + } + + if (Str::length($coilNo) > 0 && ($status == 1 || Str::contains($coilNo, '.'))) { + $coilMain = explode('.', $coilNo)[0]; // 333 + + $existingCoil = ProcessOrder::where('plant_id', $plantId) + ->where('line_id', $lineId) + ->where('process_order', $processOrder) + ->where('coil_number', $coilMain) + ->first(); + + if (! $existingCoil) { + $fail("Coil number '{$coilMain}' not exist against plant and process order!"); + } elseif ($existingCoil->rework_status == 0 && ! $get('id')) { + $fail("Rework coil number '{$coilMain}' not exist against plant and process order!"); + } + } + }; + }) + ->visible(function () { + return Filament::auth()->user()->hasRole('Super Admin'); // || Filament::auth()->user()->can('update process order rework status') + }) + ->required(), Forms\Components\FileUpload::make('attachment') ->label('PDF Upload') ->acceptedFileTypes(['application/pdf']) @@ -232,12 +708,19 @@ class ProcessOrderResource extends Resource ->disk('local') ->directory('uploads/temp') ->preserveFilenames() + ->visible(function () { + return Filament::auth()->user()->can('view process order packing slip'); + }) ->reactive(), Forms\Components\Actions::make([ Action::make('uploadNow') ->label('Upload PDF Now') + ->visible(function () { + return Filament::auth()->user()->can('view process order packing slip'); + }) ->action(function ($get, callable $set) { $uploadedFiles = $get('attachment'); + $set('updated_by', Filament::auth()->user()?->name); if (is_array($uploadedFiles) && count($uploadedFiles) > 0) { $uploaded = reset($uploadedFiles); @@ -245,6 +728,7 @@ class ProcessOrderResource extends Resource if ($uploaded instanceof TemporaryUploadedFile) { $originalName = $uploaded->getClientOriginalName(); $path = 'uploads/ProcessOrder/'.$originalName; + $processOrder = trim($get('process_order')); // Check if file already exists if (Storage::disk('local')->exists($path)) { @@ -282,34 +766,34 @@ class ProcessOrderResource extends Resource } // Get the value of process_order field - $processOrder = $get('process_order'); - if ($batchId != $processOrder) { - Notification::make() - ->title('Mismatch') - ->body("Batch ID ($batchId) does not match Process Order ($processOrder)") - ->danger() - ->send(); - return; - } + // if ($batchId != $processOrder) { + // Notification::make() + // ->title('Mismatch') + // ->body("Batch ID ($batchId) does not match Process Order ($processOrder)") + // ->danger() + // ->send(); - if ($batchId == $processOrder) { + // return; + // } + + // if ($batchId == $processOrder) { // If batch matches, store the PDF permanently $storedPath = $uploaded->storeAs( 'uploads/ProcessOrder', - $originalName, + $processOrder, 'local' ); Notification::make() ->title('Success') - ->body("Batch ID matches Process Order: $batchId. PDF uploaded successfully.") + ->body("Process Order: $processOrder. PDF uploaded successfully.") ->success() ->send(); return; - } + // } } } else { Notification::make() @@ -323,8 +807,12 @@ class ProcessOrderResource extends Resource Action::make('downloadAttachment') ->label('Download PDF') - ->action(function ($get) { - $equipmentNumber = $get('process_order'); + ->visible(function () { + return Filament::auth()->user()->can('view process order packing slip'); + }) + ->action(function ($get, callable $set) { + $equipmentNumber = trim($get('process_order')); + $set('updated_by', Filament::auth()->user()?->name); if (! $equipmentNumber) { Notification::make() @@ -356,54 +844,16 @@ class ProcessOrderResource extends Resource return response()->download(Storage::disk('local')->path($fileToDownload)); }), - - // Action::make('removeAttachment') - // ->label('Remove PDF') - // ->action(function ($get) { - // $equipmentNumber = $get('process_order'); - - // if (!$equipmentNumber) { - // Notification::make() - // ->title('No process order entered') - // ->danger() - // ->send(); - // return; - // } - - // // Get all files from uploads/temp - // $files = Storage::disk('local')->files('uploads/ProcessOrder'); - - // $fileToDelete = null; - // foreach ($files as $file) { - // if (str_contains($file, $equipmentNumber)) { - // $fileToDelete = $file; - // break; - // } - // } - - // if (!$fileToDelete) { - // Notification::make() - // ->title('PDF not found for this process order') - // ->danger() - // ->send(); - // return; - // } - - // // Delete the matched file - // Storage::disk('local')->delete($fileToDelete); - - // Notification::make() - // ->title('PDF removed successfully') - // ->body("File for process order {$equipmentNumber} has been deleted.") - // ->success() - // ->send(); - // }), ]), Forms\Components\Hidden::make('created_by') ->label('Created By') ->default(Filament::auth()->user()?->name), Forms\Components\Hidden::make('updated_by') + ->label('Updated By') ->default(Filament::auth()->user()?->name), + Forms\Components\TextInput::make('id') + ->hidden() + ->readOnly(), ]); } @@ -421,17 +871,27 @@ class ProcessOrderResource extends Resource return ($currentPage - 1) * $perPage + $rowLoop->iteration; }), Tables\Columns\TextColumn::make('plant.name') - ->label('Plant') + ->label('Plant Name') ->alignCenter() ->searchable() ->sortable(), + Tables\Columns\TextColumn::make('line.name') + ->label('Line Name') + ->searchable() + ->alignCenter() + ->sortable(), Tables\Columns\TextColumn::make('item.code') - ->label('Item') + ->label('Item Code') ->searchable() ->alignCenter() ->sortable(), Tables\Columns\TextColumn::make('item.description') - ->label('Description') + ->label('Item Description') + ->alignCenter() + ->searchable() + ->sortable(), + Tables\Columns\TextColumn::make('item.uom') + ->label('Item UOM') ->alignCenter() ->searchable() ->sortable(), @@ -450,21 +910,37 @@ class ProcessOrderResource extends Resource ->alignCenter() ->searchable() ->sortable(), + Tables\Columns\TextColumn::make('updated_order_quantity') + ->label('Updated Order Quantity') + ->alignCenter() + ->searchable() + ->sortable(), Tables\Columns\TextColumn::make('received_quantity') ->label('Received Quantity') ->alignCenter() ->searchable() ->sortable(), + Tables\Columns\TextColumn::make('scrap_quantity') + ->label('Scrap Quantity') + ->alignCenter() + ->searchable() + ->sortable(), Tables\Columns\TextColumn::make('sfg_number') ->label('SFG Number') ->alignCenter() ->searchable() ->sortable(), Tables\Columns\TextColumn::make('machine_name') - ->label('Machine ID') + ->label('Machine Name') ->alignCenter() ->searchable() ->sortable(), + Tables\Columns\TextColumn::make('rework_status') + ->label('Rework Status') + ->alignCenter() + ->searchable() + ->formatStateUsing(fn ($state) => $state == 1 ? 'Yes' : 'No') + ->sortable(), Tables\Columns\TextColumn::make('created_at') ->label('Created At') ->alignCenter() @@ -494,7 +970,220 @@ class ProcessOrderResource extends Resource ]) ->filters([ Tables\Filters\TrashedFilter::make(), + Filter::make('advanced_filters') + ->label('Advanced Filters') + ->form([ + Select::make('Plant') + ->label('Search by Plant Name') + ->nullable() + ->searchable() + ->reactive() + ->options(function (callable $get) { + $userHas = Filament::auth()->user()->plant_id; + + if ($userHas && strlen($userHas) > 0) { + return Plant::where('id', $userHas)->pluck('name', 'id')->toArray(); + } else { + return Plant::whereHas('processOrders', function ($query) { + $query->whereNotNull('id'); + })->orderBy('code')->pluck('name', 'id'); + } + + // return ($userHas && strlen($userHas) > 0) ? Plant::where('id', $userHas)->pluck('name', 'id')->toArray() : Plant::orderBy('code')->pluck('name', 'id')->toArray(); + }) + ->afterStateUpdated(function ($state, callable $set, callable $get) { + $set('Item', null); + $set('Line', null); + }), + Select::make('Line') + ->label('Search by Line Name') + ->nullable() + ->searchable() + ->reactive() + ->options(function (callable $get) { + $plantId = $get('Plant'); + + if (empty($plantId)) { + return []; + } + + return Line::whereHas('processOrders', function ($query) use ($plantId) { + if ($plantId) { + $query->where('plant_id', $plantId); + } + })->pluck('name', 'id'); + }) + ->afterStateUpdated(function ($state, callable $set, callable $get) { + $set('process_order', null); + }), + Select::make('Item') + ->label('Search by Item Code') + ->nullable() + ->searchable() + ->reactive() + ->options(function (callable $get) { + $plantId = $get('Plant'); + + if (empty($plantId)) { + return []; + } + + return Item::whereHas('processOrders', function ($query) use ($plantId) { + if ($plantId) { + $query->where('plant_id', $plantId); + } + })->pluck('code', 'id'); + }) + ->afterStateUpdated(function ($state, callable $set, callable $get) { + $set('process_order', null); + }), + TextInput::make('process_order') + ->label('Process Order') + ->reactive() + ->placeholder('Enter Process Order') + ->afterStateUpdated(function ($state, callable $set, callable $get) { + $set('Rework', null); + }), + TextInput::make('coil_number') + ->label('Coil Number') + ->reactive() + ->placeholder('Enter Coil Number'), + TextInput::make('sfg_number') + ->label('SFG Number') + ->reactive() + ->placeholder('Enter SFG Number'), + TextInput::make('machine_name') + ->label('Machine Name') + ->reactive() + ->placeholder('Enter Machine Name'), + Select::make('rework_status') + ->label('Search by Rework Status') + ->nullable() + ->searchable() + ->reactive() + ->options([ + 1 => 'Yes', + 0 => 'No', + ]), + DateTimePicker::make(name: 'created_from') + ->label('Created From') + ->placeholder('Select From DateTime') + ->reactive() + ->native(false), + DateTimePicker::make('created_to') + ->label('Created To') + ->placeholder('Select To DateTime') + ->reactive() + ->native(false), + ]) + ->query(function ($query, array $data) { + // Hide all records initially if no filters are applied + if (empty($data['Plant']) && empty($data['Line']) && empty($data['Item']) && empty($data['process_order']) && Str::length($data['coil_number']) <= 0 && empty($data['sfg_number']) && empty($data['machine_name']) && ($data['rework_status'] == null || $data['rework_status'] == '') && empty($data['created_from']) && empty($data['created_to'])) { + return $query->whereRaw('1 = 0'); + } + + if (! empty($data['Plant'])) { + $query->where('plant_id', $data['Plant']); + } else { + $userHas = Filament::auth()->user()->plant_id; + + if ($userHas && strlen($userHas) > 0) { + return $query->whereRaw('1 = 0'); + } + } + + if (! empty($data['Line'])) { + $query->where('line_id', $data['Line']); + } + + if (! empty($data['Item'])) { + $query->where('item_id', $data['Item']); + } + + if (! empty($data['process_order'])) { + $query->where('process_order', 'like', '%'.$data['process_order'].'%'); + } + + if (Str::length($data['coil_number']) > 0) { + // $query->where('machine_name', $data['machine_name']); + $query->where('coil_number', 'like', '%'.$data['coil_number'].'%'); + } + + if (! empty($data['sfg_number'])) { + $query->where('sfg_number', 'like', '%'.$data['sfg_number'].'%'); + } + + if (! empty($data['machine_name'])) { + // $query->where('machine_name', $data['machine_name']); + $query->where('machine_name', 'like', '%'.$data['machine_name'].'%'); + } + + if ($data['rework_status'] != null && $data['rework_status'] != '') { + $query->where('rework_status', $data['rework_status']); + } + + if (! empty($data['created_from'])) { + $query->where('created_at', '>=', $data['created_from']); + } + + if (! empty($data['created_to'])) { + $query->where('created_at', '<=', $data['created_to']); + } + // $query->orderBy('created_at', 'asc'); + }) + ->indicateUsing(function (array $data) { + $indicators = []; + + if (! empty($data['Plant'])) { + $indicators[] = 'Plant Name: '.Plant::where('id', $data['Plant'])->value('name'); + } else { + $userHas = Filament::auth()->user()->plant_id; + + if ($userHas && strlen($userHas) > 0) { + return 'Plant Name: Choose plant to filter records.'; + } + } + + if (! empty($data['Line'])) { + $indicators[] = 'Line Name: '.Line::where('id', $data['Line'])->value('name'); + } + + if (! empty($data['Item'])) { + $indicators[] = 'Item Code: '.Item::where('id', $data['Item'])->value('code'); + } + + if (! empty($data['process_order'])) { + $indicators[] = 'Process Order: '.$data['process_order']; + } + + if (Str::length($data['coil_number']) > 0) { + $indicators[] = 'Coil Number: '.$data['coil_number']; + } + + if (! empty($data['sfg_number'])) { + $indicators[] = 'SFG Number: '.$data['sfg_number']; + } + + if (! empty($data['machine_name'])) { + $indicators[] = 'Machine Name: '.$data['machine_name']; + } + + if ($data['rework_status'] != null && $data['rework_status'] != '') { + $indicators[] = ($data['rework_status'] == 1) ? 'Rework Status: Yes' : 'Rework Status: No'; + } + + if (! empty($data['created_from'])) { + $indicators[] = 'From: '.$data['created_from']; + } + + if (! empty($data['created_to'])) { + $indicators[] = 'To: '.$data['created_to']; + } + + return $indicators; + }), ]) + ->filtersFormMaxHeight('280px') ->actions([ Tables\Actions\ViewAction::make(), Tables\Actions\EditAction::make(), @@ -507,6 +1196,132 @@ class ProcessOrderResource extends Resource ]), ]) ->headerActions([ + Tables\Actions\Action::make('reprint_process') + ->label('Re-Print Process Order') + ->form([ + Forms\Components\Section::make() + ->schema([ + Forms\Components\Select::make('plant') + ->label('Select Plant') + // ->options(Plant::pluck('name', 'id')->toArray()) + ->options(function (callable $get) { + $userHas = Filament::auth()->user()->plant_id; + + return ($userHas && strlen($userHas) > 0) ? Plant::where('id', $userHas)->pluck('name', 'id')->toArray() : Plant::orderBy('code')->pluck('name', 'id')->toArray(); + }) + ->required() + ->reactive() + ->afterStateUpdated(function ($state, callable $set, callable $get) { + $set('Item', null); + $set('process_order', null); + $set('coil_number', null); + $set('received_quantity', null); + $set('machine_name', null); + }), + Select::make('Item') + ->label('Item Code') + ->nullable() + ->required() + ->searchable() + ->options(function (callable $get) { + $plantId = $get('plant'); + + if (empty($plantId)) { + return []; + } + + return Item::where('plant_id', $plantId)->pluck('code', 'id'); + + // return $plantId ? Item::where('plant_id', $plantId)->pluck('code', 'id') : []; + }) + ->reactive() + ->afterStateUpdated(function ($state, callable $set, callable $get) { + $set('process_order', null); + $set('coil_number', null); + $set('received_quantity', null); + $set('machine_name', null); + }), + TextInput::make('process_order') + ->label('Process Order') + ->required() + ->placeholder('Enter Process Order') + ->reactive() + ->afterStateUpdated(function ($state, callable $set, callable $get) { + $set('coil_number', null); + $set('received_quantity', null); + $set('machine_name', null); + }), + Select::make('coil_number') + ->label('Coil Number') + ->nullable() + ->required() + ->searchable() + ->options(function (callable $get) { + $plantId = $get('plant'); + $itemId = $get('Item'); + $processOrder = trim($get('process_order')); + + if (empty($plantId) || empty($itemId) || empty($processOrder)) { + return []; + } + + return ProcessOrder::where('plant_id', $plantId) + ->where('item_id', $itemId) + ->where('process_order', $processOrder) + ->pluck('coil_number', 'coil_number'); + + // return $plantId ? Item::where('plant_id', $plantId)->pluck('code', 'id') : []; + }) + ->reactive() + ->afterStateUpdated(function ($state, callable $set, callable $get) { + $plantId = $get('plant'); + $itemId = $get('Item'); + $processOrder = trim($get('process_order')); + + if (! $plantId || ! $itemId || ! $processOrder || ! $state) { + $set('received_quantity', null); + $set('machine_name', null); + + return; + } + + $processOrderData = ProcessOrder::where('plant_id', $plantId) + ->where('item_id', $itemId) + ->where('process_order', $processOrder) + ->where('coil_number', $state) + ->first(); + + $set('received_quantity', $processOrderData?->received_quantity); + $set('machine_name', $processOrderData?->machine_name); + }), + TextInput::make('received_quantity') + ->label('Received Quantity') + ->readOnly(), + + TextInput::make('machine_name') + ->label('Machine ID') + ->readOnly(), + ]) + ->columns(['default' => 1, 'sm' => 3]), + ]) + // ->action(function (array $data) { + // $selectedPalletNumber = $data['pallet_list']; + // return redirect()->route('download-reprint-qr-pdf', ['palletNo' => $selectedPalletNumber]); + // }) + ->action(function (array $data) { + $username = Filament::auth()->user()->name; + + return redirect()->route('download-reprint-process-pdf', [ + 'plant' => $data['plant'], + 'item' => $data['Item'], + 'process_order' => $data['process_order'], + 'coil_number' => $data['coil_number'], + 'name' => $username, + ]); + }) + ->visible(function () { + return Filament::auth()->user()->can('view reprint process order'); + }), ImportAction::make() ->label('Import Process Order') ->color('warning')