Milestone ultima alpha
This commit is contained in:
247
barcode_service.py
Normal file
247
barcode_service.py
Normal file
@@ -0,0 +1,247 @@
|
||||
"""Service layer for the lightweight barcode WMS client."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal
|
||||
|
||||
from barcode_repository import BarcodeRepository, LegacyMoveResult
|
||||
|
||||
|
||||
ModeName = Literal["idle", "priority_high", "priority_low", "manual_load", "manual_unload", "confirm"]
|
||||
|
||||
|
||||
@dataclass
|
||||
class BarcodeViewState:
|
||||
"""State projected from service logic into the barcode UI."""
|
||||
|
||||
mode: ModeName = "idle"
|
||||
queue_label: str = ""
|
||||
status_text: str = "Pronto."
|
||||
status_color: str = "#d9d9d9"
|
||||
source_location: str = ""
|
||||
document: str = ""
|
||||
customer: str = ""
|
||||
expected_pallet: str = ""
|
||||
destination_barcode: str = ""
|
||||
scanned_pallet: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class BarcodeActionResult:
|
||||
"""Standard result returned by user actions."""
|
||||
|
||||
ok: bool
|
||||
state: BarcodeViewState
|
||||
message: str = ""
|
||||
|
||||
|
||||
class BarcodeService:
|
||||
"""Faithful, but cleaner, port of the legacy barcode form logic."""
|
||||
|
||||
GRAY = "#d9d9d9"
|
||||
RED = "#f4cccc"
|
||||
LIGHT_GREEN = "#d9ead3"
|
||||
GREEN_YELLOW = "#e2f0cb"
|
||||
CONVENTIONAL_LOCATION_BY_CELL = {
|
||||
1000: "5E1.1",
|
||||
9999: "7G.1.1",
|
||||
}
|
||||
|
||||
def __init__(self, repository: BarcodeRepository, operator_id: int):
|
||||
self.repository = repository
|
||||
self.operator_id = int(operator_id)
|
||||
self._current_priority_state = 0
|
||||
self._state = BarcodeViewState()
|
||||
|
||||
@property
|
||||
def state(self) -> BarcodeViewState:
|
||||
"""Return a copy-safe reference to the current UI state."""
|
||||
|
||||
return self._state
|
||||
|
||||
def reset(self) -> BarcodeViewState:
|
||||
"""Return the client to its neutral state."""
|
||||
|
||||
self._current_priority_state = 0
|
||||
self._state = BarcodeViewState()
|
||||
return self._state
|
||||
|
||||
def begin_manual_load(self) -> BarcodeViewState:
|
||||
"""Prepare a real versamento into a physical warehouse cell."""
|
||||
|
||||
self._current_priority_state = 0
|
||||
self._state = BarcodeViewState(
|
||||
mode="manual_load",
|
||||
queue_label="Versamento",
|
||||
status_text="OP Carico",
|
||||
status_color=self.GRAY,
|
||||
)
|
||||
return self._state
|
||||
|
||||
def begin_manual_unload(self) -> BarcodeViewState:
|
||||
"""Prepare a direct unload toward the virtual outbound cell 9000000."""
|
||||
|
||||
self._current_priority_state = 0
|
||||
self._state = BarcodeViewState(
|
||||
mode="manual_unload",
|
||||
queue_label="Prelievo diretto",
|
||||
status_text="OP Scarico",
|
||||
status_color=self.GRAY,
|
||||
destination_barcode="9000000",
|
||||
)
|
||||
return self._state
|
||||
|
||||
async def start_priority_queue(self, id_stato: int) -> BarcodeActionResult:
|
||||
"""Load the next item of the selected legacy priority queue."""
|
||||
|
||||
row = await self.repository.fetch_next_picking(id_stato)
|
||||
self._current_priority_state = int(id_stato)
|
||||
queue_label = "Alta priorita' (F1)" if int(id_stato) == 1 else "Bassa priorita' (F2)"
|
||||
if not row:
|
||||
self._state = BarcodeViewState(
|
||||
mode="priority_high" if int(id_stato) == 1 else "priority_low",
|
||||
queue_label=queue_label,
|
||||
status_text="Pronto.",
|
||||
status_color=self.RED,
|
||||
destination_barcode="9000000",
|
||||
)
|
||||
return BarcodeActionResult(True, self._state)
|
||||
|
||||
customer = f"{row.get('CodNazione') or ''} - {row.get('NAZIONE') or ''}".strip(" -")
|
||||
source_location = self._display_location(
|
||||
cella=row.get("Cella"),
|
||||
ubicazione=row.get("Ubicazione"),
|
||||
)
|
||||
self._state = BarcodeViewState(
|
||||
mode="priority_high" if int(id_stato) == 1 else "priority_low",
|
||||
queue_label=queue_label,
|
||||
status_text=f"Ok Cella - {source_location}",
|
||||
status_color=self.LIGHT_GREEN,
|
||||
source_location=source_location,
|
||||
document=str(row.get("Documento") or ""),
|
||||
customer=customer,
|
||||
expected_pallet=str(row.get("Pallet") or ""),
|
||||
destination_barcode="9000000",
|
||||
)
|
||||
return BarcodeActionResult(True, self._state)
|
||||
|
||||
async def submit(self, *, scanned_pallet: str, destination_barcode: str) -> BarcodeActionResult:
|
||||
"""Execute the movement according to the current mode."""
|
||||
|
||||
pallet = str(scanned_pallet or "").strip()
|
||||
destination = str(destination_barcode or "").strip()
|
||||
if not pallet:
|
||||
return BarcodeActionResult(False, self._state, "Inserisci o leggi il pallet.")
|
||||
if not destination:
|
||||
return BarcodeActionResult(False, self._state, "Inserisci o leggi la destinazione.")
|
||||
if not destination.isdigit():
|
||||
return BarcodeActionResult(False, self._state, "La destinazione deve essere numerica.")
|
||||
|
||||
if self._state.mode in ("priority_high", "priority_low") and destination == "9000000":
|
||||
expected = str(self._state.expected_pallet or "").strip()
|
||||
if expected and expected != pallet:
|
||||
self._state.scanned_pallet = pallet
|
||||
self._state.status_text = "Errata lettura: il pallet letto non coincide con quello atteso."
|
||||
self._state.status_color = self.RED
|
||||
return BarcodeActionResult(False, self._state, self._state.status_text)
|
||||
|
||||
move = await self.repository.execute_legacy_move(
|
||||
operator_id=self.operator_id,
|
||||
barcode_cella=destination,
|
||||
barcode_pallet=pallet,
|
||||
numero_cella=int(destination),
|
||||
)
|
||||
if move.rc != 0:
|
||||
self._state.scanned_pallet = pallet
|
||||
self._state.status_text = f"Operazione non riuscita (RC={move.rc})."
|
||||
self._state.status_color = self.RED
|
||||
return BarcodeActionResult(False, self._state, self._state.status_text)
|
||||
|
||||
self._state = await self._build_post_move_state(
|
||||
barcode_pallet=pallet,
|
||||
destination_barcode=destination,
|
||||
last_priority_state=self._current_priority_state,
|
||||
)
|
||||
return BarcodeActionResult(True, self._state, self._state.status_text)
|
||||
|
||||
async def _build_post_move_state(
|
||||
self,
|
||||
*,
|
||||
barcode_pallet: str,
|
||||
destination_barcode: str,
|
||||
last_priority_state: int,
|
||||
) -> BarcodeViewState:
|
||||
"""Mirror the legacy confirmation flow after one stored-procedure move."""
|
||||
|
||||
picking_row = await self.repository.fetch_picking_by_pallet(barcode_pallet)
|
||||
if picking_row:
|
||||
customer = f"{picking_row.get('CodNazione') or ''} - {picking_row.get('NAZIONE') or ''}".strip(" -")
|
||||
source_location = self._display_location(
|
||||
cella=picking_row.get("Cella"),
|
||||
ubicazione=picking_row.get("Ubicazione"),
|
||||
)
|
||||
return BarcodeViewState(
|
||||
mode="confirm",
|
||||
queue_label="Alta priorita' (F1)" if last_priority_state == 1 else ("Bassa priorita' (F2)" if last_priority_state == 0 else ""),
|
||||
status_text=f"Ok Cella - {source_location}",
|
||||
status_color=self.LIGHT_GREEN,
|
||||
source_location=source_location,
|
||||
document=str(picking_row.get("Documento") or ""),
|
||||
customer=customer,
|
||||
expected_pallet=str(picking_row.get("Pallet") or ""),
|
||||
destination_barcode=destination_barcode,
|
||||
)
|
||||
|
||||
trace_row = await self.repository.fetch_trace_by_pallet(barcode_pallet)
|
||||
if trace_row:
|
||||
lotto = str(trace_row.get("Lotto") or "")
|
||||
prodotto = str(trace_row.get("Prodotto") or "")
|
||||
descrizione = str(trace_row.get("Descrizione") or "")
|
||||
queue_label = "Alta priorita' (F1)" if last_priority_state == 1 else ("Bassa priorita' (F2)" if last_priority_state == 0 else "Conferma movimento")
|
||||
return BarcodeViewState(
|
||||
mode="confirm",
|
||||
queue_label=queue_label,
|
||||
status_text=(
|
||||
"Ok Scarico" if destination_barcode == "9000000" else f"Ok Carico - {destination_barcode}"
|
||||
),
|
||||
status_color=self.GREEN_YELLOW,
|
||||
source_location=str(destination_barcode or ""),
|
||||
document=(
|
||||
self.CONVENTIONAL_LOCATION_BY_CELL[9999]
|
||||
if destination_barcode == "9000000" and last_priority_state in (0, 1)
|
||||
else lotto
|
||||
),
|
||||
customer=(
|
||||
lotto
|
||||
if destination_barcode == "9000000" and last_priority_state in (0, 1)
|
||||
else prodotto
|
||||
),
|
||||
expected_pallet=(
|
||||
" - ".join(part for part in (prodotto, descrizione) if part)
|
||||
if destination_barcode == "9000000" and last_priority_state in (0, 1)
|
||||
else descrizione
|
||||
),
|
||||
destination_barcode=destination_barcode,
|
||||
scanned_pallet=barcode_pallet,
|
||||
)
|
||||
|
||||
return BarcodeViewState(
|
||||
mode="confirm",
|
||||
queue_label="Conferma movimento",
|
||||
status_text="Movimento eseguito.",
|
||||
status_color=self.GREEN_YELLOW,
|
||||
destination_barcode=destination_barcode,
|
||||
scanned_pallet=barcode_pallet,
|
||||
)
|
||||
|
||||
def _display_location(self, *, cella: object, ubicazione: object) -> str:
|
||||
"""Return the operator-facing location, honoring legacy conventional cells."""
|
||||
|
||||
try:
|
||||
cella_int = int(cella)
|
||||
except Exception:
|
||||
cella_int = None
|
||||
if cella_int in self.CONVENTIONAL_LOCATION_BY_CELL:
|
||||
return self.CONVENTIONAL_LOCATION_BY_CELL[cella_int]
|
||||
return str(ubicazione or cella or "")
|
||||
Reference in New Issue
Block a user