From dcf97b468d6793f8f3495e98eb737d23b92abafa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Thu, 19 Mar 2026 12:49:04 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20[finance]=20=EC=86=90=EC=9D=B5=EA=B3=84?= =?UTF-8?q?=EC=82=B0=EC=84=9C=20=EC=9B=94=EB=B3=84=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?API=20+=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - GET /api/v1/income-statement/monthly?year=2026&unit=won 추가 - buildSections 공통 로직 분리 - getAccountCodes, getFiscalYear 헬퍼 분리 --- .../Api/V1/IncomeStatementController.php | 15 +++ app/Services/IncomeStatementService.php | 112 +++++++++++++----- routes/api/v1/finance.php | 1 + 3 files changed, 96 insertions(+), 32 deletions(-) diff --git a/app/Http/Controllers/Api/V1/IncomeStatementController.php b/app/Http/Controllers/Api/V1/IncomeStatementController.php index 2e13197e..de53b207 100644 --- a/app/Http/Controllers/Api/V1/IncomeStatementController.php +++ b/app/Http/Controllers/Api/V1/IncomeStatementController.php @@ -28,4 +28,19 @@ public function index(Request $request) return $this->service->data($request->only(['start_date', 'end_date', 'unit'])); }, __('message.fetched')); } + + /** + * 손익계산서 월별 조회 + */ + public function monthly(Request $request) + { + $request->validate([ + 'year' => 'required|integer|min:2020|max:2100', + 'unit' => 'nullable|in:won,thousand,million', + ]); + + return ApiResponse::handle(function () use ($request) { + return $this->service->monthly($request->only(['year', 'unit'])); + }, __('message.fetched')); + } } diff --git a/app/Services/IncomeStatementService.php b/app/Services/IncomeStatementService.php index a444e52f..ea4a3efd 100644 --- a/app/Services/IncomeStatementService.php +++ b/app/Services/IncomeStatementService.php @@ -21,7 +21,7 @@ class IncomeStatementService extends Service ]; /** - * 손익계산서 조회 + * 손익계산서 조회 (기간) */ public function data(array $params): array { @@ -33,17 +33,86 @@ public function data(array $params): array $prevStartDate = date('Y-m-d', strtotime($startDate.' -1 year')); $prevEndDate = date('Y-m-d', strtotime($endDate.' -1 year')); + $accountCodes = $this->getAccountCodes($tenantId); $currentSums = $this->getAccountSums($tenantId, $startDate, $endDate); $previousSums = $this->getAccountSums($tenantId, $prevStartDate, $prevEndDate); + $sections = $this->buildSections($accountCodes, $currentSums, $previousSums, $unit); - $accountCodes = DB::table('account_codes') + $currentYear = (int) date('Y', strtotime($endDate)); + $fiscalYear = $this->getFiscalYear($currentYear); + + return [ + 'period' => [ + 'current' => ['start' => $startDate, 'end' => $endDate, 'label' => "제 {$fiscalYear} (당)기"], + 'previous' => ['start' => $prevStartDate, 'end' => $prevEndDate, 'label' => '제 '.($fiscalYear - 1).' (전)기'], + ], + 'unit' => $unit, + 'sections' => $sections, + ]; + } + + /** + * 손익계산서 월별 조회 + */ + public function monthly(array $params): array + { + $tenantId = $this->tenantId(); + $year = (int) ($params['year'] ?? now()->year); + $unit = $params['unit'] ?? 'won'; + + $accountCodes = $this->getAccountCodes($tenantId); + $months = []; + + for ($m = 1; $m <= 12; $m++) { + $startDate = sprintf('%04d-%02d-01', $year, $m); + $endDate = date('Y-m-t', strtotime($startDate)); + + if (strtotime($startDate) > time()) { + break; + } + + $sums = $this->getAccountSums($tenantId, $startDate, $endDate); + $sections = $this->buildSections($accountCodes, $sums, $sums, $unit, true); + + $months[] = [ + 'month' => sprintf('%02d', $m), + 'label' => $m.'월', + 'sections' => $sections, + ]; + } + + $fiscalYear = $this->getFiscalYear($year); + + return [ + 'year' => $year, + 'fiscal_year' => $fiscalYear, + 'fiscal_label' => "제 {$fiscalYear} 기", + 'unit' => $unit, + 'months' => $months, + ]; + } + + private function getAccountCodes(int $tenantId) + { + return DB::table('account_codes') ->where('tenant_id', $tenantId) ->where('is_active', true) ->whereIn('category', ['revenue', 'expense']) ->orderBy('sort_order') ->orderBy('code') ->get(); + } + private function getFiscalYear(int $currentYear): int + { + return $currentYear - 2024; // 코드브릿지엑스 설립 2025-09-13, 1기 = 2025년 + } + + /** + * 섹션 조립 공통 로직 + */ + private function buildSections($accountCodes, array $currentSums, array $previousSums, string $unit, bool $currentOnly = false): array + { $sections = []; $calcValues = []; @@ -79,24 +148,17 @@ public function data(array $params): array foreach ($relatedAccounts as $ac) { $curDebit = $currentSums[$ac->code]['debit'] ?? 0; $curCredit = $currentSums[$ac->code]['credit'] ?? 0; - $prevDebit = $previousSums[$ac->code]['debit'] ?? 0; - $prevCredit = $previousSums[$ac->code]['credit'] ?? 0; + $curAmount = $ac->category === 'revenue' ? ($curCredit - $curDebit) : ($curDebit - $curCredit); - if ($ac->category === 'revenue') { - $curAmount = $curCredit - $curDebit; - $prevAmount = $prevCredit - $prevDebit; - } else { - $curAmount = $curDebit - $curCredit; - $prevAmount = $prevDebit - $prevCredit; + $prevAmount = 0; + if (! $currentOnly) { + $prevDebit = $previousSums[$ac->code]['debit'] ?? 0; + $prevCredit = $previousSums[$ac->code]['credit'] ?? 0; + $prevAmount = $ac->category === 'revenue' ? ($prevCredit - $prevDebit) : ($prevDebit - $prevCredit); } if ($curAmount != 0 || $prevAmount != 0) { - $section['items'][] = [ - 'code' => $ac->code, - 'name' => $ac->name, - 'current' => $curAmount, - 'previous' => $prevAmount, - ]; + $section['items'][] = ['code' => $ac->code, 'name' => $ac->name, 'current' => $curAmount, 'previous' => $prevAmount]; } $currentTotal += $curAmount; @@ -117,11 +179,8 @@ public function data(array $params): array } $divisor = match ($unit) { - 'thousand' => 1000, - 'million' => 1000000, - default => 1, + 'thousand' => 1000, 'million' => 1000000, default => 1 }; - if ($divisor > 1) { foreach ($sections as &$s) { $s['current_amount'] = (int) round($s['current_amount'] / $divisor); @@ -135,18 +194,7 @@ public function data(array $params): array unset($s); } - $currentYear = (int) date('Y', strtotime($endDate)); - $baseYear = 2024; // 코드브릿지엑스 설립 2025-09-13, 1기 = 2025년 - $fiscalYear = $currentYear - $baseYear; - - return [ - 'period' => [ - 'current' => ['start' => $startDate, 'end' => $endDate, 'label' => "제 {$fiscalYear} (당)기"], - 'previous' => ['start' => $prevStartDate, 'end' => $prevEndDate, 'label' => '제 '.($fiscalYear - 1).' (전)기'], - ], - 'unit' => $unit, - 'sections' => $sections, - ]; + return $sections; } private function getAccountSums(int $tenantId, string $startDate, string $endDate): array diff --git a/routes/api/v1/finance.php b/routes/api/v1/finance.php index aa21bd4f..5e3be5e9 100644 --- a/routes/api/v1/finance.php +++ b/routes/api/v1/finance.php @@ -415,3 +415,4 @@ // Income Statement API (손익계산서) Route::get('/income-statement', [IncomeStatementController::class, 'index'])->name('v1.income-statement.index'); +Route::get('/income-statement/monthly', [IncomeStatementController::class, 'monthly'])->name('v1.income-statement.monthly');