feat: [finance] 계정별원장 API 서비스 보강 (카드거래 상세, 분리전표 필터링)
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class AccountLedgerService extends Service
|
||||
@@ -49,12 +50,19 @@ public function index(array $params): array
|
||||
'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();
|
||||
|
||||
// 분리 전표(split) 유효성 필터링
|
||||
$allLines = $this->filterSplitLines($tenantId, $allLines);
|
||||
|
||||
// 카드거래 상세 조회
|
||||
$cardTxMap = $this->fetchCardTransactions($tenantId, $allLines);
|
||||
|
||||
$carryForward = $this->calculateCarryForward($tenantId, $accountCode, $startDate, $account->category);
|
||||
|
||||
$isDebitNormal = in_array($account->category, ['asset', 'expense']);
|
||||
@@ -79,16 +87,22 @@ public function index(array $params): array
|
||||
|
||||
$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;
|
||||
@@ -115,6 +129,140 @@ public function index(array $params): array
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 분리 전표(split) 유효성 필터링
|
||||
* — 삭제된 split은 제거, split이 존재하는 원본 전표도 제거
|
||||
*/
|
||||
private function filterSplitLines(int $tenantId, Collection $lines): Collection
|
||||
{
|
||||
$ecardSplitLines = $lines->filter(
|
||||
fn ($l) => $l->source_type === 'ecard_transaction' && $l->source_key && str_contains($l->source_key, '|split:')
|
||||
);
|
||||
|
||||
if ($ecardSplitLines->isEmpty()) {
|
||||
return $lines;
|
||||
}
|
||||
|
||||
$splitBaseKeys = $ecardSplitLines
|
||||
->map(fn ($l) => explode('|split:', $l->source_key)[0])
|
||||
->unique()
|
||||
->all();
|
||||
|
||||
$validSplitIds = DB::table('barobill_card_transaction_splits')
|
||||
->where('tenant_id', $tenantId)
|
||||
->whereIn('original_unique_key', $splitBaseKeys)
|
||||
->pluck('id')
|
||||
->all();
|
||||
|
||||
$validSplitSuffixes = array_map(fn ($id) => '|split:'.$id, $validSplitIds);
|
||||
|
||||
return $lines->filter(function ($l) use ($splitBaseKeys, $validSplitSuffixes) {
|
||||
if ($l->source_type !== 'ecard_transaction' || ! $l->source_key) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (str_contains($l->source_key, '|split:')) {
|
||||
foreach ($validSplitSuffixes as $suffix) {
|
||||
if (str_ends_with($l->source_key, $suffix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 원본(non-split) 전표: 분리 전표가 존재하면 제외
|
||||
return ! in_array($l->source_key, $splitBaseKeys);
|
||||
})->values();
|
||||
}
|
||||
|
||||
/**
|
||||
* 카드거래 상세 일괄 조회 (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" 또는 "...|split:N"
|
||||
$conditions = [];
|
||||
$keyMap = [];
|
||||
foreach ($sourceKeys as $key) {
|
||||
$baseKey = explode('|split:', $key)[0];
|
||||
$parts = explode('|', $baseKey);
|
||||
if (count($parts) === 4) {
|
||||
$conditions[$baseKey] = $parts;
|
||||
$keyMap[$key] = $baseKey;
|
||||
}
|
||||
}
|
||||
$conditions = array_values($conditions);
|
||||
|
||||
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,
|
||||
];
|
||||
}
|
||||
|
||||
// 분리 키(uniqueKey|split:N)도 원본 카드 데이터로 매핑
|
||||
foreach ($keyMap as $sourceKey => $baseKey) {
|
||||
if ($sourceKey !== $baseKey && isset($map[$baseKey])) {
|
||||
$map[$sourceKey] = $map[$baseKey];
|
||||
}
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
private function calculateCarryForward(int $tenantId, string $accountCode, string $startDate, string $category): array
|
||||
{
|
||||
$sums = DB::table('journal_entry_lines as jel')
|
||||
|
||||
Reference in New Issue
Block a user