
    {{i^                         d Z ddlZddlZddlmZ ddlmZmZmZmZ ddl	Z	 e	j
        e          Z G d d          Z G d d          Z G d	 d
          ZdS )a  
ENIGMA Inventario - Database Models
====================================

Models per gestione inventario con database MySQL.
Usa le tabelle esistenti:
- inv_sessioni_inventario
- inv_risultati_inventario
- inv_cache_prodotti

Author: HetGi & Claude
Date: 2026-01-14
    N)datetime)OptionalDictListAnyc            
           e Zd ZdZdefdZd Zddededed	ee         fd
Z	ddededed	ee         fdZ
deded	ee         fdZddededed	efdZddeded	ee         fdZdededed	efdZdS )InventorySessionz'Model per gestione sessioni inventario.	db_configc                     || _         dS zs
        Inizializza model.
        
        Args:
            db_config: {host, user, password, database}
        Nr
   selfr
   s     =/var/www/tmov.alphamb/tmov_inventario/app/models/inventory.py__init__zInventorySession.__init__        #    c                     t          j        | j        d         | j        d         | j        d         | j        d         dt           j        j                  S zCrea connessione database.hostuserpassworddatabaseutf8mb4)r   r   r   r   charsetcursorclasspymysqlconnectr
   cursors
DictCursorr   s    r   _get_connectionz InventorySession._get_connection$   N    ''^J/^J/2
 
 
 	
r   B  	tenant_iduser_idsede_idreturnc                    |                                  }|                                }	 |                    d||f           |                                }|rVt                              d| d|d                     |d         |                                 |                                 S d}|                    ||||f           |                                 |j        }t          	                    d| d|            ||                                 |                                 S # t          $ rd}	|                                 t                              d|	            Y d	}	~	|                                 |                                 d	S d	}	~	ww xY w# |                                 |                                 w xY w)
a  
        Crea nuova sessione inventario.
        
        Args:
            tenant_id: ID tenant
            user_id: ID utente
            sede_id: ID sede (default 17117 - Sales Point principale)
        
        Returns:
            ID sessione creata, None se errore
        z
                SELECT id FROM inv_sessioni_inventario
                WHERE id_tenant = %s AND id_user = %s AND stato = 'aperta'
                LIMIT 1
            u$   Sessione giÃƒÂ  aperta per user z: idz
                INSERT INTO inv_sessioni_inventario (
                    id_tenant, id_user, id_sede, timestamp_inizio, stato
                ) VALUES (
                    %s, %s, %s, NOW(), 'aperta'
                )
            zSessione creata: z
 per user zErrore creazione sessione: N)r#   cursorexecutefetchoneloggerwarningclosecommit	lastrowidinfo	Exceptionrollbackerror)
r   r&   r'   r(   connr,   existingsql
session_ides
             r   create_sessionzInventorySession.create_session/   s    ##%%"	NN  W%	' ' ' ((H &agaaQYZ^Q_aabbb~, LLNNNJJLLLL)C NN3GW =>>>KKMMM)JKKKJKK'KKLLL LLNNNJJLLLL  	 	 	MMOOOLL:q::;;;444LLNNNJJLLLLL	
 LLNNNJJLLLLs1   AD. .AD. .
