feat(cupang): 발주 업로드 시 미등록 입고센터 자동 등록
발주서에 있는 센터가 cupang_centers 에 없으면 자동 배분에서 제외됐다. 이제 발주서에 적힌 이름 그대로 센터를 등록(sort_order 는 기존 최대값 다음) 하고 계산에 포함한다. 경고문도 "등록되지 않은 센터" 대신 "새 입고센터 자동 등록" 으로 바꿨다. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -943,7 +943,7 @@ async def box_calc_upload(
|
|||||||
|
|
||||||
- 출고일 = F13(입고예정일)의 하루 전
|
- 출고일 = F13(입고예정일)의 하루 전
|
||||||
- 쿠팡상품코드(B) → cupang_products.coupang_item_code 로 제품 매칭
|
- 쿠팡상품코드(B) → cupang_products.coupang_item_code 로 제품 매칭
|
||||||
- 센터명(F) → cupang_centers.name 으로 매칭(못 찾으면 경고만)
|
- 센터명(F) → cupang_centers.name 으로 매칭(없으면 그 이름으로 자동 등록)
|
||||||
"""
|
"""
|
||||||
from io import BytesIO # noqa: WPS433
|
from io import BytesIO # noqa: WPS433
|
||||||
from datetime import timedelta as _timedelta # noqa: WPS433
|
from datetime import timedelta as _timedelta # noqa: WPS433
|
||||||
@@ -964,6 +964,7 @@ async def box_calc_upload(
|
|||||||
}
|
}
|
||||||
centers = store.list_centers(include_inactive=True)
|
centers = store.list_centers(include_inactive=True)
|
||||||
by_center_name = {(c.get("name") or "").strip(): c for c in centers}
|
by_center_name = {(c.get("name") or "").strip(): c for c in centers}
|
||||||
|
next_center_order = max((int(c.get("sort_order") or 0) for c in centers), default=0) + 1
|
||||||
rules = {r["product_code"]: r for r in store.list_box_rules()}
|
rules = {r["product_code"]: r for r in store.list_box_rules()}
|
||||||
|
|
||||||
warnings: list[str] = []
|
warnings: list[str] = []
|
||||||
@@ -972,7 +973,7 @@ async def box_calc_upload(
|
|||||||
# (출고일, 센터명, 제품코드) → 수량
|
# (출고일, 센터명, 제품코드) → 수량
|
||||||
agg: dict[tuple[str, str, str], dict[str, Any]] = {}
|
agg: dict[tuple[str, str, str], dict[str, Any]] = {}
|
||||||
unknown_codes: set[str] = set()
|
unknown_codes: set[str] = set()
|
||||||
unknown_centers: set[str] = set()
|
added_centers: set[str] = set()
|
||||||
no_rule: set[str] = set()
|
no_rule: set[str] = set()
|
||||||
|
|
||||||
for up in files:
|
for up in files:
|
||||||
@@ -1002,7 +1003,15 @@ async def box_calc_upload(
|
|||||||
continue
|
continue
|
||||||
cname = row["center_name"]
|
cname = row["center_name"]
|
||||||
if cname not in by_center_name:
|
if cname not in by_center_name:
|
||||||
unknown_centers.add(cname)
|
# 미등록 센터는 발주서에 적힌 이름 그대로 자동 등록해 계산에 포함한다.
|
||||||
|
try:
|
||||||
|
created = store.create_center(name=cname, sort_order=next_center_order)
|
||||||
|
next_center_order += 1
|
||||||
|
except Exception: # noqa: BLE001 - 등록 실패해도 나머지는 계산
|
||||||
|
warnings.append(f"센터 자동 등록 실패: {cname}")
|
||||||
|
continue
|
||||||
|
by_center_name[cname] = created
|
||||||
|
added_centers.add(cname)
|
||||||
code = prod["product_code"]
|
code = prod["product_code"]
|
||||||
if code not in rules:
|
if code not in rules:
|
||||||
no_rule.add(f'{prod["product_name"]}({code})')
|
no_rule.add(f'{prod["product_name"]}({code})')
|
||||||
@@ -1037,8 +1046,8 @@ async def box_calc_upload(
|
|||||||
+ ", ".join(sorted(unknown_codes)[:10])
|
+ ", ".join(sorted(unknown_codes)[:10])
|
||||||
+ (" 외" if len(unknown_codes) > 10 else "")
|
+ (" 외" if len(unknown_codes) > 10 else "")
|
||||||
)
|
)
|
||||||
if unknown_centers:
|
if added_centers:
|
||||||
warnings.append("등록되지 않은 센터: " + ", ".join(sorted(unknown_centers)))
|
warnings.append("새 입고센터 자동 등록: " + ", ".join(sorted(added_centers)))
|
||||||
if no_rule:
|
if no_rule:
|
||||||
warnings.append("상자 입수량 미설정: " + ", ".join(sorted(no_rule)))
|
warnings.append("상자 입수량 미설정: " + ", ".join(sorted(no_rule)))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user