argument('start_date') ?: '2026-01-11'; $endDate = $this->argument('end_date') ?: '2026-01-25'; $this->info("Simulating payroll for period: $startDate to $endDate"); // 1. Patch Target Users in Legacy table $this->info("Patching legacy salaries for target users..."); // Find target users dynamically $anaUser = User::where('name', 'like', '%Ana%')->first(); if ($anaUser) { $anaId = $anaUser->id; // Update her local system ID so PDF reflects the exact legacy formatting \App\Models\Employee::where('user_id', $anaId)->update(['employee_id' => 'SEB2018-2008T']); DB::connection('legacy')->table('employee_salary_structures') ->where('user_id', $anaId) ->update([ 'gross_salary' => 26000.00, 'pay_frequency' => 'semi-monthly', 'daily_rate' => 1000.00 ]); // Scrub false positive records \App\Models\AttendanceRecord::where('employee_id', $anaId) ->whereIn('date', ['2026-01-11', '2026-01-15']) ->delete(); } $paulUser = User::where('name', 'like', '%Paul%')->first(); if ($paulUser) { $paulId = $paulUser->id; DB::connection('legacy')->table('employee_salary_structures') ->where('user_id', $paulId) ->update([ 'gross_salary' => 22000.00, 'pay_frequency' => 'semi-monthly', 'daily_rate' => 846.15 ]); } // 2. Clear previous simulation runs to prevent duplicates PayrollRun::where('title', 'like', 'Simulation%')->delete(); // 3. Create a Payroll Run $payrollRun = PayrollRun::create([ 'title' => 'Simulation Run ' . Carbon::now()->format('Y-m-d H:i:s'), 'pay_period_start' => $startDate, 'pay_period_end' => $endDate, 'pay_date' => Carbon::parse($endDate)->addDays(1)->toDateString(), 'status' => 'completed', 'created_by' => 1, ]); $employees = User::where('type', 'employee')->whereHas('employee')->get(); $bar = $this->output->createProgressBar(count($employees)); $generatedFiles = []; foreach ($employees as $user) { $employee = $user->employee; if (!$employee) continue; $computation = $payrollService->calculateForPeriod($employee, $startDate, $endDate); // Create Payroll Entry $payrollEntry = PayrollEntry::create([ 'payroll_run_id' => $payrollRun->id, 'employee_id' => $user->id, 'basic_salary' => $computation['basic_salary'], 'gross_pay' => $computation['total_earnings'], 'deductions' => $computation['total_deductions'], 'net_pay' => $computation['net_pay'], 'earnings_breakdown' => $computation['earnings'], 'deductions_breakdown' => $computation['deductions'], 'created_by' => 1, ]); // Save summary explicitly to the employee model temporarily, or we inject it into the payslip view later. // Since we use strict JSON in real life, let's attach the summary to earnings_breakdown as meta to easily read it. // Actually, we can add it to the payslip or just rewrite template to read custom fields. // For now, I'll sneak it into earnings_breakdown['summary'] $earningsBreakdown = $computation['earnings']; $earningsBreakdown['summary'] = $computation['summary']; $earningsBreakdown['meta'] = [ 'gross_monthly' => $computation['gross_monthly'], 'daily_rate' => $computation['daily_rate'], 'pay_type' => $computation['pay_type'], ]; $payrollEntry->update(['earnings_breakdown' => $earningsBreakdown]); // Create Payslip $payslip = Payslip::create([ 'payroll_entry_id' => $payrollEntry->id, 'employee_id' => $user->id, 'payslip_number' => Payslip::generatePayslipNumber($employee->employee_id, $payrollRun->pay_date), 'pay_period_start' => $startDate, 'pay_period_end' => $endDate, 'pay_date' => $payrollRun->pay_date, 'status' => 'generated', 'created_by' => 1, ]); $path = $payslip->generatePDF(); $generatedFiles[] = $path; $bar->advance(); } $bar->finish(); $this->info("\nSimulation Complete! Generated " . count($generatedFiles) . " payslips:"); foreach ($generatedFiles as $file) { $this->line(" - public/storage/$file"); } } }