feat(dispatch): 일괄 완료 버튼(모두 라벨부착/모두 Kagayaku 전달) + 검색바 축소
- 출고 작업 툴바 우측에 일괄 버튼 2개. 배치 내 전 박스 상태를 완료로 일괄 설정
- db.bulk_set_status: 변경된 박스만 로그(set 기반 INSERT), 화이트리스트 검증
- 라우터 POST /batches/{id}/bulk
- 검색 입력칸 폭 축소(flex 0 1 240px)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -250,6 +250,42 @@ class DispatchStore:
|
||||
)
|
||||
return {"parcel_id": parcel_id, "field": field, "value": new_value}
|
||||
|
||||
def bulk_set_status(
|
||||
self, *, batch_id: int, field: str, value: bool = True, worker_name: str = ""
|
||||
) -> int:
|
||||
"""배치 내 모든 박스의 작업 상태 1개를 value 로 일괄 설정. 변경된 박스만 로그.
|
||||
|
||||
반환: 실제로 바뀐 박스 수(이미 value 인 박스는 건드리지 않음).
|
||||
field 는 store.STATUS_FIELDS 화이트리스트 검증(SQL 식별자 안전).
|
||||
"""
|
||||
if field not in store.STATUS_FIELDS:
|
||||
raise ValueError(f"허용되지 않는 작업 상태: {field}")
|
||||
with self._pool.connection() as conn:
|
||||
with conn.transaction():
|
||||
rows = conn.execute(
|
||||
# field 는 화이트리스트라 인젝션 위험 없음.
|
||||
f"UPDATE dispatch_parcels SET {field} = %s "
|
||||
f"WHERE batch_id = %s AND {field} = %s RETURNING id",
|
||||
(value, batch_id, not value),
|
||||
).fetchall()
|
||||
ids = [r["id"] for r in rows]
|
||||
if ids:
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO dispatch_logs
|
||||
(parcel_id, action, old_value, new_value, worker_name)
|
||||
SELECT unnest(%s::bigint[]), %s, %s, %s, %s
|
||||
""",
|
||||
(
|
||||
ids,
|
||||
field,
|
||||
str(not value).lower(),
|
||||
str(value).lower(),
|
||||
(worker_name or "").lower().strip(),
|
||||
),
|
||||
)
|
||||
return len(ids)
|
||||
|
||||
def parcel_batch_id(self, *, parcel_id: int) -> int | None:
|
||||
with self._pool.connection() as conn:
|
||||
row = conn.execute(
|
||||
|
||||
Reference in New Issue
Block a user