Files
sam-docs/system/erp-analysis/99-gap-analysis.md
권혁성 d4e5f62413 docs: [종합정비] Phase 1 시스템 현황 문서 14개 작성
- system/overview.md: 전체 아키텍처 개요
- system/api-structure.md: API 구조 (220 모델, 1027 엔드포인트, 18 라우트 도메인)
- system/react-structure.md: React 구조 (249 페이지, 612 컴포넌트)
- system/mng-structure.md: MNG 구조 (171 컨트롤러, 436 Blade 뷰)
- system/docker-setup.md: Docker 7 컨테이너 구성
- system/database/README.md + 9개 도메인 스키마 (270+ 테이블)
  - core, hr, sales, production, finance, boards, files, system, erp-analysis

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 18:03:13 +09:00

34 KiB
Raw Blame History

SAM ERP API 개발 명세서

기준 문서: SAM_ERP_Storyboard_D0.8_251216 작성일: 2025-12-17 상태: 개발 준비 완료


개발 범위 요약

구분 항목수 작업
기존 API 활용 12개 프론트엔드 연동만 진행
확장 개발 6개 기존 구조 활용, API 추가
신규 개발 8개 테이블 + API 신규 생성

Part 1: 기존 API (프론트엔드 연동)

아래 영역은 API 개발 완료. 프론트엔드 연동 시 참고.

