Files
sam-docs/dev/dev_plans/account-ledger-income-statement-plan.md
김보곤 0b92442587 docs: [finance] 계정별원장·손익계산서 기획서 추가
- 더존 프로그램 참고하여 화면 구성/데이터 설계
- 계정별원장: 계정과목별 거래내역 조회 + 드릴다운
- 손익계산서: 기간별 손익 현황 + 당기/전기 비교
2026-03-19 11:02:27 +09:00

27 KiB

계정별원장 & 손익계산서 기획서

작성일: 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 → 날짜순 정렬
                       ▼
              월별 소계 / 누계 계산
                       ▼
              계정별원장 화면 출력

핵심 쿼리 로직

-- 일반전표 분개 라인
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

응답 구조

{
    "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

응답 구조

{
    "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 라우트 정의

// 계정별원장
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를 확인한 후, 일반전표입력과 전자세금계산서 사이에 배치한다.

# 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

관련 문서


최종 업데이트: 2026-03-19