4f2543686a1b2b2874207a2e5ebb8e694b058b38
신규 컬럼: momo_items.limit_qty INTEGER (null/0 = 제한 없음) · ensureColumns 에서 ADD COLUMN IF NOT EXISTS 자동 보장 · 관리자 품목 폼: '한정 수량 (이번 마감 사이클 누적 상한)' 필드 추가 마감 사이클 정의 (sale_end_date 요일 기준): · 월요일 마감(DOW=1): 저번주 금~월 마감 시각 (마감일 -3일 00:00 ~ 마감일) · 화요일 마감(DOW=2): 저번주 금~화 마감 시각 (마감일 -4일 00:00 ~ 마감일) · 그 외: sale_start_date ~ sale_end_date (fallback) 누적 합산 대상: 같은 사이클 안에 등록된 momo_order_items.qty · status: REQUESTED/APPROVED/PAID/INVOICED (CANCELLED 제외) · kind='ITEM' (택배/용차/환불 제외) 검증 (모든 사용자/관리자/unlimited_qty 권한 무관 적용): · orders/save: 이번 요청 합 + reserved <= limit_qty 체크 · orders/items/add: 동일 검증 (트랜잭션 client 사용) · orders/items/update: newQty - oldQty 차이만 비교 (수량 증가 시) 신규 파일: · src/lib/momo-cycle.ts — getReservedQty(itemObjid, client?) 헬퍼 사용자 출고요청 화면(/m/orders/new): · 카드/리스트에 RESERVED_QTY 받아 '한정 잔여 N / 한정 M' 표시 · 한정 소진 시 '한정 수량 소진' 배지 + 담기 버튼 비활성 · 이미지 위 출고일 배지: 월요일 마감 → '수요일 출고', 화요일 마감 → '금요일 출고' · limit_qty 가 없는 품목은 무한대 (기존 동작 유지) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
모모유통 ERP (Distribution ERP)
식자재/도소매 유통·물류 업무 통합 관리 시스템. 거래처가 발주를 넣고, 본사가 출고·정산하고, 매입·입고·재고·세금계산서까지 한 화면에서 처리한다.
- 운영 도메인: https://momotogether.com
- Android 앱:
com.momotogether.app(TWA, Play 스토어 등록용 AAB / 사이드로드 APK) - 코드 저장소(원격):
git.junggomoa.com/chpark/distribution_erp - 사용자: (주)모모유통 본사·김포지사 + 계약 거래처 전용 — 일반 소비자용 아님
주요 기능
거래처(구매자) 화면
- 출고 요청 — 재고 있는 품목 선택 → 장바구니 → 발주 요청. 택배전용·용차비·환불 라인 자동/수동 추가. 상시판매·기간한정 품목 분리.
- 내 출고 이력 — 본인 발주 진행상태 추적.
- 푸시 알림 토글 — PWA 설치 후 새 상품 등록 시 알림 수신.
- 회원정보 수정 — 본인 정보·기준 명세표 변경.
본사/지사 운영
- 출고 처리 — 발주 검토 → 거래명세표 자동 생성(이미지/엑셀/메일 발송) → 재고 자동 차감.
- 입금 관리 — 출고 후 미수금 추적·입금 등록.
- 계산서·전자세금계산서 — 과세/면세 자동 분리. 국세청 ESERO 연동 어댑터(현재는 DB 기록).
- 매입 발주 — 공급업체별 발주서 작성·메일 발송. 발주지사(HQ/KIMPO) 선택.
- 매입 입금관리 — 진행상태와 결재상태(입금완료/미입금)를 분리 관리. 부분입고 시 입고금액 기준 입금 처리.
- 입고 처리 — 정상/불량 분리 입고, 유통기한·물류팀 최종완료자 체크리스트.
- 재고 관리 — 창고별 실시간 재고, 입출고 이력, 창고 간 이동, 유통기한 임박 알림.
마스터·통계·관리
- 품목 마스터 — 가격·원가·과세/면세·택배전용·1회 발주한도·판매기간(또는 상시판매) + 일괄 적용.
- 공급업체/거래처/창고/계산서 기준정보 관리.
- 푸시알림 게시판 — 관리자가 공지(이미지+본문)를 작성하고 선택한 구독자에게 푸시 발송. 사용자가 알림 탭하면 공지 페이지로 이동.
- 통계 — 대시보드, 월간/일자별 매출, 원가·마진, 거래처×일자 매출, 지사 수수료, 창고 이동 통계.
기술 스택
- 풀스택 단일 Next.js 프로젝트 — 프론트(React 19, App Router) + 백엔드(API Routes, Node.js) 한 저장소.
- TypeScript strict mode, Tailwind CSS.
- DB: 외부 PostgreSQL
121.156.99.3:5432/distribution— raw SQL(pg). - 인증: JWT(jose) + HTTP Cookie 세션 + AES-128-ECB(비밀번호).
- PWA:
manifest.json+ Service Worker(public/sw.js) — 푸시 핸들러·알림 위임(badge·icon). - 푸시:
web-push(VAPID) —momo_push_subscriptions에 endpoint 저장, 발송은lib/push.ts. - 거래명세표 캡처:
html-to-image(이미지 공유/저장). - 상태관리: Zustand (auth/menu/theme).
디렉토리 구조
src/
├── app/
│ ├── (auth)/login 로그인
│ ├── (main)/
│ │ ├── m/orders/new 거래처 — 출고요청
│ │ ├── m/orders 거래처 — 내 출고이력
│ │ ├── m/notices/[id] 공지 도달 페이지 (푸시 클릭 시)
│ │ ├── m/admin/orders 출고 처리
│ │ ├── m/admin/payments 입금 관리
│ │ ├── m/admin/invoices 계산서 발행
│ │ ├── m/admin/einvoices 전자세금계산서
│ │ ├── m/admin/procurements매입 발주서
│ │ ├── m/admin/proc-payments 매입 입금관리
│ │ ├── m/admin/inbounds 입고 처리
│ │ ├── m/admin/inventory 재고 관리
│ │ ├── m/admin/items 품목 마스터
│ │ ├── m/admin/notices 푸시알림 게시판
│ │ ├── m/admin/vendors 공급업체 관리
│ │ ├── m/admin/warehouses 창고 관리
│ │ ├── m/admin/statistics 통계 대시보드
│ │ └── profile 회원정보 수정
│ └── api/m/ 업무 API (orders/items/inbounds/push/notices …)
├── lib/
│ ├── db.ts PostgreSQL Pool (queryRows/queryOne/execute)
│ ├── auth.ts 인증 + 세션
│ ├── push.ts web-push 발송 + 구독 관리
│ ├── notices.ts 공지 테이블 자동 생성
│ ├── momo-proc.ts 매입 진행/결재 분리 마이그레이션
│ └── capture-share.ts 이미지 캡처/공유
├── components/
│ ├── layout/ Header / Sidebar
│ ├── grid/ DataGrid (TanStack Table)
│ ├── ui/ 버튼/입력/SearchableSelect 등
│ └── push-optin.tsx 푸시 알림 켜기/끄기 토글 (localStorage 영속)
└── store/ Zustand (auth/menu/theme)
public/
├── sw.js Service Worker — fetch 캐시 + push/notificationclick
├── manifest.json PWA 매니페스트
├── icon-{192,512}.png PWA 아이콘 (모모 로고)
├── badge-96.png 알림 상태바 단색 배지
└── .well-known/assetlinks.json TWA Digital Asset Links
android/
├── twa-manifest.json Bubblewrap TWA 빌드 설정 (notification delegation ON)
└── README.md APK/AAB 빌드 가이드 (PWABuilder / Bubblewrap)
각 디렉토리별 상세는 */CLAUDE.md 참고.
로컬 개발
npm install
npm run dev # http://localhost:3000
npm run build # 운영 빌드 검증
npm run lint
환경변수 (.env.development)
| 키 | 설명 |
|---|---|
DATABASE_URL |
PostgreSQL 접속 (예: postgresql://momo_app:****@121.156.99.3:5432/distribution) |
NEXTAUTH_URL |
로컬: http://localhost:3000 |
NEXTAUTH_SECRET |
JWT 서명 시크릿 |
AES_KEY |
16바이트 — 비밀번호 AES 키 (기존 데이터 호환 필요) |
MASTER_PWD |
마스터 비밀번호 (개발 편의) |
SMTP_HOST/USER/PASS/FROM |
거래명세표·계산서 메일 발송 |
MOMO_BANK_ACCOUNT, MOMO_PHONE |
거래명세표 공급자 정보 (기준명세표 미설정 폴백) |
VAPID_PUBLIC_KEY / VAPID_PRIVATE_KEY |
(선택) 웹푸시 VAPID — 미설정 시 lib/push.ts 의 기본키 사용 |
.env.momo.example 참고.
배포 — Gitea Actions 자동 배포
main 브랜치에 push 하면 .gitea/workflows/deploy.yml 이 자동 실행되어 운영 서버에서 git pull → docker compose build → up -d 까지 수행.
- 운영 서버:
121.156.99.3(SSH, chpark) - Compose 파일:
docker-compose.prod.yml - 컨테이너명:
momo-erp/ 이미지:momo-erp:latest - 리버스 프록시: Traefik (
traefik-net외부 네트워크, Let's Encrypt 자동발급) - 호스트:
momotogether.com,www.momotogether.com - 영구 스토리지: named volume
momo_data_storage↔/data_storage(업로드 이미지) - DB: 외부 공유 — 컨테이너 내부 DB 없음
⚠️ 수동 SSH 빌드 금지 — 자동 워크플로우와 충돌. 배포 검증은
build-sha.txt또는docker logs momo-erp로 확인.
Android 앱 (TWA)
com.momotogether.app — momotogether.com 을 감싸는 Trusted Web Activity.
- 알림 위임(notification delegation) ON — 웹 푸시가 "삼성 인터넷" 등 브라우저가 아니라 모모유통 앱 이름·아이콘으로 표시됨.
- AAB: Play 스토어 업로드용
- APK: 사이드로드(직접 설치) 테스트용
- 빌드 가이드:
android/README.md(PWABuilder 또는 Bubblewrap) - ⚠️ 기존 서명키 재사용 필수 — 키가 바뀌면
assetlinks.json지문이 안 맞아 위임 깨짐.
서비스워커 갱신 / 페이지 콘텐츠 변경은 APK 재빌드 불필요 — 서버 배포만으로 앱에도 반영됨.
코딩 컨벤션
상세 규칙은 .claude/rules/ 와 디렉토리별 CLAUDE.md 참고.
- SQL alias 대문자 유지:
SELECT col AS "OBJID"(큰따옴표 필수, 없으면 PG 가 소문자 반환) - 삭제 플래그:
COALESCE(is_del,'N') != 'Y' - objid 타입 변환:
objid::text AS "OBJID" - API 응답: 목록
{ RESULTLIST, TOTAL_CNT }, 단건{ success, data }, 저장{ success, objId? }, 오류{ success:false, message }+ 적절한 status code - 인증 가드: 모든 API 라우트 첫 줄에
getSession()/requireMomoAdmin()/requireMomoUser() - 신규 컬럼:
ensureColumns패턴으로 라우트 첫 호출 시 자동 ALTER (운영 무중단) - 푸시 알림 대상: 일반 발송은 모든 구독자 / 거래처 전용은
sendPush(payload, undefined, { generalOnly: true })
라이선스
내부용. 외부 배포·재사용 금지.
Description
Languages
TypeScript
94.6%
HTML
3.4%
JavaScript
1.8%
CSS
0.1%