diff --git a/app/Http/Controllers/Finance/AccountLedgerController.php b/app/Http/Controllers/Finance/AccountLedgerController.php index a288ea80..70582295 100644 --- a/app/Http/Controllers/Finance/AccountLedgerController.php +++ b/app/Http/Controllers/Finance/AccountLedgerController.php @@ -6,6 +6,7 @@ use App\Models\Barobill\AccountCode; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; class AccountLedgerController extends Controller @@ -68,12 +69,16 @@ public function list(Request $request): JsonResponse 'tp.biz_no', 'jel.debit_amount', 'jel.credit_amount', - DB::raw("'journal' as source_type"), + DB::raw("COALESCE(je.source_type, 'journal') as source_type"), 'jel.journal_entry_id as source_id', + 'je.source_key', ]) ->orderBy('je.entry_date') ->get(); + // 카드거래 상세 조회 + $cardTxMap = $this->fetchCardTransactions($tenantId, $allLines); + // 이월잔액 (일반전표만) $carryForward = $this->calculateCarryForward($tenantId, $accountCode, $startDate, $account->category); @@ -100,16 +105,22 @@ public function list(Request $request): JsonResponse $runningBalance += $isDebitNormal ? ($debit - $credit) : ($credit - $debit); + $cardTx = null; + if ($line->source_type === 'ecard_transaction' && $line->source_key) { + $cardTx = $cardTxMap[$line->source_key] ?? null; + } + $monthlyData[$month]['items'][] = [ 'date' => $line->date, 'description' => $line->description, - 'trading_partner_name' => $line->trading_partner_name, - 'biz_no' => $line->biz_no, + 'trading_partner_name' => $cardTx ? ($cardTx['merchant_name'] ?: $line->trading_partner_name) : $line->trading_partner_name, + 'biz_no' => $cardTx ? ($cardTx['merchant_biz_num'] ?: $line->biz_no) : $line->biz_no, 'debit_amount' => $debit, 'credit_amount' => $credit, 'balance' => $runningBalance, 'source_type' => $line->source_type, 'source_id' => (int) $line->source_id, + 'card_tx' => $cardTx, ]; $monthlyData[$month]['subtotal']['debit'] += $debit; @@ -141,6 +152,82 @@ public function list(Request $request): JsonResponse ]); } + /** + * 카드거래 상세 일괄 조회 (source_key 기반) + */ + private function fetchCardTransactions(int $tenantId, Collection $lines): array + { + $sourceKeys = $lines + ->filter(fn ($l) => $l->source_type === 'ecard_transaction' && $l->source_key) + ->pluck('source_key') + ->unique() + ->values() + ->all(); + + if (empty($sourceKeys)) { + return []; + } + + // source_key = "card_num|use_dt|approval_num|approval_amount" + $conditions = []; + foreach ($sourceKeys as $key) { + $parts = explode('|', $key); + if (count($parts) === 4) { + $conditions[] = $parts; + } + } + + if (empty($conditions)) { + return []; + } + + $query = DB::table('barobill_card_transactions') + ->where('tenant_id', $tenantId); + + $query->where(function ($q) use ($conditions) { + foreach ($conditions as $c) { + $q->orWhere(function ($sub) use ($c) { + $sub->where('card_num', $c[0]) + ->where('use_dt', $c[1]) + ->where('approval_num', $c[2]) + ->whereRaw('CAST(approval_amount AS SIGNED) = ?', [(int) $c[3]]); + }); + } + }); + + $txs = $query->get(); + + $map = []; + foreach ($txs as $tx) { + $uniqueKey = implode('|', [ + $tx->card_num, + $tx->use_dt, + $tx->approval_num, + (int) $tx->approval_amount, + ]); + + $supplyAmount = $tx->modified_supply_amount !== null + ? (int) $tx->modified_supply_amount + : (int) $tx->approval_amount - (int) $tx->tax; + $taxAmount = $tx->modified_tax !== null + ? (int) $tx->modified_tax + : (int) $tx->tax; + + $map[$uniqueKey] = [ + 'card_num' => $tx->card_num, + 'card_company_name' => $tx->card_company_name, + 'merchant_name' => $tx->merchant_name, + 'merchant_biz_num' => $tx->merchant_biz_num, + 'deduction_type' => $tx->deduction_type, + 'supply_amount' => $supplyAmount, + 'tax_amount' => $taxAmount, + 'approval_amount' => (int) $tx->approval_amount, + ]; + } + + return $map; + } + /** * 이월잔액 계산 (일반전표만) */ diff --git a/resources/views/finance/account-ledger.blade.php b/resources/views/finance/account-ledger.blade.php index fb445135..52ce34b9 100644 --- a/resources/views/finance/account-ledger.blade.php +++ b/resources/views/finance/account-ledger.blade.php @@ -42,6 +42,7 @@ const Printer = createIcon('printer'); const X = createIcon('x'); const ExternalLink = createIcon('external-link'); +const CreditCard = createIcon('credit-card'); // 숫자 포맷 const fmt = (n) => { @@ -122,6 +123,27 @@ function DetailModal({ detail, onClose }) { )} + {/* 카드거래 정보 */} + {detail.cardTx && ( +
+
+ + 카드거래 정보 + + {detail.cardTx.deduction_type === 'non_deductible' ? '불공제' : '공제'} + +
+
+
카드번호{'····' + detail.cardTx.card_num.slice(-4)}
+
카드사{detail.cardTx.card_company_name}
+
가맹점{detail.cardTx.merchant_name}
+
사업자번호{detail.cardTx.merchant_biz_num || '-'}
+
공급가액{fmt(detail.cardTx.supply_amount)}
+
세액{fmt(detail.cardTx.tax_amount)}
+
+
+ )} + {/* 분개 라인 테이블 */} @@ -313,12 +335,12 @@ function AccountLedger() { // 전표 드릴다운 (모달) const drillDown = (item) => { - if (item.source_type === 'journal') { + if (item.source_type === 'journal' || item.source_type === 'ecard_transaction' || item.source_type === 'bank_transaction') { fetch('/finance/journal-entries/' + item.source_id) .then(r => r.json()) .then(res => { if (res.success && res.data) { - setDetail({ type: 'journal', data: res.data, sourceId: item.source_id }); + setDetail({ type: 'journal', data: res.data, sourceId: item.source_id, cardTx: item.card_tx || null }); } }) .catch(() => {}); @@ -446,9 +468,22 @@ className="flex items-center gap-1 px-4 py-1.5 bg-indigo-600 text-white text-sm drillDown(item)}> diff --git a/resources/views/finance/journal-entries.blade.php b/resources/views/finance/journal-entries.blade.php index c6e724c1..e986a554 100644 --- a/resources/views/finance/journal-entries.blade.php +++ b/resources/views/finance/journal-entries.blade.php @@ -1395,12 +1395,23 @@ className={`px-2.5 py-1 text-xs rounded-full font-medium transition-colors ${vie )} + {row.type === 'card' && row.cardTx && ( +
+ {row.cardTx.cardCompanyName} {'····' + row.cardTx.cardNum.slice(-4)} + {row.cardTx.merchantBizNum && {row.cardTx.merchantBizNum}} +
+ )}
{item.date} - {item.description} - {item.source_type === 'hometax' && ( - [홈택스] +
+ {item.card_tx && } + {item.description} + {item.source_type === 'hometax' && ( + [홈택스] + )} + {item.card_tx && ( + + {item.card_tx.deduction_type === 'non_deductible' ? '불공제' : '공제'} + + )} +
+ {item.card_tx && ( +
+ {item.card_tx.card_company_name} {'····' + item.card_tx.card_num.slice(-4)} +
)}
{item.trading_partner_name} {row.deposit > 0 ? formatCurrency(row.deposit) : ''} {row.withdraw > 0 ? formatCurrency(row.withdraw) : ''} + {row.type === 'card' && row.cardTx && row.withdraw > 0 && ( +
+ 공급 {formatCurrency(row.cardTx.supplyAmount)} / 세액 {formatCurrency(row.cardTx.taxAmount)} +
+ )}
{row.balance !== null ? formatCurrency(row.balance) : ''}