feat(momo v0.6): 전자세금계산서 발행 모듈 골격 (별도 메뉴 + 국세청 직접 연동 준비)
Deploy momo-erp / deploy (push) Successful in 51s

[정책]
- 발주/출고/입금 흐름과 분리된 별도 메뉴 (월말 일괄 또는 신고 시점 발행 가능)
- 출고 시 자동 발행은 향후 토글 옵션으로 추가

[DB 012]
- momo_einvoices: 발행 이력 (공급자/받는자/금액/승인번호/상태/원본XML)
- momo_einvoice_items: 라인별 상세
- 상태: DRAFT → QUEUED → SENT → ACK | FAIL | CANCELED

[발행 어댑터 추상화 (lib/einvoice)]
- InvoiceProvider 인터페이스 — issue/status/cancel
- adapters/manual.ts: 자체 거래명세서 (국세청 전송 X, 기본)
- adapters/nts-esero.ts: 국세청 e-세로 직접 연동 골격
  · NTS_ESERO_MODE: stub | test | prod
  · stub 모드는 DB 기록만 (개발/CI 안전)
  · 실 통신은 사업자 공동인증서 + ERP 연계 승인 후 활성화
  · SOAP/XMLDSig 페이로드 빌더 골격 작성, 인증서 받으면 서명+전송 추가
- index.ts: EINVOICE_PROVIDER 환경변수로 어댑터 선택

[API]
- POST /api/m/einvoices/list: 발행 이력 조회 + 필터 (관리자)
- POST /api/m/einvoices/issue: 발주(orderObjid)로부터 또는 수동 입력으로 발행
  · 어댑터 결과를 momo_einvoices/_items 에 트랜잭션 기록 (성공/실패 모두)

[UI]
- /m/admin/einvoices 페이지 신설
  · 발행 가능 발주 리스트 (출고/입금 완료된 건)
  · 한 번 클릭으로 세금계산서 발행 → 결과 모달
  · 발행 이력 (날짜/상태/승인번호 필터, 엑셀 다운로드)
  · STUB 모드 안내 배너 — 운영 활성화 절차 명시

[문서]
- docs/MOMO_DISTRIBUTION_SPEC.md 부록 B (v0.6) 추가

다음 단계 (인증서 + ERP 연계 승인 후):
- nts-esero.ts 의 SOAP + XMLDSig 실제 구현
- NTS_ESERO_MODE=test 로 100건 검증
- NTS_ESERO_MODE=prod 전환

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
chpark
2026-05-07 16:14:02 +09:00
parent a336191153
commit 7e764d500e
9 changed files with 885 additions and 0 deletions
+73
View File
@@ -0,0 +1,73 @@
-- 012_einvoices.sql
-- v0.6 (2026-05-07)
-- 전자세금계산서 발행 이력 테이블 — 국세청 e-세로 직접 연동 + 향후 다른 어댑터 호환
BEGIN;
CREATE TABLE IF NOT EXISTS momo_einvoices (
objid TEXT PRIMARY KEY,
order_objid TEXT, -- 연결된 발주(있으면)
customer_objid TEXT NOT NULL, -- user_info.user_id (공급받는자)
invoice_kind VARCHAR(20) NOT NULL DEFAULT 'TAX', -- TAX(세금계산서) / TAXFREE(계산서) / RECEIPT(영수)
invoice_type VARCHAR(20) NOT NULL DEFAULT 'NORMAL', -- NORMAL / MODIFIED(수정) / CANCELED(취소)
issue_method VARCHAR(20) NOT NULL DEFAULT 'NTS', -- NTS(국세청 직접) / POPBILL / MANUAL
-- 공급자
supplier_biz_no VARCHAR(20),
supplier_name VARCHAR(200),
supplier_ceo VARCHAR(100),
supplier_address TEXT,
supplier_business VARCHAR(100), -- 업태
supplier_item VARCHAR(100), -- 종목
-- 공급받는자
buyer_biz_no VARCHAR(20),
buyer_name VARCHAR(200),
buyer_ceo VARCHAR(100),
buyer_address TEXT,
buyer_email VARCHAR(200),
buyer_phone VARCHAR(50),
-- 금액
total_supply NUMERIC(15,2) NOT NULL DEFAULT 0,
total_vat NUMERIC(15,2) NOT NULL DEFAULT 0,
total_amount NUMERIC(15,2) NOT NULL DEFAULT 0,
-- 국세청 식별자
nts_invoice_no VARCHAR(40), -- 국세청 승인번호 (24자리)
nts_response_code VARCHAR(10), -- 응답코드
nts_response_msg TEXT,
nts_sent_at TIMESTAMP, -- 국세청 전송 시각
nts_acknowledged CHAR(1) DEFAULT 'N', -- 승인 여부
-- 상태
status VARCHAR(20) NOT NULL DEFAULT 'DRAFT',
-- DRAFT(작성중) / QUEUED(전송대기) / SENT(전송완료) / ACK(승인완료) / FAIL(실패) / CANCELED(취소)
issue_date DATE NOT NULL DEFAULT CURRENT_DATE,
-- 원본 XML / 응답 (디버깅용)
request_xml TEXT,
response_xml TEXT,
-- 메타
memo TEXT,
is_del CHAR(1) DEFAULT 'N',
regdate TIMESTAMP DEFAULT NOW(),
regid TEXT,
update_date TIMESTAMP,
update_id TEXT
);
CREATE INDEX IF NOT EXISTS idx_momo_einvoices_order ON momo_einvoices(order_objid);
CREATE INDEX IF NOT EXISTS idx_momo_einvoices_buyer ON momo_einvoices(customer_objid, issue_date);
CREATE INDEX IF NOT EXISTS idx_momo_einvoices_status ON momo_einvoices(status, issue_date);
CREATE INDEX IF NOT EXISTS idx_momo_einvoices_nts ON momo_einvoices(nts_invoice_no);
CREATE TABLE IF NOT EXISTS momo_einvoice_items (
objid TEXT PRIMARY KEY,
einvoice_objid TEXT NOT NULL REFERENCES momo_einvoices(objid) ON DELETE CASCADE,
seq INT NOT NULL,
item_date DATE,
item_name VARCHAR(200) NOT NULL,
spec VARCHAR(100),
qty NUMERIC(15,2),
unit_price NUMERIC(15,2),
supply_amount NUMERIC(15,2) NOT NULL DEFAULT 0,
vat_amount NUMERIC(15,2) NOT NULL DEFAULT 0,
remark VARCHAR(200)
);
CREATE INDEX IF NOT EXISTS idx_momo_einvoice_items ON momo_einvoice_items(einvoice_objid, seq);
COMMIT;