diff --git a/app/Filament/Exports/CharacteristicValueExporter.php b/app/Filament/Exports/CharacteristicValueExporter.php new file mode 100644 index 0000000..6a8779c --- /dev/null +++ b/app/Filament/Exports/CharacteristicValueExporter.php @@ -0,0 +1,81 @@ +label('NO') + ->state(function ($record) use (&$rowNumber) { + // Increment and return the row number + return ++$rowNumber; + }), + ExportColumn::make('plant.code') + ->label('PLANT CODE'), + ExportColumn::make('line.name') + ->label('LINE NAME'), + ExportColumn::make('item.code') + ->label('ITEM CODE'), + ExportColumn::make('item.description') + ->label('DESCRIPTION'), + ExportColumn::make('machine.work_center') + ->label('WORK CENTER'), + ExportColumn::make('process_order') + ->label('PROCESS ORDER'), + ExportColumn::make('coil_number') + ->label('COIL NUMBER'), + ExportColumn::make('status') + ->label('STATUS'), + ExportColumn::make('spec_value') + ->label('Spec. Value') + ->formatStateUsing(function ($record) { + + $specVal = ProductCharacteristicsMaster::where('plant_id', $record->plant_id) + ->where('item_id', $record->item_id) + ->where('line_id', $record->line_id) + ->where('machine_id', $record->machine_id) + ->first(); + + return $specVal?->lower . ' - ' . $specVal?->upper; + }), + ExportColumn::make('observed_value') + ->label('OBSERVED VALUE'), + ExportColumn::make('created_at') + ->label('CREATED AT'), + ExportColumn::make('created_by') + ->label('CREATED BY'), + ExportColumn::make('updated_at') + ->label('UPDATED AT'), + ExportColumn::make('updated_by') + ->label('UPDATED BY'), + ExportColumn::make('deleted_at') + ->enabledByDefault(false) + ->label('DELETED AT'), + + ]; + } + + public static function getCompletedNotificationBody(Export $export): string + { + $body = 'Your characteristic value export has completed and '.number_format($export->successful_rows).' '.str('row')->plural($export->successful_rows).' exported.'; + + if ($failedRowsCount = $export->getFailedRowsCount()) { + $body .= ' '.number_format($failedRowsCount).' '.str('row')->plural($failedRowsCount).' failed to export.'; + } + + return $body; + } +} diff --git a/app/Filament/Imports/CharacteristicValueImporter.php b/app/Filament/Imports/CharacteristicValueImporter.php new file mode 100644 index 0000000..19cb4d0 --- /dev/null +++ b/app/Filament/Imports/CharacteristicValueImporter.php @@ -0,0 +1,372 @@ +requiredMapping() + ->exampleHeader('Plant Code') + ->example('1000') + ->label('Plant Code') + ->relationship(resolveUsing: 'code') + ->rules(['required']), + ImportColumn::make('line') + ->requiredMapping() + ->exampleHeader('Line Name') + ->example('4 inch pump line') + ->label('Line Name') + ->relationship(resolveUsing: 'name') + ->rules(['required']), + ImportColumn::make('item') + ->requiredMapping() + ->exampleHeader('Item Code') + ->example('123456') + ->label('Item Code') + ->relationship(resolveUsing: 'code') + ->rules(['required']), + ImportColumn::make('machine') + ->requiredMapping() + ->exampleHeader('Work Center') + ->example('RMGS09745') + ->label('Work Center') + ->relationship(resolveUsing: 'work_center') + ->rules(['required']), + ImportColumn::make('process_order') + ->requiredMapping() + ->exampleHeader('Process Order') + ->example('23455256352') + ->label('Process Order'), + ImportColumn::make('coil_number') + ->requiredMapping() + ->exampleHeader('Coil Number') + ->example('0') + ->label('Coil Number'), + ImportColumn::make('status') + ->requiredMapping() + ->exampleHeader('Status') + ->example('Ok') + ->label('Status'), + ImportColumn::make('observed_value') + ->requiredMapping() + ->exampleHeader('Observed Value') + ->example('RAW01234') + ->label('Observed Value'), + ImportColumn::make('created_by') + ->requiredMapping() + ->exampleHeader('Created By') + ->example('RAW01234') + ->label('Created By'), + ImportColumn::make('created_at') + ->requiredMapping() + ->exampleHeader('Created DateTime') + ->example('01-01-2025 08:00:00') + ->label('Created DateTime') + ->rules(['required']), + ImportColumn::make('updated_at') + ->requiredMapping() + ->exampleHeader('Updated DateTime') + ->example('01-01-2025 08:00:00') + ->label('Updated DateTime') + ->rules(['required']), + ]; + } + + public function resolveRecord(): ?CharacteristicValue + { + // return CharacteristicValue::firstOrNew([ + // // Update existing records, matching them by `$this->data['column_name']` + // 'email' => $this->data['email'], + // ]); + + $warnMsg = []; + $plantId = null; + $itemId = null; + $lineId = null; + $machineId = null; + // $itemAgainstPlant = null; + + $plantCode = $this->data['plant']; + $processOrder = trim($this->data['process_order'] ?? ''); + $iCode = trim($this->data['item']); + $workCenter = trim($this->data['machine']); + $lineName = trim($this->data['line']); + $coilNo = trim($this->data['coil_number']); + $obserVal = trim($this->data['observed_value']); + $status = trim($this->data['status']); + $createdBy = trim($this->data['created_by']); + + if ($plantCode == null || $plantCode == '') { + $warnMsg[] = 'Plant code cannot be empty'; + } elseif ($iCode == null || $iCode == '') {// process_order + $warnMsg[] = 'Item code cannot be empty'; + } elseif ($processOrder == null || $processOrder == '') {// + $warnMsg[] = 'Process Order cannot be empty'; + } elseif ($workCenter == null || $workCenter == '') { + $warnMsg[] = 'Work center cannot be empty'; + } elseif ($lineName == null || $lineName == '') { + $warnMsg[] = 'Line name cannot be empty'; + } elseif ($coilNo == null || $coilNo == '') { + $warnMsg[] = 'Coil number cannot be empty'; + } elseif ($obserVal == null || $obserVal == '') { + $warnMsg[] = 'Observed value cannot be empty'; + } elseif ($status == null || $status == '') { + $warnMsg[] = 'Status cannot be empty'; + } + + if (Str::length($plantCode) > 0 && (Str::length($plantCode) < 4 || ! is_numeric($plantCode) || ! preg_match('/^[1-9]\d{3,}$/', $plantCode))) { + $warnMsg[] = 'Invalid plant code found'; + } else { + $plant = Plant::where('code', $plantCode)->first(); + if (! $plant) { + $warnMsg[] = 'Plant not found'; + } else { + $plantId = $plant->id; + } + } + + if (Str::length($iCode) > 0 && (Str::length($iCode) < 6 || ! ctype_alnum($iCode))) { + $warnMsg[] = 'Invalid item code found'; + } else { + $itemCode = Item::where('code', $iCode)->first(); + if (! $itemCode) { + $warnMsg[] = 'Item code not found'; + } else { + if ($plantId) { + $itemCode = Item::where('code', $iCode)->where('plant_id', $plantId)->first(); + if (! $itemCode) { + $warnMsg[] = 'Item code not found for the given plant'; + } else { + $itemId = $itemCode->id; + } + } + } + } + + $lineExists = Line::where('name', $lineName)->first(); + if (! $lineExists) { + $warnMsg[] = 'Line name not found'; + } else { + if ($plantId) { + $lineAgainstPlant = Line::where('name', $lineName)->where('plant_id', $plantId)->first(); + if (! $lineAgainstPlant) { + $warnMsg[] = 'Line name not found for the given plant'; + } else { + $lineId = $lineAgainstPlant->id; + } + } + } + + $workCenterExist = Machine::where('work_center', $workCenter)->first(); + if (! $workCenterExist) { + $warnMsg[] = 'Work Center not found'; + } + + // $workCenterAgainstPlant = Machine::where('work_center', $workCenter) + // ->where('plant_id', $plantId) + // ->first(); + + // if (!$workCenterAgainstPlant) { + // $warnMsg[] = 'Work center not found for the given plant'; + // } else { + // $MachineId = $workCenterAgainstPlant->id; + // } + + if ($plantId != null && $lineId != null) { + $machineAgaPlantLine = Machine::where('plant_id', $plantId) + ->where('line_id', $lineId) + ->where('work_center', $workCenter) + ->first(); + + if (! $machineAgaPlantLine) { + $warnMsg[] = 'Work center not found for the given plant and line'; + } else { + $machineId = $machineAgaPlantLine->id; + } + } + + if (Str::length($coilNo) > 0 && ! is_numeric($coilNo)) { + $warnMsg[] = 'Coil number should contain only numeric values!'; + } + + if (Str::length($obserVal) > 0 && ! is_numeric($obserVal)) { + $warnMsg[] = 'Observed value should contain only numeric values!'; + } + + if (Str::length($status) > 0 && ! in_array($status, ['Ok', 'NotOk'], true)) { + $warnMsg[] = "Status must be either 'Ok' or 'NotOk'!"; + } + // else { + // if (Str::length($status) <= 0 || ! is_numeric($status) || ! preg_match('/^\d+(\.\d+)?$/', $status) + // ) { + // $status = 'NotOk'; + // } else { + // $specVal = ProductCharacteristicsMaster::where('plant_id', $plantId)->where('item_id', $itemId)->where('line_id', $lineId)->where('machine_id', $machineId)->first(); + // if (! $specVal) { + // $status = 'NotOk'; + // } + + // $lowLimit = $specVal?->lower ?? 0; + // $uppLimit = $specVal?->upper ?? 0; + + // if (Str::length($lowLimit) <= 0 || ! is_numeric($lowLimit) || ! preg_match('/^\d+(\.\d+)?$/', $lowLimit) + // ) { + // $status = 'NotOk'; + // } elseif (Str::length($uppLimit) <= 0 || ! is_numeric($uppLimit) || ! preg_match('/^\d+(\.\d+)?$/', $uppLimit) + // ) { + // $status = 'NotOk'; + // } + + // if (($lowLimit == 0 && $uppLimit == 0) || ($uppLimit == 0)) { + // $status = 'NotOk'; + // } + + // if ($lowLimit > $obserVal || $uppLimit < $obserVal) { + // $status = 'NotOk'; + // } + // $status = 'Ok'; + // } + // } + + if ($createdBy == null || $createdBy == '' || ! $createdBy) { + $warnMsg[] = 'Created By cannot be empty'; + } + + if ($plantId) { + $user = User::where('name', $createdBy)->first(); + + $userPlant = User::where('name', $createdBy)->where('plant_id', $plantId)->first(); + + if (! $user) { + $warnMsg[] = 'Created By user name not found!'; + } elseif (! $userPlant && ! $user->hasRole('Super Admin')) { + $warnMsg[] = "Created By user '{$createdBy}' not found for Plant '{$plantCode}'!"; + } elseif (! $user->hasRole(['Super Admin', 'Process Quality Manager', 'Process Manager', 'Process Supervisor', 'Process Employee'])) { + $warnMsg[] = 'Created By user does not have rights!'; + } + } + + if ($plantId && $processOrder) { + $existing = CharacteristicValue::where('plant_id', $plantId) + ->where('process_order', $processOrder) + ->where('coil_number', $coilNo) + ->first(); + + if ($existing) { + $warnMsg[] = "Coil number '{$coilNo}' already exists for Plant '{$plantCode}' and Process Order '{$processOrder}'."; + } + } + + if ($plant && $itemCode && $processOrder != '' && $processOrder != null) { + + $existingOrder = ProcessOrder::where('plant_id', $plantId) + ->where('process_order', $processOrder) + ->first(); + + if ($existingOrder && $existingOrder->item_id !== ($itemId ?? null)) { + $warnMsg[] = 'Same Process Order already exists for this Plant with a different Item Code'; + } + } + + $updatedBy = Filament::auth()->user()->name; // ?? 'Admin' + if (! $updatedBy) { + $warnMsg[] = 'Invalid updated by user name found'; + } + + $fromDate = $this->data['created_at']; + $toDate = $this->data['updated_at']; + + $formats = ['d-m-Y H:i', 'd-m-Y H:i:s']; // '07-05-2025 08:00' or '07-05-2025 08:00:00' + + $fdateTime = null; + $tdateTime = null; + // Try parsing with multiple formats + foreach ($formats as $format) { + try { + $fdateTime = Carbon::createFromFormat($format, $fromDate); + break; + } catch (\Exception $e) { + // $warnMsg[] = "Date format mismatch with format: $format"; + } + } + + foreach ($formats as $format) { + try { + $tdateTime = Carbon::createFromFormat($format, $toDate); + break; + } catch (\Exception $e) { + // $warnMsg[] = "Date format mismatch with format: $format"; + } + } + + if (! isset($fdateTime)) { + $warnMsg[] = "Invalid 'Created DateTime' format. Expected DD-MM-YYYY HH:MM:SS"; + } + if (! isset($tdateTime)) { + $warnMsg[] = "Invalid 'Updated DateTime' format. Expected DD-MM-YYYY HH:MM:SS"; + } + + if (isset($fdateTime) && isset($tdateTime)) { + if ($fdateTime->greaterThan($tdateTime)) { + $warnMsg[] = "'Created DataTime' is greater than 'Updated DateTime'."; + } + } + + if (! empty($warnMsg)) { + throw new RowImportFailedException(implode(', ', $warnMsg)); + } + + return CharacteristicValue::updateOrCreate([ + 'plant_id' => $plantId, + 'process_order' => $processOrder, + 'coil_number' => $coilNo, + ], + [ + 'item_id' => $itemId, + 'line_id' => $lineId, + 'machine_id' => $machineId, + 'status' => $status, + 'observed_value' => $obserVal, + 'created_by' => $createdBy, + 'updated_by' => $updatedBy, + 'created_at' => $fdateTime->format('Y-m-d H:i:s'), + 'updated_at' => $tdateTime->format('Y-m-d H:i:s'), + ]); + + // return null; + + // return new CharacteristicValue; + } + + public static function getCompletedNotificationBody(Import $import): string + { + $body = 'Your characteristic value import has completed and '.number_format($import->successful_rows).' '.str('row')->plural($import->successful_rows).' imported.'; + + if ($failedRowsCount = $import->getFailedRowsCount()) { + $body .= ' '.number_format($failedRowsCount).' '.str('row')->plural($failedRowsCount).' failed to import.'; + } + + return $body; + } +}