fix: [payroll] 식대 비과세 처리 + 국민연금 상한/하한 적용 개선
- 식대(bonus)를 과세표준에서 제외하여 4대보험/세금 산출 시 비과세 처리 - 라벨 '식대' → '식대(비과세)'로 변경 (등록/수정/상세/엑셀) - 합계 영역에 과세표준(식대 제외) 표시 추가 - 국민연금은 기존대로 settings의 상한액/하한액 적용 (과세표준 기준으로 변경)
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 mb-1">식대</label>
|
||||
<label class="block text-xs text-gray-500 mb-1">식대(비과세)</label>
|
||||
<input type="text" id="payrollBonus" name="bonus" inputmode="numeric" value="0"
|
||||
oninput="formatMoneyInput(this); recalculate()"
|
||||
onfocus="moneyFocus(this)" onblur="moneyBlur(this)"
|
||||
@@ -278,6 +278,10 @@ class="money-input w-28 px-2 py-1 border border-gray-200 rounded text-right text
|
||||
<span class="text-gray-600 font-medium">총 지급액</span>
|
||||
<span id="calcGross" class="text-blue-700 font-bold">0</span>
|
||||
</div>
|
||||
<div class="flex justify-between text-xs">
|
||||
<span class="text-gray-400">과세표준 (식대 제외)</span>
|
||||
<span id="calcTaxableBase" class="text-gray-500">0</span>
|
||||
</div>
|
||||
<div class="flex justify-between text-sm">
|
||||
<span class="text-gray-600 font-medium">총 공제액</span>
|
||||
<span id="calcTotalDeductions" class="text-red-600 font-bold">0</span>
|
||||
@@ -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 += '<tr class="bg-blue-50"><th class="px-4 py-2 text-left text-blue-800 font-medium" colspan="2">지급 항목</th></tr>';
|
||||
html += `<tr class="border-t"><td class="px-4 py-2 text-gray-500">기본급</td><td class="px-4 py-2 text-right">${numberFormat(data.base_salary)}</td></tr>`;
|
||||
html += `<tr class="border-t"><td class="px-4 py-2 text-gray-500">고정연장근로수당</td><td class="px-4 py-2 text-right">${numberFormat(data.overtime_pay)}</td></tr>`;
|
||||
html += `<tr class="border-t"><td class="px-4 py-2 text-gray-500">식대</td><td class="px-4 py-2 text-right">${numberFormat(data.bonus)}</td></tr>`;
|
||||
html += `<tr class="border-t"><td class="px-4 py-2 text-gray-500">식대(비과세)</td><td class="px-4 py-2 text-right">${numberFormat(data.bonus)}</td></tr>`;
|
||||
|
||||
if (data.allowances && data.allowances.length > 0) {
|
||||
data.allowances.forEach(a => {
|
||||
|
||||
Reference in New Issue
Block a user