- app/modules/cupang/export.py: 출고리스트 양식(셀/병합)을 한 곳에서 생성 →
xlsx 다운로드와 구글 시트 기록이 같은 표를 쓴다
- app/integrations/google_sheets.py: 서비스 계정 기반 시트 쓰기 공통 계층
(없거나 미설정이면 enabled=False 로 조용히 skip, 키는 로그에 남기지 않음)
- 확정 응답에 sheet 결과 포함, 화면에 성공/실패 메시지 표시
- env: CUPANG_SHEET_ID, GOOGLE_SHEETS_CREDENTIALS(_JSON) 추가
- requirements: google-api-python-client, google-auth
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- ③ "센터를 고르면 바로 추가" 드롭다운 삭제(업로드가 센터를 자동 구성)
- 확정 payload 에 상자 종류별 박스 수(box_summary, 예: 쿠팡상자41) 추가 →
출고 묶음의 outbound_summary 로 저장
- 센터입고일 = 출고일 + 1일로 저장(쿠팡 발주서 입고예정일과 일치)
- GET /cupang/export.xlsx?date=YYYY-MM-DD — 쿠팡로켓 밀크런 출고리스트 양식
(제목/회사명/머리글 + 센터 단위 병합, 시트명 YYYYMMDD). 확정 직후 자동 다운로드
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- 업로드 API가 (출고일, 센터, 제품)으로 합산해 groups[] 로 반환
- 화면 상단에 출고일 탭 — 날짜마다 ①직접입력 / ②박스요약 / ③센터분배 상태를
따로 보관하고 탭으로 전환 (박스 계산은 날짜 × 센터 단위로 실행)
- 확정은 현재 날짜만 대상. 남은 날짜가 있으면 달력으로 나가지 않고 다음 탭으로 이동
- 업로드 결과 카드에 출고일별 센터 수·수량 요약 추가
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- ① 카드를 "쿠팡 발주 업로드"로 개편, xlsx 다중 선택 업로드
- POST /cupang/api/box-calc/upload (openpyxl): F13 입고예정일 −1일 = 출고일,
22행부터 B=쿠팡상품코드 / F=센터명 / G=수량을 읽어 파일들을 합산
- 쿠팡상품코드 → cupang_products.coupang_item_code 로 제품 매칭,
센터명 → cupang_centers.name 매칭. 미매칭 코드/센터/박스규칙은 경고로 표시
- 박스는 센터 단위로 포장되므로 센터마다 박스 계산을 돌려 ② 요약에 합치고
③ 센터에 자동 배분(자투리 혼합 박스는 소속 센터를 라벨에 표시)
- 확정 대화상자 기본 출고일도 업로드한 발주서 날짜로 채움
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- index 라우터가 두 달치 출고를 합쳐(중복 id 제거) 달 별 격자 2개를 생성
- 상단 바: ‹ › 이동 + 기간 제목 + [오늘], 달마다 머리글/월 출고 합계 배지
- 셀 UI 정리: 오늘=검은 원, 선택=링, 출고일=건수/센터수 태그, 타월은 흐리게
- 범례 추가, 1200px 이하에서는 두 달을 세로로 배치
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- 동작 열에 [수정] 버튼 → 제품명/제품코드/쿠팡상품코드 수정 팝업
- POST /cupang/api/products/{id}/edit (JSON), 제품코드 중복은 400
- 제품코드 변경 시 cupang_box_rules 의 product_code/스냅샷도 함께 이동
(같은 코드 규칙이 이미 있으면 이동하지 않음). 출고 라인은 과거 기록이라 유지
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- cupang_products.coupang_item_code 컬럼 추가 (마이그레이션 004, init.sql 동기화)
- 설정 화면: 쿠팡상품코드 열 표시/정렬, 수기 추가 폼 입력란 추가
- 선택 등록 시 바로 등록하지 않고 쿠팡상품코드 입력 팝업을 먼저 표시
- 상태 배지를 클릭 토글로 변경 (POST /cupang/api/products/{id}/toggle)
- 빈 쿠팡상품코드는 "미입력"으로 처리해 기존 값을 덮어쓰지 않음
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
분배 확정으로 만든 출고를 지우려면 상세 페이지까지 들어가야 했다. 달력에서
날짜를 고른 뒤 오른쪽 카드에서 바로 지울 수 있게 한다. 센터가 여러 건일 때를
위해 목록 헤더에 "이 날짜 전체 삭제" 도 둔다. 둘 다 가운데 확인 팝업을 거친다.
삭제는 기존 정책대로 status='취소' soft delete 이고, 대신 달력·목록에서
취소 건을 걸러내 사용자에게는 삭제로 보이게 했다(기록은 DB 에 남는다).
- router: /{id}/delete 에 next 폼값(내부 경로만 허용) 추가, POST /cupang/day-delete 신설
- index.html: 카드 헤더에 삭제 버튼, 앵커 안에 버튼이 들어가지 않게 구조 정리
출고 묶음을 만드는 길이 둘(신규 등록 폼 / 박스 계산 분배 확정)이라 헷갈렸다.
폼 경로를 없애고 [+ 신규 등록] 이 박스 계산을 열게 한다. 중복이던 오른쪽
[박스 계산] 버튼은 제거. /cupang/new 는 북마크가 깨지지 않게 박스 계산으로
리다이렉트하고 POST /new(생성)는 삭제했다. 기존 묶음 수정은 그대로 동작한다.
달력 칸은 작성/출고/입고 세 배지를 늘어놓아 읽기 어려웠다. 출고 기준으로
"출고 N건 · 센터 M곳"만 남긴다. 오른쪽 상세도 같은 기준(출고일)으로 맞추고
센터별 출고방식·총 박스/개수와 상품별 수량·박스를 카드 안에 펼친다. 품목이
바로 보이므로 hover 툴팁은 제거.
임시 저장 기본 제목은 "2026.08.31(월) 오후 04시 50분" 형식으로.
③ 센터 분배 헤더에 [분배 확정] 을 두고, 조건이 갖춰질 때만 활성화한다.
조건은 두 가지다. 남은 박스가 0 이어야 하고, 담긴 센터마다 출고방식
(택배/파렛트)이 골라져 있어야 한다. 비활성일 때는 버튼 툴팁에 막힌 사유를
적는다(남은 박스 수, 출고방식 미선택 센터명).
확정을 누르면 달력이 바로 뜨는 팝업에서 출고일자를 고르고, 확인 시 센터마다
출고 묶음 1건(status=출고준비)을 만들어 쿠팡 달력의 그 날짜로 이동한다.
혼합 박스는 내용물 제품으로 풀어 담고 같은 제품끼리 합산한다. 박스 수는
화면 값을 믿지 않고 저장 시 서버가 수량·입수량으로 다시 계산한다.
- router: POST /cupang/api/box-calc/confirm
- box_calc.html: 센터 헤더 출고방식 select(미선택은 빨간 배경), 확정 팝업 달력
- 임시 저장 스냅샷에 출고방식 포함
- 센터입고일은 출고일과 같게 저장(상세에서 수정)
계산 중 화면 상태를 서버에 이름 붙여 저장하고 다시 불러온다. 다른 PC 에서도
이어서 작업할 수 있어야 해서 localStorage 대신 DB 에 둔다.
저장 대상은 화면 스냅샷(입력 품목·열어둔 센터·분배 내역·도장 상태)뿐이고,
박스 수는 불러온 뒤 기존 계산 API 로 서버에서 다시 구한다. 저장값을 그대로
믿지 않으므로 입수량 규칙이 바뀌어도 어긋나지 않고, 계산 결과에서 사라진
항목은 제외한 건수를 알려준다. 같은 제목으로 저장하면 덮어써 목록이 무한히
늘어나지 않는다.
- scripts/sql/cupang_db_003_box_calc_drafts.sql (신규 테이블 + 권한, 멱등)
- db.py: list/get/save/delete_box_calc_draft
- router: GET·POST /cupang/api/box-calc/drafts, GET·DELETE /{id}
- box_calc.html: [임시 저장]·[불러오기] 버튼 + 목록 대화상자
추가 시 같은 이름이 있으면 조용히 재활성되던 동작(ON CONFLICT DO UPDATE)
때문에 사용자가 중복을 알 수 없었다. 등록 전에 먼저 조회해서 이미 있으면
만들지 않고 알림(활성/비활성 구분)을 띄운다.
목록은 29개가 가로 4열로 흩어져 훑기 어려웠다. 센터명 앞 지역 접두사로
묶고(인천14 → "인천") CSS 멀티컬럼으로 한 열을 위→아래로 읽게 바꿨다.
숫자는 자연 정렬(인천4 < 인천14). 행 액션은 hover/focus 시에만 노출해
이름이 먼저 눈에 들어오게 했다.
- 라우터: _center_group / _center_sort_key / _group_centers 추가
- 검색은 그룹 단위로 접히고 결과 0건이면 안내 표시
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
이름을 바꾸면 그 센터를 쓰던 기존 출고 건과 어긋날 수 있어 수정 경로를
막는다. 잘못 등록한 센터는 삭제 후 새로 등록.
- 목록 행: 이름 input+저장 버튼 → 읽기 전용 텍스트
- center_edit 라우터에서 name 폼 파라미터 제거(활성/정렬만 수정)
- 행이 좁아져 그리드 최소폭 340px → 240px
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
센터 목록은 이름 등록·조회·관리가 목적이므로 "사용중" 표시를 없애고
활성/비활성/삭제만 남긴다. 좌우 2단(추가 카드 360px + 900px 고정 목록)
대신 상단 추가·검색 바 + 다중 컬럼 그리드로 바꿔 29개 이상 센터를
한 화면에서 훑을 수 있게 했다.
- 행마다 폼 하나(formaction/name=active)로 저장·토글·삭제 처리
- 상태는 배지 대신 좌측 점(활성 초록 / 비활성 회색+흐리게)
- 클라이언트 검색 필터, 카운트 실시간 갱신
- 라우터에서 center_in_use 조회 제거(화면에서 미사용)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
드래그가 브라우저에서 먹지 않던 문제를 추정 대신 실패 가능 지점별로
전부 막았다.
- dragstart 리스너를 카드 요소에 직접 바인딩(위임으로는 잡히지 않는
환경 대비). 렌더할 때마다 다시 바인딩한다.
- dragenter/dragover 를 모두 preventDefault — 하나만 막으면 드롭을
거부하는 브라우저가 있다.
- 드롭 대상 범위를 ③ 카드 전체로 확대하고, 대상 패널은
closest → elementFromPoint → (센터가 하나면) 그 센터 순으로 찾는다.
패널 사이 여백에 놓아도 담긴다.
- dataTransfer.setData/getData 가 막힌 환경 대비로 dragKey 를 따로 보관해
드롭 시 사용한다. 그래도 못 읽으면 클릭으로 담으라는 안내를 띄운다.
- 드래그 중에는 ③ 영역에 점선 강조를 준다.
- 드래그 직후 발생하는 click 은 무시해 대화상자가 두 번 뜨지 않게 한다.
센터 선택 목록은 이름 가나다순 정렬(한글 음절은 코드포인트 순).
tests/js: 여백 드롭·dragenter·setData 차단 환경 시나리오 추가. 전체 통과.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
① 박스 계산: 제품명·수량만 입력(결과 열 제거). 수량 칸에서 Tab 은 다음
행 수량으로 이동하고 마지막 행이면 새 행을 추가한다. Enter 는 계산.
② 박스 요약: 제품별 박스/자투리 혼합 박스 카드가 드래그 소스. 표시되는
수치는 센터에 배분하고 남은 "잔여" 기준으로 실시간 갱신되며, KPI 는
총 박스 / 센터 배분 / 미배분 잔여 3개로 바꿨다.
③ 센터 분배: 활성 입고센터(cupang_centers)만 표시. 카드를 끌어다 놓으면
해당 센터에 1박스가 담기고, 박스 수량을 직접 입력할 수 있다(잔여를
넘으면 자동으로 잘림). ✕ 로 빼면 잔여로 되돌아간다.
혼합 박스는 내용이 서로 달라 1장 = 1박스로 취급한다. 분배 내역은 화면
안에서만 유지(DB 저장 없음)하며, 재계산하면 근거가 바뀌므로 초기화한다.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
하단 "박스명별 합계" 표를 요약 카드로 교체.
- KPI 3개: 총 박스 / 제품별 박스 / 혼합 박스(자투리 N개)
- 제품별 카드: 몇 박스 + 자투리 몇 개(딱 맞으면 "딱 맞음")
- 자투리 혼합 박스: 박스 1개당 카드로 어떤 상품이 몇 개 들어가는지 표시.
상자 SVG(뚜껑 열림·내용물 차오름), 채움률 바, 카드 등장 애니메이션.
prefers-reduced-motion 에서는 애니메이션 비활성.
혼합 계산은 서버 _pack_leftovers: 박스 용량을 1 로 두고 제품 1개 =
1/units_per_box 부피로 환산, 같은 박스명끼리만 채운다. 부동소수 오차를
피하려고 Fraction 사용. 한 제품 자투리가 두 박스로 나뉘는 것은 허용해
박스 수가 최소가 되게 한다.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
달력 상단 "박스 계산" 버튼 → /cupang/box-calc. 제품명+수량을 여러 행
입력하면 제품별 박스 수·남은 낱개·총 필요 박스와 박스명별 합계를 계산.
수량/제품을 고치고 "계산 / 재계산" 으로 다시 계산할 수 있다(수량 칸에서
Enter 도 동일). 저장하지 않는 계산 전용 화면.
계산은 POST /cupang/api/box-calc 에서 store.compute_boxes 로 수행 —
클라이언트 계산을 신뢰하지 않는다. 입수량 규칙이 없는 제품은 "미설정"
으로 표시하고 합계에서 제외.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- ItemcodeReader.last_error 추가, _run 실패 시 logger.exception + 메시지 보존
- /api/products/all 응답에 error/count 추가
- products 화면: 결과 0 일 때 조회오류/사유 표시 (그냥 '결과 없음' 대신)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 추가 폼에서 정렬순서 입력란 제거(좌측), 센터 목록 우측
- 센터 목록 이름 오름차순, 프레임 900px 고정 내부 스크롤, 1열 행
- 행 편집은 이름 저장 + 활성/비활성 + 삭제 (정렬 입력 제거)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 출고 폼 입고센터 드롭다운 이름 오름차순 정렬
- 사이드바: is-active 메뉴는 hover 무변화, 그 외 진한 회색(#d9d9d9)
- 설정/센터/박스 페이지 "← 달력" → "◀◀ 달력", 검정 배경 흰 글씨(primary)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 박스 입수량 폼: 제품명(스냅샷)→제품명, 헤더그리드로 교체해 입력란 겹침 해소
- 입고센터: 추가 폼 슬림화 + 센터 목록 2열 그리드(한 줄/센터)로 압축
- 출고 상세: 완전삭제 버튼 추가(/{id}/hard-delete, 라인 CASCADE). 취소 처리는 outline 로
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 수동 등록 카드 제거
- 왼쪽: itemcode_db 전체 목록(낱개+세트) 다중선택, 등록된 항목 진회색+흰글씨
/cupang/api/products/all + /cupang/products/bulk(JSON 일괄 upsert)
- 오른쪽: 등록 제품명 목록 — 제품명/코드/상태 + 활성·비활성 토글 + 완전삭제
/products/{id}/active (토글), /products/{id}/delete (hard delete)
- ItemcodeReader.list_all 추가
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 설정 화면 추가: /cupang/products — itemcode_db 검색 등록 + 수동 등록.
제품명 드롭다운 소스. cupang_products 테이블 신규(init.sql 멱등 재실행 필요)
- 폼 품목라인: 제품명 드롭다운 선택 → 제품코드 자동 입력
- 라인 컬럼 순서 제품명·제품코드·수량·입수량·박스계산·메모
- 제품코드 140px, 수량·입수량 60px 폭 고정
- 공통 헤더 입력란 겹침 수정(min-width:0, width:100%, box-sizing)
- 달력: 일/공휴일 빨강, 토 파랑, 글자 크기 확대. 한국 공휴일 모듈(holidays.py)
- document_no null → "None" 출력 버그 수정
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>