7e764d500e
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>
570 lines
26 KiB
Markdown
570 lines
26 KiB
Markdown
# 모모유통 — 도매 유통 관리 시스템 개발 스펙
|
||
|
||
> **버전**: 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` | 국세청 상태 재조회 |
|