
    {iS                         d Z ddlZddlZddlmZmZ ddlmZmZmZm	Z	 ddl
mZ ddlZ ej        e          Z G d d          Zded	efd
ZdS )aD  
ENIGMA Inventario - Inventory Service FIXED
============================================

Service completo con:
- Token Cassanova in Flask session (PERSISTENTE)
- Integrazione con models che usano tabelle reali
- Cache prodotti locale
- Workflow completo inventario

Author: HetGi & Claude
Date: 2026-01-14 (FIXED VERSION)
    N)datetime	timedelta)OptionalDictListAny)sessionc            	           e Zd ZdZddedefdZdefdZded	edefd
Z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e         fdZddededefdZdS )CassanovaServicez5Service per API Cassanova con token in Flask session.https://api.cassanova.comapi_keyhostnamec                 0    || _         || _        d| _        d S )Nz1.0.0)r   r   api_version)selfr   r   s      G/var/www/tmov.alphamb/tmov_inventario/app/services/inventory_service.py__init__zCassanovaService.__init__   s     "    returnc                    	 t          j        d          }t          j        d          }|rZ|rXt          j        |          }t          j                    |t          d          z
  k     rt                              d           |S n*# t          $ r t                              d           Y nw xY wt          	                    d           | j
         d}d	d
d}d| j        i}t          j        |||d          }|                                 |                                }|d         }	|                    dd          }
	 |	t           d<   t          j                    t          |
          z                                   t           d<   t          	                    d|
 d           n*# t          $ r t                              d           Y nw xY w|	S )z|
        Ottiene token da Flask session o ne richiede uno nuovo.
        
        Returns:
            Token valido
        cassanova_tokencassanova_token_expires<   )secondszToken valido in sessione Flaskz3Flask session non disponibile, richiedo nuovo tokenz"Richiesta nuovo token Cassanova...z/apikey/tokenapplication/json*)Content-TypezX-Requested-WithapiKey
   )headersjsontimeoutaccess_token
expires_ini  z*Nuovo token salvato in session, scade tra sz*Session non disponibile, token non salvato)r	   getr   fromisoformatnowr   loggerdebugRuntimeErrorinfor   r   requestspostraise_for_statusr!   	isoformat)r   token
expires_at
expires_dturlr    payloadresponsedata	new_tokenr$   s              r   
_get_tokenzCassanovaService._get_token!   s   	PK 122E %>??J ! !%3J??
<>>Z)B2G2G2G%GHHLL!ABBB L 	P 	P 	P LLNOOOOO	P 	8999---. #
 
 T\*=gGRPPP!!###}}(	XXlD11
	G)2G%&:!>!>!>>ikk -. KKRZRRRSSSS 	G 	G 	GLLEFFFFF	G s%   BB $B.-B.A&F) )$GGmethodendpointc                     |                                  }| j         | }d| j        d| d}t          j        ||f|dd|}|                                 |                                S )z
        Esegue richiesta HTTP con autenticazione.
        
        Args:
            method: GET, POST, etc.
            endpoint: /products, etc.
            **kwargs: params, json, etc.
        
        Returns:
            Response JSON
        r   zBearer )r   z	X-VersionAuthorization   )r    r"   )r9   r   r   r-   requestr/   r!   )r   r:   r;   kwargsr1   r4   r    r6   s           r   _requestzCassanovaService._requestT   s     !!***.).u..
 
 #FCW"WWPVWW!!###}}r   barcodec                     	 |                      dd|ddd          }|                    dg           }|r|d         ndS # t          $ r(}t                              d	|            Y d}~dS d}~ww xY w)
