Files
HRM-System/app/Console/Commands/SimulatePayslips.php
2026-04-20 00:20:10 +08:00

140 lines
5.6 KiB
PHP

<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\User;
use App\Models\PayrollRun;
use App\Models\PayrollEntry;
use App\Models\Payslip;
use App\Services\PayrollService;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
class SimulatePayslips extends Command
{
protected $signature = 'payroll:simulate {start_date?} {end_date?}';
protected $description = 'Simulate payslip generation based on legacy attendance data';
public function handle(PayrollService $payrollService)
{
// Authenticate system user so DOMPDF/settings generators don't crash on Auth::user()
\Illuminate\Support\Facades\Auth::loginUsingId(1);
$startDate = $this->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");
}
}
}