diff --git a/app/Http/Controllers/Finance/PayableController.php b/app/Http/Controllers/Finance/PayableController.php index c7d6fa3b..ac979710 100644 --- a/app/Http/Controllers/Finance/PayableController.php +++ b/app/Http/Controllers/Finance/PayableController.php @@ -3,9 +3,12 @@ namespace App\Http\Controllers\Finance; use App\Http\Controllers\Controller; +use App\Models\Barobill\HometaxInvoiceJournal; +use App\Models\Finance\JournalEntryLine; use App\Models\Finance\Payable; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Support\Facades\DB; class PayableController extends Controller { @@ -180,4 +183,252 @@ public function destroy(int $id): JsonResponse 'message' => '미지급금이 삭제되었습니다.', ]); } + + /** + * 통합 잔액 조회: 홈택스 발생 + 일반전표 상계를 거래처별로 집계 + */ + public function integrated(Request $request): JsonResponse + { + $tenantId = session('selected_tenant_id', 1); + $startDate = $request->input('startDate', date('Y-01-01')); + $endDate = $request->input('endDate', date('Y-12-31')); + $account = $request->input('account', 'all'); + $vendor = $request->input('vendor', ''); + + $accountCodes = $account === 'all' ? ['204', '205'] : [$account]; + + // 1. 홈택스 매입세금계산서 - 대변(미지급금 발생) + $hometaxQuery = HometaxInvoiceJournal::where('tenant_id', $tenantId) + ->whereIn('account_code', $accountCodes) + ->where('dc_type', 'credit') + ->where('credit_amount', '>', 0) + ->whereBetween('write_date', [$startDate, $endDate]); + + if ($vendor) { + $hometaxQuery->where('trading_partner_name', 'like', "%{$vendor}%"); + } + + $hometaxGrouped = (clone $hometaxQuery) + ->select( + 'trading_partner_name', + 'account_code', + 'account_name', + DB::raw('SUM(credit_amount) as total_credit'), + DB::raw('COUNT(*) as cnt'), + DB::raw('MIN(write_date) as first_date'), + DB::raw('MAX(write_date) as last_date') + ) + ->groupBy('trading_partner_name', 'account_code', 'account_name') + ->orderByDesc('total_credit') + ->get(); + + $hometaxDetails = (clone $hometaxQuery) + ->select('id', 'trading_partner_name', 'account_code', 'account_name', 'credit_amount', 'write_date', 'description', 'nts_confirm_num') + ->orderByDesc('write_date') + ->get(); + + // 2. 일반전표 - 계정 204/205 차변(상계) + 대변(전표를 통한 발생) + $journalQuery = JournalEntryLine::where('journal_entry_lines.tenant_id', $tenantId) + ->whereIn('journal_entry_lines.account_code', $accountCodes) + ->join('journal_entries', function ($join) { + $join->on('journal_entries.id', '=', 'journal_entry_lines.journal_entry_id') + ->whereNull('journal_entries.deleted_at'); + }) + ->whereBetween('journal_entries.entry_date', [$startDate, $endDate]); + + if ($vendor) { + $journalQuery->where('journal_entry_lines.trading_partner_name', 'like', "%{$vendor}%"); + } + + $journalGrouped = (clone $journalQuery) + ->select( + 'journal_entry_lines.trading_partner_name', + 'journal_entry_lines.account_code', + 'journal_entry_lines.account_name', + 'journal_entry_lines.dc_type', + DB::raw('SUM(journal_entry_lines.debit_amount) as total_debit'), + DB::raw('SUM(journal_entry_lines.credit_amount) as total_credit'), + DB::raw('COUNT(*) as cnt') + ) + ->groupBy('journal_entry_lines.trading_partner_name', 'journal_entry_lines.account_code', 'journal_entry_lines.account_name', 'journal_entry_lines.dc_type') + ->orderByDesc('total_debit') + ->get(); + + $journalDetails = (clone $journalQuery) + ->select( + 'journal_entry_lines.id', + 'journal_entry_lines.trading_partner_name', + 'journal_entry_lines.account_code', + 'journal_entry_lines.account_name', + 'journal_entry_lines.dc_type', + 'journal_entry_lines.debit_amount', + 'journal_entry_lines.credit_amount', + 'journal_entry_lines.description', + 'journal_entries.entry_date', + 'journal_entries.entry_no' + ) + ->orderByDesc('journal_entries.entry_date') + ->get(); + + // 3. 거래처별 잔액 계산 + $vendorMap = []; + + // 홈택스 대변 = 발생 + foreach ($hometaxGrouped as $row) { + $key = $row->trading_partner_name.'|'.$row->account_code; + if (! isset($vendorMap[$key])) { + $vendorMap[$key] = [ + 'vendorName' => $row->trading_partner_name, + 'accountCode' => $row->account_code, + 'accountName' => $row->account_name, + 'occurred' => 0, + 'offset' => 0, + 'hometaxCount' => 0, + 'journalDebitCount' => 0, + 'journalCreditCount' => 0, + ]; + } + $vendorMap[$key]['occurred'] += $row->total_credit; + $vendorMap[$key]['hometaxCount'] += $row->cnt; + } + + // 일반전표 차변 = 상계, 대변 = 발생 + foreach ($journalGrouped as $row) { + $key = $row->trading_partner_name.'|'.$row->account_code; + if (! isset($vendorMap[$key])) { + $vendorMap[$key] = [ + 'vendorName' => $row->trading_partner_name, + 'accountCode' => $row->account_code, + 'accountName' => $row->account_name, + 'occurred' => 0, + 'offset' => 0, + 'hometaxCount' => 0, + 'journalDebitCount' => 0, + 'journalCreditCount' => 0, + ]; + } + if ($row->dc_type === 'debit') { + $vendorMap[$key]['offset'] += $row->total_debit; + $vendorMap[$key]['journalDebitCount'] += $row->cnt; + } else { + $vendorMap[$key]['occurred'] += $row->total_credit; + $vendorMap[$key]['journalCreditCount'] += $row->cnt; + } + } + + // 잔액 계산 및 정렬 + $byVendor = collect($vendorMap)->map(function ($item) { + $item['balance'] = $item['occurred'] - $item['offset']; + + return $item; + })->sortByDesc('balance')->values()->all(); + + // 4. 요약 통계 + $totalOccurred = array_sum(array_column($byVendor, 'occurred')); + $totalOffset = array_sum(array_column($byVendor, 'offset')); + + return response()->json([ + 'success' => true, + 'data' => [ + 'summary' => [ + 'totalOccurred' => $totalOccurred, + 'totalOffset' => $totalOffset, + 'totalBalance' => $totalOccurred - $totalOffset, + 'hometaxCount' => $hometaxDetails->count(), + 'journalCount' => $journalDetails->count(), + 'vendorCount' => count($byVendor), + ], + 'byVendor' => $byVendor, + 'hometaxDetails' => $hometaxDetails, + 'journalDetails' => $journalDetails, + ], + ]); + } + + /** + * 홈택스 매입세금계산서 미지급금/미지급비용 대변 내역 + */ + public function hometaxPayables(Request $request): JsonResponse + { + $tenantId = session('selected_tenant_id', 1); + $startDate = $request->input('startDate', date('Y-01-01')); + $endDate = $request->input('endDate', date('Y-12-31')); + $account = $request->input('account', 'all'); + + $accountCodes = $account === 'all' ? ['204', '205'] : [$account]; + + $items = HometaxInvoiceJournal::where('tenant_id', $tenantId) + ->whereIn('account_code', $accountCodes) + ->where('dc_type', 'credit') + ->where('credit_amount', '>', 0) + ->whereBetween('write_date', [$startDate, $endDate]) + ->select('id', 'trading_partner_name', 'account_code', 'account_name', 'credit_amount', 'write_date', 'description', 'nts_confirm_num', 'supply_amount', 'tax_amount', 'total_amount') + ->orderByDesc('write_date') + ->get(); + + $totalCredit = $items->sum('credit_amount'); + + return response()->json([ + 'success' => true, + 'data' => $items, + 'summary' => [ + 'totalCredit' => $totalCredit, + 'count' => $items->count(), + ], + ]); + } + + /** + * 일반전표 미지급금/미지급비용 내역 (차변=상계, 대변=발생) + */ + public function journalPayables(Request $request): JsonResponse + { + $tenantId = session('selected_tenant_id', 1); + $startDate = $request->input('startDate', date('Y-01-01')); + $endDate = $request->input('endDate', date('Y-12-31')); + $account = $request->input('account', 'all'); + $dcType = $request->input('dcType', 'all'); + + $accountCodes = $account === 'all' ? ['204', '205'] : [$account]; + + $query = JournalEntryLine::where('journal_entry_lines.tenant_id', $tenantId) + ->whereIn('journal_entry_lines.account_code', $accountCodes) + ->join('journal_entries', function ($join) { + $join->on('journal_entries.id', '=', 'journal_entry_lines.journal_entry_id') + ->whereNull('journal_entries.deleted_at'); + }) + ->whereBetween('journal_entries.entry_date', [$startDate, $endDate]); + + if ($dcType !== 'all') { + $query->where('journal_entry_lines.dc_type', $dcType); + } + + $items = $query->select( + 'journal_entry_lines.id', + 'journal_entry_lines.trading_partner_name', + 'journal_entry_lines.account_code', + 'journal_entry_lines.account_name', + 'journal_entry_lines.dc_type', + 'journal_entry_lines.debit_amount', + 'journal_entry_lines.credit_amount', + 'journal_entry_lines.description', + 'journal_entries.entry_date', + 'journal_entries.entry_no' + ) + ->orderByDesc('journal_entries.entry_date') + ->get(); + + $totalDebit = $items->sum('debit_amount'); + $totalCredit = $items->sum('credit_amount'); + + return response()->json([ + 'success' => true, + 'data' => $items, + 'summary' => [ + 'totalDebit' => $totalDebit, + 'totalCredit' => $totalCredit, + 'count' => $items->count(), + ], + ]); + } } diff --git a/resources/views/finance/payables.blade.php b/resources/views/finance/payables.blade.php index 9803acab..b4bb36cd 100644 --- a/resources/views/finance/payables.blade.php +++ b/resources/views/finance/payables.blade.php @@ -18,7 +18,7 @@ @verbatim diff --git a/routes/web.php b/routes/web.php index 43ec461a..3a8700df 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1197,6 +1197,9 @@ // 미지급금 관리 API Route::prefix('payables')->name('payables.')->group(function () { Route::get('/list', [\App\Http\Controllers\Finance\PayableController::class, 'index'])->name('list'); + Route::get('/integrated', [\App\Http\Controllers\Finance\PayableController::class, 'integrated'])->name('integrated'); + Route::get('/hometax', [\App\Http\Controllers\Finance\PayableController::class, 'hometaxPayables'])->name('hometax'); + Route::get('/journals', [\App\Http\Controllers\Finance\PayableController::class, 'journalPayables'])->name('journals'); Route::post('/store', [\App\Http\Controllers\Finance\PayableController::class, 'store'])->name('store'); Route::put('/{id}', [\App\Http\Controllers\Finance\PayableController::class, 'update'])->name('update'); Route::post('/{id}/pay', [\App\Http\Controllers\Finance\PayableController::class, 'pay'])->name('pay');