zCerca prodotto per barcode.GET	/productsr      )rB   startlimitparamsproductsNzErrore ricerca barcode: )rA   r&   	Exceptionr)   error)r   rB   r7   rK   es        r   search_product_by_barcodez*CassanovaService.search_product_by_barcodem   s    	==Z[fg<h<h=iiDxx
B//H"*48A;;4 	 	 	LL7A7788844444	s   =A   
A2
A--A2Nr   d   descriptionrG   rH   c                     	 ||d}|r||d<   |                      dd|          S # t          $ r,}t                              d|            dg dcY d	}~S d	}~ww xY w)
zCerca prodotti.rG   rH   rQ   rD   rE   rI   zErrore ricerca prodotti: r   )
totalCountrK   N)rA   rL   r)   rM   )r   rQ   rG   rH   rJ   rN   s         r   search_productsz CassanovaService.search_productsw   s    	5$u55F 4(3}%==F=CCC 	5 	5 	5LL8Q88999"#44444444	5s   #& 
A!AAA
batch_sizec                    g }d}	 	 |                      ||          }|                    dg           }|                    dd          }|sn]|                    |           t                              dt          |           d| d           t          |          |k    rn||z  }t                              d	t          |           d
           |S # t          $ r)}t                              d|            |cY d}~S d}~ww xY w)z$Scarica TUTTI i prodotti (paginato).r   TrS   rK   rT   
