From d4cfe0be54e5af4aa9b1310ee5726fe70e4225e1 Mon Sep 17 00:00:00 2001 From: allebonvi Date: Sat, 4 Jul 2026 09:13:25 +0200 Subject: [PATCH] Aggiungi salto UDC nel picking barcode --- apply_barcode_picking_skip_patch.sql | 43 ++++++++++++ barcode_client.py | 15 ++++- barcode_repository.py | 87 ++++++++++++++++++++++++- barcode_service.py | 38 +++++++++++ rollback_barcode_picking_skip_patch.sql | 9 +++ version_info.py | 6 +- 6 files changed, 191 insertions(+), 7 deletions(-) create mode 100644 apply_barcode_picking_skip_patch.sql create mode 100644 rollback_barcode_picking_skip_patch.sql diff --git a/apply_barcode_picking_skip_patch.sql b/apply_barcode_picking_skip_patch.sql new file mode 100644 index 0000000..5a155bd --- /dev/null +++ b/apply_barcode_picking_skip_patch.sql @@ -0,0 +1,43 @@ +SET XACT_ABORT ON; +BEGIN TRANSACTION; + +IF OBJECT_ID(N'dbo.py_BarcodePickingListSkip', N'U') IS NULL +BEGIN + CREATE TABLE dbo.py_BarcodePickingListSkip ( + ID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_py_BarcodePickingListSkip PRIMARY KEY, + Documento varchar(50) NOT NULL, + Pallet varchar(50) NOT NULL, + IDStato int NOT NULL, + IDOperatore int NOT NULL, + DataOra datetime2(0) NOT NULL CONSTRAINT DF_py_BarcodePickingListSkip_DataOra DEFAULT SYSDATETIME(), + Motivo nvarchar(200) NULL, + Risolto bit NOT NULL CONSTRAINT DF_py_BarcodePickingListSkip_Risolto DEFAULT 0, + RisoltoDa int NULL, + RisoltoDataOra datetime2(0) NULL + ); +END; + +IF NOT EXISTS ( + SELECT 1 + FROM sys.indexes + WHERE name = N'UX_py_BarcodePickingListSkip_Open' + AND object_id = OBJECT_ID(N'dbo.py_BarcodePickingListSkip', N'U') +) +BEGIN + CREATE UNIQUE INDEX UX_py_BarcodePickingListSkip_Open + ON dbo.py_BarcodePickingListSkip (Documento, Pallet, IDStato) + WHERE Risolto = 0; +END; + +IF NOT EXISTS ( + SELECT 1 + FROM sys.indexes + WHERE name = N'IX_py_BarcodePickingListSkip_Queue' + AND object_id = OBJECT_ID(N'dbo.py_BarcodePickingListSkip', N'U') +) +BEGIN + CREATE INDEX IX_py_BarcodePickingListSkip_Queue + ON dbo.py_BarcodePickingListSkip (IDStato, Documento, Pallet, Risolto); +END; + +COMMIT TRANSACTION; diff --git a/barcode_client.py b/barcode_client.py index bb167d1..18d0e66 100644 --- a/barcode_client.py +++ b/barcode_client.py @@ -388,6 +388,10 @@ class BarcodeClientApp: self.btn_submit.configure(text="[F3] Pausa PL") else: self.btn_submit.configure(text="[F3] Carica") + if state.mode in ("priority_high", "priority_low"): + self.btn_unload.configure(text="[F4] Salta UDC") + else: + self.btn_unload.configure(text="[F4] Scarica") queue_buttons_state = "disabled" if str(state.queue_label or "") == "Pausa PL" else "normal" self.btn_f1.configure(state=queue_buttons_state) self.btn_f2.configure(state=queue_buttons_state) @@ -502,9 +506,8 @@ class BarcodeClientApp: pallet = str(self.scanned_var.get() or "").strip() destination = str(self.destination_var.get() or "").strip() mode = getattr(self.service.state, "mode", "") - if pallet and mode in ("priority_high", "priority_low") and destination == self.SHIPPED_BARCODE: - # Legacy barcode flow: F4/Scarica confirms the prepared unload destination. - self._submit() + if mode in ("priority_high", "priority_low"): + self._skip_current_picking_pallet() return if pallet: self._submit_unload_with_source_check() @@ -539,6 +542,12 @@ class BarcodeClientApp: busy_message="In esecuzione...", ) + def _skip_current_picking_pallet(self) -> None: + self._run_async( + lambda: self.service.skip_current_picking_pallet(), + busy_message="Registro salto UDC...", + ) + def _run_async(self, coro_factory: Callable[[], object], busy_message: str) -> None: if self._pending is not None and not self._pending.done(): return diff --git a/barcode_repository.py b/barcode_repository.py index c06b019..96be675 100644 --- a/barcode_repository.py +++ b/barcode_repository.py @@ -26,9 +26,17 @@ SELECT TOP (1) Ubicazione, Ordinamento, IDStato -FROM dbo.py_XMag_ViewPackingList +FROM dbo.py_XMag_ViewPackingList AS pl WHERE Ordinamento > 0 AND IDStato = :id_stato + AND NOT EXISTS ( + SELECT 1 + FROM dbo.py_BarcodePickingListSkip AS s + WHERE s.Documento = CAST(pl.Documento AS varchar(50)) + AND s.Pallet = CAST(pl.Pallet AS varchar(50)) + AND s.IDStato = pl.IDStato + AND s.Risolto = 0 + ) ORDER BY Ordinamento; """ @@ -136,6 +144,44 @@ SELECT :numero_cella AS NumeroCella; """ +SQL_SKIP_PICKING_PALLET = """ +SET NOCOUNT ON; + +IF NOT EXISTS ( + SELECT 1 + FROM dbo.py_BarcodePickingListSkip + WHERE Documento = :documento + AND Pallet = :pallet + AND IDStato = :id_stato + AND Risolto = 0 +) +BEGIN + INSERT INTO dbo.py_BarcodePickingListSkip ( + Documento, + Pallet, + IDStato, + IDOperatore, + DataOra, + Motivo, + Risolto + ) + VALUES ( + :documento, + :pallet, + :id_stato, + :id_operatore, + SYSDATETIME(), + :motivo, + 0 + ); +END; + +SELECT + :documento AS Documento, + :pallet AS Pallet, + :id_stato AS IDStato; +""" + def _rows_to_dicts(res: dict[str, Any] | None) -> list[dict[str, Any]]: """Convert ``query_json`` payloads to a list of row dictionaries.""" @@ -219,6 +265,45 @@ class BarcodeRepository: res = await self.db_client.query_json(SQL_OPEN_PALLETS_BY_CELL, {"id_cella": int(id_cella)}) return _rows_to_dicts(res) + async def skip_picking_pallet( + self, + *, + documento: str, + pallet: str, + id_stato: int, + operator_id: int, + motivo: str = "UDC non trovata dal magazziniere", + ) -> None: + """Persist one skipped picking-list pallet so the queue can advance.""" + + params = { + "documento": str(documento or "").strip(), + "pallet": str(pallet or "").strip(), + "id_stato": int(id_stato), + "id_operatore": int(operator_id), + "motivo": str(motivo or "").strip(), + } + log_runtime_event( + "Barcode WMS", + ( + "PICKING SKIP START " + f"documento={params['documento']} " + f"pallet={params['pallet']} " + f"id_stato={params['id_stato']} " + f"operator={params['id_operatore']}" + ), + ) + await self.db_client.query_json(SQL_SKIP_PICKING_PALLET, params, commit=True) + log_runtime_event( + "Barcode WMS", + ( + "PICKING SKIP OK " + f"documento={params['documento']} " + f"pallet={params['pallet']} " + f"id_stato={params['id_stato']}" + ), + ) + 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 33cf4b7..266ca84 100644 --- a/barcode_service.py +++ b/barcode_service.py @@ -115,6 +115,44 @@ class BarcodeService: ) return self._state + async def skip_current_picking_pallet(self) -> BarcodeActionResult: + """Mark the currently proposed picking pallet as skipped and advance.""" + + mode = str(self._state.mode or "") + if mode not in ("priority_high", "priority_low"): + return BarcodeActionResult(False, self._state, "Nessuna picking list attiva.") + + id_stato = 1 if mode == "priority_high" else 0 + documento = str(self._state.document or "").strip() + pallet = str(self._state.expected_pallet or "").strip() + if not documento or not pallet: + self._state.status_text = "Nessuna UDC da saltare." + self._state.status_color = self.RED + return BarcodeActionResult(False, self._state, self._state.status_text) + + await self.repository.skip_picking_pallet( + documento=documento, + pallet=pallet, + id_stato=id_stato, + operator_id=self.operator_id, + ) + queue_label = "Alta priorita' (F1)" if id_stato == 1 else "Bassa priorita' (F2)" + self._current_priority_state = id_stato + self._state = BarcodeViewState( + mode="confirm", + queue_label=queue_label, + status_text="UDC saltata - chiusura manuale richiesta", + status_color=self.RED, + source_location=self._state.source_location, + document=documento, + customer="UDC non trovata", + expected_pallet=pallet, + destination_barcode=self.SHIPPED_BARCODE, + auto_advance_delay_ms=1500, + destination_readonly=True, + ) + return BarcodeActionResult(True, self._state, self._state.status_text) + async def start_priority_queue(self, id_stato: int) -> BarcodeActionResult: """Load the next item of the selected legacy priority queue.""" diff --git a/rollback_barcode_picking_skip_patch.sql b/rollback_barcode_picking_skip_patch.sql new file mode 100644 index 0000000..88f9837 --- /dev/null +++ b/rollback_barcode_picking_skip_patch.sql @@ -0,0 +1,9 @@ +SET XACT_ABORT ON; +BEGIN TRANSACTION; + +IF OBJECT_ID(N'dbo.py_BarcodePickingListSkip', N'U') IS NOT NULL +BEGIN + DROP TABLE dbo.py_BarcodePickingListSkip; +END; + +COMMIT TRANSACTION; diff --git a/version_info.py b/version_info.py index 28b875c..dce0412 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.19", - "barcode_repository": "1.0.5", - "barcode_service": "1.0.15", + "barcode_client": "1.0.20", + "barcode_repository": "1.0.6", + "barcode_service": "1.0.16", "busy_overlay": "1.0.0", "db_config": "1.0.0", "gestione_aree": "1.0.1",