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기반으로 격리한다 BelongsToTenantGlobal 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/payroll.md — 급여관리
- system/database/finance.md — 재무 DB 스키마
- changes/20260317_account_code_migration_mapping.md — 계정과목 코드 변경 매핑
최종 업데이트: 2026-03-19