# 계정별원장 & 손익계산서 기획서 > **작성일**: 2026-03-19 > **상태**: 기획 확정 > **요청자**: 경리팀 (함시녹) > **대상 프로젝트**: MNG (`/home/aweso/sam/mng`) > **참고**: 더존 프로그램 화면 참조 --- ## 1. 개요 ### 1.1 목적 MNG 회계/세무관리 메뉴에 **계정별원장**과 **손익계산서** 2개 메뉴를 신규 추가한다. 일반전표입력과 홈택스 매입/매출에 기록된 계정과목별 거래 내역을 조회하고, 기간별 손익 현황을 확인할 수 있도록 한다. ### 1.2 배경 - 현재 SAM에는 일반전표입력, 미지급금, 미수금 등 개별 거래 관리 기능은 있으나, **계정과목별 종합 원장 조회** 기능이 없다 - 경리 담당자가 특정 계정과목의 기간별 거래 내역과 잔액을 한눈에 파악할 수 없다 - 손익계산서는 경영진 의사결정에 필수적인 재무제표이나 아직 미구현 상태다 - 더존 프로그램의 동일 기능을 참고하여 SAM에 맞게 구현한다 ### 1.3 메뉴 위치 기존 **회계/세무관리** 메뉴에서 **일반전표입력**과 **전자세금계산서** 사이에 배치한다. ``` 회계/세무관리 ├── 일반전표입력 ← 기존 ├── 계정별원장 ← 신규 (NEW) ├── 손익계산서 ← 신규 (NEW) ├── 전자세금계산서 ← 기존 ├── 홈택스매출/매입 ← 기존 ├── 부가세관리 ← 기존 └── 경조사비관리 ← 기존 ``` --- ## 2. 계정별원장 ### 2.1 기능 설명 일반전표, 홈택스 매입/매출에 있는 계정들을 반영하여 **조회기간을 설정하고 조회하고자 하는 계정과목의 금액들을 확인**할 수 있는 화면이다. ### 2.2 화면 구성 #### 조회 조건 (상단 필터) | 필터 | 설명 | 기본값 | |------|------|--------| | **조회기간** | 시작일 ~ 종료일 (날짜 범위) | 당월 1일 ~ 당월 말일 | | **계정과목** | 계정코드 + 계정명 선택 (드롭다운/검색) | 전체 | | **범위** | 개별 / 전체 | 개별 | > **계정과목 선택**: 기존 `accountCodes()` API 활용 — `account_codes` 테이블에서 `is_active = true`인 항목 조회 #### 메인 테이블 (계정별 거래 내역) | 컬럼 | 설명 | 데이터 소스 | |------|------|------------| | **날짜** | 전표일자 / 세금계산서 작성일 | `journal_entries.entry_date` / `hometax_invoice_journals.write_date` | | **적요** | 거래 설명 | `journal_entry_lines.description` / `hometax_invoice_journals.description` | | **코드** | 거래처 코드 | `trading_partners.id` | | **거래처** | 거래처명 | `journal_entry_lines.trading_partner_name` / `hometax_invoice_journals.trading_partner_name` | | **사업자번호** | 거래처 사업자번호 | `trading_partners.biz_no` | | **차변** | 차변(debit) 금액 | `debit_amount` | | **대변** | 대변(credit) 금액 | `credit_amount` | | **잔액** | 누적 잔액 (이월잔액 + 차변 - 대변) | 계산 필드 | #### 소계/누계 표시 ``` 2025/01/11 복리후생비 160,000 160,000 2025/01/11 복리후생비 100,000 260,000 2025/01/11 복리후생비 90,000 350,000 2025/01/11 복리후생비 75,000 525,000 (100,000) ... 2025/01/31 직장경비 80,000 2,355,000 ────────────────────────────────────────────── 계 2,355,000 2,355,000 누 계 2,355,000 2,355,000 2025/02/20 복리후생비 600,000 2,955,000 2025/02/20 복리후생비 200,000 3,155,000 ... ────────────────────────────────────────────── 계 4,885,000 4,885,000 ``` - **월별 소계(계)**: 해당 월의 차변/대변 합계 - **누계**: 조회 시작일부터의 누적 합계 - **잔액**: 자산/비용 계정은 `차변 - 대변`, 부채/자본/수익 계정은 `대변 - 차변` #### 드릴다운 기능 > **핵심 요구사항**: 계정별원장에서 계정과목을 조회 후 해당하는 **금액을 클릭하면 일반전표입력 화면이 보여지게끔** 구성한다. | 클릭 대상 | 동작 | |----------|------| | 차변/대변 금액 클릭 | 해당 전표의 일반전표입력 화면으로 이동 (`/finance/journal-entries?id={journal_entry_id}`) | | 홈택스 분개 금액 클릭 | 해당 홈택스 세금계산서 상세로 이동 | ### 2.3 데이터 소스 & 쿼리 설계 계정별원장은 2개 데이터 소스를 통합하여 조회한다: ``` ┌──────────────────────────┐ ┌────────────────────────────┐ │ journal_entry_lines │ │ hometax_invoice_journals │ │ (일반전표 분개 라인) │ │ (홈택스 세금계산서 분개) │ │ │ │ │ │ - tenant_id │ │ - tenant_id │ │ - journal_entry_id (FK) │ │ - hometax_invoice_id (FK) │ │ - account_code │ │ - account_code │ │ - account_name │ │ - account_name │ │ - debit_amount │ │ - debit_amount │ │ - credit_amount │ │ - credit_amount │ │ - description │ │ - description │ │ - trading_partner_name │ │ - trading_partner_name │ └─────────┬────────────────┘ └─────────┬──────────────────┘ │ │ └────────────┬───────────────────┘ ▼ UNION ALL → 날짜순 정렬 ▼ 월별 소계 / 누계 계산 ▼ 계정별원장 화면 출력 ``` #### 핵심 쿼리 로직 ```sql -- 일반전표 분개 라인 SELECT je.entry_date AS date, jel.description, jel.trading_partner_id, jel.trading_partner_name, tp.biz_no, jel.debit_amount, jel.credit_amount, 'journal' AS source_type, jel.journal_entry_id AS source_id FROM journal_entry_lines jel JOIN journal_entries je ON je.id = jel.journal_entry_id LEFT JOIN trading_partners tp ON tp.id = jel.trading_partner_id WHERE jel.tenant_id = ? AND jel.account_code = ? AND je.entry_date BETWEEN ? AND ? AND je.deleted_at IS NULL UNION ALL -- 홈택스 세금계산서 분개 SELECT hij.write_date AS date, hij.description, NULL AS trading_partner_id, hij.trading_partner_name, hi.invoicee_corp_num AS biz_no, hij.debit_amount, hij.credit_amount, 'hometax' AS source_type, hij.hometax_invoice_id AS source_id FROM hometax_invoice_journals hij JOIN hometax_invoices hi ON hi.id = hij.hometax_invoice_id WHERE hij.tenant_id = ? AND hij.account_code = ? AND hij.write_date BETWEEN ? AND ? AND hi.deleted_at IS NULL ORDER BY date ASC, source_type ASC ``` #### 잔액 계산 규칙 | 계정 카테고리 | 정상 잔액 방향 | 잔액 계산 | |-------------|-------------|----------| | `asset` (자산) | 차변 | 차변 - 대변 | | `expense` (비용) | 차변 | 차변 - 대변 | | `liability` (부채) | 대변 | 대변 - 차변 | | `capital` (자본) | 대변 | 대변 - 차변 | | `revenue` (수익) | 대변 | 대변 - 차변 | > `account_codes.category` 필드로 카테고리를 판단한다. ### 2.4 API 엔드포인트 | Method | Path | 설명 | |--------|------|------| | `GET` | `/finance/account-ledger` | 계정별원장 페이지 | | `GET` | `/finance/account-ledger/list` | 계정별원장 데이터 조회 API | #### GET `/finance/account-ledger/list` 파라미터 | 파라미터 | 타입 | 필수 | 설명 | |---------|------|------|------| | `start_date` | `date` | Y | 조회 시작일 | | `end_date` | `date` | Y | 조회 종료일 | | `account_code` | `string` | N | 계정코드 (미지정 시 전체) | | `scope` | `string` | N | `individual` (기본) / `all` | #### 응답 구조 ```json { "account": { "code": "81100", "name": "복리후생비", "category": "expense" }, "period": { "start_date": "2025-01-01", "end_date": "2025-12-31" }, "carry_forward": { "debit": 0, "credit": 0, "balance": 0 }, "monthly_data": [ { "month": "2025-01", "items": [ { "date": "2025-01-11", "description": "복리후생비", "trading_partner_code": null, "trading_partner_name": "복리후생비", "biz_no": null, "debit_amount": 160000, "credit_amount": 0, "balance": 160000, "source_type": "journal", "source_id": 123 } ], "subtotal": { "debit": 2355000, "credit": 0 }, "cumulative": { "debit": 2355000, "credit": 0 } } ], "grand_total": { "debit": 7240000, "credit": 525000, "balance": 6715000 } } ``` --- ## 3. 손익계산서 ### 3.1 기능 설명 매출, 매입 금액 등을 반영하여 **기간별로 손익을 확인**할 수 있는 화면이다. 매출과 매출원가의 계정과목은 **회사(테넌트)마다 달라질 수 있다**. > **코드브릿지엑스 기준**: 매출계정은 `용역매출`, 매출원가는 `서비스 매출원가` / `외주용역비` 등으로 구분 ### 3.2 화면 구성 #### 조회 조건 (상단 필터) | 필터 | 설명 | 기본값 | |------|------|--------| | **기간** | 조회 기간 (시작일 ~ 종료일) | 당해 1/1 ~ 당해 12/31 | | **단위** | 금액 표시 단위 (원 / 천원 / 백만원) | 원 | #### 탭 구성 | 탭 | 설명 | |----|------| | **관리용** | 내부 관리 목적의 손익계산서 (기본 탭) | | **제출용** | 외부 제출용 표준 양식 | | **세목별** | 세금 항목별 상세 분류 | #### 손익계산서 구조 ``` ┌─────────────────────────────────────────────────────────┐ │ 손익계산서 │ ├──────────────────────┬──────────────┬──────────────────┤ │ 과 목 │ 제 N (당)기 │ 제 N-1 (전)기 │ │ │ 금 액 │ 금 액 │ ├──────────────────────┼──────────────┼──────────────────┤ │ I. 매출액 │ │ │ │ 상품매출 │ 2,150,500,000│ 2,150,500,000 │ │ 제품매출 │ 787,000,000│ 787,000,000 │ │ │ 2,937,500,000│ │ ├──────────────────────┼──────────────┼──────────────────┤ │ II. 매출원가 │ │ │ │ 상품매출원가 │ │ │ │ 기초상품재고액 │ 2,924,900,000│ 1,952,600,000 │ │ 당기상품매입액 │ 972,300,000│ 972,300,000 │ │ 기말상품재고액 │ 3,897,200,000│ 2,924,900,000 │ │ │ 2,937,500,000│ │ ├──────────────────────┼──────────────┼──────────────────┤ │ III. 매출총이익 │ 2,937,500,000│ 2,937,500,000 │ ├──────────────────────┼──────────────┼──────────────────┤ │ IV. 판매비와관리비 │ 995,531,100│ 995,531,100 │ │ 직원급여 │ 251,000,000│ 251,000,000 │ │ 상여금 │ 541,500,000│ 541,500,000 │ │ 잡급 │ 5,250,000│ 5,250,000 │ │ 복리후생비 │ 52,955,100│ 52,955,100 │ │ 여비교통비 │ 12,910,000│ 12,910,000 │ │ 접대비 │ 28,130,000│ 28,130,000 │ │ 통신비 │ 2,795,000│ 2,795,000 │ │ ... │ │ │ ├──────────────────────┼──────────────┼──────────────────┤ │ V. 영업이익 │ 1,941,968,900│ 1,941,968,900 │ ├──────────────────────┼──────────────┼──────────────────┤ │ VI. 영업외수익 │ 100,000 │ 100,000 │ ├──────────────────────┼──────────────┼──────────────────┤ │ VII. 영업외비용 │ 200,000 │ 200,000 │ │ 이자비용 │ 200,000 │ 200,000 │ ├──────────────────────┼──────────────┼──────────────────┤ │ VIII. 법인세비용차감전│ 1,941,868,900│ 1,941,868,900 │ │ 순이익 │ │ │ ├──────────────────────┼──────────────┼──────────────────┤ │ IX. 법인세비용 │ │ │ ├──────────────────────┼──────────────┼──────────────────┤ │ X. 당기순이익 │ 1,941,868,900│ 1,941,868,900 │ └──────────────────────┴──────────────┴──────────────────┘ ``` ### 3.3 손익계산서 항목 매핑 손익계산서의 각 항목은 `account_codes` 테이블의 계정과목을 기반으로 집계한다. #### 항목별 계정 카테고리 매핑 | 손익 항목 | 계정 카테고리 | 집계 방식 | |----------|-------------|----------| | I. 매출액 | `revenue` + `sub_category = 'sales'` | 대변 합계 | | II. 매출원가 | `expense` + `sub_category = 'cost_of_sales'` | 차변 합계 | | III. 매출총이익 | (계산) | I - II | | IV. 판매비와관리비 | `expense` + `sub_category = 'sga'` | 차변 합계 | | V. 영업이익 | (계산) | III - IV | | VI. 영업외수익 | `revenue` + `sub_category = 'non_operating'` | 대변 합계 | | VII. 영업외비용 | `expense` + `sub_category = 'non_operating'` | 차변 합계 | | VIII. 법인세비용차감전순이익 | (계산) | V + VI - VII | | IX. 법인세비용 | `expense` + `sub_category = 'tax'` | 차변 합계 | | X. 당기순이익 | (계산) | VIII - IX | > **멀티테넌트 대응**: 매출 계정과 매출원가 계정의 **세부 과목은 테넌트마다 다를 수 있다**. `account_codes` 테이블의 `category` + `sub_category`로 분류하되, 테넌트별로 등록된 계정과목에 따라 동적으로 항목이 구성된다. ### 3.4 데이터 조회 로직 ``` ┌─────────────────────────────────┐ │ journal_entry_lines │ │ + hometax_invoice_journals │ │ (당기 조회기간) │ └──────────────┬──────────────────┘ │ ▼ 계정코드별 차변/대변 SUM │ ▼ account_codes.category + account_codes.sub_category 기준 그룹핑 │ ▼ ┌──────────────────────────────────┐ │ I. 매출액 = revenue/sales SUM │ │ II. 매출원가 = expense/cogs SUM │ │ III. 매출총이익 = I - II │ │ IV. 판관비 = expense/sga SUM │ │ V. 영업이익 = III - IV │ │ ... │ │ X. 당기순이익 │ └──────────────────────────────────┘ ``` #### 당기 vs 전기 비교 | 항목 | 조회 범위 | |------|----------| | **당기 (제 N 기)** | 사용자 지정 기간 | | **전기 (제 N-1 기)** | 전년 동일 기간 (자동 계산) | 예: 당기 `2025/01/01 ~ 2025/12/31` → 전기 `2024/01/01 ~ 2024/12/31` ### 3.5 API 엔드포인트 | Method | Path | 설명 | |--------|------|------| | `GET` | `/finance/income-statement` | 손익계산서 페이지 | | `GET` | `/finance/income-statement/data` | 손익계산서 데이터 조회 API | #### GET `/finance/income-statement/data` 파라미터 | 파라미터 | 타입 | 필수 | 설명 | |---------|------|------|------| | `start_date` | `date` | Y | 당기 시작일 | | `end_date` | `date` | Y | 당기 종료일 | | `unit` | `string` | N | `won` (기본) / `thousand` / `million` | | `tab` | `string` | N | `management` (기본) / `submission` / `tax_items` | #### 응답 구조 ```json { "period": { "current": { "start": "2025-01-01", "end": "2025-12-31", "label": "제 22 (당)기" }, "previous": { "start": "2024-01-01", "end": "2024-12-31", "label": "제 21 (전)기" } }, "unit": "won", "sections": [ { "code": "I", "name": "매출액", "current_amount": 2937500000, "previous_amount": 2937500000, "items": [ { "code": "40100", "name": "상품매출", "current": 2150500000, "previous": 2150500000 }, { "code": "40200", "name": "제품매출", "current": 787000000, "previous": 787000000 } ] }, { "code": "II", "name": "매출원가", "current_amount": 2937500000, "previous_amount": null, "items": [ { "code": "50100", "name": "상품매출원가", "sub_items": [ { "name": "기초상품재고액", "current": 2924900000, "previous": 1952600000 }, { "name": "당기상품매입액", "current": 972300000, "previous": 972300000 }, { "name": "기말상품재고액", "current": 3897200000, "previous": 2924900000 } ] } ] }, { "code": "III", "name": "매출총이익", "current_amount": 2937500000, "previous_amount": 2937500000, "is_calculated": true } ] } ``` --- ## 4. account_codes 테이블 확장 ### 4.1 현재 구조 ``` account_codes ├── id, tenant_id ├── code -- 계정코드 (예: 81100) ├── name -- 계정명 (예: 복리후생비) ├── category -- asset, liability, capital, revenue, expense ├── sub_category -- 세부 분류 (현재 미활용 또는 부분 활용) ├── parent_code -- 상위 계정코드 ├── depth -- 계층 깊이 ├── department_type ├── description ├── sort_order └── is_active ``` ### 4.2 필요한 확장 손익계산서를 위해 `sub_category` 필드의 값을 표준화해야 한다: | `category` | `sub_category` | 설명 | 손익계산서 위치 | |-----------|---------------|------|--------------| | `revenue` | `sales` | 매출 | I. 매출액 | | `revenue` | `non_operating` | 영업외수익 | VI. 영업외수익 | | `expense` | `cost_of_sales` | 매출원가 | II. 매출원가 | | `expense` | `sga` | 판매비와관리비 | IV. 판매관리비 | | `expense` | `non_operating` | 영업외비용 | VII. 영업외비용 | | `expense` | `tax` | 법인세비용 | IX. 법인세비용 | > **마이그레이션 필요**: 기존 `account_codes` 레코드의 `sub_category` 값을 위 기준으로 업데이트하는 작업이 필요하다. > 이 마이그레이션은 **MNG에서 관리**한다 (MNG 전용 테이블이 아니지만, 계정과목 데이터 정리는 기존에도 MNG에서 수행). --- ## 5. 파일 구조 (MNG) ### 5.1 신규 파일 ``` mng/ ├── app/Http/Controllers/Finance/ │ ├── AccountLedgerController.php ← 신규 │ └── IncomeStatementController.php ← 신규 │ ├── resources/views/finance/ │ ├── account-ledger.blade.php ← 신규 │ └── income-statement.blade.php ← 신규 │ ├── routes/web.php ← 라우트 추가 │ └── database/migrations/ └── 2026_03_XX_update_account_codes_sub_category.php ← 신규 ``` ### 5.2 수정 파일 | 파일 | 변경 내용 | |------|----------| | `routes/web.php` | 계정별원장 + 손익계산서 라우트 추가 | ### 5.3 라우트 정의 ```php // 계정별원장 Route::get('/finance/account-ledger', [AccountLedgerController::class, 'index']) ->name('finance.account-ledger'); Route::get('/finance/account-ledger/list', [AccountLedgerController::class, 'list']) ->name('finance.account-ledger.list'); // 손익계산서 Route::get('/finance/income-statement', [IncomeStatementController::class, 'index']) ->name('finance.income-statement'); Route::get('/finance/income-statement/data', [IncomeStatementController::class, 'data']) ->name('finance.income-statement.data'); ``` --- ## 6. UI 기술 스택 | 항목 | 선택 | 이유 | |------|------|------| | **렌더링** | Blade + React (@verbatim) | 기존 일반전표입력과 동일 방식 | | **스타일** | Tailwind CSS | MNG 표준 | | **테이블** | 커스텀 테이블 (React) | 월별 소계/누계 등 복잡한 구조 | | **날짜 선택** | 기존 DatePicker 컴포넌트 | MNG 공용 | | **계정과목 선택** | 기존 `accountCodes()` API 활용 | JournalEntryController에 이미 구현됨 | --- ## 7. 메뉴 등록 기존 **회계/세무관리** 메뉴의 하위에 2개 메뉴를 추가한다. ### 7.1 메뉴 추가 (Docker tinker) > 먼저 부모 메뉴(회계/세무관리)와 기존 하위 메뉴의 `sort_order`를 확인한 후, 일반전표입력과 전자세금계산서 사이에 배치한다. ```bash # 1. 부모 메뉴 및 하위 메뉴 확인 docker exec sam-mng-1 php artisan tinker --execute=" \App\Models\Commons\Menu::withoutGlobalScopes() ->where('name', 'LIKE', '%회계%') ->orWhere('name', 'LIKE', '%세무%') ->get(['id', 'parent_id', 'name', 'url', 'sort_order']) ->each(fn(\$m) => print(\$m->id . ' | parent:' . \$m->parent_id . ' | sort:' . \$m->sort_order . ' | ' . \$m->name . ' | ' . \$m->url . PHP_EOL)); " # 2. 기존 하위 메뉴 sort_order 조정 (전자세금계산서 이하 +2) # (실행 전 현재 sort_order 확인 후 결정) # 3. 계정별원장 메뉴 추가 docker exec sam-mng-1 php artisan tinker --execute=" \App\Models\Commons\Menu::create([ 'tenant_id' => 1, 'parent_id' => <회계세무관리_ID>, 'name' => '계정별원장', 'url' => '/finance/account-ledger', 'icon' => 'book-open', 'sort_order' => <일반전표입력_sort + 1>, 'is_active' => true, ]); " # 4. 손익계산서 메뉴 추가 docker exec sam-mng-1 php artisan tinker --execute=" \App\Models\Commons\Menu::create([ 'tenant_id' => 1, 'parent_id' => <회계세무관리_ID>, 'name' => '손익계산서', 'url' => '/finance/income-statement', 'icon' => 'chart-bar', 'sort_order' => <일반전표입력_sort + 2>, 'is_active' => true, ]); " ``` --- ## 8. 구현 우선순위 | 순서 | 작업 | 설명 | |------|------|------| | **1** | `account_codes.sub_category` 데이터 정리 | 손익계산서 분류 기준 확보 | | **2** | 계정별원장 Controller + View | 더 단순한 기능부터 구현 | | **3** | 계정별원장 드릴다운 (전표 이동) | 금액 클릭 → 일반전표 화면 | | **4** | 손익계산서 Controller + View | 집계 로직 구현 | | **5** | 손익계산서 당기/전기 비교 | 전년 동기 데이터 조회 | | **6** | 메뉴 등록 | tinker로 메뉴 추가 | --- ## 9. 비즈니스 규칙 ### R1. 멀티테넌트 격리 - 모든 조회는 `tenant_id` 기반으로 격리한다 - `BelongsToTenant` Global Scope가 자동 적용된다 ### R2. 계정과목 테넌트별 차이 - 매출 계정, 매출원가 계정 등의 세부 과목은 테넌트마다 다를 수 있다 - 손익계산서는 `account_codes` 테이블의 `category` + `sub_category`로 동적 구성한다 - 신규 테넌트에는 기본 계정과목 세트가 자동 생성된다 (기존 시더 활용) ### R3. 삭제된 전표 제외 - `journal_entries.deleted_at IS NULL` 조건으로 soft delete된 전표를 제외한다 - `hometax_invoices.deleted_at IS NULL` 조건으로 삭제된 세금계산서를 제외한다 ### R4. 금액 표시 - 기본 단위: 원 (소수점 없음, 정수) - 음수 잔액: 괄호 표시 `(100,000)` 또는 `-100,000` - 천 단위 콤마 구분 --- ## 10. 참고 자료 | 자료 | 설명 | |------|------| | 더존 계정별원장 화면 | 경리팀 제공 참고 화면 (2026-03-19) | | 더존 손익계산서 화면 | 경리팀 제공 참고 화면 (2026-03-19) | | `JournalEntryController.php` | 기존 일반전표입력 컨트롤러 (계정과목/거래처 API 재활용) | | `AccountCode` 모델 | `mng/app/Models/Barobill/AccountCode.php` | | `JournalEntryLine` 모델 | `mng/app/Models/Finance/JournalEntryLine.php` | | `HometaxInvoiceJournal` 모델 | `mng/app/Models/Barobill/HometaxInvoiceJournal.php` | --- ## 관련 문서 - [features/finance/README.md](../../features/finance/README.md) — 재무관리 기능 개요 - [features/finance/payroll.md](../../features/finance/payroll.md) — 급여관리 - [system/database/finance.md](../../system/database/finance.md) — 재무 DB 스키마 - [changes/20260317_account_code_migration_mapping.md](../../changes/20260317_account_code_migration_mapping.md) — 계정과목 코드 변경 매핑 --- **최종 업데이트**: 2026-03-19