diff --git a/barcode_client.py b/barcode_client.py index ce89585..95badc0 100644 --- a/barcode_client.py +++ b/barcode_client.py @@ -410,7 +410,10 @@ class BarcodeClientApp: lambda q=next_queue: self._start_queue(q), ) - self.root.after(20, self._focus_primary_input) + if bool(getattr(state, "focus_destination", False)): + self.root.after(20, self._focus_destination_input) + else: + self.root.after(20, self._focus_primary_input) def _focus_primary_input(self) -> None: try: diff --git a/barcode_repository.py b/barcode_repository.py index 42206f1..c06b019 100644 --- a/barcode_repository.py +++ b/barcode_repository.py @@ -86,6 +86,15 @@ WHERE g.BarcodePallet = :pallet ORDER BY g.IDCella; """ +SQL_OPEN_PALLETS_BY_CELL = """ +SELECT + g.BarcodePallet, + g.IDCella +FROM dbo.XMag_GiacenzaPallet AS g +WHERE g.IDCella = :id_cella +ORDER BY g.BarcodePallet; +""" + SQL_RESOLVE_PHYSICAL_CELL = """ DECLARE @raw int = TRY_CONVERT(int, :destination); DECLARE @cell_id int = @@ -204,6 +213,12 @@ class BarcodeRepository: res = await self.db_client.query_json(SQL_OPEN_LOCATIONS_BY_PALLET, {"pallet": str(pallet or "").strip()}) return _rows_to_dicts(res) + async def fetch_open_pallets_by_cell(self, id_cella: int) -> list[dict[str, Any]]: + """Return pallets currently present in a physical cell.""" + + res = await self.db_client.query_json(SQL_OPEN_PALLETS_BY_CELL, {"id_cella": int(id_cella)}) + return _rows_to_dicts(res) + async def resolve_physical_cell(self, destination: str) -> DestinationCell | None: """Accept either an internal cell ID or the scanned legacy cell barcode.""" diff --git a/barcode_service.py b/barcode_service.py index c163a4e..c6d6e5d 100644 --- a/barcode_service.py +++ b/barcode_service.py @@ -30,6 +30,7 @@ class BarcodeViewState: scanned_pallet: str = "" auto_advance_delay_ms: int = 0 destination_readonly: bool = False + focus_destination: bool = False @dataclass @@ -176,13 +177,82 @@ class BarcodeService: self._state.status_color = self.RED return BarcodeActionResult(False, self._state, self._state.status_text) + target_barcode = destination + target_numero_cella = int(destination) + target_id_cella = 9999 if destination == self.SHIPPED_BARCODE else (1000 if destination == self.NON_SCAFFALATA_BARCODE else None) + target_display = destination + if is_direct_load: + resolved_cell = await self.repository.resolve_physical_cell(destination) + if not resolved_cell: + self._state.scanned_pallet = pallet + self._state.destination_barcode = destination + self._state.status_text = f"Cella non valida: {destination}." + self._state.status_color = self.RED + self._state.focus_destination = True + return BarcodeActionResult(False, self._state, self._state.status_text) + + occupants = await self.repository.fetch_open_pallets_by_cell(resolved_cell.id_cella) + if occupants: + log_runtime_event( + "Barcode WMS", + ( + "MOVE BLOCKED DESTINATION_OCCUPIED " + f"pallet={pallet} " + f"cell={resolved_cell.id_cella} " + f"occupants={','.join(str(row.get('BarcodePallet') or '') for row in occupants)}" + ), + ) + self._state.scanned_pallet = pallet + self._state.destination_barcode = destination + self._state.status_text = "Errore: cella scelta occupata, cambiare cella di destinazione." + self._state.status_color = self.RED + self._state.destination_readonly = False + self._state.focus_destination = True + return BarcodeActionResult(False, self._state, self._state.status_text) + + target_barcode = resolved_cell.barcode_cella + target_numero_cella = resolved_cell.numero_cella + target_id_cella = resolved_cell.id_cella + target_display = resolved_cell.ubicazione or destination + current_location = await self.repository.fetch_current_location_by_pallet(pallet) picking_before_move = await self.repository.fetch_picking_by_pallet(pallet) if not current_location and not picking_before_move: - self._state.scanned_pallet = pallet - self._state.status_text = "UDC non presente a magazzino." - self._state.status_color = self.RED - return BarcodeActionResult(False, self._state, self._state.status_text) + if is_direct_load: + trace_row = await self.repository.fetch_trace_by_pallet(pallet) + if not trace_row: + self._state.scanned_pallet = pallet + self._state.destination_barcode = destination + self._state.status_text = "Errore: UDC non riconosciuta in ERP/SAM." + self._state.status_color = self.RED + return BarcodeActionResult(False, self._state, self._state.status_text) + + log_runtime_event( + "Barcode WMS", + f"AUTO INTAKE NON_SHELVED pallet={pallet} before_cell={target_id_cella}", + ) + await self.repository.execute_legacy_move( + operator_id=self.operator_id, + barcode_cella=self.NON_SCAFFALATA_BARCODE, + barcode_pallet=pallet, + numero_cella=int(self.NON_SCAFFALATA_BARCODE), + ) + current_location = await self.repository.fetch_current_location_by_pallet(pallet) + try: + current_id_cella = int((current_location or {}).get("IDCella") or 0) + except Exception: + current_id_cella = 0 + if current_id_cella != 1000: + self._state.scanned_pallet = pallet + self._state.destination_barcode = destination + self._state.status_text = "Movimento non confermato: ingresso UDC non scaffalata non riuscito." + self._state.status_color = self.RED + return BarcodeActionResult(False, self._state, self._state.status_text) + else: + self._state.scanned_pallet = pallet + self._state.status_text = "UDC non presente a magazzino." + self._state.status_color = self.RED + return BarcodeActionResult(False, self._state, self._state.status_text) open_locations = await self.repository.fetch_open_locations_by_pallet(pallet) distinct_cells = sorted({ @@ -205,16 +275,13 @@ class BarcodeService: self._state.status_color = self.RED return BarcodeActionResult(False, self._state, self._state.status_text) - target_barcode = destination - target_numero_cella = int(destination) - target_id_cella = 9999 if destination == self.SHIPPED_BARCODE else (1000 if destination == self.NON_SCAFFALATA_BARCODE else None) - target_display = destination - if destination not in (self.NON_SCAFFALATA_BARCODE, self.SHIPPED_BARCODE): + if not is_direct_load and destination not in (self.NON_SCAFFALATA_BARCODE, self.SHIPPED_BARCODE): resolved_cell = await self.repository.resolve_physical_cell(destination) if not resolved_cell: self._state.scanned_pallet = pallet self._state.status_text = f"Cella non valida: {destination}." self._state.status_color = self.RED + self._state.focus_destination = True return BarcodeActionResult(False, self._state, self._state.status_text) target_barcode = resolved_cell.barcode_cella target_numero_cella = resolved_cell.numero_cella diff --git a/version_info.py b/version_info.py index cdc4c98..37c27ab 100644 --- a/version_info.py +++ b/version_info.py @@ -13,9 +13,9 @@ MODULE_VERSIONS: dict[str, str] = { "async_msssql_query": "1.0.0", "audit_log": "1.0.0", "main": "1.0.1", - "barcode_client": "1.0.17", - "barcode_repository": "1.0.4", - "barcode_service": "1.0.13", + "barcode_client": "1.0.18", + "barcode_repository": "1.0.5", + "barcode_service": "1.0.14", "busy_overlay": "1.0.0", "db_config": "1.0.0", "gestione_aree": "1.0.1",