feat: [ecard] 분리 데이터 기반 분개 자동 생성 기능 추가
- handleOpenJournalModal에 splits 데이터 전달 - getDefaultLines()에서 splits 기반 차변 라인 자동 생성 - 공제 항목: 비용계정 + 부가세대급금 분리, 불공제: 합산 - 분개 모달에 분리 데이터 기반 인디케이터 배지 표시 - storeJournal의 splits 자동삭제 코드 제거 (분리+분개 공존)
This commit is contained in:
@@ -1934,11 +1934,6 @@ public function storeJournal(Request $request): JsonResponse
|
||||
]);
|
||||
}
|
||||
|
||||
// 동일 uniqueKey의 구버전 splits 자동 삭제
|
||||
CardTransactionSplit::where('tenant_id', $tenantId)
|
||||
->where('original_unique_key', $request->source_key)
|
||||
->delete();
|
||||
|
||||
return $entry;
|
||||
});
|
||||
|
||||
|
||||
@@ -703,6 +703,58 @@ className={`px-3 py-1.5 text-sm cursor-pointer ${index === highlightIndex ? 'bg-
|
||||
|
||||
// 기본 분개 라인
|
||||
const getDefaultLines = () => {
|
||||
// splits 데이터가 있으면 분리 항목 기반으로 라인 생성
|
||||
const splits = log._splits || [];
|
||||
if (splits.length > 0) {
|
||||
const debitLines = [];
|
||||
let totalDebitSum = 0;
|
||||
|
||||
splits.forEach(split => {
|
||||
const splitSupply = Math.round(parseFloat(split.split_supply_amount ?? split.supplyAmount ?? split.split_amount ?? split.amount ?? 0));
|
||||
const splitTax = Math.round(parseFloat(split.split_tax ?? split.tax ?? 0));
|
||||
const splitDeductionType = split.deduction_type || split.deductionType || 'non_deductible';
|
||||
const splitAccountCode = split.account_code || split.accountCode || '826';
|
||||
const splitAccountName = split.account_name || split.accountName || '잡비';
|
||||
|
||||
if (splitDeductionType === 'deductible') {
|
||||
// 공제: 비용 계정 = 공급가액, 부가세대급금 = 세액
|
||||
debitLines.push({
|
||||
dc_type: 'debit', account_code: splitAccountCode, account_name: splitAccountName,
|
||||
debit_amount: splitSupply, credit_amount: 0,
|
||||
trading_partner_id: null, trading_partner_name: '', description: split.memo || ''
|
||||
});
|
||||
totalDebitSum += splitSupply;
|
||||
if (splitTax > 0) {
|
||||
debitLines.push({
|
||||
dc_type: 'debit', account_code: '135', account_name: '부가세대급금',
|
||||
debit_amount: splitTax, credit_amount: 0,
|
||||
trading_partner_id: null, trading_partner_name: '', description: ''
|
||||
});
|
||||
totalDebitSum += splitTax;
|
||||
}
|
||||
} else {
|
||||
// 불공제: 비용 계정 = 공급가액 + 세액
|
||||
const combined = splitSupply + splitTax;
|
||||
debitLines.push({
|
||||
dc_type: 'debit', account_code: splitAccountCode, account_name: splitAccountName,
|
||||
debit_amount: combined, credit_amount: 0,
|
||||
trading_partner_id: null, trading_partner_name: '', description: split.memo || ''
|
||||
});
|
||||
totalDebitSum += combined;
|
||||
}
|
||||
});
|
||||
|
||||
// 대변: 미지급비용 = 전체 합계
|
||||
debitLines.push({
|
||||
dc_type: 'credit', account_code: '205', account_name: '미지급비용',
|
||||
debit_amount: 0, credit_amount: totalDebitSum,
|
||||
trading_partner_id: null, trading_partner_name: '', description: ''
|
||||
});
|
||||
|
||||
return debitLines;
|
||||
}
|
||||
|
||||
// splits가 없으면 기존 로직 (원본 금액 기반)
|
||||
const expenseCode = log.accountCode || '826';
|
||||
const expenseName = log.accountName || '잡비';
|
||||
|
||||
@@ -863,6 +915,16 @@ className={`px-3 py-1.5 text-sm cursor-pointer ${index === highlightIndex ? 'bg-
|
||||
<div><span className="text-stone-500">공급가액: </span><span className="font-medium">{formatCurrency(supplyAmount)}</span></div>
|
||||
<div><span className="text-stone-500">세액: </span><span className="font-medium">{formatCurrency(taxAmount)}</span></div>
|
||||
</div>
|
||||
{(log._splits || []).length > 0 && (
|
||||
<div className="mt-3 flex items-center gap-2">
|
||||
<span className="inline-flex items-center px-2.5 py-1 bg-amber-100 text-amber-700 rounded-lg text-xs font-bold">
|
||||
분리 데이터 기반 ({log._splits.length}건)
|
||||
</span>
|
||||
{isEditMode && (
|
||||
<span className="text-xs text-stone-400">저장된 분개가 있어 참고용으로 표시됩니다</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{loadingJournal ? (
|
||||
@@ -1515,7 +1577,7 @@ className="p-1.5 text-amber-500 hover:bg-amber-100 rounded-lg transition-colors"
|
||||
if (jInfo) {
|
||||
return (
|
||||
<button
|
||||
onClick={() => onOpenJournalModal(log, uniqueKey, true)}
|
||||
onClick={() => onOpenJournalModal(log, uniqueKey, true, logSplits)}
|
||||
className="px-1.5 py-0.5 bg-emerald-100 text-emerald-700 rounded text-[10px] font-bold hover:bg-emerald-200 transition-colors"
|
||||
title={`전표: ${jInfo.entry_no}`}
|
||||
>
|
||||
@@ -1525,7 +1587,7 @@ className="px-1.5 py-0.5 bg-emerald-100 text-emerald-700 rounded text-[10px] fon
|
||||
} else {
|
||||
return (
|
||||
<button
|
||||
onClick={() => onOpenJournalModal(log, uniqueKey, false)}
|
||||
onClick={() => onOpenJournalModal(log, uniqueKey, false, logSplits)}
|
||||
className="p-1.5 text-purple-500 hover:bg-purple-100 rounded-lg transition-colors"
|
||||
title="분개 추가"
|
||||
>
|
||||
@@ -2033,12 +2095,13 @@ className="px-3 py-1 bg-green-500 text-white text-xs rounded-lg hover:bg-green-6
|
||||
};
|
||||
|
||||
// 복식부기 분개 모달 열기
|
||||
const handleOpenJournalModal = (log, uniqueKey, hasJournal) => {
|
||||
const handleOpenJournalModal = (log, uniqueKey, hasJournal, logSplits) => {
|
||||
const logWithJournalInfo = {
|
||||
...log,
|
||||
uniqueKey,
|
||||
_hasJournal: hasJournal,
|
||||
_journalData: null,
|
||||
_splits: logSplits || [],
|
||||
};
|
||||
setJournalModalLog(logWithJournalInfo);
|
||||
setJournalModalOpen(true);
|
||||
@@ -2063,7 +2126,7 @@ className="px-3 py-1 bg-green-500 text-white text-xs rounded-lg hover:bg-green-6
|
||||
setJournalModalOpen(false);
|
||||
setJournalModalLog(null);
|
||||
loadJournalStatuses();
|
||||
loadSplits(); // splits 자동 삭제 반영
|
||||
loadSplits(); // splits 상태 갱신
|
||||
} else {
|
||||
notify(data.message || '분개 저장 실패', 'error');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user