From 140894ef9c8a528f08bc0a2b179712bf65e59662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Thu, 19 Mar 2026 22:27:39 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20[finance]=20=EB=B6=84=EB=A6=AC=20?= =?UTF-8?q?=EC=B9=B4=EB=93=9C=EA=B1=B0=EB=9E=98=20=EB=B6=84=EA=B0=9C=20?= =?UTF-8?q?=EB=A7=A4=EC=B9=AD=20=EB=88=84=EB=9D=BD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 분리 거래 source_key(uniqueKey|split:N) 패턴 매칭 추가 - 일반전표입력: 복수 분리 분개를 합산하여 1행으로 표시 - 계정별원장: 분리 키도 원본 카드 데이터로 매핑 --- .../Finance/AccountLedgerController.php | 20 ++++-- .../Finance/JournalEntryController.php | 63 +++++++++++++++++-- .../views/finance/journal-entries.blade.php | 30 +++++++-- 3 files changed, 98 insertions(+), 15 deletions(-) diff --git a/app/Http/Controllers/Finance/AccountLedgerController.php b/app/Http/Controllers/Finance/AccountLedgerController.php index 70582295..bbbd0595 100644 --- a/app/Http/Controllers/Finance/AccountLedgerController.php +++ b/app/Http/Controllers/Finance/AccountLedgerController.php @@ -168,14 +168,18 @@ private function fetchCardTransactions(int $tenantId, Collection $lines): array return []; } - // source_key = "card_num|use_dt|approval_num|approval_amount" + // source_key = "card_num|use_dt|approval_num|approval_amount" 또는 "...|split:N" $conditions = []; + $keyMap = []; // 원본 source_key → 파싱된 4-part key 매핑 (분리 키 포함) foreach ($sourceKeys as $key) { - $parts = explode('|', $key); + $baseKey = explode('|split:', $key)[0]; + $parts = explode('|', $baseKey); if (count($parts) === 4) { - $conditions[] = $parts; + $conditions[$baseKey] = $parts; + $keyMap[$key] = $baseKey; } } + $conditions = array_values($conditions); if (empty($conditions)) { return []; @@ -213,7 +217,7 @@ private function fetchCardTransactions(int $tenantId, Collection $lines): array ? (int) $tx->modified_tax : (int) $tx->tax; - $map[$uniqueKey] = [ + $txData = [ 'card_num' => $tx->card_num, 'card_company_name' => $tx->card_company_name, 'merchant_name' => $tx->merchant_name, @@ -223,6 +227,14 @@ private function fetchCardTransactions(int $tenantId, Collection $lines): array 'tax_amount' => $taxAmount, 'approval_amount' => (int) $tx->approval_amount, ]; + $map[$uniqueKey] = $txData; + } + + // 분리 키(uniqueKey|split:N)도 원본 카드 데이터로 매핑 + foreach ($keyMap as $sourceKey => $baseKey) { + if ($sourceKey !== $baseKey && isset($map[$baseKey])) { + $map[$sourceKey] = $map[$baseKey]; + } } return $map; diff --git a/app/Http/Controllers/Finance/JournalEntryController.php b/app/Http/Controllers/Finance/JournalEntryController.php index be09a659..ad1cac97 100644 --- a/app/Http/Controllers/Finance/JournalEntryController.php +++ b/app/Http/Controllers/Finance/JournalEntryController.php @@ -8,6 +8,7 @@ use App\Models\Barobill\BankTransactionOverride; use App\Models\Barobill\CardTransaction; use App\Models\Barobill\CardTransactionHide; +use App\Models\Barobill\CardTransactionSplit; use App\Models\Finance\JournalEntry; use App\Models\Finance\JournalEntryLine; use App\Models\Finance\TradingPartner; @@ -1007,11 +1008,11 @@ public function cardTransactions(Request $request): JsonResponse // 각 거래의 uniqueKey 수집 $uniqueKeys = array_column($logs, 'uniqueKey'); - // 분개 완료된 source_key 조회 + // 분개 완료된 source_key 조회 (직접 매칭) $journaledKeys = JournalEntry::getJournaledSourceKeys($tenantId, 'ecard_transaction', $uniqueKeys); $journaledKeysMap = array_flip($journaledKeys); - // 분개된 전표 ID 조회 + // 분개된 전표 ID 조회 (직접 매칭) $journalMap = []; if (! empty($journaledKeys)) { $journals = JournalEntry::where('tenant_id', $tenantId) @@ -1024,12 +1025,63 @@ public function cardTransactions(Request $request): JsonResponse } } + // 분리 거래의 분개 상태 조회 (source_key LIKE 'uniqueKey|split:%') + $splitJournalMap = []; // originalKey => [['id' => ..., 'entry_no' => ...], ...] + if (! empty($uniqueKeys)) { + $splitJournals = JournalEntry::where('tenant_id', $tenantId) + ->where('source_type', 'ecard_transaction') + ->where(function ($q) use ($uniqueKeys) { + foreach ($uniqueKeys as $key) { + $q->orWhere('source_key', 'LIKE', $key.'|split:%'); + } + }) + ->select('id', 'source_key', 'entry_no') + ->get(); + + foreach ($splitJournals as $j) { + $originalKey = explode('|split:', $j->source_key)[0]; + $splitJournalMap[$originalKey][] = ['id' => $j->id, 'entry_no' => $j->entry_no]; + } + } + + // 분리 건수 조회 (전체 분개 완료 여부 판단용) + $splitCounts = []; + if (! empty($uniqueKeys)) { + $counts = CardTransactionSplit::where('tenant_id', $tenantId) + ->whereIn('original_unique_key', $uniqueKeys) + ->selectRaw('original_unique_key, COUNT(*) as cnt') + ->groupBy('original_unique_key') + ->pluck('cnt', 'original_unique_key'); + $splitCounts = $counts->toArray(); + } + // 각 거래에 분개 상태 추가 + $journaledCount = 0; foreach ($logs as &$log) { $key = $log['uniqueKey'] ?? ''; - $log['hasJournal'] = isset($journaledKeysMap[$key]); - $log['journalId'] = $journalMap[$key]['id'] ?? null; - $log['journalEntryNo'] = $journalMap[$key]['entry_no'] ?? null; + $directMatch = isset($journaledKeysMap[$key]); + $splitCount = $splitCounts[$key] ?? 0; + $splitJournals = $splitJournalMap[$key] ?? []; + $allSplitsJournaled = $splitCount > 0 && count($splitJournals) >= $splitCount; + + $log['hasJournal'] = $directMatch || $allSplitsJournaled; + $log['splitJournalIds'] = []; + + if ($directMatch) { + $log['journalId'] = $journalMap[$key]['id'] ?? null; + $log['journalEntryNo'] = $journalMap[$key]['entry_no'] ?? null; + } elseif (! empty($splitJournals)) { + $log['journalId'] = $splitJournals[0]['id']; + $log['journalEntryNo'] = $splitJournals[0]['entry_no']; + $log['splitJournalIds'] = array_column($splitJournals, 'id'); + } else { + $log['journalId'] = null; + $log['journalEntryNo'] = null; + } + + if ($log['hasJournal']) { + $journaledCount++; + } } unset($log); @@ -1045,7 +1097,6 @@ public function cardTransactions(Request $request): JsonResponse $deductibleSum += $log['approvalAmount']; } } - $journaledCount = count($journaledKeys); // 카드 목록 (드롭다운용) $cards = CardTransaction::where('tenant_id', $tenantId) diff --git a/resources/views/finance/journal-entries.blade.php b/resources/views/finance/journal-entries.blade.php index e986a554..ee207bc4 100644 --- a/resources/views/finance/journal-entries.blade.php +++ b/resources/views/finance/journal-entries.blade.php @@ -1072,8 +1072,28 @@ className="px-2.5 py-1 text-xs font-medium bg-amber-100 text-amber-700 rounded-f // 2) 카드거래 행 cardTransactionsList.forEach(ctx => { - const je = ctx.journalId ? journalMap[ctx.journalId] : null; - if (ctx.journalId) linkedJournalIds.add(ctx.journalId); + let je = null; + let combinedLines = []; + let totalDebit = 0; + let totalCredit = 0; + + // 분리 분개: 복수 journal을 합산 + if (ctx.splitJournalIds && ctx.splitJournalIds.length > 0) { + ctx.splitJournalIds.forEach(id => { + linkedJournalIds.add(id); + const sje = journalMap[id]; + if (sje) { + combinedLines = combinedLines.concat(sje.lines || []); + totalDebit += sje.total_debit || 0; + totalCredit += sje.total_credit || 0; + } + }); + je = journalMap[ctx.splitJournalIds[0]]; + } else { + je = ctx.journalId ? journalMap[ctx.journalId] : null; + if (ctx.journalId) linkedJournalIds.add(ctx.journalId); + } + rows.push({ type: 'card', key: `card-${ctx.uniqueKey}`, @@ -1086,9 +1106,9 @@ className="px-2.5 py-1 text-xs font-medium bg-amber-100 text-amber-700 rounded-f hasJournal: ctx.hasJournal, journalId: ctx.journalId, entryNo: ctx.journalEntryNo || (je && je.entry_no) || null, - lines: je ? je.lines : [], - totalDebit: je ? je.total_debit : 0, - totalCredit: je ? je.total_credit : 0, + lines: combinedLines.length > 0 ? combinedLines : (je ? je.lines : []), + totalDebit: combinedLines.length > 0 ? totalDebit : (je ? je.total_debit : 0), + totalCredit: combinedLines.length > 0 ? totalCredit : (je ? je.total_credit : 0), bankTx: null, cardTx: ctx, sortKey: ctx.useDate + (ctx.useTime || '000000'),