Scaricati /z prodotti...zDownload completato: z	 prodottizErrore download prodotti: N)rU   r&   extendr)   r,   lenrL   rM   )r   rV   all_productsrG   r7   rK   totalrN   s           r   get_all_productsz!CassanovaService.get_all_products   sC   	 $++%z+JJ88J33q11 ##H---P\):):PPUPPPQQQ|$$--#$  KKLL0A0ALLLMMM 	  	  	 LL9a99:::	 s   CC 
D
!D?D
D
salespoint_idc           	         g }d}	 t                               d|            	 |                     dd| ||d          }|                    dg           }|                    d	d          }|sn`|                    |           t                               d
t          |           d| d| d           t          |          |k    rn||z  }t                               d| dt          |           d           |t          |          dS # t          $ r9}t                               d|            |t          |          dcY d}~S d}~ww xY w)a   
        Scarica TUTTE le giacenze per un salespoint (paginato).

        Args:
            salespoint_id: ID del punto vendita
            batch_size: Numero di risultati per pagina

        Returns:
            {'stocks': [...], 'totalCount': N}
        r   z!INIZIO get_stocks per salespoint TrD   z/stocks/rS   rI   stocksrT   z
Scaricate rY   z giacenze per salespoint z...z,Download giacenze completato per salespoint : z items)ra   rT   zErrore get_stocks: N)r)   r,   rA   r&   rZ   r[   rL   rM   )	r   r_   rV   
all_stocksrG   r7   ra   r]   rN   s	            r   
get_stockszCassanovaService.get_stocks   s    
	IKKKMKKLLL$}}.}..%*Z@@ %  
 (B//q11 !!&)))mZmm5mm[hmmmnnnz??e++#%$( KKo}ooX[\fXgXgoooppp(JHHH 	I 	I 	ILL2q22333(JHHHHHHHH	Is   D
D 
E.E	EE)r   )Nr   rP   )rP   )__name__
__module____qualname____doc__strr   r9   r   rA   r   rO   intrU   r   r^   rd    r   r   r   r      sE       ??# # #s # # # #
1C 1 1 1 1fs c     2 $    	5 	53 	5c 	5c 	5\` 	5 	5 	5 	5   3  d        8)I )I )I )It )I )I )I )I )I )Ir   r   	db_configcassanova_api_keyc                 Z     ddl mmm  G  fdd          } |            S )z
    Factory per creare InventoryService.
    
    Args:
        db_config: {host, user, password, database}
        cassanova_api_key: API Key Cassanova
    
    Returns:
        InventoryService instance
    r   )InventorySessionInventoryItemProductCachec                      e Zd ZdZ fdZdedeeef         fdZ		 	 d'dededede
e         fd	Zdede
e         fd
Zdedededef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+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ed%edefd&ZdS ),2create_inventory_service.<locals>.InventoryServicezService completo inventario.c                                | _                    | _                   | _        t                    | _        d S )N)r   )session_model
item_modelcache_modelr   	cassanova)r   rp   ro   rq   rm   rl   s    r   r   z;create_inventory_service.<locals>.InventoryService.__init__   sP    !1!1)!<!<D+mI66DO+|I66D-6GHHHDNNNr   	tenant_idr   c           	         ddl }t                              d|            | j                                        }t                              dt          |           d           t          |                    dd                    }g }d}t          |          D ]\  }}|	                    dg           }	d	}
|d
k     r6t                              d| d|	                    d           d|	            |	rt|dz  }|	D ]l}t          |t                    r|	                    d          n|}|r<d}
t          j        |          }|                    |t          |          ||f           m|	                    d          r|	                    d          r|	                    dg           D ]}|	                    dg           }|D ]}t          |t                    r|	                    d          n|}|rUd}
|                                }||d<   t          j        |          }|                    |t          |          ||f           |
sU|	                    d          r@t          j        |          }|                    |t          |d                   ||f           
t                              dt          |           d| d           | j                            |          }t          |          |z
  }t          |          ||d}t                              d|            t                              d|            |S )av  
            Sincronizza TUTTI i prodotti da Cassanova alla cache locale.
            Usa batch insert per performance (singola connessione DB).

            Args:
                tenant_id: ID tenant

            Returns:
                {
                    'total': int,
                    'cached': int,
                    'errors': int
                }
            r   Nz,Inizio sincronizzazione prodotti per tenant rX   z prodotti da CassanovaCACHE_TTL_SECONDSi:	 barcodesF   	Prodotto rb   rQ   z, barcodes: rF   valueTmultivariantvariantsselected_variantidz
Preparati z barcode da z prodotti per batch insert)r]   cachederrorszSincronizzazione completata: zProdotti con barcodes: )osr)   r,   rx   r^   r[   rj   getenv	enumerater&   
isinstancedictr!   dumpsappendri   copyrw   cache_products_batch)r   ry   r   rK   ttlitems_to_cacheproducts_with_barcodesidxproductbarcodes_listhad_barcodesbarcode_objbarcode_valueproduct_jsonvariantvariant_barcodesproduct_with_variantr   r   results                       r   sync_all_productszDcreate_inventory_service.<locals>.InventoryService.sync_all_products   s    IIIKKRyRRSSS~6688HKKJS]]JJJKKKbii 3V<<==CN%&" )( 3 3  ^  ^W 'J ; ;$77KK jC j j7;;}3M3M j j[h j jkkk  f*a/*'4 f fDN{\`DaDa(r(@(@(@gr( f+/L+/:g+>+>L*119c->P>PR^`c2deee ;;~.. 
j7;;z3J3J 
j#*;;z2#>#> 	j 	j+2;;z2+F+F(+; j jKHRS^`dHeHe,vKOOG,D,D,DkvM, j/37>||~~ 4KR 45G H/3z:N/O/O . 5 5y#mBTBTVbdg6h i i ij $ ^D(9(9 ^#':g#6#6L"))9c'$-6H6H,X[*\]]]KKxS%8%8xxF\xxxyyy%::>JJF((61F X   F KK@@@AAAKKJ2HJJKKKMr   NrB   rQ   c                    |r^| j                             ||          }|r!t                              d| d           |gS t                              d| d           g S |r| j                             ||d          }|S g S )a  
            Cerca prodotti per barcode/ID o nome.
            
            IMPORTANTE: Usa SOLO cache locale (nessuna chiamata API).
            I prodotti devono essere pre-sincronizzati tramite sync_all_products().
            
            Args:
                tenant_id: ID tenant
                barcode: Codice a barre o ID prodotto (ricerca esatta)
                description: Nome prodotto (ricerca parziale)
            
            Returns:
                Lista prodotti trovati (dalla cache locale)
            r~   z trovato in cache localezBarcode z: non trovato in cache locale. Sincronizza dalla dashboard.   )rH   )rw   get_cached_productr)   r*   warningsearch_products_local)r   ry   rB   rQ   r   rK   s         r   search_productzAcreate_inventory_service.<locals>.InventoryService.search_product3  s       )<<YPP $LL!NW!N!N!NOOO"8O m'mmmnnn	 +AA)[`bAcc 	r   c                    ddl } |j        d
