feat(cupang): 발주 업로드 결과를 출고일별로 분리

- 업로드 API가 (출고일, 센터, 제품)으로 합산해 groups[] 로 반환
- 화면 상단에 출고일 탭 — 날짜마다 ①직접입력 / ②박스요약 / ③센터분배 상태를
  따로 보관하고 탭으로 전환 (박스 계산은 날짜 × 센터 단위로 실행)
- 확정은 현재 날짜만 대상. 남은 날짜가 있으면 달력으로 나가지 않고 다음 탭으로 이동
- 업로드 결과 카드에 출고일별 센터 수·수량 요약 추가

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-01 13:49:21 +09:00
parent a6d7de709d
commit 5217c4b6bb
9 changed files with 270 additions and 70 deletions
+30 -12
View File
@@ -769,8 +769,8 @@ async def box_calc_upload(
warnings: list[str] = []
file_infos: list[dict[str, Any]] = []
ship_dates: list[str] = []
# (센터명, 제품코드) → 수량
agg: dict[tuple[str, str], dict[str, Any]] = {}
# (출고일, 센터명, 제품코드) → 수량
agg: dict[tuple[str, str, str], dict[str, Any]] = {}
unknown_codes: set[str] = set()
unknown_centers: set[str] = set()
no_rule: set[str] = set()
@@ -793,6 +793,7 @@ async def box_calc_upload(
else:
warnings.append(f"{name}: F13 입고예정일을 읽지 못했습니다.")
ship_iso = ship.isoformat() if ship else ""
used = 0
for row in parsed["rows"]:
prod = by_coupang.get(row["coupang_item_code"])
@@ -805,10 +806,11 @@ async def box_calc_upload(
code = prod["product_code"]
if code not in rules:
no_rule.add(f'{prod["product_name"]}({code})')
key = (cname, code)
key = (ship_iso, cname, code)
cell = agg.setdefault(
key,
{
"ship_date": ship_iso,
"center_name": cname,
"product_code": code,
"product_name": prod["product_name"],
@@ -823,7 +825,7 @@ async def box_calc_upload(
{
"filename": name,
"arrival_date": arrival.isoformat() if arrival else "",
"ship_date": ship.isoformat() if ship else "",
"ship_date": ship_iso,
"rows": len(parsed["rows"]),
"quantity": used,
}
@@ -841,12 +843,12 @@ async def box_calc_upload(
warnings.append("박스 입수량 미설정: " + ", ".join(sorted(no_rule)))
uniq_dates = sorted(set(ship_dates))
if len(uniq_dates) > 1:
warnings.append("파일마다 출고일이 다릅니다: " + ", ".join(uniq_dates))
grouped: dict[str, dict[str, Any]] = {}
for (cname, _code), cell in agg.items():
g = grouped.setdefault(
# 출고일 → 센터 → 품목. 업로드한 파일의 출고일이 다르면 날짜별로 나뉜다.
by_date: dict[str, dict[str, dict[str, Any]]] = {}
for (ship_iso, cname, _code), cell in agg.items():
centers_of_date = by_date.setdefault(ship_iso, {})
g = centers_of_date.setdefault(
cname,
{
"center_name": cname,
@@ -862,8 +864,24 @@ async def box_calc_upload(
"quantity": cell["quantity"],
}
)
for g in grouped.values():
g["items"].sort(key=lambda it: it["product_code"])
groups: list[dict[str, Any]] = []
for ship_iso in sorted(by_date):
centers_of_date = by_date[ship_iso]
for g in centers_of_date.values():
g["items"].sort(key=lambda it: it["product_code"])
groups.append(
{
"ship_date": ship_iso,
"centers": sorted(centers_of_date.values(), key=lambda g: g["center_name"]),
"files": [f for f in file_infos if f["ship_date"] == ship_iso],
"quantity": sum(
it["quantity"]
for g in centers_of_date.values()
for it in g["items"]
),
}
)
return JSONResponse(
{
@@ -871,7 +889,7 @@ async def box_calc_upload(
"ship_date": uniq_dates[0] if uniq_dates else "",
"ship_dates": uniq_dates,
"files": file_infos,
"centers": sorted(grouped.values(), key=lambda g: g["center_name"]),
"groups": groups,
"warnings": warnings,
}
)