Files
sam-api/app/Services/Stats/FinanceStatService.php

173 lines
7.0 KiB
PHP
Raw Normal View History

<?php
namespace App\Services\Stats;
use App\Models\Stats\Daily\StatFinanceDaily;
use App\Models\Stats\Monthly\StatFinanceMonthly;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
class FinanceStatService implements StatDomainServiceInterface
{
public function aggregateDaily(int $tenantId, Carbon $date): int
{
$dateStr = $date->format('Y-m-d');
// 입금 (deposits)
$depositStats = DB::connection('mysql')
->table('deposits')
->where('tenant_id', $tenantId)
->where('deposit_date', $dateStr)
->whereNull('deleted_at')
->selectRaw('COUNT(*) as cnt, COALESCE(SUM(amount), 0) as total')
->first();
// 출금 (withdrawals)
$withdrawalStats = DB::connection('mysql')
->table('withdrawals')
->where('tenant_id', $tenantId)
->where('withdrawal_date', $dateStr)
->whereNull('deleted_at')
->selectRaw('COUNT(*) as cnt, COALESCE(SUM(amount), 0) as total')
->first();
// 매입 (purchases)
$purchaseStats = DB::connection('mysql')
->table('purchases')
->where('tenant_id', $tenantId)
->where('purchase_date', $dateStr)
->whereNull('deleted_at')
->selectRaw('
COUNT(*) as cnt,
COALESCE(SUM(supply_amount), 0) as supply_total,
COALESCE(SUM(tax_amount), 0) as tax_total
')
->first();
// 어음 발행 (bills - issued on this date)
$billIssuedStats = DB::connection('mysql')
->table('bills')
->where('tenant_id', $tenantId)
->where('issue_date', $dateStr)
->whereNull('deleted_at')
->selectRaw('COUNT(*) as cnt, COALESCE(SUM(amount), 0) as total')
->first();
// 어음 만기 (bills - matured on this date)
$billMaturedStats = DB::connection('mysql')
->table('bills')
->where('tenant_id', $tenantId)
->where('maturity_date', $dateStr)
->whereNull('deleted_at')
->selectRaw('COUNT(*) as cnt, COALESCE(SUM(amount), 0) as total')
->first();
// 카드 거래 (withdrawals with card_id)
$cardStats = DB::connection('mysql')
->table('withdrawals')
->where('tenant_id', $tenantId)
->where('withdrawal_date', $dateStr)
->whereNotNull('card_id')
->whereNull('deleted_at')
->selectRaw('COUNT(*) as cnt, COALESCE(SUM(amount), 0) as total')
->first();
// 은행 잔액 합계 (bank_transactions - 계좌별 최신 잔액)
$bankBalance = DB::connection('mysql')
->query()
->fromSub(function ($query) use ($tenantId, $dateStr) {
$query->from('bank_transactions')
->where('tenant_id', $tenantId)
->where('transaction_date', '<=', $dateStr)
->whereNull('deleted_at')
->selectRaw('bank_account_id, balance_after as latest_balance,
ROW_NUMBER() OVER(PARTITION BY bank_account_id ORDER BY transaction_date DESC, id DESC) as rn');
}, 'sub')
->where('rn', 1)
->selectRaw('COALESCE(SUM(latest_balance), 0) as total_balance')
->first();
$depositAmount = $depositStats->total ?? 0;
$withdrawalAmount = $withdrawalStats->total ?? 0;
StatFinanceDaily::updateOrCreate(
['tenant_id' => $tenantId, 'stat_date' => $dateStr],
[
'deposit_count' => $depositStats->cnt ?? 0,
'deposit_amount' => $depositAmount,
'withdrawal_count' => $withdrawalStats->cnt ?? 0,
'withdrawal_amount' => $withdrawalAmount,
'net_cashflow' => $depositAmount - $withdrawalAmount,
'purchase_count' => $purchaseStats->cnt ?? 0,
'purchase_amount' => $purchaseStats->supply_total ?? 0,
'purchase_tax_amount' => $purchaseStats->tax_total ?? 0,
'receivable_balance' => 0, // Phase 3에서 미수금 로직 추가
'payable_balance' => 0,
'overdue_receivable' => 0,
'bill_issued_count' => $billIssuedStats->cnt ?? 0,
'bill_issued_amount' => $billIssuedStats->total ?? 0,
'bill_matured_count' => $billMaturedStats->cnt ?? 0,
'bill_matured_amount' => $billMaturedStats->total ?? 0,
'card_transaction_count' => $cardStats->cnt ?? 0,
'card_transaction_amount' => $cardStats->total ?? 0,
'bank_balance_total' => $bankBalance->total_balance ?? 0,
]
);
return 1;
}
public function aggregateMonthly(int $tenantId, int $year, int $month): int
{
$dailySum = StatFinanceDaily::where('tenant_id', $tenantId)
->whereYear('stat_date', $year)
->whereMonth('stat_date', $month)
->selectRaw('
SUM(deposit_amount) as deposit_total,
SUM(withdrawal_amount) as withdrawal_total,
SUM(net_cashflow) as net_cashflow,
SUM(purchase_amount) as purchase_total,
SUM(card_transaction_amount) as card_total
')
->first();
// 월말 데이터 (해당 월의 마지막 일간 레코드)
$lastDay = StatFinanceDaily::where('tenant_id', $tenantId)
->whereYear('stat_date', $year)
->whereMonth('stat_date', $month)
->orderByDesc('stat_date')
->first();
// 전월 대비 현금흐름 변화
$prevMonth = StatFinanceMonthly::where('tenant_id', $tenantId)
->where(function ($q) use ($year, $month) {
$prev = Carbon::create($year, $month, 1)->subMonth();
$q->where('stat_year', $prev->year)->where('stat_month', $prev->month);
})
->first();
$netCashflow = $dailySum->net_cashflow ?? 0;
$momChange = null;
if ($prevMonth && $prevMonth->net_cashflow != 0) {
$momChange = (($netCashflow - $prevMonth->net_cashflow) / abs($prevMonth->net_cashflow)) * 100;
}
StatFinanceMonthly::updateOrCreate(
['tenant_id' => $tenantId, 'stat_year' => $year, 'stat_month' => $month],
[
'deposit_total' => $dailySum->deposit_total ?? 0,
'withdrawal_total' => $dailySum->withdrawal_total ?? 0,
'net_cashflow' => $netCashflow,
'purchase_total' => $dailySum->purchase_total ?? 0,
'card_total' => $dailySum->card_total ?? 0,
'receivable_end' => $lastDay->receivable_balance ?? 0,
'payable_end' => $lastDay->payable_balance ?? 0,
'bank_balance_end' => $lastDay->bank_balance_total ?? 0,
'mom_cashflow_change' => $momChange,
]
);
return 1;
}
}