Files
sam-manage/app/Http/Controllers/Finance/AccountLedgerController.php

253 lines
9.1 KiB
PHP
Raw Normal View History

<?php
namespace App\Http\Controllers\Finance;
use App\Http\Controllers\Controller;
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
{
/**
* 계정별원장 페이지
*/
public function index(Request $request)
{
if ($request->header('HX-Request')) {
return response('', 200)->header('HX-Redirect', route('finance.account-ledger'));
}
return view('finance.account-ledger');
}
/**
* 계정별원장 데이터 조회 (일반전표만)
*/
public function list(Request $request): JsonResponse
{
$tenantId = session('selected_tenant_id', 1);
$startDate = $request->input('start_date', now()->startOfMonth()->format('Y-m-d'));
$endDate = $request->input('end_date', now()->endOfMonth()->format('Y-m-d'));
$accountCode = $request->input('account_code');
if (! $accountCode) {
return response()->json([
'account' => null,
'period' => compact('startDate', 'endDate'),
'monthly_data' => [],
'grand_total' => ['debit' => 0, 'credit' => 0, 'balance' => 0],
]);
}
$account = AccountCode::withoutGlobalScopes()
->where('tenant_id', $tenantId)
->where('code', $accountCode)
->first();
if (! $account) {
return response()->json(['error' => '계정과목을 찾을 수 없습니다.'], 404);
}
// 일반전표 분개 라인만 조회 (카드/홈택스 분개는 이미 일반전표에 포함)
$allLines = DB::table('journal_entry_lines as jel')
->join('journal_entries as je', 'je.id', '=', 'jel.journal_entry_id')
->leftJoin('trading_partners as tp', function ($join) use ($tenantId) {
$join->on('tp.id', '=', 'jel.trading_partner_id')
->where('tp.tenant_id', '=', $tenantId);
})
->where('jel.tenant_id', $tenantId)
->where('jel.account_code', $accountCode)
->whereBetween('je.entry_date', [$startDate, $endDate])
->whereNull('je.deleted_at')
->select([
'je.entry_date as date',
'jel.description',
'jel.trading_partner_name',
'tp.biz_no',
'jel.debit_amount',
'jel.credit_amount',
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);
// 월별 그룹핑 + 잔액 계산
$isDebitNormal = in_array($account->category, ['asset', 'expense']);
$runningBalance = $carryForward['balance'];
$monthlyData = [];
$grandDebit = 0;
$grandCredit = 0;
foreach ($allLines as $line) {
$month = substr($line->date, 0, 7);
if (! isset($monthlyData[$month])) {
$monthlyData[$month] = [
'month' => $month,
'items' => [],
'subtotal' => ['debit' => 0, 'credit' => 0],
];
}
$debit = (int) $line->debit_amount;
$credit = (int) $line->credit_amount;
$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' => $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;
$monthlyData[$month]['subtotal']['credit'] += $credit;
$grandDebit += $debit;
$grandCredit += $credit;
}
// 누계
$cumulativeDebit = 0;
$cumulativeCredit = 0;
foreach ($monthlyData as &$md) {
$cumulativeDebit += $md['subtotal']['debit'];
$cumulativeCredit += $md['subtotal']['credit'];
$md['cumulative'] = ['debit' => $cumulativeDebit, 'credit' => $cumulativeCredit];
}
unset($md);
return response()->json([
'account' => [
'code' => $account->code,
'name' => $account->name,
'category' => $account->category,
],
'period' => ['start_date' => $startDate, 'end_date' => $endDate],
'carry_forward' => $carryForward,
'monthly_data' => array_values($monthlyData),
'grand_total' => ['debit' => $grandDebit, 'credit' => $grandCredit, 'balance' => $runningBalance],
]);
}
/**
* 카드거래 상세 일괄 조회 (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;
}
/**
* 이월잔액 계산 (일반전표만)
*/
private function calculateCarryForward(int $tenantId, string $accountCode, string $startDate, string $category): array
{
$sums = DB::table('journal_entry_lines as jel')
->join('journal_entries as je', 'je.id', '=', 'jel.journal_entry_id')
->where('jel.tenant_id', $tenantId)
->where('jel.account_code', $accountCode)
->where('je.entry_date', '<', $startDate)
->whereNull('je.deleted_at')
->selectRaw('COALESCE(SUM(jel.debit_amount), 0) as total_debit, COALESCE(SUM(jel.credit_amount), 0) as total_credit')
->first();
$debit = (int) $sums->total_debit;
$credit = (int) $sums->total_credit;
$isDebitNormal = in_array($category, ['asset', 'expense']);
$balance = $isDebitNormal ? ($debit - $credit) : ($credit - $debit);
return compact('debit', 'credit', 'balance');
}
}