Files
pds/app/Filament/Imports/ProductionPlanImporter.php
dhanabalan ece5b846bb
Some checks failed
Scan for leaked secrets using Kingfisher / kingfisher-secrets-scan (push) Has been cancelled
Gemini PR Review / Gemini PR Review (pull_request) Failing after 20s
Scan for leaked secrets using Kingfisher / kingfisher-secrets-scan (pull_request) Successful in 18s
Laravel Larastan / larastan (pull_request) Failing after 40s
Laravel Pint / pint (pull_request) Successful in 3m56s
Changed whole logic gainst based on week upload production plan import
2026-11-03 11:41:26 +05:30

515 lines
17 KiB
PHP

<?php
namespace App\Filament\Imports;
use App\Models\Block;
use App\Models\Item;
use App\Models\Line;
use App\Models\Plant;
use App\Models\ProductionCalendar;
use App\Models\ProductionPlan;
use App\Models\Shift;
use App\Models\User;
use Carbon\Carbon;
use Filament\Actions\Imports\Exceptions\RowImportFailedException;
use Filament\Actions\Imports\ImportColumn;
use Filament\Actions\Imports\Importer;
use Filament\Actions\Imports\Models\Import;
use Filament\Facades\Filament;
use Str;
use Illuminate\Support\Facades\Log;
class ProductionPlanImporter extends Importer
{
protected static ?string $model = ProductionPlan::class;
public static function getColumns(): array
{
return [
// ImportColumn::make('created_at')
// ->requiredMapping()
// ->exampleHeader('Created DateTime')
// ->example(['01-01-2025 08:00:00', '01-01-2025 19:30:00'])
// ->label('Created DateTime')
// ->rules(['required']),
ImportColumn::make('plant')
->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', '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('plan_quantity')
// ->requiredMapping()
// ->exampleHeader('Plan Quantity')
// ->example(['500', '450'])
// ->label('Plan Quantity')
// ->numeric()
// ->rules(['required', 'integer']),
ImportColumn::make('week_1')
->requiredMapping()
->exampleHeader('Week 1')
->label('Week 1'),
ImportColumn::make('week_2')
->requiredMapping()
->exampleHeader('Week 2')
->label('Week 2'),
ImportColumn::make('week_3')
->requiredMapping()
->exampleHeader('Week 3')
->label('Week 3'),
ImportColumn::make('week_4')
->requiredMapping()
->exampleHeader('Week 4')
->label('Week 4'),
ImportColumn::make('week_5')
->requiredMapping()
->exampleHeader('Week 5')
->label('Week 5'),
// ImportColumn::make('production_quantity')
// ->requiredMapping()
// ->exampleHeader('Production Quantity')
// ->example(['0', '0'])
// ->label('Production Quantity')
// ->numeric()
// ->rules(['required', 'integer']),
// ImportColumn::make('block_reference')
// ->requiredMapping() // Or optionalMapping() if not always present
// ->exampleHeader('Block Name')
// ->example(['Block A', 'Block A'])
// ->label('Block Name')
// ->rules(['required']), // Or remove if not required
// ImportColumn::make('shift')
// ->requiredMapping()
// ->exampleHeader('Shift Name') // ID
// ->example(['Day', 'Night']) // '2', '7'
// ->label('Shift Name') // ID
// ->relationship(resolveUsing: 'name')
// ->rules(['required']),
// ImportColumn::make('updated_at')
// ->requiredMapping()
// ->exampleHeader('Updated DateTime')
// ->example(['01-01-2025 08:00:00', '01-01-2025 19:30:00'])
// ->label('Updated DateTime')
// ->rules(['required']),
// ImportColumn::make('operator_id')
// ->requiredMapping()
// ->exampleHeader('Operator ID')
// ->example([Filament::auth()->user()->name, Filament::auth()->user()->name])
// ->label('Operator ID')
// ->rules(['required']),
];
}
public function resolveRecord(): ?ProductionPlan
{
$warnMsg = [];
$plantCod = $this->data['plant'];
$itemCod = $this->data['item'];
$plant = null;
$line = null;
$block = null;
if (Str::length($plantCod) < 4 || ! is_numeric($plantCod) || ! preg_match('/^[1-9]\d{3,}$/', $plantCod)) {
$warnMsg[] = 'Invalid plant code found';
} else {
$plant = Plant::where('code', $plantCod)->first();
}
if (! $plant) {
$warnMsg[] = 'Plant not found';
}
if (Str::length($itemCod) < 6 || ! is_numeric($itemCod)) {
$warnMsg[] = 'Invalid item code found';
} else {
$item = Item::where('code', $itemCod)->first();
}
if (! $item) {
$warnMsg[] = 'Item not found';
}
$plantId = $plant->id;
$itemAgaPlant = Item::where('plant_id', $plantId)->where('code', $itemCod)->first();
if(!$itemAgaPlant){
$warnMsg[] = 'Item not found against plant code';
}
else {
$line = $itemAgaPlant->line_id;
if (!$line) {
$warnMsg[] = 'Line not mapped for that item code';
}
}
$user = Filament::auth()->user();
$operatorName = $user->name;
if (!empty($this->data['plan_quantity'])) {
if (Str::length($this->data['plan_quantity']) < 0 || ! is_numeric($this->data['plan_quantity']) || $this->data['plan_quantity'] <= 0) {
$warnMsg[] = 'Invalid plan quantity found';
}
}
foreach (['week_1', 'week_2', 'week_3', 'week_4'] as $week) {
$value = $this->data[$week] ?? null;
if (filled($value) && (! is_numeric($value) || $value < 0)) {
$warnMsg[] = "Invalid {$week} value found";
}
}
$week5 = $this->data['week_5'] ?? null;
if (
filled($week5) &&
(! is_numeric($week5) || $week5 < 0)
) {
$warnMsg[] = 'Invalid week_5 value found';
}
$recordDate = now();
$month = $recordDate->month;
$year = $recordDate->year;
$previousRecord = ProductionCalendar::where('plant_id', $plant->id)
->whereMonth('created_at', $month)
->whereYear('created_at', $year)
->first();
if(!$previousRecord){
$warnMsg[] = 'Update this month production calendar';
}
if (! empty($warnMsg)) {
throw new RowImportFailedException(implode(', ', $warnMsg));
}
$recordDate = now();
$month = $recordDate->month;
$year = $recordDate->year;
$totalDays = $recordDate->daysInMonth;
$previousRecord = ProductionCalendar::where('plant_id', $plant->id)
->whereMonth('created_at', $month)
->whereYear('created_at', $year)
->first();
$workingDays = $previousRecord?->working_days ?? null;
// Get leave dates
$leaveDates = array_filter(
array_map(
'trim',
explode(',', (string) ($previousRecord?->leave_dates ?? ''))
)
);
// Convert leave dates into day numbers
$leaveDayNumbers = [];
foreach ($leaveDates as $leaveDate) {
try {
$date = Carbon::parse($leaveDate);
if ($date->format('Y-m') === $recordDate->format('Y-m')) {
$leaveDayNumbers[] = (int) $date->format('d');
}
} catch (\Throwable $e) {
// Ignore invalid leave dates
}
}
// Get existing production plan for this item
$productionPlan1 = ProductionPlan::where('plant_id', $plant->id)
->where('item_id', $itemAgaPlant->id)
->whereYear('created_at', $year)
->whereMonth('created_at', $month)
->first();
// Initialize daily data
$dailyData = [];
for ($day = 1; $day <= 31; $day++) {
if ($day <= $totalDays) {
$dailyData['day_' . $day] =
(int) ($productionPlan1->{'day_' . $day} ?? 0);
} else {
// Invalid dates must always remain zero
$dailyData['day_' . $day] = 0;
}
}
// Weekly ranges
$weekRanges = [
'week_1' => [1, min(7, $totalDays)],
'week_2' => [8, min(14, $totalDays)],
'week_3' => [15, min(21, $totalDays)],
'week_4' => [22, min(28, $totalDays)],
'week_5' => [29, min(31, $totalDays)],
];
// Process weekly values
foreach ($weekRanges as $week => [$start, $end]) {
if ($start > $totalDays) {
continue;
}
$weeklyValue = $this->data[$week] ?? null;
// Empty Excel value: retain existing daily values
if ($weeklyValue === null || $weeklyValue === '') {
continue;
}
// Get working days excluding leave dates
$remainingDays = [];
for ($day = $start; $day <= $end; $day++) {
if (! in_array($day, $leaveDayNumbers, true)) {
$remainingDays[] = $day;
} else {
$dailyData['day_' . $day] = 0;
}
}
$daysCount = count($remainingDays);
if ($daysCount === 0) {
continue;
}
// Replace existing weekly quantity with Excel quantity
$weeklyTotal = (int) round((float) $weeklyValue);
$dailyValue = intdiv($weeklyTotal, $daysCount);
$remainder = $weeklyTotal % $daysCount;
foreach ($remainingDays as $index => $day) {
$dailyData['day_' . $day] =
$dailyValue + ($index < $remainder ? 1 : 0);
}
}
// Calculate final monthly plan quantity
$planQuantity = array_sum($dailyData);
// Prepare plan data
$planData = array_merge([
'line_id' => $line,
'plan_quantity' => $planQuantity,
'working_days' => $workingDays,
'operator_id' => $operatorName,
'updated_at' => now(),
], $dailyData);
// Update or create production plan
if ($productionPlan1) {
$productionPlan1->update($planData);
} else {
ProductionPlan::create(array_merge($planData, [
'plant_id' => $plant->id,
'item_id' => $itemAgaPlant->id,
'created_at' => now(),
]));
}
//..Working Logic
// $recordDate = now();
// $month = $recordDate->month;
// $year = $recordDate->year;
// $previousRecord = ProductionPlan::where('plant_id', $plant->id)
// ->whereMonth('created_at', $month)
// ->whereYear('created_at', $year)
// ->first();
// $workingDays = $previousRecord?->working_days ?? null;
// // Get leave dates from the existing production plan
// $leaveDates = array_filter(
// array_map('trim', explode(',', (string) ($previousRecord?->leave_dates ?? '')))
// );
// // Convert leave dates into day numbers for the current month
// $leaveDayNumbers = [];
// foreach ($leaveDates as $leaveDate) {
// $date = Carbon::parse($leaveDate);
// if ($date->format('Y-m') === $recordDate->format('Y-m')) {
// $leaveDayNumbers[] = (int) $date->format('d');
// }
// }
// $totalDays = $recordDate->daysInMonth;
// $dailyData = [];
// for ($day = 1; $day <= 31; $day++) {
// $dailyData['day_' . $day] = 0;
// }
// $planQuantity = 0;
// foreach (['week_1', 'week_2', 'week_3', 'week_4', 'week_5'] as $week) {
// $planQuantity += (float) ($this->data[$week] ?? 0);
// }
// $planQuantity = (int) $planQuantity;
// // Dynamic weekly ranges
// $weekRanges = [
// 'week_1' => [1, min(7, $totalDays)],
// 'week_2' => [8, min(14, $totalDays)],
// 'week_3' => [15, min(21, $totalDays)],
// 'week_4' => [22, min(28, $totalDays)],
// 'week_5' => [29, min(31, $totalDays)],
// ];
// foreach ($weekRanges as $week => [$start, $end]) {
// if ($start > $totalDays) {
// continue;
// }
// $weeklyValue = $this->data[$week] ?? null;
// if ($weeklyValue === null || $weeklyValue === '') {
// continue;
// }
// $remainingDays = [];
// for ($day = $start; $day <= $end; $day++) {
// if (! in_array($day, $leaveDayNumbers)) {
// $remainingDays[] = $day;
// }
// }
// $daysCount = count($remainingDays);
// if ($daysCount === 0) {
// continue;
// }
// $weeklyTotal = (int) round((float) $weeklyValue);
// $dailyValue = intdiv($weeklyTotal, $daysCount);
// $remainder = $weeklyTotal % $daysCount;
// foreach ($remainingDays as $index => $day) {
// $dailyData['day_' . $day] =
// $dailyValue + ($index < $remainder ? 1 : 0);
// }
// }
// $dailyTotal = array_sum($dailyData);
// $productionPlan1 = ProductionPlan::where('plant_id', $plant->id)
// ->where('item_id', $itemAgaPlant->id)
// ->whereYear('created_at', $recordDate->year)
// ->whereMonth('created_at', $recordDate->month)
// ->first();
// $planData = array_merge([
// 'line_id' => $line,
// 'plan_quantity' => $planQuantity,
// 'working_days' => $workingDays,
// 'operator_id' => $operatorName,
// 'updated_at' => now(),
// ], $dailyData);
// if ($productionPlan1) {
// $productionPlan1->update($planData);
// } else {
// ProductionPlan::create(array_merge($planData, [
// 'plant_id' => $plant->id,
// 'item_id' => $itemAgaPlant->id,
// 'created_at' => now(),
// ]));
// }
//..Old Logic
// $productionPlan1 = ProductionPlan::where('plant_id', $plant->id)
// ->where('item_id', $itemAgaPlant->id)
// ->whereYear('created_at', $recordDate->year)
// ->whereMonth('created_at', $recordDate->month)
// ->first();
// if ($productionPlan1) {
// $productionPlan1->update([
// 'line_id' => $line,
// 'plan_quantity' => $this->data['plan_quantity'],
// 'working_days' => $workingDays,
// 'operator_id' => $operatorName,
// 'updated_at' => now(),
// ]);
// } else {
// ProductionPlan::create([
// 'plant_id' => $plant->id,
// 'line_id' => $line,
// 'item_id' => $itemAgaPlant->id,
// 'plan_quantity' => $this->data['plan_quantity'],
// 'working_days' => $workingDays,
// 'operator_id' => $operatorName,
// 'created_at' => now(),
// 'updated_at' => now(),
// ]);
// }
return null;
}
public static function getCompletedNotificationBody(Import $import): string
{
$body = 'Your production plan 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;
}
}