argument('start_date'); $endDate = $this->argument('end_date'); $query = PayrollEntry::with(['employee.employee', 'payrollRun']); if ($startDate && $endDate) { $query->whereHas('payrollRun', function ($q) use ($startDate, $endDate) { $q->where('pay_period_start', '>=', $startDate) ->where('pay_period_end', '<=', $endDate); }); $this->info("Auditing payroll entries for period: $startDate to $endDate"); } else { $this->info("Auditing all existing payroll entries"); } $entries = $query->get(); if ($entries->isEmpty()) { $this->warn("No payroll entries found to audit."); return 0; } $totalChecked = 0; $inconsistenciesFound = 0; $headers = ['Employee', 'Period', 'Metric', 'Stored', 'Computed', 'Diff']; $rows = []; foreach ($entries as $entry) { $user = $entry->employee; if (!$user) { $this->warn("Payroll Entry ID {$entry->id} has no associated user."); continue; } $employee = $user->employee; if (!$employee) { $this->warn("User {$user->name} (ID: {$user->id}) has no employee profile."); continue; } $run = $entry->payrollRun; if (!$run) { $this->warn("Payroll Entry ID {$entry->id} has no associated payroll run."); continue; } $totalChecked++; $computed = $payrollService->calculateForPeriod( $employee, $run->pay_period_start->toDateString(), $run->pay_period_end->toDateString() ); // Metrics to compare $metrics = [ 'Basic Salary' => [(float)$entry->basic_salary, (float)$computed['basic_salary']], 'Total Earnings' => [(float)$entry->total_earnings, (float)$computed['total_earnings']], 'Total Deductions' => [(float)$entry->total_deductions, (float)$computed['total_deductions']], 'Net Pay' => [(float)$entry->net_pay, (float)$computed['net_pay']], ]; $entryHasInconsistency = false; foreach ($metrics as $metricName => $values) { $storedVal = $values[0]; $computedVal = $values[1]; $diff = abs($storedVal - $computedVal); // Flag if difference is greater than 0.05 PHP if ($diff > 0.05) { $entryHasInconsistency = true; $rows[] = [ $user->name, $run->pay_period_start->format('Y-m-d') . ' / ' . $run->pay_period_end->format('Y-m-d'), $metricName, number_format($storedVal, 2), number_format($computedVal, 2), number_format($storedVal - $computedVal, 2), ]; } } if ($entryHasInconsistency) { $inconsistenciesFound++; } } if ($inconsistenciesFound > 0) { $this->error("\nFound $inconsistenciesFound inconsistent payroll entries out of $totalChecked checked:"); $this->table($headers, $rows); $this->info("\nDetailed Breakdown of Inconsistencies:"); foreach ($rows as $row) { $this->line(" - {$row[0]} ({$row[1]}): {$row[2]} differs by {$row[5]} PHP (Stored: {$row[3]} vs Computed: {$row[4]})"); } } else { $this->info("\nAll $totalChecked audited payroll entries match computed values perfectly! ✅"); } return $inconsistenciesFound > 0 ? 1 : 0; } }