# Modulo `prenota_sprenota_sql.py`

## Scopo

Il modulo contiene il port asincrono della stored procedure di prenotazione picking list. E' il ponte tra la UI Python e la stored SQL Python-only `dbo.py_sp_xExePackingListPallet`.

La funzione pubblica principale e':

```python
async def sp_xExePackingListPallet_async(db, IDOperatore: int, Documento: str, Azione: str = "P") -> SPResult
```

## Motivazione

Durante la fase di test sul campo devono convivere:

- applicazione C# legacy;
- applicazione Python.

Per evitare collisioni, il Python non chiama `dbo.sp_xExePackingListPallet` legacy ma `dbo.py_sp_xExePackingListPallet`.

## Semantica parametro `Azione`

| Azione | Significato |
| --- | --- |
| `P` | prenota il documento |
| `S` | s-prenota il documento |

Ogni altra azione ritorna:

```python
SPResult(rc=-10, message="Azione non valida: ...")
```

## Classe `SPResult`

Firma:

```python
@dataclass
class SPResult:
    rc: int = 0
    message: Optional[str] = ""
    id_result: Optional[int] = None
```

Campi:

| Campo | Ruolo |
| --- | --- |
| `rc` | codice ritorno della stored |
| `message` | messaggio errore o vuoto |
| `id_result` | predisposizione per ID risultato, non usato oggi |

## Funzioni helper

| Funzione | Firma | Ruolo |
| --- | --- | --- |
| `_query_one_value` | `_query_one_value(db, sql: str, params: Dict[str, Any]) -> Optional[Any]` | helper generico scalar, oggi non centrale nel flusso |
| `_query_all` | `_query_all(db, sql: str, params: Dict[str, Any]) -> List[Dict[str, Any]]` | normalizza query multi-riga |
| `_execute` | `_execute(db, sql: str, params: Dict[str, Any]) -> int` | helper generico DML |
| `sp_xExePackingListPallet_async` | vedi sopra | esegue stored Python-only |

## Processo logico `sp_xExePackingListPallet_async`

1. Normalizza `Azione` a maiuscolo.
2. Valida `Azione in ("P", "S")`.
3. Costruisce una batch SQL con output parameter `@RC`.
4. Esegue:

```sql
EXEC dbo.py_sp_xExePackingListPallet
    @IDOperatore = :IDOperatore,
    @Documento = :Documento,
    @Azione = :Azione,
    @RC = @RC OUTPUT;
```

5. Legge il valore `RC`.
6. Ritorna `SPResult(rc=rc, message="", id_result=None)`.
7. In caso di eccezione ritorna `SPResult(rc=-1, message=str(exc), id_result=None)`.

## Commit

La chiamata usa:

```python
commit=True
```

Motivo: la stored modifica `PyPickingListReservation` e `Celle.IDStato`.

## Rischi e note review

- Gli helper `_query_one_value`, `_query_all`, `_execute` sono generici e non tutti sono essenziali al percorso attuale.
- La funzione cattura le eccezioni e ritorna `SPResult(rc=-1)`, quindi la UI deve controllare sempre `rc`.
- La transazione reale dipende dal comportamento di `query_json(..., commit=True)` nel client DB.
