feat: [finance] 손익계산서 기수 수정 + 당기/전기 토글 + 월별 보기
- 기수: 코드브릿지엑스 설립 2025년 기준 (1기=2025, 2기=2026) - 당기만/당기+전기 토글 버튼 - 월별 보기 모드 (전체/개별 월 선택) - 월별 전체: 가로 스크롤 비교 테이블 - buildSections 공통 로직 분리
This commit is contained in:
@@ -56,7 +56,6 @@ public function data(Request $request): JsonResponse
|
||||
$currentSums = $this->getAccountSums($tenantId, $startDate, $endDate);
|
||||
$previousSums = $this->getAccountSums($tenantId, $prevStartDate, $prevEndDate);
|
||||
|
||||
// 계정과목 목록 (sub_category별 세부 항목 표시용)
|
||||
$accountCodes = AccountCode::where('tenant_id', $tenantId)
|
||||
->where('is_active', true)
|
||||
->whereIn('category', ['revenue', 'expense'])
|
||||
@@ -64,95 +63,7 @@ public function data(Request $request): JsonResponse
|
||||
->orderBy('code')
|
||||
->get();
|
||||
|
||||
// 손익계산서 구조 조립
|
||||
$sections = [];
|
||||
$calcValues = [];
|
||||
|
||||
foreach (self::PL_STRUCTURE as $item) {
|
||||
$section = [
|
||||
'code' => $item['code'],
|
||||
'name' => $item['name'],
|
||||
'current_amount' => 0,
|
||||
'previous_amount' => 0,
|
||||
'items' => [],
|
||||
'is_calculated' => $item['type'] === 'calc',
|
||||
];
|
||||
|
||||
if ($item['type'] === 'sum') {
|
||||
$relatedAccounts = $accountCodes->filter(function ($ac) use ($item) {
|
||||
// tax_codes: 특정 계정코드만 포함 (법인세/소득세)
|
||||
if (! empty($item['tax_codes'])) {
|
||||
return in_array($ac->code, $item['tax_codes']);
|
||||
}
|
||||
|
||||
// sub_categories 배열로 매칭
|
||||
$subCategories = $item['sub_categories'] ?? [];
|
||||
if ($ac->category !== $item['category'] || ! in_array($ac->sub_category, $subCategories)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// exclude_codes: 특정 계정코드 제외 (영업외비용에서 법인세 제외)
|
||||
if (! empty($item['exclude_codes']) && in_array($ac->code, $item['exclude_codes'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
$currentTotal = 0;
|
||||
$previousTotal = 0;
|
||||
|
||||
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;
|
||||
|
||||
// 수익: 대변 - 차변, 비용: 차변 - 대변
|
||||
if ($ac->category === 'revenue') {
|
||||
$curAmount = $curCredit - $curDebit;
|
||||
$prevAmount = $prevCredit - $prevDebit;
|
||||
} else {
|
||||
$curAmount = $curDebit - $curCredit;
|
||||
$prevAmount = $prevDebit - $prevCredit;
|
||||
}
|
||||
|
||||
if ($curAmount != 0 || $prevAmount != 0) {
|
||||
$section['items'][] = [
|
||||
'code' => $ac->code,
|
||||
'name' => $ac->name,
|
||||
'current' => $curAmount,
|
||||
'previous' => $prevAmount,
|
||||
];
|
||||
}
|
||||
|
||||
$currentTotal += $curAmount;
|
||||
$previousTotal += $prevAmount;
|
||||
}
|
||||
|
||||
$section['current_amount'] = $currentTotal;
|
||||
$section['previous_amount'] = $previousTotal;
|
||||
$calcValues[$item['code']] = ['current' => $currentTotal, 'previous' => $previousTotal];
|
||||
} elseif ($item['type'] === 'calc') {
|
||||
$amounts = $this->evaluateFormula($item['formula'], $calcValues);
|
||||
$section['current_amount'] = $amounts['current'];
|
||||
$section['previous_amount'] = $amounts['previous'];
|
||||
$calcValues[$item['code']] = $amounts;
|
||||
}
|
||||
|
||||
$sections[] = $section;
|
||||
}
|
||||
|
||||
// 단위 변환
|
||||
$divisor = match ($unit) {
|
||||
'thousand' => 1000,
|
||||
'million' => 1000000,
|
||||
default => 1,
|
||||
};
|
||||
|
||||
if ($divisor > 1) {
|
||||
$sections = $this->applyUnitDivisor($sections, $divisor);
|
||||
}
|
||||
$sections = $this->buildSections($accountCodes, $currentSums, $previousSums, $unit);
|
||||
|
||||
// 기수 계산
|
||||
$currentYear = (int) date('Y', strtotime($endDate));
|
||||
@@ -277,13 +188,152 @@ private function applyUnitDivisor(array $sections, int $divisor): array
|
||||
}
|
||||
|
||||
/**
|
||||
* 기수 계산 (테넌트별 설립연도 기반, 기본 1기 = 2005년)
|
||||
* 기수 계산 (코드브릿지엑스 설립: 2025-09-13, 1기 = 2025년)
|
||||
*/
|
||||
private function getFiscalYear(int $tenantId, int $currentYear): int
|
||||
{
|
||||
// 기본 1기 기준년도 (향후 테넌트 설정에서 가져올 수 있음)
|
||||
$baseYear = 2005;
|
||||
$baseYear = 2024; // 2025 - 2024 = 1기
|
||||
|
||||
return $currentYear - $baseYear + 1;
|
||||
return $currentYear - $baseYear;
|
||||
}
|
||||
|
||||
/**
|
||||
* 월별 손익계산서 조회
|
||||
*/
|
||||
public function monthly(Request $request): JsonResponse
|
||||
{
|
||||
$tenantId = session('selected_tenant_id', 1);
|
||||
$year = (int) $request->input('year', now()->year);
|
||||
$unit = $request->input('unit', 'won');
|
||||
|
||||
$accountCodes = AccountCode::where('tenant_id', $tenantId)
|
||||
->where('is_active', true)
|
||||
->whereIn('category', ['revenue', 'expense'])
|
||||
->orderBy('sort_order')
|
||||
->orderBy('code')
|
||||
->get();
|
||||
|
||||
$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($tenantId, $year);
|
||||
|
||||
return response()->json([
|
||||
'year' => $year,
|
||||
'fiscal_year' => $fiscalYear,
|
||||
'fiscal_label' => "제 {$fiscalYear} 기",
|
||||
'unit' => $unit,
|
||||
'months' => $months,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 섹션 조립 공통 로직
|
||||
*/
|
||||
private function buildSections($accountCodes, array $currentSums, array $previousSums, string $unit, bool $currentOnly = false): array
|
||||
{
|
||||
$sections = [];
|
||||
$calcValues = [];
|
||||
|
||||
foreach (self::PL_STRUCTURE as $item) {
|
||||
$section = [
|
||||
'code' => $item['code'],
|
||||
'name' => $item['name'],
|
||||
'current_amount' => 0,
|
||||
'previous_amount' => 0,
|
||||
'items' => [],
|
||||
'is_calculated' => $item['type'] === 'calc',
|
||||
];
|
||||
|
||||
if ($item['type'] === 'sum') {
|
||||
$relatedAccounts = $accountCodes->filter(function ($ac) use ($item) {
|
||||
if (! empty($item['tax_codes'])) {
|
||||
return in_array($ac->code, $item['tax_codes']);
|
||||
}
|
||||
$subCategories = $item['sub_categories'] ?? [];
|
||||
if ($ac->category !== $item['category'] || ! in_array($ac->sub_category, $subCategories)) {
|
||||
return false;
|
||||
}
|
||||
if (! empty($item['exclude_codes']) && in_array($ac->code, $item['exclude_codes'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
$currentTotal = 0;
|
||||
$previousTotal = 0;
|
||||
|
||||
foreach ($relatedAccounts as $ac) {
|
||||
$curDebit = $currentSums[$ac->code]['debit'] ?? 0;
|
||||
$curCredit = $currentSums[$ac->code]['credit'] ?? 0;
|
||||
|
||||
if ($ac->category === 'revenue') {
|
||||
$curAmount = $curCredit - $curDebit;
|
||||
} else {
|
||||
$curAmount = $curDebit - $curCredit;
|
||||
}
|
||||
|
||||
$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,
|
||||
];
|
||||
}
|
||||
|
||||
$currentTotal += $curAmount;
|
||||
$previousTotal += $prevAmount;
|
||||
}
|
||||
|
||||
$section['current_amount'] = $currentTotal;
|
||||
$section['previous_amount'] = $previousTotal;
|
||||
$calcValues[$item['code']] = ['current' => $currentTotal, 'previous' => $previousTotal];
|
||||
} elseif ($item['type'] === 'calc') {
|
||||
$amounts = $this->evaluateFormula($item['formula'], $calcValues);
|
||||
$section['current_amount'] = $amounts['current'];
|
||||
$section['previous_amount'] = $amounts['previous'];
|
||||
$calcValues[$item['code']] = $amounts;
|
||||
}
|
||||
|
||||
$sections[] = $section;
|
||||
}
|
||||
|
||||
$divisor = match ($unit) {
|
||||
'thousand' => 1000,
|
||||
'million' => 1000000,
|
||||
default => 1,
|
||||
};
|
||||
|
||||
if ($divisor > 1) {
|
||||
$sections = $this->applyUnitDivisor($sections, $divisor);
|
||||
}
|
||||
|
||||
return $sections;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user