# Golden Screen 03 — WMS-PICK-001 출고 피킹

## 목적

PDA/산업용 Android 단말 + Bluetooth/USB keyboard-wedge scanner를 기준으로 하는 현장 화면이다. Desktop ERP 화면을 반응형으로 축소하지 않는다.

## 상태 흐름

```text
READY
  ↓ 작업 시작
AWAIT_LOCATION
  ↓ 위치 barcode 확인
AWAIT_ITEM
  ↓ 상품 barcode 확인
AWAIT_ITEM (remaining > 0)
  ↓ 반복 scan 또는 검증 후 수량 직접입력
NEXT LINE
  ↓
COMPLETED
```

다음 line의 location이 현재 line과 동일하면 서버가 `location_confirmed=true`를 승계한다. 작업자에게 동일 위치를 반복 스캔시키지 않는다.

## Scanner 원칙

- keyboard wedge는 문자 buffer + Enter terminator로 처리한다.
- 일반 Input/Textarea에 focus가 있을 때는 global scanner capture가 입력을 가로채지 않는다.
- 동일 barcode를 scan했다고 client가 임의로 재고 상태를 변경하지 않는다.
- 성공 Sound/Vibration은 server ACK 뒤에만 발생한다.
- 잘못된 위치/상품은 정상적인 business rejection이며 retry queue에 넣지 않는다.

## Network / Offline

이 구현은 `완전 오프라인 피킹`이 아니라 **safe retry queue**다.

재고를 바꾸는 연속 스캔은 순서 의존적이다. 서버 응답이 사라진 상황에서 다음 scan까지 계속 허용하면 작업자가 보고 있는 상태와 서버 상태가 갈라질 수 있다. 따라서 네트워크 오류/5xx처럼 commit 여부가 불명확한 command 하나를 동일 Idempotency Key로 보관하고, ACK를 받기 전 다음 authoritative scan을 중지한다.

향후 완전 오프라인 운영이 실제 요구사항으로 확인되면 별도 Facility Policy와 작업 snapshot, local state machine, conflict resolution을 설계한다. 현재 단계에서 무리하게 범용 offline engine을 만들지 않는다.

## Idempotency

`wms.scan_receipts`는 `(task_id, idempotency_key)` unique constraint를 가진다. 이미 처리한 command를 retry하면 저장된 response를 그대로 돌려준다.

```text
Scanner
  ↓
Command + IdempotencyKey
  ↓
Task FOR UPDATE
  ↓
Existing receipt?
  ├ Yes → previous response replay
  └ No  → version/domain validation → write → receipt → commit
```

이는 모바일 네트워크 단절, reverse proxy timeout, 사용자의 재시도에서 중복 피킹을 막는 핵심 장치다.

## 수량 직접입력

대량수량을 무조건 개별 scan하게 하지 않는다. 하지만 바코드 검증도 생략하지 않는다.

1. 위치 확인
2. 상품을 최소 1회 scan
3. `picked_qty > 0` 상태에서만 `수량 직접 입력` 허용
4. 서버가 `0 < pickedQty <= requiredQty` 재검증

센터 정책에 따라 이 기능을 비활성화할 수 있다.

## 예외

현장 작업자는 자유서술보다 사유 코드 중심으로 등록한다.

- 실물 없음
- 수량 부족
- 위치 오류
- 상품 이상
- 바코드 문제
- 기타

예외가 등록되면 task를 `BLOCKED`로 전환해 정상 flow와 분리한다. 관리자가 예외 queue에서 해소하는 별도 화면으로 이어지는 것이 다음 단계다.

## 데이터 정합성

- Client stage는 표시 상태일 뿐 Source of Truth가 아니다.
- Task row를 `FOR UPDATE`로 잠가 같은 작업의 scan 순서를 직렬화한다.
- 모든 mutation은 `expectedVersion`을 확인한다.
- scanner retry는 idempotency receipt로 방어한다.
- 피킹 완료 시 `WmsPickingCompleted` Outbox Event를 같은 transaction에 적재한다.
- Audit에는 server time과 actor를 기록한다.

## 브라우저 Feedback

Sound/Vibration은 보조 피드백이다. 지원하지 않는 단말에서도 업무가 가능해야 하며, 진동 실패가 Domain Command를 실패시키지 않는다.