i | j        j        }|                                }	 d}|                    ||f           |                                }g }|D ]}|d         }	|	r|                    d|	|f           |                                }
|
r&|
d         }|
d         r|
d          d|
d          nd}nd|	 }d}|                    t          |	          |||d         d	           ||
                                 |
                                 S # |
                                 |
                                 w xY w)z
            Recupera tutti i Sales Points disponibili dai prodotti sincronizzati.
            
            Args:
                tenant_id: ID tenant
            
            Returns:
                Lista sales points con conteggio prodotti
            r   NaX  
                    SELECT 
                        JSON_EXTRACT(dati_cassanova, '$.idSalesPoint') as sp_id,
                        COUNT(*) as product_count
                    FROM inv_cache_prodotti
                    WHERE id_tenant = %s
                    GROUP BY sp_id
                    ORDER BY product_count DESC
                u   
                            SELECT nome, indirizzo, città 
                            FROM inv_sedi 
                            WHERE id = %s AND id_tenant = %s
                        rF   z,    zSede )r   nameaddressproduct_countrk   )pymysqlconnectru   rl   cursorexecutefetchallfetchoner   rj   close)r   ry   r   connr   sqlresultssalespointsrowsp_id	sede_datar   r   s                r   get_salespointszBcreate_inventory_service.<locals>.InventoryService.get_salespointsZ  s    NNN"7?BBT%7%ABBD[[]]F- sYL111 //++ "  CFE  ( $Y/	1 1 1 %+OO$5$5	$ +#,Q<DKTUV<&a1&G&G1&G&G&G]aGG#25??D&*G#**"%e**$('.-0V	, ,    # 

 

s   CD! !*E
session_idr   c                 :    | j                             |||          S )zAggiorna nome sessione.)ru   update_session_name)r   r   ry   r   s       r   r   zFcreate_inventory_service.<locals>.InventoryService.update_session_name  s    %99*iQUVVVr   B  user_idsede_idc                 :    | j                             |||          S )zCrea nuova sessione.)ru   create_sessionr   ry   r   r   s       r   start_sessionz@create_inventory_service.<locals>.InventoryService.start_session  s    %44YQQQr   c                 :    | j                             |||          S )z.Ottiene sessione aperta corrente per una sede.)ru   get_active_sessionr   s       r   get_current_sessionzFcreate_inventory_service.<locals>.InventoryService.get_current_session  s    %88GWUUUr   c                 8    | j                             ||          S )zOttiene dettagli sessione.)ru   get_session)r   r   ry   s      r   get_session_detailszFcreate_inventory_service.<locals>.InventoryService.get_session_details  s    %11*iHHHr   notec                 :    | j                             |||          S )zChiude sessione.)ru   close_session)r   r   ry   r   s       r   r   z@create_inventory_service.<locals>.InventoryService.close_session  s    %33J	4PPPr   r   rH   c                 8    | j                             ||          S )zLista sessioni recenti.)ru   get_recent_sessions)r   ry   rH   s      r   r   zFcreate_inventory_service.<locals>.InventoryService.get_recent_sessions  s    %99)UKKKr   FrF   product_datafound_in_apiquantitaid_sedec           	      B    | j                             |||||||          S )zAggiunge item a sessione.)rv   add_item)r   r   ry   rB   r   r   r   r   s           r   r   z;create_inventory_service.<locals>.InventoryService.add_item  s.     ?++IwlHg  r   c                 :    | j                             |||          S )z0Ottiene items di una sessione filtrati per sede.)rv   get_session_items)r   r   ry   r   s       r   r   zDcreate_inventory_service.<locals>.InventoryService.get_session_items  s    ?44ZGTTTr   item_idc                 8    | j                             ||          S )zElimina item da sessione.)rv   delete_item)r   r   r   s      r   r   z>create_inventory_service.<locals>.InventoryService.delete_item  s    ?..w
CCCr   new_quantityc                 :    | j                             |||          S )u   Modifica quantitÃ  item.)rv   update_item_quantity)r   r   r   r   s       r   r   zGcreate_inventory_service.<locals>.InventoryService.update_item_quantity  s    ?77\ZZZr   r_   c                 j   	 ddl }ddl}ddlm} | j                            |          }|                    dg           } |j        di | j        j        }|	                                }	d}
|D ]}d}|	
                    ||||                    d          |                    d          |                    dd          |                    d	d          |                    d
