Files
dbx-main/app/modules/malaysia/README.md
T
king 032fb78867 feat(malaysia): 재고조사 랙(rack) 박스 단위 입력
낱개/콤보 평면 수량 그리드를 창고 랙 그림(A/B 36칸) 입력으로 대체.
각 칸에 아이템·박스당 입수량·박스 수를 넣고 + 로 한 칸 여러 아이템 추가.
저장 시 SKU별 qty=SUM(입수량×박스수) 집계로 daily_stocktake_line 재생성.
이후 분해/확정 로직은 라인 기준 그대로 동작.

- DB: daily_stocktake_rack 테이블 + grant (멱등)
- store: rack_cell_codes/rack_layout/aggregate_rack 순수함수
- db: list_rack_entries/replace_rack(1 트랜잭션, draft만)
- router: rack 컨텍스트 + POST /stocktakes/{id}/rack/bulk
- UI: 랙 보드 템플릿 + malaysia_rack.js(행 추가/삭제·실시간 소계)
- 테스트 4건 추가(18 전부 통과)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 16:25:40 +09:00

121 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 말레이시아 창고 재고관리 모듈 (`malaysia`)
말레이시아 현지 창고의 입고/출고/조정과 세트 BOM, 일일 재고조사를 관리한다.
cupang 모듈과 동일한 패턴: **malaysia_stock_db(PostgreSQL) 전용**, JSON 폴백 없음.
상품명은 기존 `itemcode_db`(읽기 전용)을 재사용한다.
- 경로(prefix): `/malaysia`
- 권한키: `malaysia` (admin 은 항상 통과)
- 환경변수: `MALAYSIA_STOCK_DB_URL` (미설정 시 "설정 필요" 안내), `ITEMCODE_DB_URL`(세트 BOM/이름)
- DB: `malaysia_stock_db` / 역할 `malaysia_app`
> **세트 구성(BOM)은 별도 관리하지 않는다.** itemcode_db `set_components`
> (set_code / single_code / quantity)에서 읽는다. 모듈 안에 세트구성 메뉴 없음.
> `malaysia_stock_db.set_bom` 테이블은 더 이상 쓰지 않는다(레거시, 무시).
> 입출고·재고조사는 전체 아이템(+세트) 그리드에 **수량만 키인**해 일괄 등록한다.
---
## 재고 원칙
- **입고/출고/조정은 낱개 아이템(MT-/MX-/MZ-) 기준**으로만 movement 저장.
- **세트(MY-)는 movement 불가.** 세트는 ① 재고조사 입력, ② BOM 구성 계산에만 사용.
- 세트 주문 출고는 `set_bom` 으로 분해해 구성품별 `OUT` movement 를 생성(`/malaysia/movements/set-out`).
- 뚜껑(MD-)은 재고관리 대상에서 **완전 제외**(어디에도 사용 불가).
### 현재고 계산
```
현재고 = SUM(IN) - SUM(OUT) + SUM(ADJUST) + SUM(STOCKTAKE)
```
- `IN`/`OUT` qty 는 양수만. `ADJUST`/`STOCKTAKE` 는 음수(±) 허용.
- 재고조사 확정 시 **시스템 재고와 조사 최종치의 "차이"만** `STOCKTAKE` movement 로 기록 → 이력 보존 + 계산 단순.
### 일일 재고조사 집계
```
낱개 최종 재고(total_qty)
= direct_qty(낱개 직접 조사)
+ from_set_qty(세트 수량을 set_bom 으로 분해한 합)
```
예) `MT-0320` 낱개 100 + `MY-0002`(MT-0320 1개 포함) 20세트 → `MT-0320` total = **120**.
> 낱개 입력 시 **세트 안에 든 건 빼고** 낱개 보관분만 입력한다. 세트 안 상품은 BOM 으로 자동 계산.
### 랙(rack) 입력 — 재고조사 상세
재고조사 상세는 평면 수량 그리드 대신 **창고 랙 그림(A/B 두 면, 각 3행×3열×2분할 = 36칸)**
으로 입력한다. 각 칸에 `아이템 · 박스당 입수량 · 박스 수`를 넣고, 한 칸에 여러 아이템은
`+` 로 행을 추가한다. 저장하면 서버가 SKU별로
```
qty = SUM(units_per_box × box_count)
```
집계해 `daily_stocktake_line`을 **재생성**한다(랙이 라인의 단일 출처). 낱개·콤보(MY-)
모두 랙에 입력 가능하며, 이후 분해/확정 로직(`compute_result`/`finalize`)은 라인 기준으로
기존과 동일하게 동작한다. 칸 코드 형식: `{면}{행}-{열}-{분할}` (예 `A3-1-1`).
---
## 테이블 (`scripts/sql/malaysia_stock_db_init.sql`)
| 테이블 | 용도 |
| --- | --- |
| `warehouses` | 창고. seed: `MY-WH-01 / Malaysia Warehouse` |
| `malaysia_items` | 관리 대상 코드 스코프(낱개/세트) + 이름 스냅샷. seed: 낱개 12 + 세트 5 |
| `set_bom` | 세트 구성표. `UNIQUE(set_code, component_code)`. prefix CHECK 내장 |
| `stock_movement` | 입고/출고/조정/조사 이력. `item_code` 는 낱개만(CHECK) |
| `daily_stocktake` | 재고조사 헤더. 같은 날짜+창고 finalized 1건(부분 유니크) |
| `daily_stocktake_line` | 조사 라인(SKU별 최종 qty). 랙 입력 집계로 **재생성**됨. `UNIQUE(stocktake_id, sku_code)` |
| `daily_stocktake_rack` | 랙 칸별 입력(셀×SKU×입수량×박스수). 라인의 단일 출처. `cell_code``A3-1-1` |
검증 규칙(요구사항 8)은 **DB CHECK + 서비스 레이어(`store.py`)** 양쪽에 걸려 있다.
---
## 주요 화면 / API
화면: `/malaysia/`(재고 현황) · `/malaysia/movements`(입출고) · `/malaysia/stocktakes`(재고조사) · `/malaysia/sets`(세트 구성)
JSON API:
| Method | 경로 | 설명 |
| --- | --- | --- |
| GET | `/malaysia/api/warehouses` | 창고 목록 |
| GET | `/malaysia/api/items?kind=individual\|set` | 아이템 목록 |
| GET/POST | `/malaysia/api/bom` | 세트 구성 조회/등록 |
| GET | `/malaysia/api/stock?wh=` | 현재고 현황 |
| POST | `/malaysia/api/movements` | 낱개 movement 등록 |
| POST | `/malaysia/api/movements/set-out` | 세트 출고(BOM 분해) |
| GET | `/malaysia/api/stocktakes/{id}/result` | 재고조사 최종 계산 |
| POST | `/malaysia/stocktakes/{id}/rack/bulk` | 랙 입력 일괄 저장(병렬배열 `rk_cell/rk_sku/rk_upb/rk_box`) → 라인 재생성 |
---
## 초기화 (운영, 1회 — 사용자 승인 후)
```bash
read -s -p "malaysia_app password: " APP_PWD; echo
docker exec -i postgres-db psql -U postgres \
-v app_password="$APP_PWD" \
< scripts/sql/malaysia_stock_db_init.sql
# main-app .env 에 추가:
# MALAYSIA_STOCK_DB_URL=postgresql://malaysia_app:<APP_PWD>@postgres-db:5432/malaysia_stock_db
cd /opt/www/main && docker compose up -d --build
```
> 멱등 스크립트. 기존 DB 가 있으면 DROP 하지 않음. itemcode_db 는 건드리지 않음.
> **랙 입력 추가(`daily_stocktake_rack`)** 후 기존 운영 DB 에 반영하려면 위 init 스크립트를
> 그대로 1회 재실행한다(`CREATE TABLE IF NOT EXISTS` + grant 만 적용, 기존 데이터 보존).
## 테스트
```bash
python -m pytest tests/test_malaysia_stock.py -v # pytest 있으면
python tests/test_malaysia_stock.py # 없으면 standalone 폴백
```
순수 로직(코드 검증 / 세트 분해 / 재고조사 집계)만 검증하므로 DB 불필요.