9e707be5ac
- app/modules/cupang: router/db/store/itemcode + 템플릿 5종 - cupang_db 전용(JSON 폴백 없음). CUPANG_DB_URL 미설정 시 안내 페이지 - 월간 달력 + 출고 묶음(헤더+라인), 박스 입수량 자동계산(서버 재계산) - 입고센터 관리(사용중 soft delete), 박스규칙 upsert, 엑셀 내보내기 - 상품 검색은 itemcode_db 읽기 전용(미설정 시 수동 입력) - MODULE_KEYS/메뉴/아이콘/라벨 동기화, scripts/sql/cupang_db_init.sql(멱등) - app/data/ gitignore 추가(PII) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
181 lines
9.8 KiB
PL/PgSQL
181 lines
9.8 KiB
PL/PgSQL
-- =====================================================================
|
|
-- cupang_db 초기화 스크립트 (PostgreSQL) — 쿠팡 밀크런 모듈
|
|
-- =====================================================================
|
|
-- 멱등(idempotent): 여러 번 실행해도 안전. 기존 데이터를 삭제하지 않는다.
|
|
--
|
|
-- ⚠️ 실행 전 사용자 승인 + 백업 확인 필수. DROP/TRUNCATE 없음.
|
|
--
|
|
-- 실행 방법 (운영 PostgreSQL 컨테이너명: postgres-db):
|
|
--
|
|
-- 1) DB / 역할 / 스키마 생성 (superuser 로 1회)
|
|
-- read -s -p "cupang_app password: " APP_PWD; echo
|
|
-- docker exec -i postgres-db psql -U postgres \
|
|
-- -v app_password="$APP_PWD" \
|
|
-- < scripts/sql/cupang_db_init.sql
|
|
--
|
|
-- 2) main-app .env 에 연결 정보 등록
|
|
-- CUPANG_DB_URL=postgresql://cupang_app:<APP_PWD>@postgres-db:5432/cupang_db
|
|
--
|
|
-- 3) main-app 재기동
|
|
-- cd /opt/www/main && docker compose up -d --build
|
|
--
|
|
-- 주의:
|
|
-- - 기존 DB 가 있으면 DROP 하지 않는다(CREATE DATABASE 는 미존재 시에만).
|
|
-- - 비밀번호는 절대 Git 에 커밋하지 않는다. psql -v 로만 전달.
|
|
-- - itemcode_db 는 이 스크립트가 건드리지 않는다(상품은 읽기 전용 참조).
|
|
-- =====================================================================
|
|
|
|
\set ON_ERROR_STOP on
|
|
|
|
-- DB 가 없을 때만 생성
|
|
SELECT 'CREATE DATABASE cupang_db ENCODING ''UTF8'' TEMPLATE template0'
|
|
WHERE NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'cupang_db')
|
|
\gexec
|
|
|
|
-- 앱 전용 로그인 역할 (expense_app 패턴과 동일)
|
|
SELECT 'CREATE ROLE cupang_app LOGIN PASSWORD ' || quote_literal(:'app_password')
|
|
WHERE NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'cupang_app')
|
|
\gexec
|
|
|
|
-- 항상 최신 비밀번호로 동기화
|
|
SELECT 'ALTER ROLE cupang_app WITH LOGIN PASSWORD ' || quote_literal(:'app_password')
|
|
\gexec
|
|
|
|
GRANT CONNECT ON DATABASE cupang_db TO cupang_app;
|
|
|
|
-- cupang_db 컨텍스트로 전환
|
|
\connect cupang_db
|
|
|
|
-- ── updated_at 자동 갱신 트리거 함수 (멱등: CREATE OR REPLACE) ──
|
|
CREATE OR REPLACE FUNCTION cupang_set_updated_at() RETURNS trigger AS $$
|
|
BEGIN
|
|
NEW.updated_at := now();
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- ════════════════════════════════════════════════════════════
|
|
-- 1) 입고센터
|
|
-- ════════════════════════════════════════════════════════════
|
|
CREATE TABLE IF NOT EXISTS cupang_centers (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
name TEXT NOT NULL UNIQUE,
|
|
active BOOLEAN NOT NULL DEFAULT TRUE,
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
DROP TRIGGER IF EXISTS trg_cupang_centers_updated ON cupang_centers;
|
|
CREATE TRIGGER trg_cupang_centers_updated
|
|
BEFORE UPDATE ON cupang_centers
|
|
FOR EACH ROW EXECUTE FUNCTION cupang_set_updated_at();
|
|
|
|
-- ════════════════════════════════════════════════════════════
|
|
-- 2) 박스 입수량 규칙
|
|
-- ════════════════════════════════════════════════════════════
|
|
CREATE TABLE IF NOT EXISTS cupang_box_rules (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
product_code TEXT NOT NULL UNIQUE,
|
|
product_name_snapshot TEXT,
|
|
box_name TEXT NOT NULL DEFAULT '쿠팡박스',
|
|
units_per_box INTEGER NOT NULL CHECK (units_per_box > 0),
|
|
active BOOLEAN NOT NULL DEFAULT TRUE,
|
|
memo TEXT NOT NULL DEFAULT '',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_cupang_box_rules_code ON cupang_box_rules (product_code);
|
|
|
|
DROP TRIGGER IF EXISTS trg_cupang_box_rules_updated ON cupang_box_rules;
|
|
CREATE TRIGGER trg_cupang_box_rules_updated
|
|
BEFORE UPDATE ON cupang_box_rules
|
|
FOR EACH ROW EXECUTE FUNCTION cupang_set_updated_at();
|
|
|
|
-- ════════════════════════════════════════════════════════════
|
|
-- 3) 출고 묶음 (헤더)
|
|
-- ════════════════════════════════════════════════════════════
|
|
CREATE TABLE IF NOT EXISTS cupang_shipments (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
document_no TEXT UNIQUE,
|
|
created_by TEXT NOT NULL,
|
|
document_date DATE NOT NULL,
|
|
ship_date DATE NOT NULL,
|
|
center_arrival_date DATE NOT NULL,
|
|
center_id BIGINT REFERENCES cupang_centers(id),
|
|
center_name_snapshot TEXT NOT NULL DEFAULT '',
|
|
ship_method TEXT NOT NULL DEFAULT '택배',
|
|
outbound_summary TEXT NOT NULL DEFAULT '',
|
|
worker TEXT NOT NULL DEFAULT '',
|
|
status TEXT NOT NULL DEFAULT '작성중',
|
|
memo TEXT NOT NULL DEFAULT '',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_cupang_ship_doc_date ON cupang_shipments (document_date);
|
|
CREATE INDEX IF NOT EXISTS idx_cupang_ship_ship_date ON cupang_shipments (ship_date);
|
|
CREATE INDEX IF NOT EXISTS idx_cupang_ship_arr_date ON cupang_shipments (center_arrival_date);
|
|
CREATE INDEX IF NOT EXISTS idx_cupang_ship_status ON cupang_shipments (status);
|
|
CREATE INDEX IF NOT EXISTS idx_cupang_ship_center ON cupang_shipments (center_id);
|
|
|
|
DROP TRIGGER IF EXISTS trg_cupang_shipments_updated ON cupang_shipments;
|
|
CREATE TRIGGER trg_cupang_shipments_updated
|
|
BEFORE UPDATE ON cupang_shipments
|
|
FOR EACH ROW EXECUTE FUNCTION cupang_set_updated_at();
|
|
|
|
-- ════════════════════════════════════════════════════════════
|
|
-- 4) 출고 라인
|
|
-- ════════════════════════════════════════════════════════════
|
|
CREATE TABLE IF NOT EXISTS cupang_shipment_lines (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
shipment_id BIGINT NOT NULL REFERENCES cupang_shipments(id) ON DELETE CASCADE,
|
|
line_no INTEGER NOT NULL,
|
|
product_code TEXT NOT NULL,
|
|
product_name_snapshot TEXT NOT NULL,
|
|
quantity INTEGER NOT NULL CHECK (quantity > 0),
|
|
box_rule_id BIGINT REFERENCES cupang_box_rules(id),
|
|
units_per_box INTEGER,
|
|
calculated_boxes INTEGER,
|
|
remainder_units INTEGER,
|
|
manual_box_text TEXT NOT NULL DEFAULT '',
|
|
memo TEXT NOT NULL DEFAULT '',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
UNIQUE (shipment_id, line_no)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_cupang_lines_shipment ON cupang_shipment_lines (shipment_id);
|
|
CREATE INDEX IF NOT EXISTS idx_cupang_lines_code ON cupang_shipment_lines (product_code);
|
|
|
|
DROP TRIGGER IF EXISTS trg_cupang_lines_updated ON cupang_shipment_lines;
|
|
CREATE TRIGGER trg_cupang_lines_updated
|
|
BEFORE UPDATE ON cupang_shipment_lines
|
|
FOR EACH ROW EXECUTE FUNCTION cupang_set_updated_at();
|
|
|
|
-- ════════════════════════════════════════════════════════════
|
|
-- 5) 초기 입고센터 seed (멱등: ON CONFLICT DO NOTHING)
|
|
-- sort_order 는 목록 순서대로 부여.
|
|
-- ════════════════════════════════════════════════════════════
|
|
INSERT INTO cupang_centers (name, sort_order) VALUES
|
|
('대구3', 1), ('인천32', 2), ('이천1', 3), ('인천42', 4), ('인천26', 5),
|
|
('인천16', 6), ('인천28', 7), ('안성8', 8), ('천안8(RC)', 9), ('시흥2', 10),
|
|
('인천36', 11), ('MGMH5', 12), ('XRC10(RC)', 13), ('인천14', 14), ('경기광주5', 15),
|
|
('경기광주3', 16), ('XRC06(RC)', 17), ('용인1', 18), ('인천30', 19), ('마장1', 20),
|
|
('안성4', 21), ('대구6', 22), ('전라광주2', 23), ('창원1', 24), ('고양1', 25),
|
|
('동탄1', 26), ('이천4', 27), ('XRC09(RC)', 28)
|
|
ON CONFLICT (name) DO NOTHING;
|
|
|
|
-- ════════════════════════════════════════════════════════════
|
|
-- 6) 권한 (cupang_app: CRUD only)
|
|
-- ════════════════════════════════════════════════════════════
|
|
GRANT USAGE ON SCHEMA public TO cupang_app;
|
|
GRANT SELECT, INSERT, UPDATE, DELETE ON
|
|
cupang_centers, cupang_box_rules, cupang_shipments, cupang_shipment_lines
|
|
TO cupang_app;
|
|
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO cupang_app;
|
|
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
|
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO cupang_app;
|
|
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
|
GRANT USAGE, SELECT ON SEQUENCES TO cupang_app;
|
|
|
|
SELECT 'cupang_db ready' AS status;
|