F81F)F FF *G	Nc                    |                                  }|                                }	 d}||g}||dz  }|                    |           |dz  }|                    ||           |                                |                                 |                                 S # |                                 |                                 w xY w)a/  
        Ottiene sessione aperta corrente per una sede specifica.
        
        Args:
            tenant_id: ID tenant
            user_id: ID utente
            sede_id: ID sede (opzionale - se None, ritorna prima sessione trovata)
        
        Returns:
            Dati sessione o None
        a  
                SELECT 
                    s.id, s.id_tenant, s.id_user, s.id_sede,
                    s.timestamp_inizio, s.stato, s.note, s.nome_sessione,
                    COUNT(DISTINCT r.id) as total_items,
                    SUM(r.quantita_fisica) as total_scans
                FROM inv_sessioni_inventario s
                LEFT JOIN inv_risultati_inventario r ON s.id = r.id_sessione
                WHERE s.id_tenant = %s AND s.id_user = %s AND s.stato = 'aperta'
            Nz AND s.id_sede = %szt
                GROUP BY s.id
                ORDER BY s.timestamp_inizio DESC
                LIMIT 1
            )r#   r,   appendr-   r.   r1   )r   r&   r'   r(   r8   r,   r:   paramss           r   get_active_sessionz#InventorySession.get_active_sessionc   s     ##%%		C  )F ",,g&&&  C
 NN3'''??$$LLNNNJJLLLL LLNNNJJLLLLs   AB# #*Cr;   c                 Z   |                                  }|                                }	 d}|                    |||f           |                                |                                 |                                 S # |                                 |                                 w xY w)z
        Ottiene dettagli sessione.
        
        Args:
            session_id: ID sessione
            tenant_id: ID tenant
        
        Returns:
            Dati sessione o None
        a  
                SELECT 
                    s.*,
                    u.email as user_email,
                    u.nome as user_name,
                    COUNT(r.id) as items_count
                FROM inv_sessioni_inventario s
                LEFT JOIN tenant_users u ON s.id_user = u.id
                LEFT JOIN inv_risultati_inventario r ON s.id = r.id_sessione
                WHERE s.id = %s AND s.id_tenant = %s
                GROUP BY s.id
            )r#   r,   r-   r.   r1   )r   r;   r&   r8   r,   r:   s         r   get_sessionzInventorySession.get_session   s     ##%%	C NN3Y 7888??$$LLNNNJJLLLL LLNNNJJLLLL   -B   *B*notec                 "   |                                  }|                                }	 |                    d|f           |                                }|r|d         nd}|r|d         nd}d}	|                    |	||||f           |                                 |j        }
t                              d| d| d           |
dk    |                                 |                                 S # t          $ rd}|
                                 t                              d	|            Y d
}~|                                 |                                 dS d
}~ww xY w# |                                 |                                 w xY w)z
        Chiude una sessione inventario.
        
        Args:
            session_id: ID sessione
            tenant_id: ID tenant
            note: Note opzionali
        
        Returns:
            True se successo, False altrimenti
        z
                SELECT COUNT(DISTINCT id) as total_items, COUNT(id) as total_scans
                FROM inv_risultati_inventario
                WHERE id_sessione = %s
            total_itemsr   total_scansa  
                UPDATE inv_sessioni_inventario
                SET stato = 'chiusa',
                    timestamp_fine = NOW(),
                    total_items = %s,
                    total_scans = %s
                WHERE id = %s AND id_tenant = %s AND stato = 'aperta'
            z	Sessione z	 chiusa (z items)zErrore chiusura sessione: NF)r#   r,   r-   r.   r2   rowcountr/   r4   r1   r5   r6   r7   )r   r;   r&   rE   r8   r,   countsrG   rH   r:   affectedr<   s               r   close_sessionzInventorySession.close_session   s    ##%%"	NN  	      __&&F39@&//qK39@&//qKC NN3k:y QRRRKKMMMHKKMJMMMMMNNNa< LLNNNJJLLLL  	 	 	MMOOOLL9a99:::555LLNNNJJLLLLL	
 LLNNNJJLLLLs*   B C3 3
E!=1E.E$ E!!E$ $*F
   limitc                 Z   |                                  }|                                }	 d}|                    |||f           |                                |                                 |                                 S # |                                 |                                 w xY w)z
        Ottiene sessioni recenti.
        
        Args:
            tenant_id: ID tenant
            limit: Numero massimo risultati
        
        Returns:
            Lista sessioni
        a	  
                SELECT 
                    s.*,
                    u.email as user_email,
                    u.nome as user_name,
                    COUNT(r.id) as items_count
                FROM inv_sessioni_inventario s
                LEFT JOIN tenant_users u ON s.id_user = u.id
                LEFT JOIN inv_risultati_inventario r ON s.id = r.id_sessione
                WHERE s.id_tenant = %s
                GROUP BY s.id
                ORDER BY s.timestamp_inizio DESC
                LIMIT %s
            r#   r,   r-   fetchallr1   )r   r&   rN   r8   r,   r:   s         r   get_recent_sessionsz$InventorySession.get_recent_sessions   s     ##%%	C NN3E 2333??$$LLNNNJJLLLL LLNNNJJLLLLrD   namec                    |                                  }|                                }	 d}|                    ||||f           |                                 |j        }t
                              d| d|            |dk    |                                 |                                 S # t          $ rd}|	                                 t
          
                    d|            Y d}~|                                 |                                 dS d}~ww xY w# |                                 |                                 w xY w)z
        Aggiorna nome sessione.
        
        Args:
            session_id: ID sessione
            tenant_id: ID tenant
            name: Nome sessione
        
        Returns:
            True se successo, False altrimenti
        z
                UPDATE inv_sessioni_inventario
                SET nome_sessione = %s
                WHERE id = %s AND id_tenant = %s
            zNome sessione z aggiornato: r   zErrore update nome sessione: NFr#   r,   r-   r2   rI   r/   r4   r1   r5   r6   r7   )	r   r;   r&   rS   r8   r,   r:   rK   r<   s	            r   update_session_namez$InventorySession.update_session_name  s9    ##%%	C
 NN3z9 =>>>KKMMMHKKHHH$HHIIIa< LLNNNJJLLLL  	 	 	MMOOOLL<<<===555LLNNNJJLLLLL	
 LLNNNJJLLLL*   AB. .
D81D)D DD *E	)r%   N)rM   )__name__
__module____qualname____doc__r   r   r#   intr   r=   rA   rC   strboolrL   r   rR   rV    r   r   r	   r	      s       11#$ # # # #	
 	
 	
1 1 1c 1C 1T\]`Ta 1 1 1 1h* *C *# * *W_`dWe * * * *Zc c htn    B1 1 1 13 1RV 1 1 1 1f! !S ! !d4j ! ! ! !F"c "c " "QU " " " " " "r   r	   c                       e Zd ZdZdefdZd Z	 	 dded	ed
ede	e         de
dedede	e         fdZdded	ededee         fdZdedede
fdZdededede
fdZdS )InventoryItemz#Model per gestione item inventario.r
   c                     || _         dS r   r   r   s     r   r   zInventoryItem.__init__/  r   r   c                     t          j        | j        d         | j        d         | j        d         | j        d         dt           j        j                  S r   r   r"   s    r   r#   zInventoryItem._get_connection8  r$   r   NF   r;   r&   barcodeproduct_datafound_in_apiquantitaid_seder)   c                 $   |                                  }|                                }		 d}
d}d}d}|r|                    d          }
|                    d          p|                    d          }t          |                    d          t                    r)|                    di                               d          nd}|                    dg           }|r/t          |          dk    r|d                             d          }nd}|	                    d	||f           |	                                }|r|d
         |z   }|	                    d||d         f           |                                 t          
                    d|d          d|            |d         |	                                 |                                 S d}|rt          j        |          nd}| }|	                    ||||||
|||||||f           |                                 |	j        }t          
                    d| d|            ||	                                 |                                 S # t          $ rd}|                                 t                              d|            Y d}~|	                                 |                                 dS d}~ww xY w# |	                                 |                                 w xY w)u  
        Aggiunge item alla sessione inventario.
        
        Args:
            session_id: ID sessione
            tenant_id: ID tenant
            barcode: Codice a barre
            product_data: Dati prodotto da API (opzionale)
            found_in_api: Se trovato in Cassanova
            quantita: QuantitÃƒÂ  (default 1)
        
        Returns:
            ID item creato, None se errore
        Nr+   descriptionrS   categorypricesr   valuez
                SELECT id, quantita_fisica FROM inv_risultati_inventario
                WHERE id_sessione = %s AND barcode = %s
                LIMIT 1
            quantita_fisicaz
                    UPDATE inv_risultati_inventario
                    SET quantita_fisica = %s
                    WHERE id = %s
                zItem aggiornato: u   , nuova quantitÃƒÂ : a  
                    INSERT INTO inv_risultati_inventario (
                        id_tenant, id_sessione, id_sede, barcode, codice_prodotto,
                        product_data, found_in_api, manual_entry,
                        scanned_at, nome_prodotto, categoria, 
                        quantita_fisica, prezzo_pubblico
                    ) VALUES (
                        %s, %s, %s, %s, %s, %s, %s, %s, NOW(), %s, %s, %s, %s
                    )
                zItem creato: z per sessione zErrore add_item: )r#   r,   get
isinstancedictlenr-   r.   r2   r/   r4   r1   jsondumpsr3   r5   r6   r7   )r   r;   r&   rf   rg   rh   ri   rj   r8   r,   codice_prodottonome_prodotto	categoriaprezzo_pubblicorn   r9   new_quantityr:   product_jsonmanual_entryitem_idr<   s                         r   add_itemzInventoryItem.add_itemC  s=   " ##%%I	"O MI"O 
+"."2"24"8"8 , 0 0 ? ? [<CSCSTZC[C[LVWcWgWghrWsWsuyLzLz  EL,,Z<<@@HHH  AE	 &))(B77 +c&kkAoo&,QimmG&<&<OO&*O NN  g&	( ( ( ((H %'(9:XE   #HTN3	5 5 5
 gggYegghhh~D LLNNNJJLLLL?	 <HQtz,777T#//sz7G_ ,!9h%   
  *OGOO:OOPPP LLNNNJJLLLL  	 	 	MMOOOLL0Q00111444LLNNNJJLLLLL	
 LLNNNJJLLLLs1   E=I4 A;I4 4
K">1K/K% K""K% %*Lc                    |                                  }|                                }	 |rd}|                    ||||f           nd}|                    |||f           |                                |                                 |                                 S # |                                 |                                 w xY w)z0Ottiene items di una sessione filtrati per sede.z
                    SELECT *
                    FROM inv_risultati_inventario
                    WHERE id_sessione = %s AND id_tenant = %s AND id_sede = %s
                    ORDER BY scanned_at DESC
                z
                    SELECT *
                    FROM inv_risultati_inventario
                    WHERE id_sessione = %s AND id_tenant = %s
                    ORDER BY scanned_at DESC
                rP   )r   r;   r&   rj   r8   r,   r:   s          r   get_session_itemszInventoryItem.get_session_items  s    ##%%	 = sZG$DEEEE sZ$;<<<??$$LLNNNJJLLLL LLNNNJJLLLLs   AB *Cr~   c                    |                                  }|                                }	 d}|                    |||f           |                                 |j        }t
                              d| d           |dk    |                                 |                                 S # t          $ rd}|	                                 t
          
                    d|            Y d}~|                                 |                                 dS d}~ww xY w# |                                 |                                 w xY w)z
        Elimina un item dalla sessione.
        
        Args:
            item_id: ID item
            session_id: ID sessione
        
        Returns:
            True se successo, False altrimenti
        zu
                DELETE FROM inv_risultati_inventario
                WHERE id = %s AND id_sessione = %s
            Item z
 eliminator   zErrore delete_item: NFrU   )r   r~   r;   r8   r,   r:   rK   r<   s           r   delete_itemzInventoryItem.delete_item  s2    ##%%	C NN3* 5666KKMMMHKK3333444a< LLNNNJJLLLL  	 	 	MMOOOLL333444555LLNNNJJLLLLL	
 LLNNNJJLLLLs*   AB+ +
D51D&D DD *Er{   c                    |                                  }|                                }	 d}|                    ||||f           |                                 |j        }t
                              d| d|            |dk    |                                 |                                 S # t          $ rd}|	                                 t
          
                    d|            Y d}~|                                 |                                 dS d}~ww xY w# |                                 |                                 w xY w)u  
        Modifica la quantitÃ  di un item esistente.
        
        Args:
            item_id: ID item
            session_id: ID sessione
            new_quantity: Nuova quantitÃ 
        
        Returns:
            True se successo, False altrimenti
        z
                UPDATE inv_risultati_inventario
                SET quantita_fisica = %s
                WHERE id = %s AND id_sessione = %s
            r   u    aggiornato a quantitÃ  r   zErrore update_item_quantity: NFrU   )	r   r~   r;   r{   r8   r,   r:   rK   r<   s	            r   update_item_quantityz"InventoryItem.update_item_quantity  s9    ##%%	C
 NN3w
 CDDDKKMMMHKKQQQ<QQRRRa< LLNNNJJLLLL  	 	 	MMOOOLL<<<===555LLNNNJJLLLLL	
 LLNNNJJLLLLrW   )NFre   NrX   )rY   rZ   r[   r\   r   r   r#   r]   r^   r   r_   r   r   r   r   r   r`   r   r   rb   rb   ,  sR       --#$ # # # #	
 	
 	
 LQ37] ]3 ]3 ] ]'~]DH]]-0]<DSM] ] ] ]~ C C # Y]^bYc    6 3  C  D        D"C "S "PS "X\ " " " " " "r   rb   c                       e Zd ZdZdefdZd Z	 ddededed	ed
e	f
dZ
ddeded
efdZdeded
ee         fdZddededed
ee         fdZdS )ProductCachez&Model per cache prodotti da Cassanova.r
   c                     || _         dS r   r   r   s     r   r   zProductCache.__init__  r   r   c                     t          j        | j        d         | j        d         | j        d         | j        d         dt           j        j                  S r   r   r"   s    r   r#   zProductCache._get_connection  r$   r   Nr&   rf   rg   ttlr)   c                    |'ddl }t          |                    dd                    }|                                 }|                                }	 d}t          j        |          }	|                    ||||	|f           |                                 t          
                    d| d           	 |                                 |                                 dS # t          $ rd}
|                                 t                              d	|
            Y d}
~
|                                 |                                 d
S d}
~
ww xY w# |                                 |                                 w xY w)a;  
        Salva prodotto in cache.
        
        Args:
            tenant_id: ID tenant
            barcode: Codice a barre
            product_data: Dati prodotto da Cassanova
            ttl: Time-to-live in secondi (default 24h)
        
        Returns:
            True se successo, False altrimenti
        Nr   CACHE_TTL_SECONDSi:	 a  
                INSERT INTO inv_cache_prodotti (
                    id_tenant, codice_barcode, dati_cassanova, 
                    cache_timestamp, ttl
                ) VALUES (
                    %s, %s, %s, NOW(), %s
                )
                ON DUPLICATE KEY UPDATE
                    dati_cassanova = VALUES(dati_cassanova),
                    cache_timestamp = NOW(),
                    ttl = VALUES(ttl)
            z	Prodotto z salvato in cacheTzErrore cache_product: F)osr]   getenvr#   r,   ru   rv   r-   r2   r/   debugr1   r5   r6   r7   )r   r&   rf   rg   r   r   r8   r,   r:   r|   r<   s              r   cache_productzProductCache.cache_product  sf    ;IIIbii 3V<<==C##%%	C  :l33LNN3G\3 GHHHKKMMMLL?W???@@@ LLNNNJJLLLLL  	 	 	MMOOOLL5!55666555LLNNNJJLLLLL	
 LLNNNJJLLLLs+   A"C   
E*1E	E 	EE *E;  items
batch_sizec           	      &   |sdS |                                  }|                                }d}	 d}t          dt          |          |          D ]y}||||z            }|                    ||           |                                 |t          |          z  }t                              d| dt          |           d           z||                                 |                                 S # t          $ re}	|
                                 t                              d|	            |cY d}	~	|                                 |                                 S d}	~	ww xY w# |                                 |                                 w xY w)a)  
        Salva prodotti in cache in batch con una singola connessione DB.

        Args:
            items: Lista di tuple (tenant_id, barcode, product_json_str, ttl)
            batch_size: Dimensione batch per commit

        Returns:
            Numero di prodotti salvati con successo
        r   a  
                INSERT INTO inv_cache_prodotti (
                    id_tenant, codice_barcode, dati_cassanova,
                    cache_timestamp, ttl
                ) VALUES (
                    %s, %s, %s, NOW(), %s
                )
                ON DUPLICATE KEY UPDATE
                    dati_cassanova = VALUES(dati_cassanova),
                    cache_timestamp = NOW(),
                    ttl = VALUES(ttl)
            zBatch cache: /z salvatizErrore cache_products_batch: N)r#   r,   rangert   executemanyr2   r/   r4   r1   r5   r6   r7   )
r   r   r   r8   r,   cachedr:   ibatchr<   s
             r   cache_products_batchz!ProductCache.cache_products_batchN  s     	1##%%	C 1c%jj*55 K KaJ./""3...#e**$IFIISZZIIIJJJJ LLNNNJJLLLL  	 	 	MMOOOLL<<<===MMMMMLLNNNJJLLLL	
 LLNNNJJLLLLs0   BC4 4
E#>2E0E#1E& E##E& &*Fc                    |                                  }|                                }	 d}|                    |||f           |                                }|rJ|d         rBt	          j        |d                   |                                 |                                 S 	 |                                 |                                 dS # t          $ rP}t          	                    d|            Y d}~|                                 |                                 dS d}~ww xY w# |                                 |                                 w xY w)z
        Recupera prodotto dalla cache.
        
        Args:
            tenant_id: ID tenant
            barcode: Codice a barre
        
        Returns:
            Dati prodotto o None se non in cache o scaduto
        z
                SELECT dati_cassanova, cache_timestamp, ttl
                FROM inv_cache_prodotti
                WHERE id_tenant = %s AND codice_barcode = %s
                AND TIMESTAMPDIFF(SECOND, cache_timestamp, NOW()) < ttl
            dati_cassanovaNzErrore get_cached_product: )
r#   r,   r-   r.   ru   loadsr1   r5   r/   r7   )r   r&   rf   r8   r,   r:   resultr<   s           r   get_cached_productzProductCache.get_cached_product  sH    ##%%	C NN3G 4555__&&F <&!12 <z&)9":;; LLNNNJJLLLL  LLNNNJJLLLLL  	 	 	LL:q::;;;444LLNNNJJLLLLL	 LLNNNJJLLLLs*   AC 
D)D$6D, $D))D, ,*E   queryrN   c                    |                                  }|                                }	 t          |          dk     r*g |                                 |                                 S d}d| d}|                    |||||f           |                                }g }	|D ]s}
|
d         ri	 t          j        |
d                   }|	                    |           ;# t          j	        $ r& t                              d|
d                     Y ow xY wtt                              dt          |	           d|            |	|                                 |                                 S # t          $ rQ}t                              d	|            g cY d
}~|                                 |                                 S d
}~ww xY w# |                                 |                                 w xY w)a  
        Cerca prodotti nella cache locale per nome.
        
        Args:
            tenant_id: ID tenant
            query: Testo da cercare (minimo 3 caratteri)
            limit: Numero massimo risultati
        
        Returns:
            Lista prodotti trovati
           a  
                SELECT codice_barcode, dati_cassanova
                FROM inv_cache_prodotti
                WHERE id_tenant = %s
                AND TIMESTAMPDIFF(SECOND, cache_timestamp, NOW()) < ttl
                AND (
                    LOWER(dati_cassanova) LIKE LOWER(%s)
                    OR LOWER(codice_barcode) LIKE LOWER(%s)
                )
                LIMIT %s
            %r   zJSON invalido per barcode codice_barcodezTrovati z prodotti per query: zErrore search_products_local: N)r#   r,   rt   r1   r-   rQ   ru   r   r?   JSONDecodeErrorr/   r7   r4   r5   )r   r&   r   rN   r8   r,   r:   search_patternresultsproductsrowproductr<   s                r   search_products_localz"ProductCache.search_products_local  s    ##%%*	 5zzA~~J LLNNNJJLLLLE
C *\\\NNN3NNE RSSSoo''G H [ ['( [["&*S1A-B"C"C 0000/ [ [ [%Y#FVBW%Y%YZZZZZ[	[ KKN3x==NNuNNOOO LLNNNJJLLLL  	 	 	LL=!==>>>IIIIILLNNNJJLLLL	 LLNNNJJLLLLsZ   E, 'AE, ,/CE, 2DE, D2E, ,
G6GGG
 GG
 
*G4rX   )r   )r   )rY   rZ   r[   r\   r   r   r#   r]   r^   r_   r   listr   r   r   r   r   r`   r   r   r   r     s"       00#$ # # # #	
 	
 	
 591 1s 1S 1#'1.11=A1 1 1 1f/ /$ /C /# / / / /b!C !# !(4. ! ! ! !F9 9s 93 9s 9TXY]T^ 9 9 9 9 9 9r   r   )r\   r   ru   r   typingr   r   r   r   logging	getLoggerrY   r/   r	   rb   r   r`   r   r   <module>r      s            , , , , , , , , , , , , 		8	$	$Q Q Q Q Q Q Q QhU U U U U U U UpW W W W W W W W W Wr   