fix: [finance] 계정코드 매핑 이미지 기준 재수정
- 204→25300(미지급금), 205→26200(미지급비용) - 207→25400(예수금), 208→25500(부가세예수금) - 826→83700(건물관리비), 253→30800(장기성지급어음) - 501→45100(상품매출원가), 117→13500(부가세대급금) - 201→25100(외상매입금) - 801 대표이사→80100(임원급여), 나머지→80200(직원급여) 분기
This commit is contained in:
@@ -842,11 +842,11 @@ public function generateJournalEntry(Request $request): JsonResponse
|
||||
}
|
||||
|
||||
// 계정과목 조회
|
||||
$accountCodes = AccountCode::whereIn('code', ['80100', '20700', '20500'])
|
||||
$accountCodes = AccountCode::whereIn('code', ['80100', '80200', '25400', '26200'])
|
||||
->where('is_active', true)
|
||||
->pluck('name', 'code');
|
||||
|
||||
$missingCodes = array_diff(['80100', '20700', '20500'], $accountCodes->keys()->toArray());
|
||||
$missingCodes = array_diff(['80100', '80200', '25400', '26200'], $accountCodes->keys()->toArray());
|
||||
if (! empty($missingCodes)) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
@@ -885,12 +885,14 @@ public function generateJournalEntry(Request $request): JsonResponse
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 차변: 80100 급여 / 임직원 — 총지급액
|
||||
// 1. 차변: 80100 임원급여(대표이사) / 80200 직원급여(나머지)
|
||||
if ($grossAmount > 0) {
|
||||
// 대표이사 여부에 따라 계정코드 분기
|
||||
$salaryCode = $this->isCeoPayroll($year, $month) ? '80100' : '80200';
|
||||
$lines[] = [
|
||||
'dc_type' => 'debit',
|
||||
'account_code' => '80100',
|
||||
'account_name' => $accountCodes['80100'],
|
||||
'account_code' => $salaryCode,
|
||||
'account_name' => $accountCodes[$salaryCode],
|
||||
'trading_partner_id' => $partners['임직원'],
|
||||
'trading_partner_name' => '임직원',
|
||||
'debit_amount' => $grossAmount,
|
||||
@@ -916,8 +918,8 @@ public function generateJournalEntry(Request $request): JsonResponse
|
||||
} else {
|
||||
$creditLines[$mergeKey] = [
|
||||
'dc_type' => 'credit',
|
||||
'account_code' => '20700',
|
||||
'account_name' => $accountCodes['20700'],
|
||||
'account_code' => '25400',
|
||||
'account_name' => $accountCodes['25400'],
|
||||
'trading_partner_id' => $partners[$partnerName],
|
||||
'trading_partner_name' => $partnerName,
|
||||
'debit_amount' => 0,
|
||||
@@ -935,12 +937,12 @@ public function generateJournalEntry(Request $request): JsonResponse
|
||||
$lines[] = $creditLine;
|
||||
}
|
||||
|
||||
// 최종: 대변 20500 미지급비용 / 임직원 — 실수령액 (DB 값)
|
||||
// 최종: 대변 26200 미지급비용 / 임직원 — 실수령액 (DB 값)
|
||||
if ($netSalary > 0) {
|
||||
$lines[] = [
|
||||
'dc_type' => 'credit',
|
||||
'account_code' => '20500',
|
||||
'account_name' => $accountCodes['20500'],
|
||||
'account_code' => '26200',
|
||||
'account_name' => $accountCodes['26200'],
|
||||
'trading_partner_id' => $partners['임직원'],
|
||||
'trading_partner_name' => '임직원',
|
||||
'debit_amount' => 0,
|
||||
@@ -1087,4 +1089,25 @@ public function calculate(Request $request): JsonResponse
|
||||
'data' => array_merge($result, ['family_count' => $familyCount]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 해당 월 급여가 대표이사 전용인지 판별
|
||||
* 대표이사만 있으면 80100(임원급여), 그 외 80200(직원급여)
|
||||
*/
|
||||
private function isCeoPayroll(int $year, int $month): bool
|
||||
{
|
||||
// 해당 월 급여 대상자 중 대표이사만 있는지 확인
|
||||
$tenantId = session('selected_tenant_id', 1);
|
||||
|
||||
return \App\Models\HR\Payroll::where('tenant_id', $tenantId)
|
||||
->where('year', $year)
|
||||
->where('month', $month)
|
||||
->whereHas('employee', fn ($q) => $q->where('position', '대표이사'))
|
||||
->exists()
|
||||
&& ! \App\Models\HR\Payroll::where('tenant_id', $tenantId)
|
||||
->where('year', $year)
|
||||
->where('month', $month)
|
||||
->whereHas('employee', fn ($q) => $q->where('position', '!=', '대표이사'))
|
||||
->exists();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1919,7 +1919,7 @@ private function syncJournalAmounts(int $tenantId, string $uniqueKey, float $new
|
||||
'journal_entry_id' => $journal->id,
|
||||
'line_no' => $lineNo++,
|
||||
'dc_type' => 'debit',
|
||||
'account_code' => $expenseAccount?->account_code ?? '82600',
|
||||
'account_code' => $expenseAccount?->account_code ?? '83700',
|
||||
'account_name' => $expenseAccount?->account_name ?? '잡비',
|
||||
'debit_amount' => $supplyInt,
|
||||
'credit_amount' => 0,
|
||||
@@ -1945,7 +1945,7 @@ private function syncJournalAmounts(int $tenantId, string $uniqueKey, float $new
|
||||
'journal_entry_id' => $journal->id,
|
||||
'line_no' => $lineNo++,
|
||||
'dc_type' => 'debit',
|
||||
'account_code' => $expenseAccount?->account_code ?? '82600',
|
||||
'account_code' => $expenseAccount?->account_code ?? '83700',
|
||||
'account_name' => $expenseAccount?->account_name ?? '잡비',
|
||||
'debit_amount' => $totalAmount,
|
||||
'credit_amount' => 0,
|
||||
@@ -1962,7 +1962,7 @@ private function syncJournalAmounts(int $tenantId, string $uniqueKey, float $new
|
||||
'journal_entry_id' => $journal->id,
|
||||
'line_no' => $lineNo,
|
||||
'dc_type' => 'credit',
|
||||
'account_code' => $creditAccount?->account_code ?? '20500',
|
||||
'account_code' => $creditAccount?->account_code ?? '26200',
|
||||
'account_name' => $creditAccount?->account_name ?? '미지급비용',
|
||||
'debit_amount' => 0,
|
||||
'credit_amount' => $totalAmount,
|
||||
|
||||
@@ -196,7 +196,7 @@ public function integrated(Request $request): JsonResponse
|
||||
$account = $request->input('account', 'all');
|
||||
$vendor = $request->input('vendor', '');
|
||||
|
||||
$accountCodes = $account === 'all' ? ['20400', '20500'] : [$account];
|
||||
$accountCodes = $account === 'all' ? ['25300', '26200'] : [$account];
|
||||
|
||||
// 0. 이월 잔액 계산 (startDate 이전의 누적 잔액)
|
||||
$priorBalanceMap = [];
|
||||
@@ -431,7 +431,7 @@ public function hometaxPayables(Request $request): JsonResponse
|
||||
$endDate = $request->input('endDate', date('Y-12-31'));
|
||||
$account = $request->input('account', 'all');
|
||||
|
||||
$accountCodes = $account === 'all' ? ['20400', '20500'] : [$account];
|
||||
$accountCodes = $account === 'all' ? ['25300', '26200'] : [$account];
|
||||
|
||||
$items = HometaxInvoiceJournal::where('tenant_id', $tenantId)
|
||||
->whereIn('account_code', $accountCodes)
|
||||
@@ -465,7 +465,7 @@ public function journalPayables(Request $request): JsonResponse
|
||||
$account = $request->input('account', 'all');
|
||||
$dcType = $request->input('dcType', 'all');
|
||||
|
||||
$accountCodes = $account === 'all' ? ['20400', '20500'] : [$account];
|
||||
$accountCodes = $account === 'all' ? ['25300', '26200'] : [$account];
|
||||
|
||||
$query = JournalEntryLine::where('journal_entry_lines.tenant_id', $tenantId)
|
||||
->whereIn('journal_entry_lines.account_code', $accountCodes)
|
||||
|
||||
@@ -22,7 +22,7 @@ public function up(): void
|
||||
debit_amount, credit_amount, COUNT(*) as cnt,
|
||||
MIN(id) as keep_id, GROUP_CONCAT(id ORDER BY id) as all_ids
|
||||
FROM journal_entry_lines
|
||||
WHERE account_code IN ('20400', '20500')
|
||||
WHERE account_code IN ('25300', '26200')
|
||||
GROUP BY journal_entry_id, account_code, dc_type, trading_partner_name,
|
||||
debit_amount, credit_amount
|
||||
HAVING cnt > 1
|
||||
|
||||
@@ -716,7 +716,7 @@ className={`px-3 py-1.5 text-sm cursor-pointer ${index === highlightIndex ? 'bg-
|
||||
const splitSupply = Math.round(parseFloat(singleSplit.split_supply_amount ?? singleSplit.supplyAmount ?? singleSplit.split_amount ?? singleSplit.amount ?? 0));
|
||||
const splitTax = Math.round(parseFloat(singleSplit.split_tax ?? singleSplit.tax ?? 0));
|
||||
const splitDeductionType = singleSplit.deduction_type || singleSplit.deductionType || 'non_deductible';
|
||||
const splitAccountCode = singleSplit.account_code || singleSplit.accountCode || '82600';
|
||||
const splitAccountCode = singleSplit.account_code || singleSplit.accountCode || '83700';
|
||||
const splitAccountName = singleSplit.account_name || singleSplit.accountName || '잡비';
|
||||
|
||||
const lines = [];
|
||||
@@ -751,7 +751,7 @@ className={`px-3 py-1.5 text-sm cursor-pointer ${index === highlightIndex ? 'bg-
|
||||
|
||||
// 대변: 미지급비용 = 합계
|
||||
lines.push({
|
||||
dc_type: 'credit', account_code: '20500', account_name: '미지급비용',
|
||||
dc_type: 'credit', account_code: '26200', account_name: '미지급비용',
|
||||
debit_amount: 0, credit_amount: totalDebitSum,
|
||||
trading_partner_id: null, trading_partner_name: '', description: ''
|
||||
});
|
||||
@@ -760,19 +760,19 @@ className={`px-3 py-1.5 text-sm cursor-pointer ${index === highlightIndex ? 'bg-
|
||||
}
|
||||
|
||||
// splits가 없으면 기존 로직 (원본 금액 기반, 분리 없는 거래용)
|
||||
const expenseCode = log.accountCode || '82600';
|
||||
const expenseCode = log.accountCode || '83700';
|
||||
const expenseName = log.accountName || '잡비';
|
||||
|
||||
if (isDeductible) {
|
||||
return [
|
||||
{ dc_type: 'debit', account_code: expenseCode, account_name: expenseName, debit_amount: supplyAmount, credit_amount: 0, trading_partner_id: null, trading_partner_name: '', description: '' },
|
||||
{ dc_type: 'debit', account_code: '13500', account_name: '부가세대급금', debit_amount: taxAmount, credit_amount: 0, trading_partner_id: null, trading_partner_name: '', description: '' },
|
||||
{ dc_type: 'credit', account_code: '20500', account_name: '미지급비용', debit_amount: 0, credit_amount: totalAmount, trading_partner_id: null, trading_partner_name: '', description: '' },
|
||||
{ dc_type: 'credit', account_code: '26200', account_name: '미지급비용', debit_amount: 0, credit_amount: totalAmount, trading_partner_id: null, trading_partner_name: '', description: '' },
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
{ dc_type: 'debit', account_code: expenseCode, account_name: expenseName, debit_amount: totalAmount, credit_amount: 0, trading_partner_id: null, trading_partner_name: '', description: '' },
|
||||
{ dc_type: 'credit', account_code: '20500', account_name: '미지급비용', debit_amount: 0, credit_amount: totalAmount, trading_partner_id: null, trading_partner_name: '', description: '' },
|
||||
{ dc_type: 'credit', account_code: '26200', account_name: '미지급비용', debit_amount: 0, credit_amount: totalAmount, trading_partner_id: null, trading_partner_name: '', description: '' },
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2292,13 +2292,13 @@ className={`px-3 py-1.5 text-xs cursor-pointer ${
|
||||
return [
|
||||
{ dc_type: 'debit', account_code: '10800', account_name: '외상매출금', debit_amount: totalAmount, credit_amount: 0, description: '' },
|
||||
{ dc_type: 'credit', account_code: '40100', account_name: '상품매출', debit_amount: 0, credit_amount: supplyAmount, description: '' },
|
||||
{ dc_type: 'credit', account_code: '20800', account_name: '부가세예수금', debit_amount: 0, credit_amount: taxAmount, description: '' },
|
||||
{ dc_type: 'credit', account_code: '25500', account_name: '부가세예수금', debit_amount: 0, credit_amount: taxAmount, description: '' },
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
{ dc_type: 'debit', account_code: '50100', account_name: '상품매출원가', debit_amount: supplyAmount, credit_amount: 0, description: '' },
|
||||
{ dc_type: 'debit', account_code: '11700', account_name: '부가세대급금', debit_amount: taxAmount, credit_amount: 0, description: '' },
|
||||
{ dc_type: 'credit', account_code: '20100', account_name: '외상매입금', debit_amount: 0, credit_amount: totalAmount, description: '' },
|
||||
{ dc_type: 'debit', account_code: '45100', account_name: '상품매출원가', debit_amount: supplyAmount, credit_amount: 0, description: '' },
|
||||
{ dc_type: 'debit', account_code: '13500', account_name: '부가세대급금', debit_amount: taxAmount, credit_amount: 0, description: '' },
|
||||
{ dc_type: 'credit', account_code: '25100', account_name: '외상매입금', debit_amount: 0, credit_amount: totalAmount, description: '' },
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1968,13 +1968,13 @@ className="px-4 py-2 text-sm font-medium bg-blue-600 text-white rounded-lg hover
|
||||
return [
|
||||
{ key: 1, dc_type: 'debit', account_code: expenseCode, account_name: expenseName, trading_partner_id: null, trading_partner_name: '', debit_amount: supplyAmount, credit_amount: 0, description: '' },
|
||||
{ key: 2, dc_type: 'debit', account_code: '13500', account_name: '부가세대급금', trading_partner_id: null, trading_partner_name: '', debit_amount: taxAmount, credit_amount: 0, description: '' },
|
||||
{ key: 3, dc_type: 'credit', account_code: '25300', account_name: '미지급금', trading_partner_id: null, trading_partner_name: '', debit_amount: 0, credit_amount: amount, description: '' },
|
||||
{ key: 3, dc_type: 'credit', account_code: '30800', account_name: '미지급금', trading_partner_id: null, trading_partner_name: '', debit_amount: 0, credit_amount: amount, description: '' },
|
||||
];
|
||||
} else {
|
||||
// 불공제: 차변(비용=합계) / 대변(미지급금=합계)
|
||||
return [
|
||||
{ key: 1, dc_type: 'debit', account_code: expenseCode, account_name: expenseName, trading_partner_id: null, trading_partner_name: '', debit_amount: amount, credit_amount: 0, description: '' },
|
||||
{ key: 2, dc_type: 'credit', account_code: '25300', account_name: '미지급금', trading_partner_id: null, trading_partner_name: '', debit_amount: 0, credit_amount: amount, description: '' },
|
||||
{ key: 2, dc_type: 'credit', account_code: '30800', account_name: '미지급금', trading_partner_id: null, trading_partner_name: '', debit_amount: 0, credit_amount: amount, description: '' },
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -187,8 +187,8 @@ function IntegratedTab({ startDate, endDate, account, vendorSearch }) {
|
||||
<p className="text-sm font-medium text-gray-900">{v.vendorName || '(거래처 미지정)'}</p>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-center">
|
||||
<span className={`px-2 py-1 rounded text-xs font-medium ${v.accountCode === '20400' ? 'bg-purple-100 text-purple-700' : 'bg-indigo-100 text-indigo-700'}`}>
|
||||
{v.accountCode === '20400' ? '미지급금' : '미지급비용'}
|
||||
<span className={`px-2 py-1 rounded text-xs font-medium ${v.accountCode === '25300' ? 'bg-purple-100 text-purple-700' : 'bg-indigo-100 text-indigo-700'}`}>
|
||||
{v.accountCode === '25300' ? '미지급금' : '미지급비용'}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm font-medium text-right">
|
||||
@@ -348,7 +348,7 @@ function HometaxTab({ startDate, endDate, account }) {
|
||||
<td className="px-6 py-3 text-sm text-gray-600">{item.write_date?.substring(0, 10)}</td>
|
||||
<td className="px-6 py-3 text-sm font-medium text-gray-900">{item.trading_partner_name || '-'}</td>
|
||||
<td className="px-6 py-3 text-center">
|
||||
<span className={`px-2 py-1 rounded text-xs font-medium ${item.account_code === '20400' ? 'bg-purple-100 text-purple-700' : 'bg-indigo-100 text-indigo-700'}`}>
|
||||
<span className={`px-2 py-1 rounded text-xs font-medium ${item.account_code === '25300' ? 'bg-purple-100 text-purple-700' : 'bg-indigo-100 text-indigo-700'}`}>
|
||||
{item.account_name}
|
||||
</span>
|
||||
</td>
|
||||
@@ -446,7 +446,7 @@ className={`px-4 py-2 rounded-lg text-sm font-medium ${dcFilter === type
|
||||
<td className="px-6 py-3 text-sm text-gray-500">{item.entry_no}</td>
|
||||
<td className="px-6 py-3 text-sm font-medium text-gray-900">{item.trading_partner_name || '-'}</td>
|
||||
<td className="px-6 py-3 text-center">
|
||||
<span className={`px-2 py-1 rounded text-xs font-medium ${item.account_code === '20400' ? 'bg-purple-100 text-purple-700' : 'bg-indigo-100 text-indigo-700'}`}>
|
||||
<span className={`px-2 py-1 rounded text-xs font-medium ${item.account_code === '25300' ? 'bg-purple-100 text-purple-700' : 'bg-indigo-100 text-indigo-700'}`}>
|
||||
{item.account_name}
|
||||
</span>
|
||||
</td>
|
||||
@@ -595,9 +595,9 @@ className={`flex items-center gap-2 px-4 py-3 text-sm font-medium border-b-2 tra
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
<label className="text-sm text-gray-600 whitespace-nowrap">계정</label>
|
||||
<select value={account} onChange={(e) => setAccount(e.target.value)} className="px-3 py-2 border border-gray-300 rounded-lg text-sm">
|
||||
<option value="all">전체 (20400+20500)</option>
|
||||
<option value="20400">20400 미지급금</option>
|
||||
<option value="20500">20500 미지급비용</option>
|
||||
<option value="all">전체 (25300+26200)</option>
|
||||
<option value="25300">25300 미지급금</option>
|
||||
<option value="26200">26200 미지급비용</option>
|
||||
</select>
|
||||
</div>
|
||||
{activeTab === 'integrated' && (
|
||||
|
||||
Reference in New Issue
Block a user