영역 기존 API 스토리보드 참조
사원관리 /v1/employees/* 슬라이드 28-35
부서관리 /v1/departments/* 슬라이드 36-38
근태관리 /v1/attendances/* 슬라이드 23-27, 39-42
게시판 /v1/boards/* -
권한관리 /v1/roles/*, /v1/permissions/* 슬라이드 96
테넌트 /v1/tenants/* 슬라이드 14-22
메뉴 /v1/menus/* 슬라이드 8-13
거래처 /v1/clients/* 슬라이드 60-67
공통코드 /v1/settings/common/* 슬라이드 93-95
파일 /v1/files/* -
설정 /v1/settings/* -
프로필 /v1/users/me/*, /v1/profiles/* -

Part 2: 확장 개발 (기존 구조 활용)

2.1 휴가 관리

스펙

  • 스토리보드: 슬라이드 43-46, 100
  • 의존성: attendances 테이블 참조

테이블: leaves

CREATE TABLE leaves (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL,
    user_id BIGINT NOT NULL,
    leave_type VARCHAR(20) NOT NULL COMMENT '연차/반차/병가/경조사/출산/육아',
    start_date DATE NOT NULL,
    end_date DATE NOT NULL,
    days DECIMAL(3,1) NOT NULL COMMENT '사용일수',
    reason TEXT,
    status VARCHAR(20) DEFAULT 'pending' COMMENT 'pending/approved/rejected/cancelled',
    approved_by BIGINT NULL,
    approved_at TIMESTAMP NULL,
    reject_reason TEXT NULL,
    created_by BIGINT,
    updated_by BIGINT,
    deleted_at TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_tenant_user (tenant_id, user_id),
    INDEX idx_status (status),
    INDEX idx_dates (start_date, end_date)
);

테이블: leave_balances

CREATE TABLE leave_balances (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL,
    user_id BIGINT NOT NULL,
    year INT NOT NULL,
    total_days DECIMAL(4,1) NOT NULL DEFAULT 15 COMMENT '연간 부여일수',
    used_days DECIMAL(4,1) NOT NULL DEFAULT 0 COMMENT '사용일수',
    remaining_days DECIMAL(4,1) GENERATED ALWAYS AS (total_days - used_days) STORED,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY uk_tenant_user_year (tenant_id, user_id, year)
);

API 엔드포인트

GET    /v1/leaves                   # 목록 (필터: status, user_id, date_range)
POST   /v1/leaves                   # 신청
GET    /v1/leaves/{id}              # 상세
PATCH  /v1/leaves/{id}              # 수정 (pending 상태만)
DELETE /v1/leaves/{id}              # 취소 (pending 상태만)
POST   /v1/leaves/{id}/approve      # 승인
POST   /v1/leaves/{id}/reject       # 반려 (reject_reason 필수)

GET    /v1/leaves/balance           # 내 잔여휴가
GET    /v1/leaves/balance/{userId}  # 특정 사용자 잔여휴가

GET    /v1/settings/leave           # 휴가 설정
PUT    /v1/settings/leave           # 휴가 설정 수정

Request/Response 예시

// POST /v1/leaves
{
    "leave_type": "annual",
    "start_date": "2025-01-20",
    "end_date": "2025-01-21",
    "days": 2,
    "reason": "개인 사유"
}

// GET /v1/leaves/balance
{
    "year": 2025,
    "total_days": 15,
    "used_days": 3,
    "remaining_days": 12
}

2.2 근무/출퇴근 설정

스펙

  • 스토리보드: 슬라이드 97-99
  • 의존성: tenant_field_settings 활용 가능

테이블: work_settings

CREATE TABLE work_settings (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL UNIQUE,
    work_type VARCHAR(20) DEFAULT 'fixed' COMMENT 'fixed/flexible/custom',
    standard_hours INT DEFAULT 40 COMMENT '주당 소정근로시간',
    overtime_hours INT DEFAULT 12 COMMENT '주당 연장근로시간',
    overtime_limit INT DEFAULT 52 COMMENT '연장근로한도',
    work_days JSON COMMENT '["mon","tue","wed","thu","fri"]',
    start_time TIME DEFAULT '09:00:00',
    end_time TIME DEFAULT '18:00:00',
    break_minutes INT DEFAULT 60,
    break_start TIME DEFAULT '12:00:00',
    break_end TIME DEFAULT '13:00:00',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

테이블: attendance_settings

CREATE TABLE attendance_settings (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL UNIQUE,
    use_gps BOOLEAN DEFAULT FALSE,
    allowed_radius INT DEFAULT 100 COMMENT '허용반경(m)',
    hq_address VARCHAR(255),
    hq_latitude DECIMAL(10,8),
    hq_longitude DECIMAL(11,8),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

테이블: sites (현장)

CREATE TABLE sites (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL,
    name VARCHAR(100) NOT NULL,
    address VARCHAR(255),
    latitude DECIMAL(10,8),
    longitude DECIMAL(11,8),
    is_active BOOLEAN DEFAULT TRUE,
    created_by BIGINT,
    deleted_at TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_tenant (tenant_id)
);

API 엔드포인트

GET    /v1/settings/work            # 근무 설정 조회
PUT    /v1/settings/work            # 근무 설정 수정

GET    /v1/settings/attendance      # 출퇴근 설정 조회
PUT    /v1/settings/attendance      # 출퇴근 설정 수정

GET    /v1/sites                    # 현장 목록
POST   /v1/sites                    # 현장 등록
GET    /v1/sites/{id}               # 현장 상세
PUT    /v1/sites/{id}               # 현장 수정
DELETE /v1/sites/{id}               # 현장 삭제

2.3 카드/계좌 관리

스펙

  • 스토리보드: 슬라이드 101-104
  • 보안: 카드번호/비밀번호 암호화 필수

테이블: cards

CREATE TABLE cards (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL,
    card_company VARCHAR(50) NOT NULL COMMENT '카드사',
    card_number_encrypted TEXT NOT NULL COMMENT '암호화된 카드번호',
    card_number_last4 VARCHAR(4) NOT NULL COMMENT '끝 4자리',
    expiry_date VARCHAR(5) NOT NULL COMMENT 'MM/YY',
    card_password_encrypted TEXT COMMENT '암호화된 비밀번호 앞2자리',
    card_name VARCHAR(100) NOT NULL,
    status VARCHAR(20) DEFAULT 'active' COMMENT 'active/inactive',
    assigned_user_id BIGINT NULL,
    created_by BIGINT,
    deleted_at TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_tenant (tenant_id),
    INDEX idx_status (status)
);

테이블: bank_accounts

CREATE TABLE bank_accounts (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL,
    bank_code VARCHAR(10) NOT NULL,
    bank_name VARCHAR(50) NOT NULL,
    account_number VARCHAR(30) NOT NULL,
    account_holder VARCHAR(50) NOT NULL,
    account_name VARCHAR(100) NOT NULL COMMENT '계좌별칭',
    status VARCHAR(20) DEFAULT 'active' COMMENT 'active/inactive',
    assigned_user_id BIGINT NULL,
    is_primary BOOLEAN DEFAULT FALSE COMMENT '대표계좌',
    created_by BIGINT,
    deleted_at TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_tenant (tenant_id),
    INDEX idx_status (status)
);

API 엔드포인트

GET    /v1/cards                    # 카드 목록 (필터: status)
POST   /v1/cards                    # 카드 등록
GET    /v1/cards/{id}               # 카드 상세
PUT    /v1/cards/{id}               # 카드 수정
DELETE /v1/cards/{id}               # 카드 삭제
PATCH  /v1/cards/{id}/toggle        # 사용/정지 토글

GET    /v1/bank-accounts            # 계좌 목록 (필터: status)
POST   /v1/bank-accounts            # 계좌 등록
GET    /v1/bank-accounts/{id}       # 계좌 상세
PUT    /v1/bank-accounts/{id}       # 계좌 수정
DELETE /v1/bank-accounts/{id}       # 계좌 삭제
PATCH  /v1/bank-accounts/{id}/toggle      # 사용/정지 토글
PATCH  /v1/bank-accounts/{id}/set-primary # 대표계좌 설정

2.4 입금/출금 관리

스펙

  • 스토리보드: 슬라이드 68-77
  • 의존성: clients, bank_accounts 참조

테이블: deposits (입금)

CREATE TABLE deposits (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL,
    deposit_date DATE NOT NULL,
    client_id BIGINT NULL,
    client_name VARCHAR(100) COMMENT '비회원 거래처명',
    bank_account_id BIGINT NULL,
    amount DECIMAL(15,2) NOT NULL,
    payment_method VARCHAR(20) NOT NULL COMMENT 'cash/transfer/card/check',
    account_code VARCHAR(20) COMMENT '계정과목',
    description TEXT,
    reference_type VARCHAR(50) NULL COMMENT 'sales/receivable/etc',
    reference_id BIGINT NULL,
    created_by BIGINT,
    deleted_at TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_tenant_date (tenant_id, deposit_date),
    INDEX idx_client (client_id)
);

테이블: withdrawals (출금)

CREATE TABLE withdrawals (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL,
    withdrawal_date DATE NOT NULL,
    client_id BIGINT NULL,
    client_name VARCHAR(100),
    bank_account_id BIGINT NULL,
    amount DECIMAL(15,2) NOT NULL,
    payment_method VARCHAR(20) NOT NULL COMMENT 'cash/transfer/card/check',
    account_code VARCHAR(20) COMMENT '계정과목',
    description TEXT,
    reference_type VARCHAR(50) NULL COMMENT 'purchase/payable/payroll/etc',
    reference_id BIGINT NULL,
    created_by BIGINT,
    deleted_at TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_tenant_date (tenant_id, withdrawal_date),
    INDEX idx_client (client_id)
);

API 엔드포인트

GET    /v1/deposits                 # 입금 목록 (필터: date_range, client_id, payment_method)
POST   /v1/deposits                 # 입금 등록
GET    /v1/deposits/{id}            # 입금 상세
PUT    /v1/deposits/{id}            # 입금 수정
DELETE /v1/deposits/{id}            # 입금 삭제
GET    /v1/deposits/summary         # 입금 요약 (기간별 합계)

GET    /v1/withdrawals              # 출금 목록
POST   /v1/withdrawals              # 출금 등록
GET    /v1/withdrawals/{id}         # 출금 상세
PUT    /v1/withdrawals/{id}         # 출금 수정
DELETE /v1/withdrawals/{id}         # 출금 삭제
GET    /v1/withdrawals/summary      # 출금 요약

2.5 매출/매입 관리

스펙

  • 스토리보드: 슬라이드 78-87
  • 의존성: clients, deposits, withdrawals 참조

테이블: sales (매출)

CREATE TABLE sales (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL,
    sale_number VARCHAR(30) NOT NULL COMMENT '매출번호',
    sale_date DATE NOT NULL,
    client_id BIGINT NOT NULL,
    supply_amount DECIMAL(15,2) NOT NULL COMMENT '공급가액',
    tax_amount DECIMAL(15,2) NOT NULL COMMENT '세액',
    total_amount DECIMAL(15,2) NOT NULL COMMENT '합계',
    description TEXT,
    status VARCHAR(20) DEFAULT 'draft' COMMENT 'draft/confirmed/invoiced',
    tax_invoice_id BIGINT NULL COMMENT '세금계산서 ID',
    deposit_id BIGINT NULL COMMENT '입금 연결',
    created_by BIGINT,
    deleted_at TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY uk_tenant_number (tenant_id, sale_number),
    INDEX idx_tenant_date (tenant_id, sale_date),
    INDEX idx_client (client_id)
);

테이블: purchases (매입)

CREATE TABLE purchases (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL,
    purchase_number VARCHAR(30) NOT NULL,
    purchase_date DATE NOT NULL,
    client_id BIGINT NOT NULL,
    supply_amount DECIMAL(15,2) NOT NULL,
    tax_amount DECIMAL(15,2) NOT NULL,
    total_amount DECIMAL(15,2) NOT NULL,
    description TEXT,
    status VARCHAR(20) DEFAULT 'draft' COMMENT 'draft/confirmed',
    withdrawal_id BIGINT NULL COMMENT '출금 연결',
    created_by BIGINT,
    deleted_at TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY uk_tenant_number (tenant_id, purchase_number),
    INDEX idx_tenant_date (tenant_id, purchase_date),
    INDEX idx_client (client_id)
);

API 엔드포인트

GET    /v1/sales                    # 매출 목록
POST   /v1/sales                    # 매출 등록
GET    /v1/sales/{id}               # 매출 상세
PUT    /v1/sales/{id}               # 매출 수정
DELETE /v1/sales/{id}               # 매출 삭제
POST   /v1/sales/{id}/confirm       # 매출 확정
POST   /v1/sales/{id}/tax-invoice   # 세금계산서 발행 (바로빌 연동)

GET    /v1/purchases                # 매입 목록
POST   /v1/purchases                # 매입 등록
GET    /v1/purchases/{id}           # 매입 상세
PUT    /v1/purchases/{id}           # 매입 수정
DELETE /v1/purchases/{id}           # 매입 삭제
POST   /v1/purchases/{id}/confirm   # 매입 확정

2.6 보고서

스펙

  • 스토리보드: 슬라이드 106-109
  • 의존성: deposits, withdrawals, sales, purchases 집계

API 엔드포인트

GET    /v1/reports/daily                    # 일일 일보
       Query: date (기준일)
       Response: {
           previous_balance, daily_deposit, daily_withdrawal, current_balance,
           details: [{ type, client_name, account_code, deposit_amount, withdrawal_amount, description }]
       }

GET    /v1/reports/daily/export             # 일일 일보 엑셀 다운로드
       Query: date
       Response: Excel file

GET    /v1/reports/expense-estimate         # 지출 예상 내역서
       Query: year_month
       Response: {
           total_estimate, account_balance, expected_balance,
           items: [{ expected_date, item_name, amount, client_name, account_name }],
           monthly_summary: [{ month, total }]
       }

GET    /v1/reports/expense-estimate/export  # 엑셀 다운로드
       Query: year_month
       Response: Excel file

Part 3: 신규 개발

3.1 전자결재 모듈

스펙

  • 스토리보드: 슬라이드 47-59
  • 핵심 기능: 기안/결재/반려/참조

테이블: approval_forms (결재 양식)

CREATE TABLE approval_forms (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL,
    name VARCHAR(100) NOT NULL COMMENT '양식명',
    code VARCHAR(50) NOT NULL COMMENT '양식코드',
    category VARCHAR(50) COMMENT '분류',
    template JSON NOT NULL COMMENT '양식 템플릿 (필드 정의)',
    is_active BOOLEAN DEFAULT TRUE,
    created_by BIGINT,
    deleted_at TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY uk_tenant_code (tenant_id, code),
    INDEX idx_tenant (tenant_id)
);

테이블: approval_lines (결재선 템플릿)

CREATE TABLE approval_lines (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL,
    name VARCHAR(100) NOT NULL,
    steps JSON NOT NULL COMMENT '[{order, type: approval/agreement/reference, user_id, position}]',
    is_default BOOLEAN DEFAULT FALSE,
    created_by BIGINT,
    deleted_at TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_tenant (tenant_id)
);

테이블: approvals (결재 문서)

CREATE TABLE approvals (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL,
    document_number VARCHAR(50) NOT NULL,
    form_id BIGINT NOT NULL,
    title VARCHAR(200) NOT NULL,
    content JSON NOT NULL COMMENT '양식 데이터',
    status VARCHAR(20) DEFAULT 'draft' COMMENT 'draft/pending/approved/rejected/cancelled',
    drafter_id BIGINT NOT NULL COMMENT '기안자',
    drafted_at TIMESTAMP NULL,
    completed_at TIMESTAMP NULL,
    current_step INT DEFAULT 0,
    attachments JSON COMMENT '첨부파일 IDs',
    created_by BIGINT,
    deleted_at TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY uk_tenant_number (tenant_id, document_number),
    INDEX idx_tenant_status (tenant_id, status),
    INDEX idx_drafter (drafter_id)
);

테이블: approval_steps (결재 단계)

CREATE TABLE approval_steps (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    approval_id BIGINT NOT NULL,
    step_order INT NOT NULL,
    step_type VARCHAR(20) NOT NULL COMMENT 'approval/agreement/reference',
    approver_id BIGINT NOT NULL,
    status VARCHAR(20) DEFAULT 'pending' COMMENT 'pending/approved/rejected/skipped',
    comment TEXT,
    acted_at TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_approval (approval_id),
    INDEX idx_approver_status (approver_id, status)
);

API 엔드포인트

# 결재 문서
GET    /v1/approvals/drafts         # 기안함 (내가 기안한 문서)
GET    /v1/approvals/inbox          # 결재함 (내 결재 대기)
GET    /v1/approvals/completed      # 결재 완료함
GET    /v1/approvals/reference      # 참조함
POST   /v1/approvals                # 문서 기안
GET    /v1/approvals/{id}           # 문서 상세
PUT    /v1/approvals/{id}           # 문서 수정 (draft만)
DELETE /v1/approvals/{id}           # 문서 삭제/회수
POST   /v1/approvals/{id}/submit    # 결재 상신
POST   /v1/approvals/{id}/approve   # 승인 (comment 선택)
POST   /v1/approvals/{id}/reject    # 반려 (comment 필수)

# 결재선 템플릿
GET    /v1/approval-lines           # 결재선 목록
POST   /v1/approval-lines           # 결재선 생성
PUT    /v1/approval-lines/{id}      # 결재선 수정
DELETE /v1/approval-lines/{id}      # 결재선 삭제

# 결재 양식
GET    /v1/approval-forms           # 양식 목록
POST   /v1/approval-forms           # 양식 생성
GET    /v1/approval-forms/{id}      # 양식 상세
PUT    /v1/approval-forms/{id}      # 양식 수정
DELETE /v1/approval-forms/{id}      # 양식 삭제

상태 전이

draft → pending (상신) → approved (전체 승인)
                      → rejected (반려)
pending → cancelled (회수, 기안자만)
rejected → draft (재기안 시 새 문서 생성)

3.2 급여 관리

스펙

  • 스토리보드: 미상세 (회계관리 연계)
  • 의존성: employees, attendances, leaves

테이블: payrolls

CREATE TABLE payrolls (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL,
    user_id BIGINT NOT NULL,
    pay_year INT NOT NULL,
    pay_month INT NOT NULL,
    base_salary DECIMAL(15,2) NOT NULL COMMENT '기본급',
    overtime_pay DECIMAL(15,2) DEFAULT 0 COMMENT '연장근로수당',
    bonus DECIMAL(15,2) DEFAULT 0 COMMENT '상여금',
    allowances JSON COMMENT '수당 상세 [{name, amount}]',
    gross_salary DECIMAL(15,2) NOT NULL COMMENT '총지급액',
    income_tax DECIMAL(15,2) DEFAULT 0 COMMENT '소득세',
    resident_tax DECIMAL(15,2) DEFAULT 0 COMMENT '주민세',
    health_insurance DECIMAL(15,2) DEFAULT 0 COMMENT '건강보험',
    pension DECIMAL(15,2) DEFAULT 0 COMMENT '국민연금',
    employment_insurance DECIMAL(15,2) DEFAULT 0 COMMENT '고용보험',
    deductions JSON COMMENT '공제 상세',
    total_deductions DECIMAL(15,2) NOT NULL COMMENT '총공제액',
    net_salary DECIMAL(15,2) NOT NULL COMMENT '실수령액',
    status VARCHAR(20) DEFAULT 'draft' COMMENT 'draft/confirmed/paid',
    paid_at TIMESTAMP NULL,
    withdrawal_id BIGINT NULL COMMENT '출금 연결',
    created_by BIGINT,
    deleted_at TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY uk_tenant_user_month (tenant_id, user_id, pay_year, pay_month),
    INDEX idx_tenant_month (tenant_id, pay_year, pay_month)
);

API 엔드포인트

GET    /v1/payrolls                 # 급여 목록 (필터: year, month, user_id, status)
POST   /v1/payrolls                 # 급여 등록
GET    /v1/payrolls/{id}            # 급여 상세
PUT    /v1/payrolls/{id}            # 급여 수정 (draft만)
DELETE /v1/payrolls/{id}            # 급여 삭제 (draft만)
POST   /v1/payrolls/{id}/confirm    # 급여 확정
POST   /v1/payrolls/{id}/pay        # 지급 처리 (출금 연결)
GET    /v1/payrolls/{id}/payslip    # 급여명세서 조회
GET    /v1/payrolls/{id}/payslip/pdf # 급여명세서 PDF

POST   /v1/payrolls/calculate       # 급여 일괄 계산
       Body: { year, month, user_ids?: [] } // user_ids 없으면 전체

GET    /v1/settings/payroll         # 급여 설정 (세율, 보험료율 등)
PUT    /v1/settings/payroll         # 급여 설정 수정

3.3 대시보드

스펙

  • 스토리보드: 미상세 (진입점 화면)
  • 의존성: 전 모듈 집계

API 엔드포인트

GET    /v1/dashboard/summary
       Response: {
           today: { date, attendances_count, leaves_count, approvals_pending },
           finance: { monthly_deposit, monthly_withdrawal, balance },
           sales: { monthly_sales, monthly_purchases },
           tasks: { pending_approvals, pending_leaves }
       }

GET    /v1/dashboard/charts
       Query: period (week/month/quarter)
       Response: {
           deposit_trend: [{ date, amount }],
           withdrawal_trend: [{ date, amount }],
           sales_by_client: [{ client_name, amount }]
       }

GET    /v1/dashboard/notifications
       Query: limit (default 10)
       Response: {
           items: [{ id, type, message, is_read, created_at }]
       }

GET    /v1/dashboard/approvals
       Response: {
           items: [{ id, title, drafter_name, status, created_at }]
       }

3.4 AI 리포트

스펙

  • 스토리보드: 슬라이드 113
  • 의존성: 전 모듈 데이터 분석

테이블: ai_reports

CREATE TABLE ai_reports (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL,
    report_date DATE NOT NULL,
    report_type VARCHAR(50) NOT NULL COMMENT 'daily/weekly/monthly',
    content JSON NOT NULL COMMENT '리포트 내용',
    summary TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_tenant_date (tenant_id, report_date)
);

API 엔드포인트

GET    /v1/reports/ai               # AI 리포트 목록
POST   /v1/reports/ai/generate      # AI 리포트 생성
       Body: { report_type: 'daily'|'weekly'|'monthly', target_date? }
GET    /v1/reports/ai/{id}          # AI 리포트 상세
DELETE /v1/reports/ai/{id}          # AI 리포트 삭제

AI 리포트 출력 형식

{
    "report": [
        { "area": "지출분석", "status": "warning", "message": "...", "detail": "..." },
        { "area": "미수금", "status": "caution", "message": "...", "detail": "..." }
    ],
    "summary": "전체 요약 메시지"
}
  • status: warning(빨강), caution(주황), positive(녹색), normal(파랑)

3.5 가지급금 관리

스펙

  • 스토리보드: 슬라이드 110-112
  • 인정이자율: 4.6% (연도별 변동)

테이블: loans (가지급금)

CREATE TABLE loans (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL,
    user_id BIGINT NOT NULL COMMENT '가지급금 수령자',
    loan_date DATE NOT NULL COMMENT '지급일',
    amount DECIMAL(15,2) NOT NULL COMMENT '가지급금액',
    purpose TEXT COMMENT '사용목적',
    settlement_date DATE NULL COMMENT '정산일',
    settlement_amount DECIMAL(15,2) NULL COMMENT '정산금액',
    status VARCHAR(20) DEFAULT 'outstanding' COMMENT 'outstanding/settled/partial',
    withdrawal_id BIGINT NULL COMMENT '출금 연결',
    created_by BIGINT,
    deleted_at TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_tenant_user (tenant_id, user_id),
    INDEX idx_status (status)
);

API 엔드포인트

GET    /v1/loans                    # 가지급금 목록
POST   /v1/loans                    # 가지급금 등록
GET    /v1/loans/{id}               # 가지급금 상세
PUT    /v1/loans/{id}               # 가지급금 수정
DELETE /v1/loans/{id}               # 가지급금 삭제
POST   /v1/loans/{id}/settle        # 정산 처리
       Body: { settlement_date, settlement_amount }

POST   /v1/loans/calculate-interest # 인정이자 계산
       Body: { year, user_id? }
       Response: {
           balance, interest_rate, recognized_interest,
           corporate_tax, income_tax, local_tax, total_tax
       }

GET    /v1/reports/loan-interest    # 인정이자 리포트
       Query: year

계산 공식

경과일수 = 정산일 - 지급일
일이자율 = 연이자율 / 365
인정이자 = 가지급금 × 일이자율 × 경과일수
법인세추가 = 인정이자 × 0.19
소득세추가 = 인정이자 × 0.35
지방소득세 = 소득세추가 × 0.10

3.6 구독/결제 관리

스펙

  • 스토리보드: 미상세 (SaaS 과금)
  • 별도 결제 시스템 연동 필요

테이블: subscriptions

CREATE TABLE subscriptions (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL UNIQUE,
    plan_code VARCHAR(50) NOT NULL COMMENT 'basic/standard/premium',
    status VARCHAR(20) DEFAULT 'active' COMMENT 'trial/active/expired/cancelled',
    started_at DATE NOT NULL,
    expires_at DATE NOT NULL,
    user_limit INT DEFAULT 5,
    storage_limit_mb INT DEFAULT 1024,
    auto_renew BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

테이블: payments

CREATE TABLE payments (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL,
    subscription_id BIGINT NOT NULL,
    amount DECIMAL(10,2) NOT NULL,
    payment_method VARCHAR(20) COMMENT 'card/transfer',
    status VARCHAR(20) DEFAULT 'pending' COMMENT 'pending/completed/failed/refunded',
    paid_at TIMESTAMP NULL,
    pg_transaction_id VARCHAR(100),
    receipt_url VARCHAR(500),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_tenant (tenant_id),
    INDEX idx_status (status)
);

API 엔드포인트

GET    /v1/subscriptions            # 구독 정보
POST   /v1/subscriptions            # 구독 신청/변경
PUT    /v1/subscriptions/{id}       # 구독 수정
DELETE /v1/subscriptions/{id}       # 구독 해지

GET    /v1/payments                 # 결제 내역
GET    /v1/payments/{id}            # 결제 상세
POST   /v1/payments/retry/{id}      # 결제 재시도

3.7 고객센터

스펙

  • 스토리보드: 미상세 (CS 기능)

테이블: support_tickets

CREATE TABLE support_tickets (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NULL COMMENT 'NULL이면 비회원 문의',
    user_id BIGINT NULL,
    ticket_number VARCHAR(20) NOT NULL,
    category VARCHAR(50) NOT NULL COMMENT '문의유형',
    title VARCHAR(200) NOT NULL,
    content TEXT NOT NULL,
    status VARCHAR(20) DEFAULT 'open' COMMENT 'open/in_progress/resolved/closed',
    priority VARCHAR(20) DEFAULT 'normal' COMMENT 'low/normal/high/urgent',
    assigned_to BIGINT NULL COMMENT '담당자',
    resolved_at TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY uk_ticket_number (ticket_number),
    INDEX idx_status (status)
);

테이블: support_ticket_replies

CREATE TABLE support_ticket_replies (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    ticket_id BIGINT NOT NULL,
    user_id BIGINT NULL COMMENT 'NULL이면 관리자 답변',
    is_staff BOOLEAN DEFAULT FALSE,
    content TEXT NOT NULL,
    attachments JSON,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_ticket (ticket_id)
);

테이블: faqs

CREATE TABLE faqs (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    category VARCHAR(50) NOT NULL,
    question VARCHAR(500) NOT NULL,
    answer TEXT NOT NULL,
    sort_order INT DEFAULT 0,
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_category (category)
);

API 엔드포인트

GET    /v1/support/tickets          # 문의 목록
POST   /v1/support/tickets          # 문의 등록
GET    /v1/support/tickets/{id}     # 문의 상세
PUT    /v1/support/tickets/{id}     # 문의 수정
POST   /v1/support/tickets/{id}/reply  # 답변/추가문의

GET    /v1/support/faq              # FAQ 목록 (필터: category)
GET    /v1/support/faq/{id}         # FAQ 상세

3.8 바로빌 연동

스펙

  • 스토리보드: 슬라이드 63, 78
  • 외부 API: 바로빌 (사업자조회, 세금계산서)

테이블: tax_invoices

CREATE TABLE tax_invoices (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT NOT NULL,
    invoice_type VARCHAR(20) NOT NULL COMMENT 'sales/purchase',
    invoice_number VARCHAR(50) NOT NULL COMMENT '승인번호',
    issue_date DATE NOT NULL,
    supplier_business_no VARCHAR(20) NOT NULL,
    supplier_name VARCHAR(100) NOT NULL,
    buyer_business_no VARCHAR(20) NOT NULL,
    buyer_name VARCHAR(100) NOT NULL,
    supply_amount DECIMAL(15,2) NOT NULL,
    tax_amount DECIMAL(15,2) NOT NULL,
    total_amount DECIMAL(15,2) NOT NULL,
    status VARCHAR(20) DEFAULT 'issued' COMMENT 'issued/cancelled',
    barobill_id VARCHAR(100) COMMENT '바로빌 문서 ID',
    reference_type VARCHAR(50) NULL COMMENT 'sales/purchases',
    reference_id BIGINT NULL,
    issued_at TIMESTAMP NULL,
    cancelled_at TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_tenant_date (tenant_id, issue_date),
    INDEX idx_invoice_number (invoice_number)
);

API 엔드포인트

POST   /v1/external/barobill/verify-business
       Body: { business_number }
       Response: { valid, company_name, ceo_name, business_type, ... }

POST   /v1/external/barobill/tax-invoice
       Body: { invoice_type, supplier, buyer, items, ... }
       Response: { id, invoice_number, barobill_id }

GET    /v1/external/barobill/tax-invoice/{id}
       Response: 세금계산서 상세

POST   /v1/external/barobill/tax-invoice/{id}/cancel
       Response: 취소 결과

GET    /v1/tax-invoices             # 세금계산서 목록 (내부 조회용)
GET    /v1/tax-invoices/{id}        # 세금계산서 상세

Part 4: 기획 확인 필요

아래 항목은 API 구현 전 비즈니스 로직 확정 필요

4.1 상태 전이 조건

대상 확인 항목 기본값 (확정 전)
테넌트 신청→승인→만료→해지 전이 조건 수동 전환
사원 휴직→복직/퇴사 전이 조건 수동 전환
결재 반려 후 재기안 프로세스 새 문서 생성
미수금 연체 판정 기준일 30일
악성채권 악성채권 판정 조건 90일 + 수동

4.2 모듈 간 연동

연동 확인 항목 기본값
전자결재→회계 지출결의서 승인 시 출금 자동 생성 수동 연결
휴가신청→결재 휴가가 결재 문서로 생성되는지 별도 관리
휴가승인→근태 승인 휴가가 근태 자동 반영 자동 반영
GPS출퇴근→근태 GPS 기록이 근태 자동 반영 자동 반영
급여→출금 급여 확정 시 출금 자동 생성 수동 연결

4.3 외부 연동

항목 확인 필요
바로빌 API 비용 호출당 비용, 월 한도
연동 은행 범위 지원 은행 목록
연동 실패 처리 Retry 정책, fallback

4.4 MES 메뉴

메뉴 확인 필요
영업관리 기존 Quote API 활용 여부
판매관리 상세 요구사항
구매관리 상세 요구사항

4.5 어음관리

항목 확인 필요
기능 필요성 사용 여부
상세 기능 등록/만기일/추심/부도

개발 순서

Phase 1: 확장 개발 (1-2주)
├── 2.1 휴가 관리
├── 2.2 근무/출퇴근 설정
├── 2.3 카드/계좌 관리
├── 2.4 입금/출금 관리
├── 2.5 매출/매입 관리
└── 2.6 보고서

Phase 2: 핵심 신규 (2-4주)
├── 3.1 전자결재 모듈 ★
├── 3.2 급여 관리
└── 3.3 대시보드

Phase 3: 추가 기능 (4-6주)
├── 3.4 AI 리포트
├── 3.5 가지급금 관리
└── 3.8 바로빌 연동

Phase 4: SaaS 기능 (별도)
├── 3.6 구독/결제 관리
└── 3.7 고객센터

참고 문서

문서 경로
DB 스키마 docs/system/database/README.md
시스템 아키텍처 docs/system/overview.md
HR API 분석 docs/features/hr/hr-api-analysis.md
기존 API 라우트 api/routes/api.php
스토리보드 상세 docs/system/erp-analysis/01-common.md ~ 08-reports.md