diff --git a/app/Http/Controllers/Api/Admin/HR/PayrollController.php b/app/Http/Controllers/Api/Admin/HR/PayrollController.php index 35af56b2..98990a71 100644 --- a/app/Http/Controllers/Api/Admin/HR/PayrollController.php +++ b/app/Http/Controllers/Api/Admin/HR/PayrollController.php @@ -310,7 +310,7 @@ public function export(Request $request): StreamedResponse $file = fopen('php://output', 'w'); fwrite($file, "\xEF\xBB\xBF"); // UTF-8 BOM - fputcsv($file, ['사원명', '부서', '기본급', '고정연장근로수당', '식대', '총지급액', '국민연금', '건강보험', '장기요양보험', '고용보험', '근로소득세', '지방소득세', '총공제액', '실수령액', '상태']); + fputcsv($file, ['사원명', '부서', '기본급', '고정연장근로수당', '식대(비과세)', '총지급액', '국민연금', '건강보험', '장기요양보험', '고용보험', '근로소득세', '지방소득세', '총공제액', '실수령액', '상태']); foreach ($payrolls as $payroll) { $profile = $payroll->user?->tenantProfiles?->first(); diff --git a/app/Services/HR/PayrollService.php b/app/Services/HR/PayrollService.php index d49d92bc..c0599a4e 100644 --- a/app/Services/HR/PayrollService.php +++ b/app/Services/HR/PayrollService.php @@ -374,6 +374,9 @@ public function bulkGenerate(int $year, int $month): array /** * 급여 금액 자동 계산 + * + * 식대(bonus)는 비과세 항목으로, 총 지급액에는 포함되지만 + * 4대보험 및 세금 산출 기준(과세표준)에서는 제외된다. */ public function calculateAmounts(array $data, ?PayrollSetting $settings = null): array { @@ -381,7 +384,7 @@ public function calculateAmounts(array $data, ?PayrollSetting $settings = null): $baseSalary = (float) ($data['base_salary'] ?? 0); $overtimePay = (float) ($data['overtime_pay'] ?? 0); - $bonus = (float) ($data['bonus'] ?? 0); + $bonus = (float) ($data['bonus'] ?? 0); // 식대 (비과세) // 수당 합계 $allowancesTotal = 0; @@ -392,17 +395,20 @@ public function calculateAmounts(array $data, ?PayrollSetting $settings = null): } } - // 총 지급액 + // 총 지급액 (비과세 포함) $grossSalary = $baseSalary + $overtimePay + $bonus + $allowancesTotal; - // 4대보험 계산 - $healthInsurance = $this->calculateHealthInsurance($grossSalary, $settings); - $longTermCare = $this->calculateLongTermCare($grossSalary, $settings); - $pension = $this->calculatePension($grossSalary, $settings); - $employmentInsurance = $this->calculateEmploymentInsurance($grossSalary, $settings); + // 과세표준 = 총 지급액 - 식대(비과세) + $taxableBase = $grossSalary - $bonus; - // 근로소득세 (간이세액표) - $incomeTax = $this->calculateIncomeTax($grossSalary); + // 4대보험 계산 (과세표준 기준) + $healthInsurance = $this->calculateHealthInsurance($taxableBase, $settings); + $longTermCare = $this->calculateLongTermCare($taxableBase, $settings); + $pension = $this->calculatePension($taxableBase, $settings); + $employmentInsurance = $this->calculateEmploymentInsurance($taxableBase, $settings); + + // 근로소득세 (간이세액표, 과세표준 기준) + $incomeTax = $this->calculateIncomeTax($taxableBase); // 지방소득세 (근로소득세의 10%) $residentTax = (int) floor($incomeTax * ($settings->resident_tax_rate / 100)); @@ -423,6 +429,7 @@ public function calculateAmounts(array $data, ?PayrollSetting $settings = null): return [ 'gross_salary' => (int) $grossSalary, + 'taxable_base' => (int) $taxableBase, 'income_tax' => $incomeTax, 'resident_tax' => $residentTax, 'health_insurance' => $healthInsurance, diff --git a/resources/views/hr/payrolls/index.blade.php b/resources/views/hr/payrolls/index.blade.php index 392ee371..4d931147 100644 --- a/resources/views/hr/payrolls/index.blade.php +++ b/resources/views/hr/payrolls/index.blade.php @@ -193,7 +193,7 @@ class="money-input w-full px-3 py-2 border border-gray-300 rounded-lg text-sm te class="money-input w-full px-3 py-2 border border-gray-300 rounded-lg text-sm text-right focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
- + 총 지급액 0
+
+ 과세표준 (식대 제외) + 0 +
총 공제액 0 @@ -482,6 +486,7 @@ function openEditPayrollModal(id, data) { } }); document.getElementById('calcGross').textContent = numberFormat(d.gross_salary); + document.getElementById('calcTaxableBase').textContent = numberFormat(d.taxable_base); updateDeductionTotals(); } }) @@ -593,6 +598,7 @@ function doRecalculate() { if (!el.dataset.manual) setMoneyValue(el, val); }); document.getElementById('calcGross').textContent = numberFormat(d.gross_salary); + document.getElementById('calcTaxableBase').textContent = numberFormat(d.taxable_base); updateDeductionTotals(); } }) @@ -607,7 +613,7 @@ function resetCalculation() { el.classList.remove('bg-yellow-50', 'border-yellow-300'); el.classList.add('border-gray-200'); }); - ['calcGross','calcTotalDeductions','calcNet'].forEach(id => { + ['calcGross','calcTaxableBase','calcTotalDeductions','calcNet'].forEach(id => { document.getElementById(id).textContent = '0'; }); } @@ -881,7 +887,7 @@ function openPayrollDetail(id, data) { html += '지급 항목'; html += `기본급${numberFormat(data.base_salary)}`; html += `고정연장근로수당${numberFormat(data.overtime_pay)}`; - html += `식대${numberFormat(data.bonus)}`; + html += `식대(비과세)${numberFormat(data.bonus)}`; if (data.allowances && data.allowances.length > 0) { data.allowances.forEach(a => {