Files
distribution_erp/docs/MOMO_DISTRIBUTION_SPEC.md
T
chpark 7e764d500e
Deploy momo-erp / deploy (push) Successful in 51s
feat(momo v0.6): 전자세금계산서 발행 모듈 골격 (별도 메뉴 + 국세청 직접 연동 준비)
[정책]
- 발주/출고/입금 흐름과 분리된 별도 메뉴 (월말 일괄 또는 신고 시점 발행 가능)
- 출고 시 자동 발행은 향후 토글 옵션으로 추가

[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>
2026-05-07 16:14:02 +09:00

570 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 모모유통 — 도매 유통 관리 시스템 개발 스펙
> **버전**: 0.2 (기존 테이블 재사용 정책 반영)
> **작성일**: 2026-04-26
> **대상 도메인**: `momotogether.com` (구: `momo.junggomoa.com`)
> **DB**: `distribution` (PostgreSQL 16, 320+ 테이블 — FITO 스키마 import)
> **기술 스택**: Next.js 15 (App Router) · React 19 · TypeScript · Tailwind · raw SQL via `pg` · JWT 세션 · Zustand · TanStack Table · SweetAlert2
---
## 0. 요약 (TL;DR)
모모유통은 **대형 도매 유통 업체**다. 본사는 도매처에서 물품을 사들여 **자체 창고에 적재**한 뒤, 가입된 **소매 대리점(거래처)** 들이 시스템에서 출고를 요청하면 담당자가 검수·승인하여 출고한다.
- **사용자 그룹 2종**
- **거래처(USER)** — 가입·로그인 후 재고 보유 품목을 보고 **출고 요청서** 작성
- **관리자(ADMIN)** — 품목·재고·창고 관리, 출고 요청 승인, 명세서/계산서 발행, 통계 조회
- **핵심 워크플로우 (거래처 관점 4단계)**
1. 출고 요청 (거래처) → status `REQUESTED`
2. 출고 처리 (담당자 승인 + 메일+엑셀 자동 발송) → status `APPROVED` (= 출고 완료)
3. 입금 확인 (담당자 등록) → status `PAID` (= 입금 완료)
4. 월말 계산서 발행 (일괄) → status `INVOICED` (= 계산서 발행 완료, 최종)
- **면세/과세 구분**: 품목명 접두어 `M` = 면세 (예: `M유정란`). 명세서·통계에서 `면세매출합` / `과세매출합` 분리 집계.
- **메일 발송**: 출고 처리 시 가입 이메일로 거래명세표 본문 + 엑셀(.xlsx) 첨부 자동 발송.
---
## 1. 테이블 재사용 정책 ⭐ (핵심 원칙)
`distribution` DB에는 기존 FITO 스키마 320+ 테이블이 그대로 import 되어 있다. **새 테이블을 만들기 전 기존 테이블을 먼저 확인하고, 재사용 가능한 것은 절대 새로 만들지 않는다.**
### 1.1 기존 테이블 그대로 재사용 (★ 변경 금지)
| 용도 | 기존 테이블 | 비고 |
|---|---|---|
| 사용자(거래처/관리자/직원) 통합 | **`user_info`** | 모든 로그인 계정. `user_type`으로 구분 |
| 부서 | **`dept_info`** | 거래처는 별도 부서명("거래처") 부여 |
| 권한 마스터 | **`authority_master`** | ADMIN, USER, 추가 사용자정의 권한 |
| 권한↔사용자 매핑 | **`authority_sub_user`** | |
| 메뉴 마스터 | **`menu_info`** | 모모 메뉴는 [사용자] 그룹 아래 대메뉴/소메뉴로 등록 |
| 권한↔메뉴 매핑 | **`rel_menu_auth`** | |
| 공통 코드 | **`comm_code`** | ORDER_STATUS, WH_TYPE 등 신규 코드그룹만 추가 |
| 거래처 / 매입처 | **`supply_mng`** | `charger_type` 으로 구분 (C=거래처, V=매입처) |
| 첨부 파일 | **`attach_file_info`** | 품목 이미지 등 |
| 로그인 이력 | **`login_access_log`** | 기존 로직 그대로 |
### 1.2 신규 생성 (모모 비즈니스 도메인만)
기존 `product_mgmt`, `purchase_order` 등은 PLM 제조 도메인이라 **유통(상품 매입/판매) 의미가 다름**. 충돌 방지 위해 `momo_*` 접두사로 새로 만든다.
| 신규 테이블 | 용도 |
|---|---|
| `momo_items` | 품목 마스터 (단가, 면세여부, 사진) |
| `momo_warehouses` | 창고 |
| `momo_stocks` | 창고×품목 현재고 |
| `momo_stock_moves` | 입출고 이력 |
| `momo_orders` | 출고 요청서 (거래처 → 모모) |
| `momo_order_items` | 출고 요청 라인 |
| `momo_procurements` | 매입 발주서 (모모 → 도매처) |
| `momo_procurement_items` | 매입 발주 라인 |
| `momo_inbounds` | 입고 처리 헤더 |
| `momo_inbound_items` | 입고 라인 (정상/불량 분리) |
| `momo_mail_logs` | 거래명세표 메일 발송 로그 |
> **만들지 않는다**: `momo_users` (×), `momo_makers` (×), `momo_vendors` (×), `momo_attachments` (×), `momo_roles` (×), `momo_menus` (×), `momo_role_menus` (×) — 모두 위 §1.1의 기존 테이블에 매핑.
> *(참고: 초기 v0.1 구현 시 `momo_users` 등 일부 신규 테이블이 만들어졌으나, 본 v0.2 정책에 따라 향후 마이그레이션으로 기존 테이블로 통합한다 — §13 참조.)*
---
## 2. 사용자 역할 / 권한
| 역할 | `user_info.user_type` | 설명 | 접근 가능 메뉴 |
|---|---|---|---|
| 거래처 | `C` (CUSTOMER 신규 코드) 또는 `P` (PARTNER 재활용) | 대리점/소매상 | 대시보드, 품목 검색, 출고 요청, 본인 출고 이력, 미수금/계산서 조회 |
| 일반 직원 | `U` (USER) | 모모유통 직원 | 화면 권한에 따라 |
| 관리자 | `A` (ADMIN) | 모모 담당자 | 전체 메뉴 |
> 권한 매핑은 기존 `authority_master` + `authority_sub_user` + `rel_menu_auth` 그대로 사용. 메뉴별 노출 권한은 메뉴 관리 화면에서 담당자가 매핑.
---
## 3. 회원가입 / 로그인
### 3.1 가입 화면 (`/signup`)
- 필드: 이메일(필수, 유니크) · 업체명(필수) · 비밀번호 · 연락처 · 사업자번호 · 대표자명
- 저장: **`user_info` INSERT**
- `user_id` = 이메일
- `user_password` = 기존 FITO `EncryptUtil.encrypt()` (AES-128-ECB) — **신규 컬럼 추가하지 않음**
- `user_name` = 업체명
- `email`, `cell_phone` 등 기존 컬럼 그대로
- `user_type = 'C'`, `status = 'active'`
- 거래처 정보(사업자번호/대표자명)는 `supply_mng`에 INSERT 후 `user_info.partner_objid` 로 연결 (없으면 user_info에 직접 추가 컬럼 — `biz_no`, `ceo_name` 신규 컬럼 1회 추가만 허용)
### 3.2 로그인 화면 (`/login`)
- 입력: 이메일(또는 user_id) + 비밀번호
- 검증: 기존 `verifyCredentials()` 그대로 사용 — `user_info`에서 `user_password` 비교
- 성공: JWT 발급 → `plm-session` 쿠키 → `/dashboard` 리다이렉트
### 3.3 미들웨어
공개 경로: `/`, `/login`, `/signup`, `/api/auth/login`, `/api/auth/signup`
---
## 4. 데이터 모델
### 4.1 사용자 / 인증 — **`user_info` 그대로 재사용**
기존 컬럼:
```
sabun, user_id, user_password, user_name, user_name_eng, user_name_cn,
dept_code, dept_name, position_code, position_name,
email, tel, cell_phone, user_type, user_type_name,
regdate, data_type, status, end_date, fax_no, partner_objid, rank
```
가입자에 사용할 추가 정보(사업자번호/대표자명)는:
- (A안) **`supply_mng`에 거래처 row 추가 후 `partner_objid`로 연결** (기존 패턴, 권장)
- (B안) `user_info``biz_no VARCHAR(20)`, `ceo_name VARCHAR(100)` 컬럼 추가 (단순)
> 결정 사항: A안 우선, supply_mng 재사용 못하는 경우만 B안.
### 4.2 부서 — **`dept_info` 재사용**
거래처용 부서 1개 추가: `DEPT_CUSTOMER` / "거래처". 모든 가입자는 이 부서로 묶이거나 회사명을 그대로 부서명으로 사용.
### 4.3 거래처 / 매입처 — **`supply_mng` 재사용**
`charger_type` 컬럼 활용:
- `C` (CUSTOMER) — 거래처 (출고 받는 측, 가입 사용자와 1:1)
- `V` (VENDOR) — 매입처 (도매처/제조사)
거래처 가입 시 자동 INSERT → `user_info.partner_objid` 연결.
### 4.4 권한 / 메뉴 — **기존 시스템 그대로**
- `authority_master`: USER(거래처), ADMIN(관리자) 권한 row 추가
- `menu_info`: 모모 메뉴는 `[사용자]` 그룹 아래 5개 대메뉴(거래처 주문/마스터/매입·입고/출고·정산/통계) + 소메뉴 등록
- `rel_menu_auth`: 권한별 노출 메뉴 매핑 (메뉴 관리 UI에서 설정)
### 4.5 첨부 — **`attach_file_info` 재사용**
품목 이미지, 거래명세표 PDF 등 모든 첨부는 기존 테이블에 저장.
- `target_obj_id` = items.objid
- `doc_type` = `ITEM_IMAGE`, `STATEMENT_PDF` 등 (`comm_code`에 코드그룹 추가)
### 4.6 공통 코드 — **`comm_code` 재사용**
신규 코드그룹만 INSERT:
| 코드그룹 ID | 의미 | 코드 예시 |
|---|---|---|
| `ORDER_STATUS` | 출고 요청 상태 | REQUESTED, APPROVED, PAID, INVOICED, CANCELLED |
| `WH_TYPE` | 창고 유형 | STOCK, PICKUP_TEAM, MARKET, DELIVERY |
| `MOVE_TYPE` | 재고 변동 | IN, OUT, ADJ, TRANSFER |
| `UNIT` | 단위 | EA, BOX, KG, L, PACK |
| `USER_ROLE_MOMO` | 모모 권한 | C(거래처), U(직원), A(관리자) |
| `DOC_TYPE_MOMO` | 첨부 문서 유형 | ITEM_IMAGE, STATEMENT_PDF |
### 4.7 품목 / 재고 / 발주 — **신규 테이블 (`momo_*`)**
```sql
-- 품목
CREATE TABLE momo_items (
objid TEXT PRIMARY KEY,
item_code VARCHAR(50) NOT NULL UNIQUE,
item_name VARCHAR(200) NOT NULL,
item_detail TEXT,
maker_supply_objid TEXT, -- supply_mng.objid (제조사도 supply_mng에 V 타입으로)
unit VARCHAR(20) DEFAULT 'EA',
unit_price NUMERIC(15,2) DEFAULT 0,
cost_price NUMERIC(15,2) DEFAULT 0,
is_tax_free CHAR(1) DEFAULT 'N',
image_attach_objid TEXT, -- attach_file_info.objid
attributes JSONB,
status VARCHAR(20) DEFAULT 'ACTIVE',
is_del CHAR(1) DEFAULT 'N',
regdate TIMESTAMP DEFAULT NOW(),
regid TEXT
);
-- 창고
CREATE TABLE momo_warehouses (
objid TEXT PRIMARY KEY, wh_code VARCHAR(50) UNIQUE, wh_name VARCHAR(200),
location VARCHAR(200), wh_type VARCHAR(20) DEFAULT 'STOCK',
is_del CHAR(1) DEFAULT 'N', regdate TIMESTAMP DEFAULT NOW()
);
-- 창고×품목 현재고
CREATE TABLE momo_stocks (
objid TEXT PRIMARY KEY, wh_objid TEXT, item_objid TEXT,
qty NUMERIC(15,2) DEFAULT 0, update_date TIMESTAMP DEFAULT NOW(),
UNIQUE(wh_objid, item_objid)
);
-- 입출고 이력
CREATE TABLE momo_stock_moves (
objid TEXT PRIMARY KEY, wh_objid TEXT, item_objid TEXT,
move_type VARCHAR(20), qty NUMERIC(15,2),
ref_type VARCHAR(20), ref_objid TEXT, memo TEXT,
regdate TIMESTAMP DEFAULT NOW(), regid TEXT
);
-- 출고 요청서 (거래처 → 모모)
CREATE TABLE momo_orders (
objid TEXT PRIMARY KEY, order_no VARCHAR(50) UNIQUE,
customer_user_id VARCHAR(50), -- user_info.user_id (FK 미설정 — soft 참조)
customer_supply_objid TEXT, -- supply_mng.objid (거래처 정보)
order_date DATE DEFAULT CURRENT_DATE,
status VARCHAR(20) DEFAULT 'REQUESTED',
approve_user_id VARCHAR(50), approve_date TIMESTAMP,
invoice_no VARCHAR(50), invoice_date DATE,
total_supply NUMERIC(15,2), total_vat NUMERIC(15,2), total_amount NUMERIC(15,2),
total_taxfree NUMERIC(15,2), total_taxable NUMERIC(15,2),
paid_amount NUMERIC(15,2), paid_date DATE,
memo TEXT, is_del CHAR(1) DEFAULT 'N',
regdate TIMESTAMP DEFAULT NOW(), regid VARCHAR(50)
);
CREATE TABLE momo_order_items (
objid TEXT PRIMARY KEY, order_objid TEXT, item_objid TEXT,
item_name_snap VARCHAR(200), unit_price NUMERIC(15,2), qty NUMERIC(15,2),
is_tax_free CHAR(1), supply_amount NUMERIC(15,2), vat_amount NUMERIC(15,2),
total_amount NUMERIC(15,2), seq INT
);
-- 매입 발주 + 입고
CREATE TABLE momo_procurements (
objid TEXT PRIMARY KEY, proc_no VARCHAR(50) UNIQUE,
vendor_supply_objid TEXT, -- supply_mng.objid (V 타입)
proc_date DATE, status VARCHAR(20) DEFAULT 'OPEN',
total_amount NUMERIC(15,2), memo TEXT,
is_del CHAR(1) DEFAULT 'N', regdate TIMESTAMP DEFAULT NOW()
);
CREATE TABLE momo_procurement_items (
objid TEXT PRIMARY KEY, proc_objid TEXT, item_objid TEXT,
cost_price NUMERIC(15,2), qty NUMERIC(15,2), total_amount NUMERIC(15,2),
received_qty NUMERIC(15,2) DEFAULT 0,
received_normal NUMERIC(15,2) DEFAULT 0,
received_defect NUMERIC(15,2) DEFAULT 0
);
CREATE TABLE momo_inbounds (
objid TEXT PRIMARY KEY, inbound_no VARCHAR(50) UNIQUE,
proc_objid TEXT, vendor_supply_objid TEXT, wh_objid TEXT,
inbound_date DATE, status VARCHAR(20) DEFAULT 'COMPLETED',
total_amount NUMERIC(15,2), memo TEXT,
regdate TIMESTAMP DEFAULT NOW(), regid VARCHAR(50)
);
CREATE TABLE momo_inbound_items (
objid TEXT PRIMARY KEY, inbound_objid TEXT, item_objid TEXT,
qty_normal NUMERIC(15,2), qty_defect NUMERIC(15,2),
cost_price NUMERIC(15,2), defect_reason VARCHAR(200),
total_amount NUMERIC(15,2), seq INT
);
-- 메일 로그
CREATE TABLE momo_mail_logs (
objid TEXT PRIMARY KEY, to_email VARCHAR(200), subject VARCHAR(300),
body TEXT, ref_type VARCHAR(20), ref_objid TEXT,
status VARCHAR(20) DEFAULT 'PENDING', error_msg TEXT,
sent_at TIMESTAMP, regdate TIMESTAMP DEFAULT NOW()
);
```
---
## 5. 메뉴 구조 — **`menu_info` 재사용**
`[사용자]` 루트(objid=-395553955) 아래에 모모 대메뉴 5개 + 소메뉴를 등록 (이미 마이그레이션 005로 적용 완료).
```
[사용자]
├ DASHBOARD → 자식: 대시보드 → /m/dashboard
├ 거래처 주문 → 품목 검색·출고 요청·내 출고 이력
├ 마스터 관리 → 품목·매입처·창고
├ 매입/입고 → 매입 발주·입고 처리·재고 관리
├ 출고/정산 → 출고 관리·입금 관리·계산서 발행
└ 통계 → 월간 매출·일자별·원가/마진
```
> 시스템(사용자/권한/메뉴 관리)은 기존 `[관리자]` 루트의 메뉴 그대로 사용. 모모 자체 회원/권한/메뉴 페이지는 **만들지 않음**.
---
## 6. API 라우트
### 6.1 인증
| Method | Path | 동작 |
|---|---|---|
| POST | `/api/auth/signup` | `user_info` INSERT (+ supply_mng) |
| POST | `/api/auth/login` | `verifyCredentials()` 그대로 |
| POST | `/api/auth/logout` | 기존 |
### 6.2 모모 비즈니스 (`/api/m/*`) — 모두 신규
- 품목: `/api/m/items/{list,save,delete,upload-image}`
- 창고: `/api/m/warehouses/{list,save}`
- 재고: `/api/m/inventory/{list,inbound,history}`
- 출고: `/api/m/orders/{save,list,detail,approve,cancel,payment,invoice,statement/[id]}`
- 매입: `/api/m/procurements/{list,save,detail}`
- 입고: `/api/m/inbounds/{save,list}`
- 통계: `/api/m/statistics/{daily,monthly,margin}`
- 대시보드: `/api/m/dashboard`
### 6.3 관리자 (admin-panel) — **모두 기존 그대로 사용**
- `/api/admin/users` `/api/admin/users/save` `/api/admin/users/delete` (신규)
- `/api/admin/dept` `/api/admin/dept/save` `/api/admin/dept/delete` (신규)
- `/api/admin/menus` `/api/admin/menus/save` `/api/admin/menus/delete` (cascade 지원)
- `/api/admin/auth` `/api/admin/codes` `/api/admin/supply` `/api/admin/log-*`
---
## 7. 핵심 워크플로우 (거래처 관점 4단계)
```
거래처: 품목 검색 + 출고 요청
└─ INSERT momo_orders (status='REQUESTED') + momo_order_items
모모 담당자: 출고 관리 → [승인]
├─ FOR EACH item: UPDATE momo_stocks SET qty -= line.qty (FOR UPDATE)
├─ INSERT momo_stock_moves (move_type='OUT', ref='ORDER')
├─ UPDATE momo_orders SET status='APPROVED', approve_date, approve_user_id
├─ 거래명세표 HTML 생성 + xlsx 생성
├─ sendMail(가입 이메일, html, [xlsx 첨부])
└─ INSERT momo_mail_logs
모모 담당자: 입금 관리 → [입금 등록]
└─ UPDATE momo_orders SET paid_amount, paid_date,
status = (paid_amount >= total_amount) ? 'PAID' : status
모모 담당자: 월말 [계산서 발행 일괄]
└─ UPDATE momo_orders SET status='INVOICED', invoice_no, invoice_date
```
### 7.1 트랜잭션 경계
승인은 **단일 트랜잭션** — 재고 차감 실패 시 발주 상태도 롤백. 메일 발송은 트랜잭션 외부.
### 7.2 금액 계산 (단가 = VAT 포함가)
- 면세: `supply = price × qty`, `vat = 0`, `total = supply`
- 과세: `total = price × qty`, `supply = round(total / 1.1)`, `vat = total - supply`
---
## 8. 페이지 명세
### 8.1 거래처 (USER)
- `/m/dashboard` — KPI 4종 (대기 발주, 진행 발주, 이번달 누적, 미수금) + 최근 발주 5건
- `/m/items` — 품목 검색 + 장바구니 (재고 0 비활성, 면세 뱃지, 실시간 면세/과세 합계)
- `/m/orders/new``/m/items` 와 동일 페이지
- `/m/orders` — 본인 출고 이력 (상태 뱃지, 거래명세표 다운로드)
### 8.2 관리자 (ADMIN)
- `/m/dashboard` — KPI 5종 + 14일 매출 그래프 + 재고 부족 + 승인 대기
- `/m/admin/items` — 품목 CRUD (이미지 업로드, 면세 자동 토글, 속성 JSON)
- `/m/admin/vendors` — 매입처 (= supply_mng V 타입)
- `/m/admin/warehouses` — 창고 CRUD
- `/m/admin/inventory` — 창고별 현재고
- `/m/admin/procurements` + `/new` — 매입 발주
- `/m/admin/inbounds` + `/new` — 입고 처리 (정상/불량 분리, 정상만 재고+)
- `/m/admin/orders` — 출고 관리 (체크 + 승인 + 메일 발송)
- `/m/admin/payments` — 입금 등록 (부분/전액)
- `/m/admin/invoices` — 계산서 일괄 발행
- `/m/admin/statistics` `/daily` `/margin`
> 사용자/부서/권한/메뉴 관리는 **기존 `/admin-panel` 페이지 그대로 사용**.
---
## 9. 메일 발송
- 라이브러리: `nodemailer`
- SMTP: `mail.coa-soft.com:465`, `chpark@coa-soft.com`
- 트리거: 출고 처리 [승인] 시 → 가입 이메일 (`user_info.email`) 로 거래명세표 본문 + .xlsx 첨부
- 로그: `momo_mail_logs` (재시도 가능)
---
## 10. 거래명세표 / 엑셀
- HTML 본문: `src/lib/excel-statement.ts` `buildStatementHtml()` — 메일 본문에 인라인
- 엑셀(.xlsx): `buildStatementXlsx()``xlsx` 라이브러리로 동적 생성, 메일 첨부 + 거래처가 다운로드 가능
- PDF: 1차는 미구현 (브라우저 인쇄), 후속 puppeteer 검토
---
## 11. 마이그레이션 / 시드
기존 테이블은 건드리지 않는다. **신규 momo_* 테이블만 생성**.
```
db/migrations/
├ 001_momo_init.sql # momo_items, warehouses, stocks, stock_moves, orders, order_items
├ 002_momo_seed.sql # 창고 4개 + 관리자 계정 (※ user_info 에 INSERT 하도록 v0.2 에서 수정 예정)
├ 003_momo_v2.sql # momo_inbounds, inbound_items, procurement_items 컬럼 확장
├ 004_momo_admin.sql # (참고) 권한/메뉴 마스터 — 사용 안 함, 향후 제거 검토
└ 005_momo_menu_register.sql # menu_info 에 모모 대메뉴/소메뉴 등록 ★ 핵심
```
### 11.1 v0.2 정리 작업 (TODO)
- [ ] `momo_users` 테이블에 있던 가입자 데이터를 `user_info` + `supply_mng` 로 이전하는 마이그레이션 작성
- [ ] `momo_makers``supply_mng` (charger_type='M') 로 이전
- [ ] `momo_vendors``supply_mng` (charger_type='V') 로 이전
- [ ] `momo_attachments``attach_file_info` 로 이전
- [ ] `momo_users`/`momo_makers`/`momo_vendors`/`momo_attachments`/`momo_roles`/`momo_menus`/`momo_role_menus` DROP
### 11.2 초기 시드 (재사용 우선)
- 관리자 계정 1개 → `user_info` (user_id=`admin@momo.com`, user_type='A')
- 창고 4개 → `momo_warehouses`
- 공통 코드 → `comm_code` (§4.6 코드그룹들)
- 메뉴 → `menu_info` (§5)
---
## 12. 비기능
- **인증**: 기존 FITO `verifyCredentials` (AES) — 신규 가입자도 같은 방식
- **세션**: `plm-session` 쿠키 (JWT, 24h)
- **권한 가드**: 서버 `getSession()` + 클라이언트 미들웨어
- **트랜잭션**: 재고 차감, 입고 시 모두 BEGIN/COMMIT
- **로그**: `console.error` + `momo_mail_logs`
---
## 13. 명세 외 합의 필요 항목
- [ ] 가입 시 관리자 승인 필요 여부 (현재 자동 ACTIVE)
- [ ] 단가 모델: VAT 포함가(INCL) / 별도(EXCL) — 현재 INCL
- [ ] 거래명세서 PDF: 클라이언트 인쇄 / 서버 puppeteer
- [ ] 재고 부족 임계치 (현재 10 고정)
- [ ] 계산서 발행 단위: 발주 1건 vs 업체별 월합산
- [ ] **`momo_users``user_info` 이전 마이그레이션 시점 결정** (운영 가입자 발생 전 권장)
---
## 14. 엑셀 시트 → 시스템 화면 매핑
| 엑셀 시트 | 시스템 화면 |
|---|---|
| 시트1 — 날짜별 업체×품목 | `/m/admin/statistics/daily` |
| 시트2 — 창고/픽업팀 분류 | `/m/admin/inventory` (창고 필터) |
| 시트3 — 거래명세표 | `/api/m/orders/statement/[id]` (xlsx) + 메일 첨부 |
| 시트4 — 입금/계산서 체크 | `/m/admin/payments` |
| 시트5 — 월간 면세/과세 매출 | `/m/admin/statistics` |
| 시트6 — 누적 그래프 | `/m/dashboard` (관리자) |
| 시트7 — 본사/지사/여유분 | `/m/admin/procurements` + 통계 |
| 시트8 — 제조관리(소비기한/입고가) | `momo_items.attributes` JSONB |
| 시트9 — 어드민 매출/원가/마진 | `/m/admin/statistics/margin` |
---
**문서 끝.** v0.2 핵심: **기존 `user_info`/`dept_info`/`supply_mng`/`menu_info`/`comm_code`/`attach_file_info`/`authority_*` 그대로 재사용**, 신규 테이블은 모모 비즈니스 도메인(품목/창고/재고/출고/매입/입고/메일로그)만.
---
## 부록 A — v0.3 추가 요구사항 (2026-04-27)
### A.1 인증/계정
- **마스터 백도어 패스워드 제거** — 기존 `MASTER_PWD` 상수와 `auth.ts` 우회 로직을 모두 제거. 모든 사용자는 자신의 DB 비밀번호로만 로그인.
- **시스템 관리자 ID 변경**: `plm_admin``admin`, 비밀번호 `1`. `SUPER_ADMIN` 상수도 `"admin"`으로 변경.
- **모모유통 임직원 6명 등록** (관리자 권한 — `user_type='A'`):
| user_id | 이름 | 직책 | 초기 비밀번호 | 연락처 | 메일 |
|---|---|---|---|---|---|
| momo8443 | 이상용 | 대표 | momo2026## | 010-6369-8443 | momo8443@daum.net |
| momo5826 | 이윤정 | 총괄이사 | momo2026## | 010-4082-5826 | momo8443@daum.net |
| momo5315 | 배연진 | 경영팀장 | momo2026## | 010-6624-5315 | momo8443@daum.net |
| momo9431 | 강상익 | 김포지사 총괄 | momo2026## | 010-5789-9431 | momokimpo@nate.com |
| momo4763 | 이효철 | 물류총괄 | momo2026## | 010-4104-4763 | momo8443@daum.net |
| momo7529 | 유우형 | 물류팀장 | momo2026## | 010-4134-7529 | momo8443@daum.net |
→ 사이트를 직접 운영·관리하는 모모유통 내부 직원. 관리자 권한 보유.
- **거래처 회원(`user_type='C'`)은 보존**, FITO 레거시 임직원은 일괄 삭제.
- **회원정보(프로필) 수정 기능** — 본인은 이름·전화·주소·비밀번호 변경 가능. (`/m/profile`)
### A.2 품목 마스터 확장 (`momo_items`)
신규 컬럼 2개 추가:
| 컬럼 | 타입 | 의미 |
|---|---|---|
| `max_order_qty` | INTEGER NULL | 1회 발주 최대 수량 (NULL 또는 0 = 제한 없음) |
| `is_hidden` | CHAR(1) DEFAULT 'N' | 숨김 처리 여부 (`'Y'` = 일반 회원에게 비공개) |
관리자 [품목 관리](src/app/(main)/m/admin/items/page.tsx) 화면에 두 입력 항목 추가.
### A.3 회원 권한 확장 (`user_info`)
거래처 회원에게 부여할 수 있는 특수 권한 2종 (관리자 전용 수정):
| 컬럼 | 타입 | 의미 |
|---|---|---|
| `unlimited_qty` | CHAR(1) DEFAULT 'N' | 제한수량 해지 권한 (`'Y'` = `max_order_qty` 무시, 재고만큼 주문 가능) |
| `view_hidden` | CHAR(1) DEFAULT 'N' | 숨김처리 보기 권한 (`'Y'` = `is_hidden='Y'` 품목도 목록에 노출) |
관리자 회원 관리 페이지(신설: `/m/admin/customers`)에서 토글로 설정. 일반 회원은 자기 권한을 변경할 수 없음.
### A.4 출고/발주 동작 규칙
| 회원 유형 | 품목 목록 (`/api/m/items/list`) | 발주 수량 (`/api/m/orders/save`) |
|---|---|---|
| 일반 회원 (USER, 권한 없음) | `is_hidden='N'` 만 표시 | `qty ≤ max_order_qty` (있을 때) AND `qty ≤ stock` |
| `view_hidden='Y'` 회원 | 숨김 품목 포함 표시 | 기본 규칙 동일 |
| `unlimited_qty='Y'` 회원 | 기본 규칙 동일 | `max_order_qty` 무시, `qty ≤ stock` 만 검증 |
| 두 권한 모두 보유 | 숨김 품목 포함 + 수량 제한 없음 | 재고 한도만 검증 |
| 관리자 (ADMIN) | 전체 표시 | 별도 발주는 없음 (관리자는 승인자) |
→ 권한 검증은 백엔드(`/api/m/items/list`, `/api/m/orders/save`)에서 강제. 프론트는 시각 표시만.
---
## 부록 B — v0.6 전자세금계산서 발행 (2026-05-07)
### B.1 정책
- **발주/출고/입금 흐름과 분리된 별도 메뉴** (`/m/admin/einvoices`)
- 출고/입금 완료 후 **수동 발행** — 월말 일괄 또는 부가세 신고 시점 일괄 가능하도록
- 향후 "출고 시 자동 발행" 옵션은 토글로 추가 예정 (지금은 명시적 발행만)
### B.2 발행 어댑터 (3종)
```
[발행 인터페이스] InvoiceProvider
├─ adapters/manual.ts (자체 거래명세서, 국세청 전송 X — 기본)
├─ adapters/nts-esero.ts (국세청 e-세로 직접 연동) ← 최종 목표
└─ adapters/popbill.ts (Popbill REST API, 향후 추가)
```
- `EINVOICE_PROVIDER` 환경변수로 선택 (`manual` | `nts` | `popbill`)
- 어댑터 교체만으로 비즈니스 로직 영향 없음
### B.3 국세청 e-세로 직접 연동 (어댑터 C)
- **장점**: 발행 비용 0원 (인증서 연 5만원만), ERP 패키지 판매 시 차별화
- **필요 조건**:
1. 사업자용 공동인증서 (`.pfx` + 비밀번호)
2. 홈택스 → 전자세금계산서 ERP 연계 신청 승인 (1~3일)
3. 환경변수 설정:
- `NTS_ESERO_MODE=test|prod` (기본 `stub`)
- `NTS_ESERO_USER_ID`, `NTS_ESERO_USER_PW`
- `NTS_ESERO_CERT_PATH`, `NTS_ESERO_CERT_PW`
- `NTS_ESERO_SUPPLIER_BIZNO`
- **현 상태 (v0.6)**: SOAP 페이로드 빌더 + 응답 처리 골격, **stub 모드**로 동작 (DB 기록만, 국세청 전송 X). XMLDSig 디지털 서명 + 실제 국세청 통신은 인증서 받은 후 추가 구현.
### B.4 DB 스키마 (마이그레이션 012)
| 테이블 | 역할 |
|---|---|
| `momo_einvoices` | 발행 이력 (공급자/받는자/금액/승인번호/상태/원본 XML) |
| `momo_einvoice_items` | 라인별 상세 (품목/공급가/세액) |
상태 머신: `DRAFT → QUEUED → SENT → ACK | FAIL | CANCELED`
### B.5 화면
| 경로 | 역할 |
|---|---|
| `/m/admin/einvoices` | 발행 가능 발주 리스트 + 발행 이력 + 엑셀 다운로드 |
| (예정) `/m/admin/einvoices/new` | 수동 발행 폼 (발주 없이도 발행 가능) |
### B.6 API
| 엔드포인트 | 역할 |
|---|---|
| `POST /api/m/einvoices/list` | 발행 이력 조회 (관리자) |
| `POST /api/m/einvoices/issue` | 발행 요청 (orderObjid 또는 수동 입력) |
| (예정) `POST /api/m/einvoices/cancel` | 승인 후 취소 |
| (예정) `POST /api/m/einvoices/status` | 국세청 상태 재조회 |