feat(cupang): 출고방식 파렛트, 상태/문서번호 폼 제거, 폼·상세 레이아웃 개편
- 출고방식 선택지에 "파렛트" 추가 - 신규/수정 폼에서 상태·문서번호 입력 제거 (status는 작성중 기본 + 취소처리로만 변경) - document_no DB 컬럼 제거: init.sql 반영 + 기존 DB용 002 드롭 마이그레이션(승인 후 실행) - 폼: 공통 헤더(좌) / 품목 라인(우) 2열 배치 - 상세: 엑셀·상태변경 버튼 삭제, ◀◀ 달력(검정) 좌측, 수정 우측 정렬, 문서번호 표시 제거 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -323,14 +323,13 @@ class CupangDBStore:
|
|||||||
row = conn.execute(
|
row = conn.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO cupang_shipments
|
INSERT INTO cupang_shipments
|
||||||
(document_no, created_by, document_date, ship_date,
|
(created_by, document_date, ship_date,
|
||||||
center_arrival_date, center_id, center_name_snapshot,
|
center_arrival_date, center_id, center_name_snapshot,
|
||||||
ship_method, outbound_summary, worker, status, memo)
|
ship_method, outbound_summary, worker, status, memo)
|
||||||
VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
|
VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
|
||||||
RETURNING *
|
RETURNING *
|
||||||
""",
|
""",
|
||||||
(
|
(
|
||||||
h["document_no"] or None,
|
|
||||||
created_by.lower().strip(),
|
created_by.lower().strip(),
|
||||||
h["document_date"],
|
h["document_date"],
|
||||||
h["ship_date"],
|
h["ship_date"],
|
||||||
@@ -360,7 +359,7 @@ class CupangDBStore:
|
|||||||
row = conn.execute(
|
row = conn.execute(
|
||||||
"""
|
"""
|
||||||
UPDATE cupang_shipments
|
UPDATE cupang_shipments
|
||||||
SET document_no = %s, document_date = %s, ship_date = %s,
|
SET document_date = %s, ship_date = %s,
|
||||||
center_arrival_date = %s, center_id = %s,
|
center_arrival_date = %s, center_id = %s,
|
||||||
center_name_snapshot = %s, ship_method = %s,
|
center_name_snapshot = %s, ship_method = %s,
|
||||||
outbound_summary = %s, worker = %s, memo = %s
|
outbound_summary = %s, worker = %s, memo = %s
|
||||||
@@ -368,7 +367,6 @@ class CupangDBStore:
|
|||||||
RETURNING *
|
RETURNING *
|
||||||
""",
|
""",
|
||||||
(
|
(
|
||||||
h["document_no"] or None,
|
|
||||||
h["document_date"],
|
h["document_date"],
|
||||||
h["ship_date"],
|
h["ship_date"],
|
||||||
h["center_arrival_date"],
|
h["center_arrival_date"],
|
||||||
@@ -526,7 +524,6 @@ class CupangDBStore:
|
|||||||
center_id = None
|
center_id = None
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"document_no": str(header.get("document_no") or "").strip(),
|
|
||||||
"document_date": document_date,
|
"document_date": document_date,
|
||||||
"ship_date": ship_date,
|
"ship_date": ship_date,
|
||||||
"center_arrival_date": center_arrival_date,
|
"center_arrival_date": center_arrival_date,
|
||||||
|
|||||||
@@ -248,7 +248,6 @@ async def new_form(request: Request) -> HTMLResponse:
|
|||||||
async def create(
|
async def create(
|
||||||
request: Request,
|
request: Request,
|
||||||
lines_json: str = Form("[]"),
|
lines_json: str = Form("[]"),
|
||||||
document_no: str = Form(""),
|
|
||||||
document_date: str = Form(...),
|
document_date: str = Form(...),
|
||||||
ship_date: str = Form(...),
|
ship_date: str = Form(...),
|
||||||
center_arrival_date: str = Form(...),
|
center_arrival_date: str = Form(...),
|
||||||
@@ -257,7 +256,6 @@ async def create(
|
|||||||
ship_method: str = Form("택배"),
|
ship_method: str = Form("택배"),
|
||||||
outbound_summary: str = Form(""),
|
outbound_summary: str = Form(""),
|
||||||
worker: str = Form(""),
|
worker: str = Form(""),
|
||||||
status: str = Form("작성중"),
|
|
||||||
memo: str = Form(""),
|
memo: str = Form(""),
|
||||||
user: dict[str, Any] = Depends(_require_user),
|
user: dict[str, Any] = Depends(_require_user),
|
||||||
) -> RedirectResponse:
|
) -> RedirectResponse:
|
||||||
@@ -265,7 +263,6 @@ async def create(
|
|||||||
if store is None:
|
if store is None:
|
||||||
raise HTTPException(status_code=503, detail="cupang_db 미설정")
|
raise HTTPException(status_code=503, detail="cupang_db 미설정")
|
||||||
header = {
|
header = {
|
||||||
"document_no": document_no,
|
|
||||||
"document_date": document_date,
|
"document_date": document_date,
|
||||||
"ship_date": ship_date,
|
"ship_date": ship_date,
|
||||||
"center_arrival_date": center_arrival_date,
|
"center_arrival_date": center_arrival_date,
|
||||||
@@ -274,7 +271,6 @@ async def create(
|
|||||||
"ship_method": ship_method,
|
"ship_method": ship_method,
|
||||||
"outbound_summary": outbound_summary,
|
"outbound_summary": outbound_summary,
|
||||||
"worker": worker,
|
"worker": worker,
|
||||||
"status": status,
|
|
||||||
"memo": memo,
|
"memo": memo,
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
@@ -351,7 +347,6 @@ async def update(
|
|||||||
request: Request,
|
request: Request,
|
||||||
shipment_id: int,
|
shipment_id: int,
|
||||||
lines_json: str = Form("[]"),
|
lines_json: str = Form("[]"),
|
||||||
document_no: str = Form(""),
|
|
||||||
document_date: str = Form(...),
|
document_date: str = Form(...),
|
||||||
ship_date: str = Form(...),
|
ship_date: str = Form(...),
|
||||||
center_arrival_date: str = Form(...),
|
center_arrival_date: str = Form(...),
|
||||||
@@ -367,7 +362,6 @@ async def update(
|
|||||||
if store is None:
|
if store is None:
|
||||||
raise HTTPException(status_code=503, detail="cupang_db 미설정")
|
raise HTTPException(status_code=503, detail="cupang_db 미설정")
|
||||||
header = {
|
header = {
|
||||||
"document_no": document_no,
|
|
||||||
"document_date": document_date,
|
"document_date": document_date,
|
||||||
"ship_date": ship_date,
|
"ship_date": ship_date,
|
||||||
"center_arrival_date": center_arrival_date,
|
"center_arrival_date": center_arrival_date,
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ STATUSES: tuple[str, ...] = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
# 출고방식 기본 후보 (자유 입력 허용, 아래는 select 기본값)
|
# 출고방식 기본 후보 (자유 입력 허용, 아래는 select 기본값)
|
||||||
SHIP_METHODS: tuple[str, ...] = ("택배", "직접배송", "화물", "기타")
|
SHIP_METHODS: tuple[str, ...] = ("택배", "직접배송", "화물", "파렛트", "기타")
|
||||||
|
|
||||||
# 엑셀/시트 컬럼 순서 — 기존 구글시트와 동일하게 유지
|
# 엑셀/시트 컬럼 순서 — 기존 구글시트와 동일하게 유지
|
||||||
SHEET_COLUMNS: tuple[str, ...] = (
|
SHEET_COLUMNS: tuple[str, ...] = (
|
||||||
|
|||||||
@@ -6,18 +6,7 @@
|
|||||||
<section class="cpg">
|
<section class="cpg">
|
||||||
|
|
||||||
<div class="erp-page-actions cpg-actions">
|
<div class="erp-page-actions cpg-actions">
|
||||||
<a class="erp-btn erp-btn-primary" href="/cupang/{{ shipment.id }}/edit">수정</a>
|
<a class="erp-btn erp-btn-primary" href="/cupang/?date={{ shipment.ship_date }}">◀◀ 달력</a>
|
||||||
<a class="erp-btn erp-btn-outline" href="/cupang/{{ shipment.id }}/export">엑셀</a>
|
|
||||||
<a class="erp-btn erp-btn-outline" href="/cupang/?date={{ shipment.ship_date }}">달력으로</a>
|
|
||||||
|
|
||||||
<form method="post" action="/cupang/{{ shipment.id }}/status" class="cpg-inline-form">
|
|
||||||
<select class="erp-select" name="status">
|
|
||||||
{% for s in statuses %}
|
|
||||||
<option value="{{ s }}" {% if shipment.status == s %}selected{% endif %}>{{ s }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
<button type="submit" class="erp-btn erp-btn-outline">상태 변경</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<form method="post" action="/cupang/{{ shipment.id }}/delete" class="cpg-inline-form"
|
<form method="post" action="/cupang/{{ shipment.id }}/delete" class="cpg-inline-form"
|
||||||
onsubmit="return confirm('이 출고 묶음을 취소 처리합니다(상태=취소). 계속할까요?');">
|
onsubmit="return confirm('이 출고 묶음을 취소 처리합니다(상태=취소). 계속할까요?');">
|
||||||
@@ -28,6 +17,8 @@
|
|||||||
onsubmit="return confirm('이 출고를 완전 삭제합니다(복구 불가, 품목 포함). 계속할까요?');">
|
onsubmit="return confirm('이 출고를 완전 삭제합니다(복구 불가, 품목 포함). 계속할까요?');">
|
||||||
<button type="submit" class="erp-btn erp-btn-danger">삭제</button>
|
<button type="submit" class="erp-btn erp-btn-danger">삭제</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<a class="erp-btn erp-btn-primary cpg-push-right" href="/cupang/{{ shipment.id }}/edit">수정</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 헤더 -->
|
<!-- 헤더 -->
|
||||||
@@ -48,7 +39,6 @@
|
|||||||
<div><dt>입고센터</dt><dd>{{ shipment.center_name_snapshot or '—' }}</dd></div>
|
<div><dt>입고센터</dt><dd>{{ shipment.center_name_snapshot or '—' }}</dd></div>
|
||||||
<div><dt>출고방식</dt><dd>{{ shipment.ship_method }}</dd></div>
|
<div><dt>출고방식</dt><dd>{{ shipment.ship_method }}</dd></div>
|
||||||
<div><dt>작업자</dt><dd>{{ shipment.worker or '—' }}</dd></div>
|
<div><dt>작업자</dt><dd>{{ shipment.worker or '—' }}</dd></div>
|
||||||
<div><dt>문서번호</dt><dd>{{ shipment.document_no or '—' }}</dd></div>
|
|
||||||
<div class="cpg-full"><dt>출고/박스 요약</dt><dd>{{ shipment.outbound_summary or '—' }}</dd></div>
|
<div class="cpg-full"><dt>출고/박스 요약</dt><dd>{{ shipment.outbound_summary or '—' }}</dd></div>
|
||||||
<div class="cpg-full"><dt>메모</dt><dd>{{ shipment.memo or '—' }}</dd></div>
|
<div class="cpg-full"><dt>메모</dt><dd>{{ shipment.memo or '—' }}</dd></div>
|
||||||
</dl>
|
</dl>
|
||||||
|
|||||||
@@ -9,8 +9,10 @@
|
|||||||
<form id="cpg-form" method="post" action="{{ action }}">
|
<form id="cpg-form" method="post" action="{{ action }}">
|
||||||
<input type="hidden" name="lines_json" id="cpg-lines-json" value="[]" />
|
<input type="hidden" name="lines_json" id="cpg-lines-json" value="[]" />
|
||||||
|
|
||||||
<!-- ── 공통 헤더 ── -->
|
<div class="cpg-form-2col">
|
||||||
<div class="erp-card cpg-form-card">
|
|
||||||
|
<!-- ── 공통 헤더 (왼쪽) ── -->
|
||||||
|
<div class="erp-card cpg-form-card cpg-form-head">
|
||||||
<div class="cpg-card-head"><h2>공통 헤더</h2></div>
|
<div class="cpg-card-head"><h2>공통 헤더</h2></div>
|
||||||
<div class="cpg-header-grid">
|
<div class="cpg-header-grid">
|
||||||
<label class="erp-field"><span>작성일 *</span>
|
<label class="erp-field"><span>작성일 *</span>
|
||||||
@@ -45,17 +47,6 @@
|
|||||||
<label class="erp-field"><span>작업자</span>
|
<label class="erp-field"><span>작업자</span>
|
||||||
<input class="erp-input" type="text" name="worker"
|
<input class="erp-input" type="text" name="worker"
|
||||||
value="{{ shipment.worker if shipment else '' }}" /></label>
|
value="{{ shipment.worker if shipment else '' }}" /></label>
|
||||||
|
|
||||||
{% if mode == 'new' %}
|
|
||||||
<label class="erp-field"><span>상태</span>
|
|
||||||
<select class="erp-select" name="status">
|
|
||||||
{% for s in statuses %}<option value="{{ s }}">{{ s }}</option>{% endfor %}
|
|
||||||
</select></label>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<label class="erp-field"><span>문서번호(선택)</span>
|
|
||||||
<input class="erp-input" type="text" name="document_no"
|
|
||||||
value="{{ shipment.document_no or '' if shipment else '' }}" /></label>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label class="erp-field cpg-full"><span>출고/박스 요약 (수동 보정 메모)</span>
|
<label class="erp-field cpg-full"><span>출고/박스 요약 (수동 보정 메모)</span>
|
||||||
@@ -66,8 +57,8 @@
|
|||||||
<textarea class="erp-input" name="memo" rows="2">{{ shipment.memo if shipment else '' }}</textarea></label>
|
<textarea class="erp-input" name="memo" rows="2">{{ shipment.memo if shipment else '' }}</textarea></label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ── 품목 라인 ── -->
|
<!-- ── 품목 라인 (오른쪽) ── -->
|
||||||
<div class="erp-card cpg-form-card">
|
<div class="erp-card cpg-form-card cpg-form-lines">
|
||||||
<div class="cpg-card-head">
|
<div class="cpg-card-head">
|
||||||
<h2>품목 라인</h2>
|
<h2>품목 라인</h2>
|
||||||
<span class="erp-muted">
|
<span class="erp-muted">
|
||||||
@@ -90,6 +81,8 @@
|
|||||||
<button type="button" class="erp-btn erp-btn-outline" id="cpg-add-line">+ 라인 추가</button>
|
<button type="button" class="erp-btn erp-btn-outline" id="cpg-add-line">+ 라인 추가</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</div><!-- /cpg-form-2col -->
|
||||||
|
|
||||||
<div class="erp-page-actions">
|
<div class="erp-page-actions">
|
||||||
<button type="submit" class="erp-btn erp-btn-primary">저장</button>
|
<button type="submit" class="erp-btn erp-btn-primary">저장</button>
|
||||||
{% if mode == 'edit' %}
|
{% if mode == 'edit' %}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
.cpg { display: flex; flex-direction: column; gap: 16px; }
|
.cpg { display: flex; flex-direction: column; gap: 16px; }
|
||||||
.cpg-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
|
.cpg-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
|
||||||
|
/* 맨 오른쪽으로 밀기 (수정 버튼) */
|
||||||
|
.cpg-push-right { margin-left: auto; }
|
||||||
|
|
||||||
/* ── 레이아웃: 왼쪽 큰 달력 + 오른쪽 리스트 ── */
|
/* ── 레이아웃: 왼쪽 큰 달력 + 오른쪽 리스트 ── */
|
||||||
.cpg-layout {
|
.cpg-layout {
|
||||||
@@ -68,6 +70,16 @@
|
|||||||
|
|
||||||
/* ── 폼 ── */
|
/* ── 폼 ── */
|
||||||
.cpg-form-card { padding: 16px; margin-bottom: 16px; }
|
.cpg-form-card { padding: 16px; margin-bottom: 16px; }
|
||||||
|
|
||||||
|
/* 공통 헤더(좌) + 품목 라인(우) 2열 */
|
||||||
|
.cpg-form-2col {
|
||||||
|
display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-start;
|
||||||
|
}
|
||||||
|
.cpg-form-head { flex: 1 1 380px; min-width: 0; margin-bottom: 0; }
|
||||||
|
.cpg-form-lines { flex: 1 1 520px; min-width: 0; margin-bottom: 0; }
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.cpg-form-head, .cpg-form-lines { flex-basis: 100%; }
|
||||||
|
}
|
||||||
.cpg-card-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 12px; flex-wrap: wrap; }
|
.cpg-card-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 12px; flex-wrap: wrap; }
|
||||||
.cpg-card-head h2 { font-size: 16px; font-weight: 600; margin: 0; }
|
.cpg-card-head h2 { font-size: 16px; font-weight: 600; margin: 0; }
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
-- =====================================================================
|
||||||
|
-- cupang_db 마이그레이션 002 — cupang_shipments.document_no 컬럼 제거
|
||||||
|
-- =====================================================================
|
||||||
|
-- 사유: 출고 폼에서 "문서번호" 항목 제거(미사용).
|
||||||
|
-- 멱등: IF EXISTS. 운영 적용 전 백업 권장(DROP COLUMN 은 되돌릴 수 없음).
|
||||||
|
--
|
||||||
|
-- 실행:
|
||||||
|
-- docker exec -i postgres-db psql -U postgres -d cupang_db \
|
||||||
|
-- < scripts/sql/cupang_db_002_drop_document_no.sql
|
||||||
|
--
|
||||||
|
-- 주의: 이 컬럼에 보관된 값이 있으면 함께 삭제된다. 현재 폼에서 입력받지
|
||||||
|
-- 않으므로 값이 없거나 NULL 일 가능성이 높다. 확인 후 실행:
|
||||||
|
-- SELECT count(*) FROM cupang_shipments WHERE document_no IS NOT NULL AND document_no <> '';
|
||||||
|
-- =====================================================================
|
||||||
|
|
||||||
|
\set ON_ERROR_STOP on
|
||||||
|
\connect cupang_db
|
||||||
|
|
||||||
|
ALTER TABLE cupang_shipments DROP COLUMN IF EXISTS document_no;
|
||||||
|
|
||||||
|
SELECT 'cupang_db 002 done' AS status;
|
||||||
@@ -116,7 +116,6 @@ CREATE TRIGGER trg_cupang_products_updated
|
|||||||
-- ════════════════════════════════════════════════════════════
|
-- ════════════════════════════════════════════════════════════
|
||||||
CREATE TABLE IF NOT EXISTS cupang_shipments (
|
CREATE TABLE IF NOT EXISTS cupang_shipments (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
document_no TEXT UNIQUE,
|
|
||||||
created_by TEXT NOT NULL,
|
created_by TEXT NOT NULL,
|
||||||
document_date DATE NOT NULL,
|
document_date DATE NOT NULL,
|
||||||
ship_date DATE NOT NULL,
|
ship_date DATE NOT NULL,
|
||||||
|
|||||||
Reference in New Issue
Block a user