d          |                    dd          |                    dd          f	           |
dz  }
|                                 |	                                 |                                 t                              d|
 d|            |
S # t          $ r(}t                              d|            Y d}~dS d}~ww xY w)a  
            Sincronizza giacenze da Cassanova per un salespoint.
            
            Args:
                tenant_id: ID tenant
                salespoint_id: ID sede da sincronizzare
            
            Returns:
                Numero di giacenze sincronizzate
            r   N)r   ra   a  
                        INSERT INTO inv_giacenze (
                            id_tenant, id_sede, id_prodotto, id_variante,
                            quantita, quantita_in_arrivo, quantita_in_uscita,
                            soglia_minima, gestisci_giacenza, data_sincronizzazione
                        ) VALUES (
                            %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW()
                        )
                        ON DUPLICATE KEY UPDATE
                            quantita = VALUES(quantita),
                            quantita_in_arrivo = VALUES(quantita_in_arrivo),
                            quantita_in_uscita = VALUES(quantita_in_uscita),
                            soglia_minima = VALUES(soglia_minima),
                            gestisci_giacenza = VALUES(gestisci_giacenza),
                            data_sincronizzazione = NOW()
                    	idProductidProductVariantquantityincomingQuantityoutgoingQuantitywarningLevelmanageStockTrF   zSincronizzate z giacenze per sede zErrore sync_stocks: rk   )r   r!   r   rx   rd   r&   r   ru   rl   r   r   commitr   r)   r,   rL   rM   )r   ry   r_   r   r!   r   r6   ra   r   r   syncedstockr   rN   s                 r   sync_stocksz>create_inventory_service.<locals>.InventoryService.sync_stocks  s   6------  >44]CC!h33&wFF);)EFF#    EC" NN3!%		+..		"455		*a00		"4a88		"4a88		.!44		-66
) 
 
 
 aKFF

WVWWWWXXX   7A77888qqqqqs   E=F   
F2
F--F2)NN)r   )N)r   )NFrF   N)re   rf   rg   rh   r   rj   r   ri   r   r   r   r   r   boolr   r   r   r   r   r   r   r   r   r   r   r   )rp   ro   rq   rm   rl   s   r   InventoryServicers      s       **	I 	I 	I 	I 	I 	I 	I 	I 	IH	s H	tCH~ H	 H	 H	 H	^ AE-1!	 !	C !	# !	'*!	6:4j!	 !	 !	 !	N<	S <	T$Z <	 <	 <	 <	|	W# 	W# 	WS 	WUY 	W 	W 	W 	W	R 	R3 	R 	Rs 	RW_`cWd 	R 	R 	R 	R	V 	V 	Vs 	VS 	V\dei\j 	V 	V 	V 	V	I# 	I# 	I(SW. 	I 	I 	I 	I	Q 	QC 	QC 	Qs 	QVZ 	Q 	Q 	Q 	Q	L 	L 	LS 	L$t* 	L 	L 	L 	L OT6:	 	s 	s 	S 	"*4.	GK	!	03	?G}	 	 	 		U 	U 	U 	Uc 	U]abf]g 	U 	U 	U 	U	Ds 	D 	D 	D 	D 	D 	D	[ 	[ 	[TW 	[\` 	[ 	[ 	[ 	[A	 A	S A	S A	 A	 A	 A	 A	 A	r   r   )app.models.inventoryro   rp   rq   )rl   rm   r   rp   ro   rq   s   `` @@@r   create_inventory_servicer      s     SRRRRRRRRRw w w w w w w w w w w w w wr	 r   )rh   r-   r!   r   r   typingr   r   r   r   flaskr	   logging	getLoggerre   r)   r   ri   r   rk   r   r   <module>r      s       ( ( ( ( ( ( ( ( , , , , , , , , , , , ,       		8	$	$nI nI nI nI nI nI nI nIbF F F F F F F Fr   