tenantId(); $startDate = $params['start_date'] ?? null; $endDate = $params['end_date'] ?? null; $search = $params['search'] ?? null; $perPage = $params['per_page'] ?? 20; // 거래처 목록 조회 $query = Client::query() ->where('tenant_id', $tenantId) ->where('is_active', true); // 거래처명 검색 if (! empty($search)) { $query->where('name', 'like', "%{$search}%"); } // 거래처 ID 목록 $clientsQuery = clone $query; $clients = $clientsQuery->select('id', 'name', 'sales_payment_day')->get(); // 매출 집계 서브쿼리 $salesSubquery = Sale::query() ->select('client_id', DB::raw('SUM(total_amount) as total_sales')) ->where('tenant_id', $tenantId) ->when($startDate, fn ($q) => $q->where('sale_date', '>=', $startDate)) ->when($endDate, fn ($q) => $q->where('sale_date', '<=', $endDate)) ->groupBy('client_id'); // 수금 집계 서브쿼리 $depositsSubquery = Deposit::query() ->select('client_id', DB::raw('SUM(amount) as total_collection')) ->where('tenant_id', $tenantId) ->when($startDate, fn ($q) => $q->where('deposit_date', '>=', $startDate)) ->when($endDate, fn ($q) => $q->where('deposit_date', '<=', $endDate)) ->groupBy('client_id'); // 이월잔액 서브쿼리 (기간 시작 전 매출 - 수금) $carryoverSalesSubquery = null; $carryoverDepositsSubquery = null; if ($startDate) { $carryoverSalesSubquery = Sale::query() ->select('client_id', DB::raw('COALESCE(SUM(total_amount), 0) as carryover_sales')) ->where('tenant_id', $tenantId) ->where('sale_date', '<', $startDate) ->groupBy('client_id'); $carryoverDepositsSubquery = Deposit::query() ->select('client_id', DB::raw('COALESCE(SUM(amount), 0) as carryover_deposits')) ->where('tenant_id', $tenantId) ->where('deposit_date', '<', $startDate) ->groupBy('client_id'); } // 메인 쿼리 $mainQuery = Client::query() ->select([ 'clients.id', 'clients.name', 'clients.sales_payment_day', DB::raw('COALESCE(sales_agg.total_sales, 0) as period_sales'), DB::raw('COALESCE(deposits_agg.total_collection, 0) as period_collection'), ]) ->where('clients.tenant_id', $tenantId) ->where('clients.is_active', true) ->leftJoinSub($salesSubquery, 'sales_agg', 'clients.id', '=', 'sales_agg.client_id') ->leftJoinSub($depositsSubquery, 'deposits_agg', 'clients.id', '=', 'deposits_agg.client_id'); if ($carryoverSalesSubquery && $carryoverDepositsSubquery) { $mainQuery->addSelect([ DB::raw('COALESCE(carryover_sales_agg.carryover_sales, 0) as carryover_sales'), DB::raw('COALESCE(carryover_deposits_agg.carryover_deposits, 0) as carryover_deposits'), ]) ->leftJoinSub($carryoverSalesSubquery, 'carryover_sales_agg', 'clients.id', '=', 'carryover_sales_agg.client_id') ->leftJoinSub($carryoverDepositsSubquery, 'carryover_deposits_agg', 'clients.id', '=', 'carryover_deposits_agg.client_id'); } else { $mainQuery->addSelect([ DB::raw('0 as carryover_sales'), DB::raw('0 as carryover_deposits'), ]); } // 검색 if (! empty($search)) { $mainQuery->where('clients.name', 'like', "%{$search}%"); } // 정렬 $sortBy = $params['sort_by'] ?? 'name'; $sortDir = $params['sort_dir'] ?? 'asc'; $mainQuery->orderBy("clients.{$sortBy}", $sortDir); return $mainQuery->paginate($perPage); } /** * 거래처원장 요약 통계 */ public function summary(array $params): array { $tenantId = $this->tenantId(); $startDate = $params['start_date'] ?? null; $endDate = $params['end_date'] ?? null; // 기간 내 매출 합계 $totalSales = Sale::query() ->where('tenant_id', $tenantId) ->when($startDate, fn ($q) => $q->where('sale_date', '>=', $startDate)) ->when($endDate, fn ($q) => $q->where('sale_date', '<=', $endDate)) ->sum('total_amount'); // 기간 내 수금 합계 $totalCollection = Deposit::query() ->where('tenant_id', $tenantId) ->when($startDate, fn ($q) => $q->where('deposit_date', '>=', $startDate)) ->when($endDate, fn ($q) => $q->where('deposit_date', '<=', $endDate)) ->sum('amount'); // 이월잔액 (기간 시작 전 매출 - 수금) $carryoverBalance = 0; if ($startDate) { $carryoverSales = Sale::query() ->where('tenant_id', $tenantId) ->where('sale_date', '<', $startDate) ->sum('total_amount'); $carryoverDeposits = Deposit::query() ->where('tenant_id', $tenantId) ->where('deposit_date', '<', $startDate) ->sum('amount'); $carryoverBalance = (float) $carryoverSales - (float) $carryoverDeposits; } // 현재 잔액 = 이월잔액 + 매출 - 수금 $balance = $carryoverBalance + (float) $totalSales - (float) $totalCollection; return [ 'carryover_balance' => $carryoverBalance, 'total_sales' => (float) $totalSales, 'total_collection' => (float) $totalCollection, 'balance' => $balance, ]; } /** * 거래처원장 상세 (거래처별 거래 내역) */ public function show(int $clientId, array $params): array { $tenantId = $this->tenantId(); $startDate = $params['start_date'] ?? null; $endDate = $params['end_date'] ?? null; // 거래처 정보 $client = Client::query() ->where('tenant_id', $tenantId) ->findOrFail($clientId); // 이월잔액 계산 $carryoverBalance = 0; if ($startDate) { $carryoverSales = Sale::query() ->where('tenant_id', $tenantId) ->where('client_id', $clientId) ->where('sale_date', '<', $startDate) ->sum('total_amount'); $carryoverDeposits = Deposit::query() ->where('tenant_id', $tenantId) ->where('client_id', $clientId) ->where('deposit_date', '<', $startDate) ->sum('amount'); $carryoverBalance = (float) $carryoverSales - (float) $carryoverDeposits; } // 거래 내역 조회 (매출 + 수금) $transactions = collect(); // 매출 내역 $sales = Sale::query() ->where('tenant_id', $tenantId) ->where('client_id', $clientId) ->when($startDate, fn ($q) => $q->where('sale_date', '>=', $startDate)) ->when($endDate, fn ($q) => $q->where('sale_date', '<=', $endDate)) ->orderBy('sale_date') ->get() ->map(fn ($sale) => [ 'id' => $sale->id, 'date' => $sale->sale_date->format('Y-m-d'), 'type' => 'sales', 'description' => $sale->description ?? '매출', 'sales_amount' => (float) $sale->total_amount, 'collection_amount' => 0, 'reference_id' => $sale->id, 'reference_type' => 'sale', 'has_action' => false, 'is_highlighted' => ! $sale->tax_invoice_issued, ]); // 수금 내역 $deposits = Deposit::query() ->where('tenant_id', $tenantId) ->where('client_id', $clientId) ->when($startDate, fn ($q) => $q->where('deposit_date', '>=', $startDate)) ->when($endDate, fn ($q) => $q->where('deposit_date', '<=', $endDate)) ->orderBy('deposit_date') ->get() ->map(fn ($deposit) => [ 'id' => $deposit->id, 'date' => $deposit->deposit_date->format('Y-m-d'), 'type' => 'collection', 'description' => $deposit->description ?? '입금', 'sales_amount' => 0, 'collection_amount' => (float) $deposit->amount, 'reference_id' => $deposit->id, 'reference_type' => 'deposit', 'has_action' => false, 'is_highlighted' => false, 'is_parenthesis' => $deposit->payment_method === 'check', ]); // 어음 내역 $bills = Bill::query() ->where('tenant_id', $tenantId) ->where('client_id', $clientId) ->when($startDate, fn ($q) => $q->where('issue_date', '>=', $startDate)) ->when($endDate, fn ($q) => $q->where('issue_date', '<=', $endDate)) ->orderBy('issue_date') ->get() ->map(fn ($bill) => [ 'id' => $bill->id, 'date' => $bill->issue_date, 'type' => 'note', 'description' => "수취 어음 (만기 {$bill->maturity_date})", 'sales_amount' => 0, 'collection_amount' => (float) $bill->amount, 'reference_id' => $bill->id, 'reference_type' => 'bill', 'has_action' => true, 'is_highlighted' => false, 'is_parenthesis' => true, 'note_info' => $bill->maturity_date, ]); // 전체 거래 내역 합치기 및 정렬 $allTransactions = $sales->merge($deposits)->merge($bills) ->sortBy('date') ->values(); // 잔액 계산 $runningBalance = $carryoverBalance; $transactions = $allTransactions->map(function ($item) use (&$runningBalance) { $runningBalance = $runningBalance + $item['sales_amount'] - $item['collection_amount']; $item['balance'] = $runningBalance; return $item; }); // 합계 계산 $totalSales = $transactions->sum('sales_amount'); $totalCollection = $transactions->sum('collection_amount'); return [ 'client' => [ 'id' => $client->id, 'name' => $client->name, 'business_number' => $client->business_no, 'representative_name' => $client->contact_person, 'phone' => $client->phone, 'mobile' => $client->mobile, 'fax' => $client->fax, 'email' => $client->email, 'address' => $client->address, ], 'period' => [ 'start_date' => $startDate, 'end_date' => $endDate, ], 'carryover_balance' => $carryoverBalance, 'total_sales' => $totalSales, 'total_collection' => $totalCollection, 'balance' => $runningBalance, 'transactions' => $transactions->toArray(), ]; } }