
    C"jk7	                   J   U d dl mZ d dlmZ d dlmZ d dlmZmZmZm	Z	m
Z
 d dlZd dlZd dlZd dlmZ d dlZd dlZd dlZd dlZd dlm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 d d
lm Z  d dl!m"Z" d dl#m$Z$ d dl%m&Z& d dl'm(Z( d dl)m*Z* dZ+dZ,dZ-dZ.dZ/dZ0dZ1dZ2dZ3dZ4dZ5h dZ6ddddZ7dd Z8dd!Z9dd"Z:dd#Z;dd$Z<dd%Z=dd&Z>dd'Z?dd(Z@dd)ZAdd*ZBdd+ZCdd,ZDdd-ZEdd.ZF	 	 	 	 	 	 	 	 dd/ZGdddd0	 	 	 	 	 	 	 	 	 dd1ZHdd2ZIdd3ZJdd4ZKdd5ZLdd6ZMdd7ZNdd8ZOdd9ZP	 	 	 	 	 	 	 	 dd:ZQdd;ZR G d< d=e      ZS G d> d?e      ZT G d@ dAe      ZU G dB dCe      ZV G dD dEeS      ZW G dF dGe      ZX G dH dIe      ZY G dJ dKe      ZZ G dL dMe      Z[ G dN dOe      Z\ G dP dQe      Z] G dR dSe      Z^ G dT dUe      Z_ G dV dWe      Z` G dX dYe      Za G dZ d[e      Zb G d\ d]e      Zc G d^ d_e      Zd G d` dae      Ze G db dce      Zf G dd dee      Zg G df dge      Zh G dh die      Zi G dj dke      Zj G dl dme      Zk G dn doe      Zl G dp dqe      Zm G dr dse      Zn G dt due      Zo G dv dwe      Zp G dx dye      Zq G dz d{e      Zr G d| d}e      Zs ed~       G d d             Zt ed~       G d d             Zu ed~       G d d             Zv ed~       G d d             Zw ed~       G d d             Zx ed~       G d d             Zy ed~       G d d             Zz ed~       G d d             Z{ ed~       G d d             Z| ed~       G d d             Z} ed~       G d d             Z~ G d d      Zdaded<   ddZy)    )annotations)contextmanager)	dataclass)datedatetimetime	timedeltatimezoneN)Path)Literal)ZoneInfoZoneInfoNotFoundError)	BaseModel
ConfigDictField)get_settings)MODULE_CATALOG)Tenant)TenantModule)User)Venuetenant_powerup_adminvenue_powerup_adminuser_powerup_adminzPowerUp Adminzpowerup-adminzzanchinfede@gmail.comzsuperadmin@powerup.local)	assistantordiniprenotazioni	documentsmenuhomemadehomemade_managefiscal_documents	timeclock	inventoryconsumptionsreports	tips_salatips_sala_managetips_bartips_bar_manage)
r   r   r   r   r    r"   r#   r$   r'   r)   )r   r   r    pz>   barboth
restaurantBar
RistorantezBar e ristorante)r,   r.   r-   c                 H    t        j                  t        j                        S N)r   nowr
   utc     -apps/backend-hub/app/services/tenant_store.py_utcnowr8   O   s    <<%%r6   c                 2    t               j                         S r2   )r8   	isoformatr5   r6   r7   _iso_nowr;   S   s    9  r6   c                 ~    	 t        t               j                        S # t        t        f$ r t        d      cY S w xY w)NzEurope/Rome)r   r   assistant_timezoner   
ValueErrorr5   r6   r7   _tenant_local_timezoner?   W   s9    '99::!:. '&&'s    <<c                 <    t        j                  t                     S r2   )r   r3   r?   r5   r6   r7   
_local_nowrA   ^   s    <<.011r6   c                 2    t               j                         S r2   )rA   r:   r5   r6   r7   _local_iso_nowrC   b   s    <!!##r6   c                   t        j                  d|       j                  dd      j                  d      }t	        j
                  dd|j                               j                  d      }|xs dt        j                  d       S )NNFKDasciiignorez[^a-zA-Z0-9]+-ztenant-   )
unicodedata	normalizeencodedecoderesublowerstripsecrets	token_hex)value
normalizedslugs      r7   _normalize_slugrW   f   sr    &&vu5<<WhOVVW^_J66"C)9)9);<BB3GD3WW..q1233r6   c                >    | j                         j                         S r2   rQ   rP   rT   s    r7   _normalize_lookupr[   l   s    ;;=  r6   c                f    | xs dj                         j                         }|dv ry|dv ry|dv ryy)Nr-   >   r.   
ristoranter.   >   r,   clubr,   >   allr-   entrambeentrambirY   rT   rU   s     r7   _normalize_homemade_usage_scoperc   p   sC    /6((*002J11_$<<r6   c                &    t         t        |          S r2   )HOMEMADE_USAGE_SCOPE_LABELSrc   rZ   s    r7   _homemade_usage_scope_labelrf   {   s    &'Fu'MNNr6   c                    t        j                  ddt        | xs d      j                               j	                         S )N\s+ )rN   rO   strrQ   rP   rZ   s    r7   _normalize_inventory_barcoderk      s0    66&"c%+2.4467==??r6   c                   t        |       }|s
t               S |g}|j                         rAt        |      dkD  r3|j	                  d      r|dd  nd| }|r||k7  r|j                  |       t        t        j                  |            S )N   0)rk   tupleisdigitlen
startswithappenddictfromkeys)rT   rU   aliases	alternates       r7   _build_carton_barcode_aliasesrx      s    -e4JwlGJ! 3&0&;&;C&@JqrN*FV	j0NN9%w'((r6   c                p    | xs dj                         j                         }|dv ry|dv ry|dv ry|dv ry	y
)Nri   >   ggrgrammigrammorz   >   g/lgr/lg litrogr litrogrammi litrog_per_literr   >   drop/ldrops/lgocce/lgocce litrogocce/litrogoccia litrogocce per litrodrops_per_literr   >   partpartepartipartsr   mlrY   rb   s     r7   #_normalize_homemade_ingredient_unitr      sV    +2$$&,,.J44ZZ 	 	 !88r6   c                    | D ch c]@  }t        |j                  d      xs d      dv rt        |j                  d      xs d      B }}|dhk(  rdS dS c c}w )Nmeasurement_unitr   >   rz   r   r   rz   rj   get)ingredientsitemproportional_unitss      r7   (_derive_homemade_recipe_measurement_unitr      sn      txx*+3t48KK 	DHH'(0D1 
 %-3747s   AAc                    d}d}| D ]`  }t        t        |j                  d      xs d            }t        |j                  d      xs d      }|dk  rN|dvr||z  }|dv s\||z  }b |dkD  r|S |S )N        r   r   part_amountr      r   r   >   r   r   r   rj   r   float)r   liquid_reference_totalfallback_proportional_total
ingredientr   r   s         r7   !_compute_homemade_reference_totalr      s     "%! 2
>s:>>RdCeCmim?noJNN=9@SA!#EE';6'~-"k1"2 &<a%?!`E``r6   c                    d}| D ]W  }t        t        |j                  d      xs d            }|dvr/t        |j                  d      xs d      }|dkD  sS||z  }Y |S )Nr   r   r   >   r   r   r   r   r   )r   totalr   r   r   s        r7   (_compute_homemade_liquid_reference_totalr      sq    E! !
>s:>>RdCeCmim?no>1JNN=9@SA?[ E! Lr6   c                    	 t        |xs d      }t        |xs d      }t        t	        | xs d      j                               xr |dkD  xr |dkD  S # t        t        f$ r Y yw xY w)Nr   Fri   )r   	TypeErrorr>   boolrj   rQ   )yield_ingredient_nameyield_input_amountyield_output_mlinput_amount	output_mls        r7   _has_homemade_explicit_yieldr      sv    /415/.Q/	 )/R06689`lQ>N`S\_`S`` z" s   A A&%A&r   r   r   c               v    t        |||      rt        |xs d      S t        |       }|dkD  r|S t        |       S )Nr   r   )r   r   r   r   )r   r   r   r   r   s        r7   (_compute_homemade_recipe_reference_totalr      sK     $3-'
 _)**EkR!%%,[99r6   c                h    | xs dj                         j                         }|dvrt        d      |S )Nri   >   r,   salazArea mance non valida.)rQ   rP   r>   rb   s     r7   _normalize_tips_arear      s7    +2$$&,,.J(122r6   c                    t        j                  d      }d}t        j                  d| j	                  d      ||      }d| d|j                          d|j                          S )N   i sha256utf-8zpbkdf2_sha256$$)osurandomhashlibpbkdf2_hmacrL   hex)passwordsalt
iterationsdigests       r7   _hash_passwordr      sX    ::b>DJ  8??7+CT:VFJ<qAfjjl^DDr6   c                `   	 |j                  dd      \  }}}}|dk7  ry	 t        |      }t        j	                  |      }t        j	                  |      }t        j                  d| j                  d      ||      }	t        j                  |	|      S # t        $ r Y yw xY w# t        $ r Y yw xY w)Nr   rI   Fpbkdf2_sha256r   r   )
splitr>   intbytesfromhexr   r   rL   rR   compare_digest)
r   stored_hash	algorithmraw_iterationssalt_hex
digest_hexr   r   expected_digest	candidates
             r7   _verify_passwordr      s    :E:K:KCQR:S7	>8Z O#(
}}X&--
3 ##Hhoog.FjYI!!)_==    s"   B 5B! 	BB!	B-,B-c                
   | s
t               S g }t               }| D ]\  }t        |xs d      j                         j	                         }|t
        vs||v r;|j                  |       |j                  |       ^ t        |      S Nri   )ro   setrj   rQ   rP   TENANT_PERMISSION_KEYSrs   addvaluesrU   seen	raw_valuerT   s        r7   _normalize_permission_valuesr     s    wJUD 	IO$**,224..%4-%  r6   c                   | dv rt         S || dk(  rt        S t               S d }	 t        j                  |      }t        |t              r|D cg c]  }t        |       }}t        |      }|S # t        j
                  $ r d }Y Lw xY wc c}w )N>   ownersuper_adminstaff)
r   DEFAULT_STAFF_PERMISSIONSro   jsonloadsJSONDecodeError
isinstancelistrj   r   )rolepermissions_jsonraw_permissionsparsedr   rU   s         r7   _resolve_role_permissionsr     s    ''%%,0GO(HH:>O,- &$1783t988-o>J   9s   A/ B
/BBc                Z    | dv ry t        j                  t        t        |                  S N>   r   r   )r   dumpsr   r   )r   permissionss     r7   _serialize_role_permissionsr   .  s(    ''::d7DEFFr6   c                
   | s
t               S g }t               }| D ]\  }t        |xs d      j                         j	                         }|t
        vs||v r;|j                  |       |j                  |       ^ t        |      S r   )ro   r   rj   rQ   rP   ASSISTANT_SCOPE_KEYSrs   r   r   s        r7   !_normalize_assistant_scope_valuesr   4  s    wJUD 	IO$**,224,,%  r6   c                    t        |       }g }t               }|D ]P  }d }|t        v r|}n|dk(  rd}n|dk(  rd}n|dk(  rd}|s*||vs/|j                  |       |j	                  |       R t        |      S )Nr!   r    r(   r'   r*   r)   )r   r   r   rs   r   ro   )r   normalized_permissionsscopesr   
permissionmapped_scopes         r7   "_assistant_scopes_from_permissionsr   C  s    9+FFUD, #
--%L,,%L--&L,,%LL4MM,'HH\"# =r6   c                   | dv rt         S d }|I	 t        j                  |      }t	        |t
              r|D cg c]  }t        |       }}t        |      S t        | |      }t        |      S # t        j                  $ r d }Y aw xY wc c}w r   )
r   r   r   r   r   r   rj   r   r   r   )r   r   assistant_scopes_json
raw_scopesr   r   r   s          r7   _resolve_user_assistant_scopesr   W  s    
 ''##59J(	ZZ 56F fd#067#d)7J70<<+D2BCK-k:: ## 	F	 8s   A/ B
/BBc                Z    | dv ry t        j                  t        t        |                  S r   )r   r   r   r   )r   r   s     r7    _serialize_user_assistant_scopesr   m  s(    ''::d<VDEFFr6   c                      e Zd ZU  ed      Zded<   dZded<   dZded	<   dZded
<    ed      Z	ded<    ed      Z
ded<    ed      Zded<    ed      Zded<   y)RegisterTenantPayload   
min_lengthrj   locale_nameri   addressN
str | Nonephone_numberwhatsapp_number
admin_namerI   username   email   r   __name__
__module____qualname__r   r  __annotations__r  r	  r
  r  r  r  r   r5   r6   r7   r  r  s  sk    *K*GS#L*#"&OZ&q)J)Q'Hc'!$E3$Q'Hc'r6   r  c                  F    e Zd ZU  ed      Zded<    ed      Zded<   y)LoginPayloadrI   r  rj   
identifierrm   r   N)r  r  r  r   r  r  r   r5   r6   r7   r  r  ~  s     q)J)Q'Hc'r6   r  c                  *    e Zd ZU  ed      Zded<   y)BootstrapSuperAdminPayload   r  rj   r   N)r  r  r  r   r   r  r5   r6   r7   r  r    s    Q'Hc'r6   r  c                  H    e Zd ZU  edd      Zded<    ed      Zded	<   y)
UpdatePasswordPayloadNrm   defaultr  r  current_passwordr  r  rj   new_password)r  r  r  r   r  r  r   r5   r6   r7   r  r    s#    #(!#DjD+L#+r6   r  c                      e Zd Zy)AdminCreateTenantPayloadN)r  r  r  r5   r6   r7   r"  r"    s    r6   r"  c                      e Zd ZU  ed      Zded<   dZded<   dZded	<   dZded
<    ed      Z	ded<    ed      Z
ded<    ed      Zded<    edd      Zded<   y)AdminUpdateTenantAdminPayloadr  r  rj   r  ri   r  Nr  r	  r
  r  rI   r  r  r  r  r  r   r  r5   r6   r7   r$  r$    sm    *K*GS#L*#"&OZ&q)J)Q'Hc'!$E3$ !<Hj<r6   r$  c                      e Zd ZU  ed      Zded<    ed      Zded<    ed      Zded<   d	Zd
ed<    ed      Z	ded<    ee
      Zded<    ee
      Zded<   y	)TenantStaffUserCreatePayloadr  r  rj   namerI   r  r  r  Nr  r	  r  r   default_factory	list[str]r   assistant_scopesr  r  r  r   r'  r  r  r  r	  r   r   r   r+  r5   r6   r7   r&  r&    sg    #D##Q'Hc'!$E3$#L*#Q'Hc'"48K8"'"=i=r6   r&  c                      e Zd ZU  ed      Zded<    ed      Zded<    ed      Zded<   d	Zd
ed<    ed	d      Z	d
ed<    ee
      Zded<    ee
      Zded<   y	)TenantStaffUserUpdatePayloadr  r  rj   r'  rI   r  r  r  Nr  r	  r  r  r   r(  r*  r   r+  r,  r5   r6   r7   r.  r.    si    #D##Q'Hc'!$E3$#L*# !<Hj<"48K8"'"=i=r6   r.  c                  X    e Zd ZU dZded<   dZded<   dZded<    eddd	      Zd
ed<   y)TimeclockOverviewQueryNr  user_id
start_dateend_date   rm     )r  geler   limit)	r  r  r  r1  r  r2  r3  r   r8  r5   r6   r7   r0  r0    s3    GZ!J
!HjsqT2E32r6   r0  c                  ,    e Zd ZU  edd      Zded<   y)InventoryWarehouseCreatePayloadr  x   r  
max_lengthrj   r'  N)r  r  r  r   r'  r  r5   r6   r7   r:  r:    s    s3D#3r6   r:  c                      e Zd ZU  ed      Zded<    ed      Zded<   dZd	ed
<    edd      Zded<    edd      Z	ded<   dZ
ded<   y)InventoryStockWritePayloadrm   r6  r   
product_idr   r   quantityr   Literal['add', 'set']	operationN(   r  r=  r  lot_coder  gtfloat | Noneunits_per_packFr   save_inventory_session)r  r  r  r   rA  r  rB  rD  rG  rK  rL  r5   r6   r7   r?  r?    sV    qkJ!qkHe!',I$, "=Hj=#(!#<NL<#(D(r6   r?  c                  J    e Zd ZU  edd      Zded<    edd      Zd	ed
<   y) InventoryProductCodeWritePayloadrI   r;  r<  rj   product_codeNrE  rF  r  rG  )r  r  r  r   rO  r  rG  r5   r6   r7   rN  rN    s$    s;L#; "=Hj=r6   rN  c                  v   e Zd ZU  edd      Zded<    edd      Zded<    edd      Zded	<    ed
d      Zded<    ed
d      Z	ded<    ed
d      Z
ded<    ed
d      Zded<    ed
d      Zded<    ed
d      Zded<    ed
d      Zded<    ed
d      Zded<    ed
d      Zded<   y
)InventoryProductCreatePayloadr     r<  rj   product_namerm   r;  rG  supplier_nameNrF  r  rO  r   r  r6  rJ  final_price_vatvat_rate	weight_kgunit_price_per_kgcategory  notesrH  rK  liters_per_unit)r  r  r  r   rS  r  rG  rT  rO  rV  rW  rX  rY  rZ  r\  rK  r]  r5   r6   r7   rQ  rQ    s    s;L#;Q37Hc7!<M3<$TcBL*B$)$1$=O\="4A6Hl6#DQ7I|7&+DQ&?|? #>Hj>dt<E:<#(!#<NL<$)$1$=O\=r6   rQ  c                      e Zd ZU dZded<   y)InventorySessionCreatePayloadNr  inventory_date)r  r  r  r`  r  r5   r6   r7   r_  r_    s    !%NJ%r6   r_  c                      e Zd ZU dZded<   y)InventorySnapshotCreatePayloadNr  snapshot_date)r  r  r  rc  r  r5   r6   r7   rb  rb    s     $M:$r6   rb  c                      e Zd ZU  edd      Zded<    ed      Zded	<    ed
      Zded<    edd      Zded<    edd
      Z	ded<    edd      Z
ded<   y) InventoryConsumptionWritePayloadrI   r;  r<  rj   warehouse_idrm   r@  r   rA  r   rI  r   rB  NrE  rF  r  rG  rH  rJ  rK  occurred_at)r  r  r  r   rf  r  rA  rB  rG  rK  rh  r5   r6   r7   re  re    sb    s;L#;qkJ!qkHe! "=Hj=#(!#<NL<#DR@K@r6   re  c                      e Zd ZU  edd      Zded<    edd      Zded<    edd      Zded<    ed	      Zd
ed<    edd      Z	ded<    edd      Z
ded<    edd      Zded<    edd      Zded<   y)InventoryTransferWritePayloadrI   r;  r<  rj   source_warehouse_iddestination_warehouse_idstock_item_idr   rg  r   rB  NrE  rF  r  rG  source_lot_coderH  rJ  rK  rh  )r  r  r  r   rk  r  rl  rm  rB  rG  rn  rK  rh  r5   r6   r7   rj  rj    s    $cBB$)Q3$GcG!<M3<qkHe! "=Hj="'"DOZD#(!#<NL<#DR@K@r6   rj  c                  t    e Zd ZU  edd      Zded<    ed      Zded	<   d
Zded<    edd      Zded<   y)HomemadeRecipeIngredientPayloadN   rF  r  ingredient_namer   rg  r   r   r   <Literal['ml', 'g', 'g_per_liter', 'drops_per_liter', 'part']r   r;  linked_recipe_id)	r  r  r  r   rr  r  r   r   rt  r5   r6   r7   rp  rp    s?    "'"EOZE!K$UYRY#(##FjFr6   rp  c                      e Zd ZU  edd      Zded<   dZded<   d	Zd
ed<    edd      Zded<    edd      Z	ded<    edd      Z
ded<   dZded<    edd      Zded<    eedd      Zded<   y)HomemadeRecipeWritePayloadr  r;  r<  rj   r'  r   Literal['ml', 'g']r   r-   $Literal['bar', 'restaurant', 'both']usage_scopeN  rF  r  r\  rq  r   r   rH  rJ  r   zLiteral['g', 'ml'] | Noneyield_input_unitr   rm   <   )r)  r  r=  %list[HomemadeRecipeIngredientPayload]r   )r  r  r  r   r'  r  r   ry  r\  r   r   r{  r   r   r   r5   r6   r7   rv  rv    s    s3D#3+/(/8>K5>dt<E:<(-ds(K:K',Ta'@@26/6$)$1$=O\=9>t`anp9qK6qr6   rv  c                  ,    e Zd ZU  edd      Zded<   y)HomemadePreparationWritePayload
   r<  rj   prepared_onN)r  r  r  r   r  r  r5   r6   r7   r  r    s    r:K:r6   r  c                  V    e Zd ZU  edd      Zded<    ed      Zded	<   d
Zded<   y)HomemadeStockWritePayloadrI   r;  r<  rj   	recipe_idr   r@  r   rB  r   rC  rD  N)r  r  r  r   r  r  rB  rD  r5   r6   r7   r  r  
  s,    aC8Is8qkHe!',I$,r6   r  c                  h    e Zd ZU  edd      Zded<    edd      Zded<    eed      Zd	ed
<   y)&HomemadeOperationalCalendarRulePayloadNr  rF  r  r2  r3     r)  r=  z	list[int]weekdays)	r  r  r  r   r2  r  r3  r   r  r5   r6   r7   r  r    s5    "4B?J
? "=Hj=CHiCr6   r  c                  ,    e Zd ZU  eed      Zded<   y)"HomemadeOperationalCalendarPayload   r  z,list[HomemadeOperationalCalendarRulePayload]rulesN)r  r  r  r   r   r  r  r5   r6   r7   r  r    s    :?PTac:dE7dr6   r  c                  H    e Zd ZU  edd      Zded<   dZded<   dZded	<   y)
HomemadeStockSettingsPayloadr   im  )r6  r7  r   minimum_stock_daysNz)HomemadeOperationalCalendarPayload | Nonebar_calendarrestaurant_calendar)r  r  r  r   r  r  r  r  r5   r6   r7   r  r    s+     %s 33>BL;BEIBIr6   r  c                  H    e Zd ZU  edd      Zded<    ed      Zded	<   y
)TipsRosterEntryPayloadr  r;  r<  rj   r'  r   r@  r   scoreN)r  r  r  r   r'  r  r  r5   r6   r7   r  r     s!    s3D#3A;E5r6   r  c                  ,    e Zd ZU  eed      Zded<   y)TipsRosterWritePayloadr4  r  zlist[TipsRosterEntryPayload]entriesN)r  r  r  r   r   r  r  r5   r6   r7   r  r  %  s    ,1$SV,WG)Wr6   r  c                      e Zd ZU  eddd      Zded<    ed      Zded	<    edd
      Zded<    edd
      Zded<    ee	d      Z
ded<    ee	d      Zded<   dZded<   y)TipsRunPreviewPayloadNr  )r  r  r=  r  tip_dater   r@  r   total_amountrU  
pos_amountrJ  pos_effective_amountr4  r  r*  absent_namesr;  history_run_idsFr   mark_as_delivered)r  r  r  r   r  r  r  r  r  r   r  r  r  r5   r6   r7   r  r  )  ss     "LHjL1+L%%aA.J.).t)B,B#DSIL)I!&t!LOYL#t#r6   r  c                      e Zd ZU dZded<   y)TipsRunPayoutStatusPayloadTr   	deliveredN)r  r  r  r  r  r5   r6   r7   r  r  3  s    Itr6   r  c                      e Zd ZU  edd      Zded<    edd      Zded<   d	Zd
ed<    edd      Zded<   dZ	ded<   y)ReportCreatePayloadrI   rq  r<  rj   titler  rz  descriptionproblemzTLiteral['problem', 'damage', 'breakage', 'malfunction', 'purchase_request', 'other']rZ  NrF  r  locationnormalz*Literal['low', 'normal', 'high', 'urgent']priority)
r  r  r  r   r  r  r  rZ  r  r  r5   r6   r7   r  r  7  sK    !4E34d;K;enHbn #>Hj>;CH8Cr6   r  c                  6    e Zd ZU ded<    edd      Zded<   y)ReportStatusUpdatePayloadzJLiteral['new', 'reviewed', 'in_progress', 'reported_to_owner', 'resolved']statusNrz  rF  r  
admin_note)r  r  r  r  r   r  r5   r6   r7   r  r  ?  s    VV"4DAJ
Ar6   r  c                  J    e Zd ZU  edd      Zded<    edd      Zded<   y	)
PushSubscriptionKeysPayload      r<  rj   p256dhr     authN)r  r  r  r   r  r  r  r5   r6   r7   r  r  D  s$    2#6FC6s3D#3r6   r  c                  x    e Zd ZU  edd      Zded<    ed      Zded	<    ed
d      Zded<    ed      Z	y
)PushSubscriptionPayloadr     r<  rj   endpointkeys)aliasr  subscription_keysNr  rF  r  
user_agentT)populate_by_name)
r  r  r  r   r  r  r  r  r   model_configr5   r6   r7   r  r  I  s?    RD9Hc95:5H2H"4C@J
@t4Lr6   r  c                  ,    e Zd ZU  edd      Zded<   y)PushSubscriptionDeletePayloadr  r  r<  rj   r  N)r  r  r  r   r  r  r5   r6   r7   r  r  Q  s    RD9Hc9r6   r  T)frozenc                      e Zd ZU ded<   ded<   ded<   ded<   ded<   ded<   ded<   ded	<   ded
<   ded<   ded<   ded<   ded<   ded<   ded<   ded<   y)SessionIdentityrj   token	tenant_idtenant_slugtenant_namehome_tenant_idhome_tenant_slughome_tenant_namer1  
user_emailr  r  	user_namer   tuple[str, ...]r   r+  database_path
expires_atNr  r  r  r  r5   r6   r7   r  r  U  s\    JNLO
I  %%Or6   r  c                      e Zd ZU ded<   ded<   ded<   ded<   ded<   ded<   ded	<   ded
<   ded<   ded<   ded<   ded<   ded<   ded<   y)MenuAssetRecordrj   idr  original_namedisplay_name	mime_typekindr   file_size_bytesstorage_pathextracted_textanalysis_textr  r  error_detail
created_at
updated_atNr  r5   r6   r7   r  r  i  sO    GNN
IKOOr6   r  c                     e Zd ZU ded<   ded<   ded<   ded<   ded<   ded<   ded	<   d
ed<   ded<   ded<   d
ed<   d
ed<   d
ed<   ded<   ded<   ded<   ded<   ded<   d
ed<   d
ed<   d
ed<   ded<   ded<   ded<   d
ed<   ded<   ded<   y )!FiscalDocumentRecordrj   r  r  r  r  r  r  r   r  r  	file_hashr  document_typedocument_numberdocument_daterT  rJ  r  currencysummary_textr  preview_textdrive_file_iddrive_web_urldrive_uploaded_atr  matching_statusreview_statusr  r  r  Nr  r5   r6   r7   r  r  {  s    GNN
IM!!KOOr6   r  c                      e Zd ZU ded<   ded<   ded<   ded<   ded<   ded	<   ded
<   ded<   ded<   ded<   ded<   ded<   ded<   ded<   ded<   ded<   ded<   ded<   ded<   ded<   y)FiscalDocumentLineItemRecordrj   r  r  document_idr   
line_indexr  rO  iso_coder  category_code	unit_coderJ  
pack_countrB  gross_quantitytare_quantitynet_quantity
unit_price
line_totalvat_coderaw_row_textr  r  Nr  r5   r6   r7   r  r    su    GNO  OOr6   r  c                  ,    e Zd ZU ded<   ded<   ded<   y)FiscalDocumentSettingsRecordrj   r  r  inbound_emailr  Nr  r5   r6   r7   r   r     s    Nr6   r   c                      e Zd ZU ded<   ded<   ded<   ded<   ded<   ded<   ded	<   ded
<   ded<   ded<   ded<   ded<   ded<   ded<   ded<   y)FiscalDocumentInboxItemRecordrj   r  r  
message_idattachment_idr  r  subjectsenderreceived_atattachment_namer  sync_statusr  r  r  r  Nr  r5   r6   r7   r  r    sU    GNOLNOOr6   r  c                      e Zd ZU ded<   ded<   ded<   ded<   ded<   ded<   ded	<   d
ed<   ded<   ded<   ded<   ded<   ded<   y)TimeclockEntryRecordrj   r  r1  r  r  r  r  
started_atended_at
int | Noneduration_secondsstarted_sourceended_sourcer\  r  r  Nr  r5   r6   r7   r  r    sK    GLO  OOr6   r  c                      e Zd ZU ded<   ded<   ded<   ded<   ded<   ded<   ded	<   ded
<   ded<   ded<   ded<   ded<   ded<   ded<   ded<   ded<   ded<   y)TenantReportRecordrj   r  reporter_user_idr  reporter_namereporter_usernamereporter_emailr  r  rZ  r  r  r  r  status_updated_by_user_idstatus_updated_by_nameresolved_atr  r  Nr  r5   r6   r7   r  r    sa    G!!JMMK))&&OOr6   r  c                  ^    e Zd ZU ded<   ded<   ded<   ded<   ded<   ded<   ded	<   ded
<   y)AssistantThreadRecordrj   r  r  r1  surfacer  r  
state_jsonr  r  Nr  r5   r6   r7   r  r    s+    GNLLKOOr6   r  c                  J    e Zd ZU ded<   ded<   ded<   ded<   ded<   ded<   y	)
AssistantStoredMessagerj   r  	thread_idr   contentr  r   
sort_indexNr  r5   r6   r7   r!  r!    s     GN
ILOOr6   r!  c                  |    e Zd ZU ded<   ded<   ded<   ded<   ded<   ded<   ded<   ded	<   ded
<   ded<   ded<   y)AssistantRunRecordrj   r  r"  r  r1  r  routemodeluser_messageassistant_reply
trace_jsonr  Nr  r5   r6   r7   r&  r&    s;    GNNLLJJOOr6   r&  c                  Z   e Zd Zd<dZd<dZd=dZd>dZd>dZd=dZe	d        Z
e	d?d       Z	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d@d	Zd=d
Z	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dAdZdBdZdCdZdDdZdEdZdFdZdGdZdHdZdIdZdJdZdKdZdLdZdMdZdNdZdOdZdPdZdQdZdRdZdSdZ 	 	 	 	 dTdZ!	 	 	 	 dUdZ"dVd Z#dVd!Z$dWd"Z%dWd#Z&dXd$Z'dYd%Z(d&d'dZd(Z)d&d&d&d)d*d+	 	 	 	 	 	 	 	 	 	 	 	 	 d[d,Z*d&d-	 	 	 	 	 d\d.Z+d]d/Z,d^d0Z-d_d1Z.d`d2Z/dad3Z0dVd4Z1dVd5Z2dVd6Z3dVd7Z4dVd8Z5dVd9Z6dVd:Z7dVd;Z8dVd<Z9dbd=Z:dbd>Z;dcd?Z<ddd@Z=d^dAZ>d^dBZ?d^dCZ@d^dDZAd^dEZB	 de	 	 	 	 	 dfdFZC	 	 	 	 	 	 dgdGZDd^dHZEd^dIZFdhdJZG	 	 	 	 	 	 didKZH	 	 	 	 	 	 	 	 djdLZI	 	 	 	 	 	 	 	 	 	 dkdMZJ	 	 	 	 	 	 	 	 dldNZK	 	 	 	 	 	 	 	 dmdOZL	 	 	 	 	 	 	 	 dndPZMdodQZNdodRZOdodSZPdpdTZQd^dUZRdqdVZSd^dWZT	 	 	 	 	 	 	 	 drdXZUd&dY	 	 	 	 	 dsdZZVdtd[ZWdtd\ZXdud]ZYdvd^ZZdvd_Z[	 	 	 	 	 	 dwd`Z\	 	 	 	 	 	 dwdaZ]	 	 	 	 	 	 dxdbZ^	 	 	 	 	 	 dydcZ_	 	 	 	 	 	 dzddZ`	 	 	 	 	 	 d{deZa	 	 	 	 	 	 d|dfZb	 	 	 	 	 	 d}dgZcd&d&dh	 	 	 	 	 	 	 	 	 d~diZd	 	 	 	 	 	 	 	 	 	 ddjZe	 	 	 	 	 	 	 	 	 	 ddkZf	 	 	 	 	 	 	 	 	 	 ddlZg	 	 	 	 	 	 ddmZh	 	 	 	 	 	 	 	 ddnZi	 	 	 	 	 	 	 	 ddoZj	 	 	 	 	 	 	 	 ddpZkd&dY	 	 	 	 	 dsdqZld=drZm	 	 	 	 	 	 	 	 ddsZn	 	 	 	 	 	 ddtZodduZp	 	 	 	 	 	 ddvZq	 	 	 	 	 	 	 	 ddwZr	 	 	 	 	 	 ddxZsddyZt	 	 	 	 	 	 ddzZu	 	 	 	 	 	 	 	 dd{Zv	 	 	 	 	 	 dd|Zw	 	 	 	 	 	 dd}Zx	 	 	 	 	 	 	 	 dd~Zy	 	 	 	 	 	 	 	 ddZz	 	 	 	 	 	 ddZ{	 	 	 	 	 	 	 	 ddZ|	 	 	 	 	 	 	 	 	 	 	 	 ddZ}	 	 	 	 	 	 	 	 	 	 	 	 ddZ~	 	 	 	 	 	 	 	 ddZ	 	 	 	 ddZddZ	 	 	 	 	 	 ddZddZ	 	 	 	 	 	 	 	 	 	 	 	 ddZddZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZddZddZddZ	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 ddZ	 de	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZd&dd	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 ddZd&d&d	 	 	 	 	 	 	 	 	 ddZdd	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZddZ	 	 	 	 	 	 	 	 ddZddZ	 	 	 	 	 	 ddZd&d&d	 	 	 	 	 	 	 ddZddZ	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZd&dd	 	 	 	 	 	 	 ddZd&d&dd	 	 	 	 	 	 	 	 	 ddZddZd&dd	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZdd	 	 	 	 	 	 	 	 	 ddZddd&dd	 	 	 	 	 	 	 	 	 	 	 ddZd&d*d	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZddZd&d	 	 	 	 	 	 	 ddZd&d	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddÄZ	 	 	 	 	 	 ddĄZddńZddƄZ	 	 	 	 	 	 ddǄZ	 	 	 	 	 	 	 	 ddȄZ	 	 	 	 	 	 ddɄZd&d&d&dʜ	 	 	 	 	 	 	 	 	 	 	 	 	 dd˄Zdd̄Zdd̈́Zdd΄Z	 	 	 	 	 	 ddτZddЄZddфZdd҄ZddӄZddԄZddՄZddքZddׄZd&d&d&d&d؜	 	 	 	 	 	 	 	 	 	 	 ddلZÐddڄZĐddۄZŐdd܄ZƐdd݄Zǐd^dބZȐdd߄ZɐddZʐddZːddZ̐ddZ͐ddZΐddZ	 	 	 	 	 	 	 	 ddZАddZѐddZҐd^dZӐd^dZԐd^dZ	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZd&d	 	 	 	 	 	 	 	 	 ddZڐddZ	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 ddZd&d*d	 	 	 	 	 	 	 ddZddZddZ	 	 	 	 	 	 	 	 ddZddZddZddZd)d 	 	 	 	 	 	 	 	 	 dܐdZddZddZddZdݐdZdސdZdߐdZddZdd	Zdd
Zddd&d	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZddZddZd&d&d&d&d&d	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZddZddZd&d&d&d&d&dddddddd&d	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZddZddZddZddZ	 	 	 	 	 	 	 	 ddZddZdd Zd&d&d&d&d&d&d&d&d&d&d&d&d&d&d&d&d&d&d!	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd"Z dd#Zdd$Z	 	 	 	 	 	 	 	 dd%Zd&ddd'Zdd(Z	 	 	 	 	 	 	 	 dd)Zd&d&d&d*	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd+Zd&d&d&d&d&d&d&d&d&d,		 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd-Zdd.Z	ddd0Z
d1ddd2Zddd3Zd/d&d4	 	 	 	 	 	 	 dd5Z	 	 	 	 	 	 dd6Zd/d&d4	 	 	 	 	 	 	 dd7Zd d8Zdd9Z	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd:Zdddd;Zy&(  TenantStorec                p   t               }t        |j                        | _        t        |j                        | _        t        |j                        | _        t        |j                        | _	        t        |j                        | _        t        j                         | _        | j!                          y )N)hours)r   r   tenancy_registry_database_registry_pathtenancy_databases_dir_tenant_dirmenu_asset_storage_dir_menu_assets_dirfiscal_document_storage_dir_fiscal_documents_dirr	   tenancy_session_duration_hours_session_duration	threadingRLock_lock_ensure_storage)selfsettingss     r7   __init__zTenantStore.__init__  s    >"8#E#EF > >? $X%D%D E%)(*N*N%O"!*1X1X!Y __&
r6   c                :   | j                   j                  j                  dd       | j                  j                  dd       | j                  j                  dd       | j
                  j                  dd       | j                         5 }|j                  d       | j                  |ddd       | j                  |ddd       | j                  |dd	d       | j                  |dd
d       | j                  |ddd       | j                  |ddd       | j                  |ddd       | j                  |dd	d       | j                  |ddd       |j                  d       |j                  d       | j                  |       d d d        y # 1 sw Y   y xY w)NTparentsexist_oka4*  
                CREATE TABLE IF NOT EXISTS tenants (
                    id TEXT PRIMARY KEY,
                    name TEXT NOT NULL,
                    slug TEXT NOT NULL UNIQUE,
                    database_path TEXT NOT NULL,
                    created_at TEXT NOT NULL
                );

                CREATE TABLE IF NOT EXISTS venues (
                    id TEXT PRIMARY KEY,
                    tenant_id TEXT NOT NULL,
                    name TEXT NOT NULL,
                    address TEXT NOT NULL DEFAULT '',
                    phone_number TEXT,
                    whatsapp_number TEXT,
                    created_at TEXT NOT NULL,
                    FOREIGN KEY(tenant_id) REFERENCES tenants(id)
                );

                CREATE TABLE IF NOT EXISTS users (
                    id TEXT PRIMARY KEY,
                    tenant_id TEXT NOT NULL,
                    name TEXT NOT NULL,
                    username TEXT NOT NULL UNIQUE,
                    email TEXT NOT NULL UNIQUE,
                    phone_number TEXT,
                    password_hash TEXT NOT NULL,
                    role TEXT NOT NULL,
                    permissions_json TEXT,
                    assistant_scopes_json TEXT,
                    created_at TEXT NOT NULL,
                    FOREIGN KEY(tenant_id) REFERENCES tenants(id)
                );

                CREATE TABLE IF NOT EXISTS tenant_modules (
                    id TEXT PRIMARY KEY,
                    tenant_id TEXT NOT NULL,
                    module_key TEXT NOT NULL,
                    enabled INTEGER NOT NULL,
                    plan_name TEXT NOT NULL,
                    activated_at TEXT,
                    expires_at TEXT,
                    FOREIGN KEY(tenant_id) REFERENCES tenants(id)
                );

                CREATE UNIQUE INDEX IF NOT EXISTS idx_tenant_modules_unique
                    ON tenant_modules(tenant_id, module_key);

                CREATE TABLE IF NOT EXISTS sessions (
                    token TEXT PRIMARY KEY,
                    tenant_id TEXT NOT NULL,
                    user_id TEXT NOT NULL,
                    created_at TEXT NOT NULL,
                    expires_at TEXT NOT NULL,
                    FOREIGN KEY(tenant_id) REFERENCES tenants(id),
                    FOREIGN KEY(user_id) REFERENCES users(id)
                );

                CREATE TABLE IF NOT EXISTS tenant_admin_push_recipients (
                    tenant_id TEXT NOT NULL,
                    user_id TEXT NOT NULL,
                    enabled INTEGER NOT NULL DEFAULT 1,
                    created_at TEXT NOT NULL,
                    updated_at TEXT NOT NULL,
                    PRIMARY KEY (tenant_id, user_id),
                    FOREIGN KEY(tenant_id) REFERENCES tenants(id),
                    FOREIGN KEY(user_id) REFERENCES users(id)
                );

                CREATE TABLE IF NOT EXISTS tenant_llm_settings (
                    tenant_id TEXT NOT NULL,
                    scope TEXT NOT NULL,
                    config_json TEXT NOT NULL,
                    updated_at TEXT NOT NULL,
                    PRIMARY KEY (tenant_id, scope),
                    FOREIGN KEY(tenant_id) REFERENCES tenants(id)
                );

                CREATE TABLE IF NOT EXISTS tenant_menu_assets (
                    id TEXT PRIMARY KEY,
                    tenant_id TEXT NOT NULL,
                    original_name TEXT NOT NULL,
                    display_name TEXT NOT NULL,
                    mime_type TEXT NOT NULL,
                    kind TEXT NOT NULL,
                    file_size_bytes INTEGER NOT NULL,
                    storage_path TEXT NOT NULL,
                    extracted_text TEXT NOT NULL DEFAULT '',
                    analysis_text TEXT NOT NULL DEFAULT '',
                    status TEXT NOT NULL,
                    error_detail TEXT,
                    created_at TEXT NOT NULL,
                    updated_at TEXT NOT NULL,
                    FOREIGN KEY(tenant_id) REFERENCES tenants(id)
                );

                CREATE INDEX IF NOT EXISTS idx_tenant_menu_assets_tenant
                    ON tenant_menu_assets(tenant_id, created_at DESC);

                CREATE TABLE IF NOT EXISTS tenant_fiscal_documents (
                    id TEXT PRIMARY KEY,
                    tenant_id TEXT NOT NULL,
                    original_name TEXT NOT NULL,
                    display_name TEXT NOT NULL,
                    mime_type TEXT NOT NULL,
                    kind TEXT NOT NULL,
                    file_size_bytes INTEGER NOT NULL,
                    file_hash TEXT,
                    storage_path TEXT NOT NULL,
                    document_type TEXT NOT NULL,
                    document_number TEXT,
                    document_date TEXT,
                    supplier_name TEXT,
                    total_amount REAL,
                    currency TEXT NOT NULL DEFAULT 'EUR',
                    summary_text TEXT NOT NULL DEFAULT '',
                    extracted_text TEXT NOT NULL DEFAULT '',
                    preview_text TEXT NOT NULL DEFAULT '',
                    drive_file_id TEXT,
                    drive_web_url TEXT,
                    drive_uploaded_at TEXT,
                    status TEXT NOT NULL,
                    matching_status TEXT NOT NULL DEFAULT 'pending',
                    review_status TEXT NOT NULL DEFAULT 'to_review',
                    error_detail TEXT,
                    created_at TEXT NOT NULL,
                    updated_at TEXT NOT NULL,
                    FOREIGN KEY(tenant_id) REFERENCES tenants(id)
                );

                CREATE INDEX IF NOT EXISTS idx_tenant_fiscal_documents_tenant
                    ON tenant_fiscal_documents(tenant_id, created_at DESC);

                CREATE TABLE IF NOT EXISTS tenant_fiscal_document_items (
                    id TEXT PRIMARY KEY,
                    tenant_id TEXT NOT NULL,
                    document_id TEXT NOT NULL,
                    line_index INTEGER NOT NULL,
                    product_code TEXT,
                    iso_code TEXT,
                    description TEXT NOT NULL,
                    category_code TEXT,
                    unit_code TEXT,
                    pack_count REAL,
                    quantity REAL,
                    gross_quantity REAL,
                    tare_quantity REAL,
                    net_quantity REAL,
                    unit_price REAL,
                    line_total REAL,
                    vat_code TEXT,
                    raw_row_text TEXT NOT NULL DEFAULT '',
                    created_at TEXT NOT NULL,
                    updated_at TEXT NOT NULL,
                    FOREIGN KEY(tenant_id) REFERENCES tenants(id),
                    FOREIGN KEY(document_id) REFERENCES tenant_fiscal_documents(id)
                );

                CREATE INDEX IF NOT EXISTS idx_tenant_fiscal_document_items_document
                    ON tenant_fiscal_document_items(tenant_id, document_id, line_index ASC);

                CREATE TABLE IF NOT EXISTS tenant_fiscal_document_settings (
                    tenant_id TEXT PRIMARY KEY,
                    inbound_email TEXT,
                    updated_at TEXT NOT NULL,
                    FOREIGN KEY(tenant_id) REFERENCES tenants(id)
                );

                CREATE TABLE IF NOT EXISTS tenant_fiscal_document_inbox_items (
                    id TEXT PRIMARY KEY,
                    tenant_id TEXT NOT NULL,
                    message_id TEXT NOT NULL,
                    attachment_id TEXT NOT NULL,
                    file_hash TEXT,
                    subject TEXT NOT NULL DEFAULT '',
                    sender TEXT,
                    received_at TEXT,
                    attachment_name TEXT NOT NULL,
                    mime_type TEXT NOT NULL,
                    sync_status TEXT NOT NULL,
                    document_id TEXT,
                    error_detail TEXT,
                    created_at TEXT NOT NULL,
                    updated_at TEXT NOT NULL,
                    FOREIGN KEY(tenant_id) REFERENCES tenants(id),
                    FOREIGN KEY(document_id) REFERENCES tenant_fiscal_documents(id),
                    UNIQUE(tenant_id, message_id, attachment_id)
                );

                CREATE INDEX IF NOT EXISTS idx_tenant_fiscal_document_inbox_items_tenant
                    ON tenant_fiscal_document_inbox_items(tenant_id, created_at DESC);

                CREATE TABLE IF NOT EXISTS assistant_threads (
                    id TEXT PRIMARY KEY,
                    tenant_id TEXT NOT NULL,
                    user_id TEXT NOT NULL,
                    surface TEXT NOT NULL,
                    status TEXT NOT NULL DEFAULT 'active',
                    state_json TEXT,
                    created_at TEXT NOT NULL,
                    updated_at TEXT NOT NULL,
                    FOREIGN KEY(tenant_id) REFERENCES tenants(id),
                    FOREIGN KEY(user_id) REFERENCES users(id)
                );

                CREATE INDEX IF NOT EXISTS idx_assistant_threads_lookup
                    ON assistant_threads(tenant_id, user_id, surface, status, updated_at DESC);

                CREATE TABLE IF NOT EXISTS assistant_messages (
                    id TEXT PRIMARY KEY,
                    thread_id TEXT NOT NULL,
                    role TEXT NOT NULL,
                    content TEXT NOT NULL,
                    created_at TEXT NOT NULL,
                    sort_index INTEGER NOT NULL,
                    FOREIGN KEY(thread_id) REFERENCES assistant_threads(id) ON DELETE CASCADE
                );

                CREATE INDEX IF NOT EXISTS idx_assistant_messages_thread
                    ON assistant_messages(thread_id, sort_index ASC);

                CREATE TABLE IF NOT EXISTS assistant_runs (
                    id TEXT PRIMARY KEY,
                    thread_id TEXT NOT NULL,
                    tenant_id TEXT NOT NULL,
                    user_id TEXT NOT NULL,
                    surface TEXT NOT NULL,
                    route TEXT NOT NULL,
                    model TEXT NOT NULL,
                    user_message TEXT NOT NULL,
                    assistant_reply TEXT NOT NULL,
                    trace_json TEXT NOT NULL,
                    created_at TEXT NOT NULL,
                    FOREIGN KEY(thread_id) REFERENCES assistant_threads(id) ON DELETE CASCADE,
                    FOREIGN KEY(tenant_id) REFERENCES tenants(id),
                    FOREIGN KEY(user_id) REFERENCES users(id)
                );

                CREATE INDEX IF NOT EXISTS idx_assistant_runs_thread
                    ON assistant_runs(thread_id, created_at DESC);
                usersr   TEXTr   tenant_fiscal_documentsr  r  zTEXT NOT NULL DEFAULT ''r  r  r  "tenant_fiscal_document_inbox_itemsassistant_threadsr  z
                CREATE INDEX IF NOT EXISTS idx_tenant_fiscal_documents_hash
                    ON tenant_fiscal_documents(tenant_id, file_hash)
                z
                CREATE INDEX IF NOT EXISTS idx_tenant_fiscal_document_inbox_items_hash
                    ON tenant_fiscal_document_inbox_items(tenant_id, file_hash)
                )r1  parentmkdirr3  r5  r7  _connect_registryexecutescript_ensure_registry_columnexecute_ensure_super_admin_accountr>  
connections     r7   r=  zTenantStore._ensure_storage%  s   ""(((Etd;##D4#@""(((E##% J	9$$qsh ((W>PRXY((W>UW]^((5NP[]cd((5NP^`z{((5NP_agh((5NP_agh((5NPcekl((5Y[fhno((5H,X^_  ,,Z8UJ	9 J	9 J	9s   C9FFc                `   |j                  d       | j                  |ddd       | j                  |ddd       | j                  |ddd       | j                  |ddd       | j                  |dd	d
       | j                  |ddd       | j                  |ddd
       | j                  |ddd       | j                  |ddd       | j                  |ddd       | j                  |ddd       | j                  |ddd       | j                  |ddd       | j                  |ddd       |j                  d      j                         D ch c]  }|d   	 }}d|v }| j                  |ddd       | j                  |ddd       | j                  |ddd       | j                  |ddd       | j                  |dd d       | j                  |dd!d       | j                  |dd"d       |j                  d#       |j                  d$       |j                  d%       |s|j                  d&       |j                  d'       |j                  d(       |j                  d)t        t        f       |j                  d*t        t        f       | j                  |       |j                          y c c}w )+Nai  
            CREATE TABLE IF NOT EXISTS tenant_profile (
                tenant_id TEXT PRIMARY KEY,
                tenant_name TEXT NOT NULL,
                tenant_slug TEXT NOT NULL UNIQUE,
                venue_name TEXT NOT NULL,
                address TEXT NOT NULL DEFAULT '',
                phone_number TEXT,
                whatsapp_number TEXT,
                admin_name TEXT NOT NULL,
                admin_username TEXT NOT NULL,
                admin_email TEXT NOT NULL,
                admin_phone_number TEXT,
                password_hash TEXT NOT NULL,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL
            );

            CREATE TABLE IF NOT EXISTS tenant_module_settings (
                module_key TEXT PRIMARY KEY,
                enabled INTEGER NOT NULL,
                plan_name TEXT NOT NULL,
                activated_at TEXT,
                expires_at TEXT
            );

            CREATE TABLE IF NOT EXISTS tenant_timeclock_entries (
                id TEXT PRIMARY KEY,
                user_id TEXT NOT NULL,
                user_name TEXT,
                username TEXT,
                user_email TEXT,
                started_at TEXT NOT NULL,
                ended_at TEXT,
                duration_seconds INTEGER,
                started_source TEXT NOT NULL DEFAULT 'portal',
                ended_source TEXT,
                notes TEXT,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL
            );

            CREATE INDEX IF NOT EXISTS idx_tenant_timeclock_user_started
                ON tenant_timeclock_entries(user_id, started_at DESC);

            CREATE INDEX IF NOT EXISTS idx_tenant_timeclock_active
                ON tenant_timeclock_entries(ended_at, started_at DESC);

            CREATE TABLE IF NOT EXISTS tenant_reports (
                id TEXT PRIMARY KEY,
                reporter_user_id TEXT NOT NULL,
                reporter_name TEXT,
                reporter_username TEXT,
                reporter_email TEXT,
                title TEXT NOT NULL,
                description TEXT NOT NULL,
                category TEXT NOT NULL,
                location TEXT,
                priority TEXT NOT NULL DEFAULT 'normal',
                status TEXT NOT NULL DEFAULT 'new',
                admin_note TEXT,
                status_updated_by_user_id TEXT,
                status_updated_by_name TEXT,
                resolved_at TEXT,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL
            );

            CREATE INDEX IF NOT EXISTS idx_tenant_reports_created
                ON tenant_reports(created_at DESC);

            CREATE INDEX IF NOT EXISTS idx_tenant_reports_status
                ON tenant_reports(status, updated_at DESC);

            CREATE INDEX IF NOT EXISTS idx_tenant_reports_reporter
                ON tenant_reports(reporter_user_id, created_at DESC);

            CREATE TABLE IF NOT EXISTS tenant_push_subscriptions (
                id TEXT PRIMARY KEY,
                user_id TEXT NOT NULL,
                endpoint TEXT NOT NULL UNIQUE,
                p256dh TEXT NOT NULL,
                auth TEXT NOT NULL,
                user_agent TEXT,
                enabled INTEGER NOT NULL DEFAULT 1,
                last_error TEXT,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL
            );

            CREATE INDEX IF NOT EXISTS idx_tenant_push_subscriptions_user
                ON tenant_push_subscriptions(user_id, enabled, updated_at DESC);

            CREATE TABLE IF NOT EXISTS tenant_inventory_warehouses (
                id TEXT PRIMARY KEY,
                name TEXT NOT NULL,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL
            );

            CREATE TABLE IF NOT EXISTS tenant_inventory_stock_items (
                id TEXT PRIMARY KEY,
                warehouse_id TEXT NOT NULL,
                product_id INTEGER,
                product_name TEXT NOT NULL,
                product_lookup TEXT NOT NULL,
                supplier_name TEXT NOT NULL,
                supplier_lookup TEXT NOT NULL,
                total_equivalent_units REAL NOT NULL DEFAULT 0,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL,
                FOREIGN KEY (warehouse_id) REFERENCES tenant_inventory_warehouses(id) ON DELETE CASCADE,
                UNIQUE (warehouse_id, product_lookup, supplier_lookup)
            );

            CREATE TABLE IF NOT EXISTS tenant_inventory_stock_lots (
                id TEXT PRIMARY KEY,
                item_id TEXT NOT NULL,
                lot_code TEXT NOT NULL,
                lot_lookup TEXT NOT NULL,
                quantity REAL NOT NULL DEFAULT 0,
                units_per_pack REAL,
                equivalent_units REAL NOT NULL DEFAULT 0,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL,
                FOREIGN KEY (item_id) REFERENCES tenant_inventory_stock_items(id) ON DELETE CASCADE,
                UNIQUE (item_id, lot_lookup)
            );

            CREATE INDEX IF NOT EXISTS idx_tenant_inventory_warehouse_name
                ON tenant_inventory_warehouses(name ASC);

            CREATE INDEX IF NOT EXISTS idx_tenant_inventory_items_warehouse
                ON tenant_inventory_stock_items(warehouse_id, product_name ASC);

            CREATE INDEX IF NOT EXISTS idx_tenant_inventory_lots_item
                ON tenant_inventory_stock_lots(item_id, lot_code ASC);

            CREATE TABLE IF NOT EXISTS tenant_inventory_sessions (
                id TEXT PRIMARY KEY,
                warehouse_id TEXT NOT NULL,
                warehouse_name TEXT NOT NULL,
                inventory_date TEXT NOT NULL,
                label TEXT,
                created_by_user_id TEXT,
                created_by_name TEXT,
                total_products INTEGER NOT NULL DEFAULT 0,
                total_equivalent_units REAL NOT NULL DEFAULT 0,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL,
                FOREIGN KEY (warehouse_id) REFERENCES tenant_inventory_warehouses(id) ON DELETE CASCADE,
                UNIQUE (warehouse_id, inventory_date)
            );

            CREATE TABLE IF NOT EXISTS tenant_inventory_session_items (
                id TEXT PRIMARY KEY,
                session_id TEXT NOT NULL,
                product_id INTEGER,
                product_name TEXT NOT NULL,
                product_lookup TEXT NOT NULL,
                supplier_name TEXT NOT NULL,
                supplier_lookup TEXT NOT NULL,
                total_equivalent_units REAL NOT NULL DEFAULT 0,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL,
                FOREIGN KEY (session_id) REFERENCES tenant_inventory_sessions(id) ON DELETE CASCADE,
                UNIQUE (session_id, product_lookup, supplier_lookup)
            );

            CREATE TABLE IF NOT EXISTS tenant_inventory_session_lots (
                id TEXT PRIMARY KEY,
                session_item_id TEXT NOT NULL,
                lot_code TEXT NOT NULL,
                lot_lookup TEXT NOT NULL,
                quantity REAL NOT NULL DEFAULT 0,
                units_per_pack REAL,
                equivalent_units REAL NOT NULL DEFAULT 0,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL,
                FOREIGN KEY (session_item_id) REFERENCES tenant_inventory_session_items(id) ON DELETE CASCADE,
                UNIQUE (session_item_id, lot_lookup)
            );

            CREATE INDEX IF NOT EXISTS idx_tenant_inventory_session_warehouse_date
                ON tenant_inventory_sessions(warehouse_id, inventory_date DESC);

            CREATE INDEX IF NOT EXISTS idx_tenant_inventory_session_items_session
                ON tenant_inventory_session_items(session_id, product_name ASC);

            CREATE INDEX IF NOT EXISTS idx_tenant_inventory_session_lots_item
                ON tenant_inventory_session_lots(session_item_id, lot_code ASC);

            CREATE TABLE IF NOT EXISTS tenant_inventory_movements (
                id TEXT PRIMARY KEY,
                warehouse_id TEXT NOT NULL,
                product_id INTEGER,
                product_name TEXT NOT NULL,
                product_lookup TEXT NOT NULL,
                supplier_name TEXT NOT NULL,
                supplier_lookup TEXT NOT NULL,
                lot_code TEXT NOT NULL,
                lot_lookup TEXT NOT NULL,
                quantity REAL NOT NULL DEFAULT 0,
                units_per_pack REAL,
                equivalent_units REAL NOT NULL DEFAULT 0,
                movement_kind TEXT NOT NULL DEFAULT 'in',
                source_type TEXT NOT NULL DEFAULT 'manual_stock_add',
                source_label TEXT,
                occurred_at TEXT NOT NULL,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL,
                FOREIGN KEY (warehouse_id) REFERENCES tenant_inventory_warehouses(id) ON DELETE CASCADE
            );

            CREATE INDEX IF NOT EXISTS idx_tenant_inventory_movements_warehouse_occurred
                ON tenant_inventory_movements(warehouse_id, occurred_at DESC);

            CREATE TABLE IF NOT EXISTS tenant_inventory_consumption_product_stats (
                id TEXT PRIMARY KEY,
                product_id INTEGER,
                product_lookup TEXT NOT NULL,
                supplier_lookup TEXT NOT NULL,
                product_name TEXT NOT NULL,
                supplier_name TEXT NOT NULL,
                total_consumed_units REAL NOT NULL DEFAULT 0,
                workdays_count INTEGER NOT NULL DEFAULT 0,
                consumed_days_count INTEGER NOT NULL DEFAULT 0,
                average_daily_consumed_units REAL NOT NULL DEFAULT 0,
                average_consumed_units_on_consumption_days REAL NOT NULL DEFAULT 0,
                movement_count INTEGER NOT NULL DEFAULT 0,
                first_consumption_date TEXT,
                last_consumption_date TEXT,
                calculation_source TEXT NOT NULL DEFAULT 'manual_consumption_movements',
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL,
                UNIQUE (product_lookup, supplier_lookup)
            );

            CREATE INDEX IF NOT EXISTS idx_tenant_inventory_consumption_product_stats_avg
                ON tenant_inventory_consumption_product_stats(average_daily_consumed_units DESC, product_name ASC);

            CREATE INDEX IF NOT EXISTS idx_tenant_inventory_consumption_product_stats_last
                ON tenant_inventory_consumption_product_stats(last_consumption_date DESC, product_name ASC);

            CREATE TABLE IF NOT EXISTS tenant_inventory_daily_consumptions (
                id TEXT PRIMARY KEY,
                warehouse_id TEXT NOT NULL,
                warehouse_name TEXT NOT NULL,
                consumption_date TEXT NOT NULL,
                period_start_date TEXT NOT NULL,
                period_end_date TEXT NOT NULL,
                period_days REAL NOT NULL DEFAULT 0,
                start_session_id TEXT,
                end_session_id TEXT NOT NULL,
                product_lookup TEXT NOT NULL,
                supplier_lookup TEXT NOT NULL,
                product_name TEXT NOT NULL,
                supplier_name TEXT NOT NULL,
                opening_units REAL NOT NULL DEFAULT 0,
                incoming_units REAL NOT NULL DEFAULT 0,
                outgoing_transfer_units REAL NOT NULL DEFAULT 0,
                closing_units REAL NOT NULL DEFAULT 0,
                consumed_units REAL NOT NULL DEFAULT 0,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL,
                FOREIGN KEY (warehouse_id) REFERENCES tenant_inventory_warehouses(id) ON DELETE CASCADE,
                FOREIGN KEY (start_session_id) REFERENCES tenant_inventory_sessions(id) ON DELETE SET NULL,
                FOREIGN KEY (end_session_id) REFERENCES tenant_inventory_sessions(id) ON DELETE CASCADE,
                UNIQUE (warehouse_id, consumption_date, product_lookup, supplier_lookup)
            );

            CREATE INDEX IF NOT EXISTS idx_tenant_inventory_daily_consumptions_date
                ON tenant_inventory_daily_consumptions(consumption_date DESC, warehouse_name ASC, product_name ASC);

            CREATE INDEX IF NOT EXISTS idx_tenant_inventory_daily_consumptions_warehouse_date
                ON tenant_inventory_daily_consumptions(warehouse_id, consumption_date DESC);

            CREATE TABLE IF NOT EXISTS tenant_inventory_estimated_consumptions (
                id TEXT PRIMARY KEY,
                consumption_date TEXT NOT NULL,
                period_start_date TEXT NOT NULL,
                period_end_date TEXT NOT NULL,
                period_days REAL NOT NULL DEFAULT 0,
                product_id INTEGER,
                product_lookup TEXT NOT NULL,
                supplier_lookup TEXT NOT NULL,
                product_name TEXT NOT NULL,
                supplier_name TEXT NOT NULL,
                warehouse_count INTEGER NOT NULL DEFAULT 0,
                opening_units REAL NOT NULL DEFAULT 0,
                incoming_units REAL NOT NULL DEFAULT 0,
                closing_units REAL NOT NULL DEFAULT 0,
                consumed_units REAL NOT NULL DEFAULT 0,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL,
                UNIQUE (consumption_date, product_lookup, supplier_lookup)
            );

            CREATE INDEX IF NOT EXISTS idx_tenant_inventory_estimated_consumptions_date
                ON tenant_inventory_estimated_consumptions(consumption_date DESC, product_name ASC);

            CREATE INDEX IF NOT EXISTS idx_tenant_inventory_estimated_consumptions_product
                ON tenant_inventory_estimated_consumptions(product_lookup, supplier_lookup, consumption_date DESC);

            CREATE TABLE IF NOT EXISTS tenant_inventory_snapshots (
                id TEXT PRIMARY KEY,
                snapshot_date TEXT NOT NULL UNIQUE,
                label TEXT,
                created_by_user_id TEXT,
                created_by_name TEXT,
                total_warehouses INTEGER NOT NULL DEFAULT 0,
                total_products INTEGER NOT NULL DEFAULT 0,
                total_equivalent_units REAL NOT NULL DEFAULT 0,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL
            );

            CREATE TABLE IF NOT EXISTS tenant_inventory_snapshot_items (
                id TEXT PRIMARY KEY,
                snapshot_id TEXT NOT NULL,
                warehouse_id TEXT,
                warehouse_name TEXT NOT NULL,
                product_lookup TEXT NOT NULL,
                supplier_lookup TEXT NOT NULL,
                product_name TEXT NOT NULL,
                supplier_name TEXT NOT NULL,
                total_equivalent_units REAL NOT NULL DEFAULT 0,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL,
                FOREIGN KEY (snapshot_id) REFERENCES tenant_inventory_snapshots(id) ON DELETE CASCADE,
                UNIQUE (snapshot_id, warehouse_id, product_lookup, supplier_lookup)
            );

            CREATE TABLE IF NOT EXISTS tenant_inventory_snapshot_lots (
                id TEXT PRIMARY KEY,
                snapshot_item_id TEXT NOT NULL,
                lot_code TEXT NOT NULL,
                lot_lookup TEXT NOT NULL,
                quantity REAL NOT NULL DEFAULT 0,
                units_per_pack REAL,
                equivalent_units REAL NOT NULL DEFAULT 0,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL,
                FOREIGN KEY (snapshot_item_id) REFERENCES tenant_inventory_snapshot_items(id) ON DELETE CASCADE,
                UNIQUE (snapshot_item_id, lot_lookup)
            );

            CREATE INDEX IF NOT EXISTS idx_tenant_inventory_snapshot_date
                ON tenant_inventory_snapshots(snapshot_date DESC);

            CREATE INDEX IF NOT EXISTS idx_tenant_inventory_snapshot_items_snapshot
                ON tenant_inventory_snapshot_items(snapshot_id, warehouse_name ASC, product_name ASC);

            CREATE INDEX IF NOT EXISTS idx_tenant_inventory_snapshot_lots_item
                ON tenant_inventory_snapshot_lots(snapshot_item_id, lot_code ASC);

            CREATE TABLE IF NOT EXISTS tenant_homemade_recipes (
                id TEXT PRIMARY KEY,
                name TEXT NOT NULL,
                name_lookup TEXT NOT NULL UNIQUE,
                measurement_unit TEXT NOT NULL DEFAULT 'ml',
                usage_scope TEXT NOT NULL DEFAULT 'both',
                notes TEXT,
                yield_ingredient_name TEXT,
                yield_ingredient_lookup TEXT,
                yield_input_amount REAL,
                yield_input_unit TEXT,
                yield_output_ml REAL,
                total_parts REAL NOT NULL DEFAULT 0,
                ingredient_count INTEGER NOT NULL DEFAULT 0,
                preparation_date TEXT,
                preparation_drive_file_id TEXT,
                preparation_drive_web_url TEXT,
                preparation_drive_updated_at TEXT,
                created_by_user_id TEXT,
                created_by_name TEXT,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL
            );

            CREATE TABLE IF NOT EXISTS tenant_homemade_recipe_ingredients (
                id TEXT PRIMARY KEY,
                recipe_id TEXT NOT NULL,
                ingredient_name TEXT NOT NULL,
                ingredient_lookup TEXT NOT NULL,
                linked_recipe_id TEXT,
                part_amount REAL NOT NULL DEFAULT 0,
                measurement_unit TEXT NOT NULL DEFAULT 'ml',
                share_ratio REAL NOT NULL DEFAULT 0,
                percentage REAL NOT NULL DEFAULT 0,
                sort_order INTEGER NOT NULL DEFAULT 0,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL,
                FOREIGN KEY (recipe_id) REFERENCES tenant_homemade_recipes(id) ON DELETE CASCADE
            );

            CREATE INDEX IF NOT EXISTS idx_tenant_homemade_recipe_name
                ON tenant_homemade_recipes(name ASC);

            CREATE INDEX IF NOT EXISTS idx_tenant_homemade_ingredient_recipe
                ON tenant_homemade_recipe_ingredients(recipe_id, sort_order ASC, ingredient_name ASC);

            CREATE TABLE IF NOT EXISTS tenant_homemade_stock_warehouses (
                id TEXT PRIMARY KEY,
                name TEXT NOT NULL,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL
            );

            CREATE TABLE IF NOT EXISTS tenant_homemade_stock_items (
                id TEXT PRIMARY KEY,
                warehouse_id TEXT NOT NULL,
                recipe_id TEXT NOT NULL,
                recipe_name TEXT NOT NULL,
                recipe_lookup TEXT NOT NULL,
                measurement_unit TEXT NOT NULL DEFAULT 'pz',
                quantity REAL NOT NULL DEFAULT 0,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL,
                FOREIGN KEY (warehouse_id) REFERENCES tenant_homemade_stock_warehouses(id) ON DELETE CASCADE,
                FOREIGN KEY (recipe_id) REFERENCES tenant_homemade_recipes(id) ON DELETE CASCADE,
                UNIQUE (warehouse_id, recipe_lookup)
            );

            CREATE INDEX IF NOT EXISTS idx_tenant_homemade_stock_warehouse_name
                ON tenant_homemade_stock_warehouses(name ASC);

            CREATE INDEX IF NOT EXISTS idx_tenant_homemade_stock_items_warehouse
                ON tenant_homemade_stock_items(warehouse_id, recipe_name ASC);

            CREATE TABLE IF NOT EXISTS tenant_homemade_stock_sessions (
                id TEXT PRIMARY KEY,
                warehouse_id TEXT NOT NULL,
                warehouse_name TEXT NOT NULL,
                inventory_date TEXT NOT NULL,
                label TEXT,
                created_by_user_id TEXT,
                created_by_name TEXT,
                total_recipes INTEGER NOT NULL DEFAULT 0,
                total_quantity REAL NOT NULL DEFAULT 0,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL,
                FOREIGN KEY (warehouse_id) REFERENCES tenant_homemade_stock_warehouses(id) ON DELETE CASCADE,
                UNIQUE (warehouse_id, inventory_date)
            );

            CREATE TABLE IF NOT EXISTS tenant_homemade_stock_session_items (
                id TEXT PRIMARY KEY,
                session_id TEXT NOT NULL,
                recipe_id TEXT NOT NULL,
                recipe_name TEXT NOT NULL,
                recipe_lookup TEXT NOT NULL,
                measurement_unit TEXT NOT NULL DEFAULT 'pz',
                quantity REAL NOT NULL DEFAULT 0,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL,
                FOREIGN KEY (session_id) REFERENCES tenant_homemade_stock_sessions(id) ON DELETE CASCADE,
                FOREIGN KEY (recipe_id) REFERENCES tenant_homemade_recipes(id) ON DELETE CASCADE,
                UNIQUE (session_id, recipe_lookup)
            );

            CREATE INDEX IF NOT EXISTS idx_tenant_homemade_stock_session_warehouse_date
                ON tenant_homemade_stock_sessions(warehouse_id, inventory_date DESC);

            CREATE INDEX IF NOT EXISTS idx_tenant_homemade_stock_session_items_session
                ON tenant_homemade_stock_session_items(session_id, recipe_name ASC);

            CREATE TABLE IF NOT EXISTS tenant_homemade_stock_settings (
                id TEXT PRIMARY KEY,
                minimum_stock_days REAL NOT NULL DEFAULT 0,
                bar_calendar_json TEXT,
                restaurant_calendar_json TEXT,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL
            );

            CREATE TABLE IF NOT EXISTS tenant_homemade_stock_movements (
                id TEXT PRIMARY KEY,
                warehouse_id TEXT NOT NULL,
                warehouse_name TEXT NOT NULL,
                recipe_id TEXT NOT NULL,
                recipe_name TEXT NOT NULL,
                recipe_lookup TEXT NOT NULL,
                measurement_unit TEXT NOT NULL DEFAULT 'pz',
                quantity_before REAL NOT NULL DEFAULT 0,
                quantity_after REAL NOT NULL DEFAULT 0,
                delta_quantity REAL NOT NULL DEFAULT 0,
                added_quantity REAL NOT NULL DEFAULT 0,
                consumed_quantity REAL NOT NULL DEFAULT 0,
                movement_type TEXT NOT NULL DEFAULT 'stock_set',
                occurred_at TEXT NOT NULL,
                created_by_user_id TEXT,
                created_by_name TEXT,
                created_at TEXT NOT NULL,
                FOREIGN KEY (warehouse_id) REFERENCES tenant_homemade_stock_warehouses(id) ON DELETE CASCADE,
                FOREIGN KEY (recipe_id) REFERENCES tenant_homemade_recipes(id) ON DELETE CASCADE
            );

            CREATE INDEX IF NOT EXISTS idx_tenant_homemade_stock_movements_recipe_date
                ON tenant_homemade_stock_movements(recipe_lookup, occurred_at DESC);

            CREATE INDEX IF NOT EXISTS idx_tenant_homemade_stock_movements_warehouse_date
                ON tenant_homemade_stock_movements(warehouse_id, occurred_at DESC);

            CREATE TABLE IF NOT EXISTS tenant_tips_roster_entries (
                id TEXT PRIMARY KEY,
                area TEXT NOT NULL,
                staff_name TEXT NOT NULL,
                staff_lookup TEXT NOT NULL,
                score REAL NOT NULL DEFAULT 0,
                sort_order INTEGER NOT NULL DEFAULT 0,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL,
                UNIQUE (area, staff_lookup)
            );

            CREATE TABLE IF NOT EXISTS tenant_tips_runs (
                id TEXT PRIMARY KEY,
                area TEXT NOT NULL,
                tip_date TEXT NOT NULL,
                total_tip_amount REAL NOT NULL DEFAULT 0,
                tip_pos_amount REAL NOT NULL DEFAULT 0,
                tip_pos_effective_amount REAL NOT NULL DEFAULT 0,
                tip_cash_amount REAL NOT NULL DEFAULT 0,
                total_score REAL NOT NULL DEFAULT 0,
                historical_total_amount REAL NOT NULL DEFAULT 0,
                payable_total_amount REAL NOT NULL DEFAULT 0,
                present_staff_count INTEGER NOT NULL DEFAULT 0,
                absent_staff_count INTEGER NOT NULL DEFAULT 0,
                payout_status TEXT NOT NULL DEFAULT 'pending',
                settled_at TEXT,
                settled_by_user_id TEXT,
                settled_by_name TEXT,
                saved_by_user_id TEXT,
                saved_by_name TEXT,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL,
                UNIQUE (area, tip_date)
            );

            CREATE TABLE IF NOT EXISTS tenant_tips_run_entries (
                id TEXT PRIMARY KEY,
                run_id TEXT NOT NULL,
                area TEXT NOT NULL,
                staff_name TEXT NOT NULL,
                staff_lookup TEXT NOT NULL,
                score REAL NOT NULL DEFAULT 0,
                is_present INTEGER NOT NULL DEFAULT 0,
                amount_today REAL NOT NULL DEFAULT 0,
                historical_amount REAL NOT NULL DEFAULT 0,
                total_amount REAL NOT NULL DEFAULT 0,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL,
                FOREIGN KEY (run_id) REFERENCES tenant_tips_runs(id) ON DELETE CASCADE,
                UNIQUE (run_id, staff_lookup)
            );

            CREATE TABLE IF NOT EXISTS tenant_tips_run_history_sources (
                id TEXT PRIMARY KEY,
                run_id TEXT NOT NULL,
                source_run_id TEXT NOT NULL,
                source_tip_date TEXT NOT NULL,
                created_at TEXT NOT NULL,
                updated_at TEXT NOT NULL,
                FOREIGN KEY (run_id) REFERENCES tenant_tips_runs(id) ON DELETE CASCADE,
                FOREIGN KEY (source_run_id) REFERENCES tenant_tips_runs(id) ON DELETE CASCADE,
                UNIQUE (run_id, source_run_id)
            );

            CREATE INDEX IF NOT EXISTS idx_tenant_tips_roster_area
                ON tenant_tips_roster_entries(area, sort_order ASC, staff_name ASC);

            CREATE INDEX IF NOT EXISTS idx_tenant_tips_runs_area_date
                ON tenant_tips_runs(area, tip_date DESC, updated_at DESC);

            CREATE INDEX IF NOT EXISTS idx_tenant_tips_run_entries_run
                ON tenant_tips_run_entries(run_id, staff_name ASC);

            CREATE INDEX IF NOT EXISTS idx_tenant_tips_history_sources_run
                ON tenant_tips_run_history_sources(run_id, source_tip_date DESC);
            tenant_homemade_recipespreparation_daterF  ry  zTEXT NOT NULL DEFAULT 'both'r   yield_ingredient_lookupr   REALr{  r   preparation_drive_file_idpreparation_drive_web_urlpreparation_drive_updated_at"tenant_homemade_recipe_ingredientsrt  r   zTEXT NOT NULL DEFAULT 'ml'tenant_homemade_stock_settingsbar_calendar_jsonrestaurant_calendar_jsonz#PRAGMA table_info(tenant_tips_runs)r'  tip_pos_effective_amounttenant_tips_runstip_pos_amountzREAL NOT NULL DEFAULT 0tip_cash_amountpayout_statuszTEXT NOT NULL DEFAULT 'pending'
settled_atsettled_by_user_idsettled_by_namez
            UPDATE tenant_tips_runs
            SET payout_status = 'pending'
            WHERE payout_status IS NULL OR trim(payout_status) = ''
            a  
            UPDATE tenant_tips_runs
            SET payout_status = 'carried'
            WHERE payout_status = 'pending'
              AND id IN (
                  SELECT DISTINCT source_run_id
                  FROM tenant_tips_run_history_sources
              )
            z
            UPDATE tenant_tips_runs
            SET tip_cash_amount = total_tip_amount
            WHERE COALESCE(tip_pos_amount, 0) = 0
              AND COALESCE(tip_cash_amount, 0) = 0
              AND COALESCE(total_tip_amount, 0) > 0
            z
                UPDATE tenant_tips_runs
                SET tip_pos_effective_amount = tip_pos_amount
                WHERE COALESCE(tip_pos_effective_amount, 0) = 0
                  AND COALESCE(tip_pos_amount, 0) > 0
                a  
            UPDATE tenant_homemade_recipe_ingredients
            SET measurement_unit = (
                SELECT CASE
                    WHEN lower(COALESCE(recipes.measurement_unit, 'ml')) = 'g' THEN 'g'
                    ELSE 'ml'
                END
                FROM tenant_homemade_recipes AS recipes
                WHERE recipes.id = tenant_homemade_recipe_ingredients.recipe_id
            )
            WHERE measurement_unit IS NULL OR trim(measurement_unit) = ''
            z
            UPDATE tenant_homemade_recipes
            SET usage_scope = 'both'
            WHERE usage_scope IS NULL
               OR trim(usage_scope) = ''
               OR lower(trim(usage_scope)) NOT IN ('bar', 'restaurant', 'both')
            z
            UPDATE tenant_homemade_stock_items
            SET measurement_unit = ?
            WHERE measurement_unit IS NULL OR trim(measurement_unit) != ?
            z
            UPDATE tenant_homemade_stock_session_items
            SET measurement_unit = ?
            WHERE measurement_unit IS NULL OR trim(measurement_unit) != ?
            )rM  _ensure_tenant_columnrO  fetchallHOMEMADE_STOCK_UNIT'_ensure_inventory_catalog_sync_triggerscommit)r>  rR  rowexisting_tips_run_columnshad_tip_pos_effective_amounts        r7   _ensure_tenant_database_schemaz*TenantStore._ensure_tenant_database_schema7  s     D	F		
N 	""%		
 	""%*		
 	""%#		
 	""%%		
 	""% 		
 	""%		
 	""%		
 	""%'		
 	""%'		
 	""%*		
 	""0		
 	""0(		
 	"",		
 	"",&		
 "))*OPYY[%
 K%
! %
 (BE^'^$""%		
 	""&%		
 	""%		
 	""-		
 	""		
 	"" 		
 	""		
 		
 	
	
 		
 , 		
 		
 	
 !"56	
 	
 !"56	
 	44Z@w%
s   J+c                    |j                  d| d      j                         D ch c]  }|d   	 }}||vr;|j                  d| d| d|        |j                          |j                          y y c c}w NPRAGMA table_info()r'  zALTER TABLE z ADD COLUMN  rO  rh  rk  r>  rR  
table_namecolumn_name
column_sqlrl  existing_columnss          r7   rN  z#TenantStore._ensure_registry_columnP  s     ")),>zl!*LMVVX
 K
 
 ..j\k]RST^S_`a /	
s   A4c                    |j                  d| d      j                         D ch c]  }|d   	 }}||vr+|j                  d| d| d|        |j                          y y c c}w rq  ru  rv  s          r7   rg  z!TenantStore._ensure_tenant_columnZ  s     ")),>zl!*LMVVX
 K
 
 ..j\k]RST^S_`a /	
s   A$c                    | j                  |d      sy | j                  |d      }d|vsd|vry |j                  d       y )Nordini_productsrS  rT  a_  
            CREATE TRIGGER IF NOT EXISTS trg_ordini_products_sync_inventory_names
            AFTER UPDATE OF product_name, supplier_name ON ordini_products
            WHEN COALESCE(OLD.product_name, '') <> COALESCE(NEW.product_name, '')
              OR COALESCE(OLD.supplier_name, '') <> COALESCE(NEW.supplier_name, '')
            BEGIN
                UPDATE tenant_inventory_stock_items
                SET
                    product_name = trim(COALESCE(NEW.product_name, '')),
                    product_lookup = lower(trim(COALESCE(NEW.product_name, ''))),
                    supplier_name = trim(COALESCE(NEW.supplier_name, '')),
                    supplier_lookup = lower(trim(COALESCE(NEW.supplier_name, ''))),
                    updated_at = datetime('now')
                WHERE product_id = NEW.id;

                UPDATE tenant_inventory_session_items
                SET
                    product_name = trim(COALESCE(NEW.product_name, '')),
                    product_lookup = lower(trim(COALESCE(NEW.product_name, ''))),
                    supplier_name = trim(COALESCE(NEW.supplier_name, '')),
                    supplier_lookup = lower(trim(COALESCE(NEW.supplier_name, ''))),
                    updated_at = datetime('now')
                WHERE product_id = NEW.id;

                UPDATE tenant_inventory_movements
                SET
                    product_name = trim(COALESCE(NEW.product_name, '')),
                    product_lookup = lower(trim(COALESCE(NEW.product_name, ''))),
                    supplier_name = trim(COALESCE(NEW.supplier_name, '')),
                    supplier_lookup = lower(trim(COALESCE(NEW.supplier_name, ''))),
                    updated_at = datetime('now')
                WHERE product_id = NEW.id;

                UPDATE tenant_inventory_daily_consumptions
                SET
                    product_name = trim(COALESCE(NEW.product_name, '')),
                    product_lookup = lower(trim(COALESCE(NEW.product_name, ''))),
                    supplier_name = trim(COALESCE(NEW.supplier_name, '')),
                    supplier_lookup = lower(trim(COALESCE(NEW.supplier_name, ''))),
                    updated_at = datetime('now')
                WHERE product_name = OLD.product_name
                  AND supplier_name = OLD.supplier_name;

                UPDATE tenant_inventory_snapshot_items
                SET
                    product_name = trim(COALESCE(NEW.product_name, '')),
                    product_lookup = lower(trim(COALESCE(NEW.product_name, ''))),
                    supplier_name = trim(COALESCE(NEW.supplier_name, '')),
                    supplier_lookup = lower(trim(COALESCE(NEW.supplier_name, ''))),
                    updated_at = datetime('now')
                WHERE product_name = OLD.product_name
                  AND supplier_name = OLD.supplier_name;
            END;
            )_tenant_table_exists_tenant_table_columnsrM  )r>  rR  product_columnss      r7   rj  z3TenantStore._ensure_inventory_catalog_sync_triggersc  sN    ((5FG44ZARS0O?4Z  57	
r6   c              #    K   t        j                  | j                        }t         j                  |_        |j                  d       |j                  d       	 | |j                          y # |j                          w xY ww)NPRAGMA foreign_keys = ON;zPRAGMA journal_mode = WAL;)sqlite3connectr1  Rowrow_factoryrO  closerQ  s     r7   rL  zTenantStore._connect_registry  sg     __T%8%89
!(
6778	Js   ABA/ B/BBc              #  H  K   t        |      }|j                  j                  dd       t        j                  |      }t        j
                  |_        |j                  d       | j                  |       	 | |j                          y # |j                          w xY ww)NTrB  r  )
r   rJ  rK  r  r  r  r  rO  ro  r  )r>  r  pathrR  s       r7   _connect_tenant_databasez$TenantStore._connect_tenant_database  s     M"$6__T*
!(
67++J7	Js   A5B"8B <B"BB"c               T    |j                  d||||||||	|
||||t               f       y )Na  
            INSERT OR REPLACE INTO tenant_profile (
                tenant_id,
                tenant_name,
                tenant_slug,
                venue_name,
                address,
                phone_number,
                whatsapp_number,
                admin_name,
                admin_username,
                admin_email,
                admin_phone_number,
                password_hash,
                created_at,
                updated_at
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            )rO  r;   )r>  rR  r  r  r  
venue_namer  r	  r
  r  admin_usernameadmin_emailadmin_phone_numberpassword_hashr  s                  r7   _upsert_tenant_profilez"TenantStore._upsert_tenant_profile  sJ    $ 	& "
'#	
r6   c                   t               }t        | j                  t         dz        }|j	                  dt
        f      j                         }|#|j	                  dt
        t        t        ||f       |j	                  dt        f      j                         }|"|j	                  dt        t
        t        |f       |j	                  dt        f      j                         }|.|j	                  dt        t
        dt        t        d	|f       d
}d}n3|d   xs d
}|d   xs d}|j	                  dt        t        t        f       | j                  |t
        t        t        t        d
d d |t        t        d ||       | j                  |      5 }	|	j	                  d       |	j                          d d d        y # 1 sw Y   y xY w)N.sqlite3+SELECT id FROM tenants WHERE id = ? LIMIT 1z
                INSERT INTO tenants (id, name, slug, database_path, created_at)
                VALUES (?, ?, ?, ?, ?)
                z*SELECT id FROM venues WHERE id = ? LIMIT 1z
                INSERT INTO venues (id, tenant_id, name, address, phone_number, whatsapp_number, created_at)
                VALUES (?, ?, ?, '', NULL, NULL, ?)
                z>SELECT id, password_hash, name FROM users WHERE id = ? LIMIT 1z
                INSERT INTO users (id, tenant_id, name, username, email, phone_number, password_hash, role, created_at)
                VALUES (?, ?, ?, ?, ?, NULL, '', ?, ?)
                Super Adminr   ri   r  r'  z5UPDATE users SET username = ?, email = ? WHERE id = ?r  r  r  r  r  r  r	  r
  r  r  r  r  r  r  z"DELETE FROM tenant_module_settings)r;   rj   r3  SUPER_ADMIN_TENANT_SLUGrO  SUPER_ADMIN_TENANT_IDfetchoneSUPER_ADMIN_TENANT_NAMESUPER_ADMIN_VENUE_IDSUPER_ADMIN_USER_IDSUPER_ADMIN_USERNAMESUPER_ADMIN_EMAIL_create_tenant_databaser  rk  )
r>  rR  r  r  tenant_existsvenue_existsuser_existsr  r  tenant_connections
             r7   rP  z'TenantStore._ensure_super_admin_account  s   Z
D,,2I1J(/SST"**9"$
 (* 	  
 *++! "))8!#
 (* 	 
 ))+	 !((L "
 (* 	 
 ()!(%! M&J'8>BM$V,=JG%'8:MN
 	$$'+//. !/)#'! 	% 	
  **=9 	'=N%%&JK$$&	' 	' 	's   "F88Gc               @   | j                  |      5 }| j                  |||||||||	|
||||       t        D ]@  }|j                  dk(  rdnd}|rdnd}|j	                  d|j
                  |||r|nd d f       B |j                          d d d        y # 1 sw Y   y xY w)Nr  r  r  r  r  r	  r
  r  r  r  r  r  r  activerm   r   Platform ActivePlanneda7  
                    INSERT OR REPLACE INTO tenant_module_settings (
                        module_key,
                        enabled,
                        plan_name,
                        activated_at,
                        expires_at
                    ) VALUES (?, ?, ?, ?, ?)
                    )r  r  r   r  rO  keyrk  )r>  r  r  r  r  r  r  r	  r
  r  r  r  r  r  r  rR  moduleenabled	plan_names                      r7   r  z#TenantStore._create_tenant_databaseK  s    $ **=9 (	 Z''#''%) /%-'#5+% ( " ) %}}8!a18-i	"" 

!&-
4, Q(	  (	  (	 s   A9BBc                   t        di d|d   d|d   d|d   d|d   d|d   d|d   d|d   d|d   d	|d	   d
|d
   d|d   d|d   dt        |d   d|j                         v r|d   nd       dt        |d   d|j                         v r|d   nd d|j                         v r|d   nd       d|d   d|d   S )Nr  r  r  r  r  r  r  r1  r  r  r  r   r   r   r+  r   r  r  r5   )r  r   r  r   r>  rl  s     r7   _session_identity_from_rowz&TenantStore._session_identity_from_row  s[    
g,
+&
 M*
 M*	

 /0
 !!34
 !!34
 	N
 <(
 _
 +&
 V
 2#f+ZlpspxpxpzZzsCU?V  AE  F
 <F+=+K&'QU0G388:0U+,[_
& o.'
( <()
 	
r6   c                    t        |d   |d   |d   |d   |d   |d   t        |d         |d   |d	   xs d
|d   xs d
|d   |d   |d   |d         S )Nr  r  r  r  r  r  r  r  r  ri   r  r  r  r  r  )r  r  r  r  r  r  r  r  r  r  r  r  r  r  )r  r   r  s     r7   _menu_asset_from_rowz TenantStore._menu_asset_from_row  s    4y+&o.^,+&V$5 67^,/06Bo.4"x=^,<(<(
 	
r6   c           	        |d   }t        d i d|d   d|d   d|d   d|d   d|d   d|d   dt        |d         d	|d	   d
|d
   d|d   d|d   d|d   d|d   d|t        |      nd d|d   xs dd|d   xs dd|d   xs dd|d   xs dd|d   d|d   d|d   d|d   d|d   xs dd|d   xs dd|d   d|d   d|d   S )!Nr  r  r  r  r  r  r  r  r  r  r  r  r  rT  r  EURr  ri   r  r  r  r  r  r  r  pendingr  	to_reviewr  r  r  r5   )r  r   r   )r>  rl  r  s      r7   _fiscal_document_from_rowz%TenantStore._fiscal_document_from_row  s   >*# 
4y
+&
 o.
 ^,	

 +&
 V
  $5 67
 +&
 ^,
 o.
   12
 o.
 o.
 1=0H|,d
 _-
  ^,2!
" /06B#
$ ^,2%
& o.'
( o.)
* ""56+
, x=-
.   12?i/
0 o.=+1
2 ^,3
4 <(5
6 <(7
 	
r6   c           	        dd}t        di d|d   d|d   d|d   dt        |d         d|d   d|d   d|d   xs d	d
|d
   d|d   d ||d         d ||d         d ||d         d ||d         d ||d         d ||d         d ||d         d|d   d|d   xs d	d|d   d|d   S )Nc                     | t        |       S d S r2   r   rZ   s    r7   _float_or_nonezGTenantStore._fiscal_document_line_item_from_row.<locals>._float_or_none  s    #(#45<>$>r6   r  r  r  r  rO  r  r  ri   r  r  r  rB  r  r  r  r  r  r  r  r  r  )rT   objectreturnrJ  r5   )r  r   )r>  rl  r  s      r7   #_fiscal_document_line_item_from_rowz/TenantStore._fiscal_document_line_item_from_row  so   	? , 
4y
+&
 M*
 3|,-	

 ^,
 _
 M*0b
 o.
 +&
 &c,&78
 $C
O4
 *#.>*?@
 )_)=>
 (N(;<
 &c,&78
  &c,&78!
" _#
$ ^,2%
& <('
( <()
 	
r6   c           
        d|j                   xs dj                         j                         |j                  xs dj                         j                         |j                  xs dj                         |j
                  xs dj                         j                         |j                  t        t        |j                        d      nd |j                  xs dj                         j                         fS )Nsemanticri   r  )
r  rQ   casefoldr  r  rT  r  roundr   r  )r>  records     r7   _fiscal_document_dedup_keyz&TenantStore._fiscal_document_dedup_key  s    !!'R..099;##)r002;;=!!'R..0!!'R..099;4:4G4G4SE%++,a0Y]!!'R..099;
 	
r6   c                   |j                   xs dj                         }|j                  xs dj                         j                         }|r|rd||fS |j                  xs dj                         }|rd|fS d|j
                  fS )Nri   messagehashr  )r  rQ   r	  r  r  r  )r>  r  r  r	  r  s        r7    _fiscal_document_inbox_dedup_keyz,TenantStore._fiscal_document_inbox_dedup_key  s    ''-2446
!117R>>@IIK/z?;;%%+224	I&&fii  r6   c                    |j                   xs dj                         }|dk(  r|j                  xs dj                         ry|dk(  ry|j                  xs dj                         ry|dk(  ry|dk(  ry	y
)Nri   importedr   rm   r  unsupportedrI   error   r  )r
  rQ   r  r  )r>  r  r  s      r7   %_fiscal_document_inbox_dedup_priorityz1TenantStore._fiscal_document_inbox_dedup_priority	  sz    $$*113ZV%7%7%=2$D$D$Fz!$"++-}$wr6   c                    t        |d   |d   |d   |d   |d   d|j                         v r|d   nd |d   |d   	      S )
Nr  r  r1  r  r  r  r  r  )r  r  r1  r  r  r  r  r  )r  r  r  s     r7   _assistant_thread_from_rowz&TenantStore._assistant_thread_from_row	  sZ    $4y+&	N	Nx=,8CHHJ,Fs<(D<(<(	
 		
r6   c                Z    t        |d   |d   |d   |d   |d   t        |d               S )Nr  r"  r   r#  r  r$  r  r"  r   r#  r  r$  )r!  r   r  s     r7   _assistant_message_from_rowz'TenantStore._assistant_message_from_row	  sA    %4y+&V	N<(3|,-
 	
r6   c                p    t        |d   |d   |d   |d   |d   |d   |d   |d   |d	   |d
   |d         S )Nr  r"  r  r1  r  r'  r(  r)  r*  r+  r  )r  r"  r  r1  r  r'  r(  r)  r*  r+  r  )r&  r  s     r7   _assistant_run_from_rowz#TenantStore._assistant_run_from_row%	  sc    !4y+&+&	N	Ng,g,^, 12<(<(
 	
r6   c                0    t        |d   |d   |d         S )Nr  r  r  r  r  r  )r   r  s     r7   "_fiscal_document_settings_from_rowz.TenantStore._fiscal_document_settings_from_row4	  s'    ++&o.<(
 	
r6   c                    t        |d   |d   |d   |d   |d   |d   xs d|d   |d	   |d
   |d   |d   |d   |d   |d   |d         S )Nr  r  r  r  r  r  ri   r  r  r	  r  r
  r  r  r  r  )r  r  r  r  r  r  r  r  r	  r  r
  r  r  r  r  )r  r  s     r7   $_fiscal_document_inbox_item_from_rowz0TenantStore._fiscal_document_inbox_item_from_row;	  s    ,4y+&<(o.+&	N(bx=M* 12+&M*M*^,<(<(
 	
r6   c                    |d   }t        |d   |d   |d   |d   |d   |d   |d   |t        |      nd |d	   xs d
|d   |d   |d   |d         S )Nr  r  r1  r  r  r  r  r  r  portalr  r\  r  r  )r  r1  r  r  r  r  r  r  r  r  r\  r  r  )r  r   )r>  rl  r  s      r7   _timeclock_entry_from_rowz%TenantStore._timeclock_entry_from_rowN	  s    12#4y	N+&_<(<(_6F6RS!12X\/0<H^,g,<(<(
 	
r6   c                   t        di d|d   d|d   d|d   d|d   d|d   d|d   xs dd|d   xs dd	|d	   xs d
d|d   d|d   xs dd|d   xs dd|d   d|d   d|d   d|d   d|d   d|d   S )Nr  r  r  r  r  r  ri   r  rZ  r  r  r  r  r  newr  r  r  r  r  r  r5   )r  r  s     r7   _report_from_rowzTenantStore._report_from_row`	  s'   ! 
4y
 !34
 o.
 ""56	

 /0
 g,$"
 M*0b
 _1	
 _
 _0
 x=)E
 <(
 '**E&F
 $''?#@
 M*
  <(!
" <(#
 	
r6   c                   t        j                  d      }t               }t               | j                  z   j                         }|j                  d|||||f       |j                          | j                  |      S )N    z
            INSERT INTO sessions (token, tenant_id, user_id, created_at, expires_at)
            VALUES (?, ?, ?, ?, ?)
            )	rR   token_urlsafer;   r8   r9  r:   rO  rk  get_session)r>  rR  r  r1  r  r  r  s          r7   _create_sessionzTenantStore._create_sessionu	  sv    %%b)Z
i$"8"88CCE
 Iw
J?	
 	&&r6   c                \    t        |      }|j                  d||f      j                         S )Nat  
            SELECT
                users.*,
                tenants.name AS tenant_name,
                tenants.slug AS tenant_slug,
                tenants.database_path AS database_path
            FROM users
            JOIN tenants ON tenants.id = users.tenant_id
            WHERE lower(users.email) = ? OR lower(users.username) = ?
            LIMIT 1
            )r[   rO  r  r>  rR  r  rU   s       r7   _lookup_user_for_loginz"TenantStore._lookup_user_for_login	  s7    &z2
!!
 $
 (*	r6   c           	     V    t        |      }t        |j                  d||||f            S )Na  
                SELECT
                    users.*,
                    tenants.name AS tenant_name,
                    tenants.slug AS tenant_slug,
                    tenants.database_path AS database_path
                FROM users
                JOIN tenants ON tenants.id = users.tenant_id
                WHERE lower(users.email) = ? OR lower(users.username) = ?
                ORDER BY
                    CASE
                        WHEN lower(users.username) = ? THEN 0
                        WHEN lower(users.email) = ? THEN 1
                        ELSE 2
                    END,
                    CASE WHEN users.role = 'super_admin' THEN 0 ELSE 1 END,
                    users.created_at ASC
                )r[   r   rO  r  s       r7   _lookup_login_candidatesz$TenantStore._lookup_login_candidates	  s:    &z2
$ ZZ@'
 	
r6   c                   |j                  d|f      j                         }|t        d| d      t        |j                  d|f            }t        |j                  d|f            }t        |j                  d|f            }||||fS )Nz"SELECT * FROM tenants WHERE id = ?Tenant  non trovatoz@SELECT * FROM venues WHERE tenant_id = ? ORDER BY created_at ASCz?SELECT * FROM users WHERE tenant_id = ? ORDER BY created_at ASCzVSELECT * FROM tenant_modules WHERE tenant_id = ? ORDER BY enabled DESC, module_key ASC)rO  r  KeyErrorr   )r>  rR  r  tenantvenuesrE  moduless          r7   _context_rowszTenantStore._context_rows	  s    ##$H9,W``b>WYK|<==j(()knwmyz{Z''(ilukwxyh
 vug--r6   c                   |j                   j                         |j                  j                         |j                  r|j                  j                         nd |j                  r|j                  j                         nd |j
                  j                         |j                  j                         |j                  j                         dS )N)r  r  r	  r
  r  r  r  )r  rQ   r  r	  r
  r  r  r  r>  payloads     r7    _normalize_register_like_payloadz,TenantStore._normalize_register_like_payload	  s    
 #..446,,.<C<P<PG00668VZBIBYBYw66<<>_c!,,224((..0]]((*
 	
r6   c                    |j                   j                         |j                  j                         |j                  j                         |j                  r|j                  j                         dS d dS )N)r'  r  r  r	  )r'  rQ   r  r  r	  r  s     r7   _normalize_tenant_staff_payloadz+TenantStore._normalize_tenant_staff_payload	  sk    
 LL&&(((..0]]((*<C<P<PG00668	
 	
 W[	
 	
r6   c                F    |j                   t        k(  ry|j                  dv S )NF>   r   r   )r  r  r   r>  sessions     r7   _can_manage_tenant_accountsz'TenantStore._can_manage_tenant_accounts	  s$     55||777r6   c                n    |j                   t        k(  ry|j                  dv xs | j                  |d      S )NF>   r   r   r#   )r  r  r   session_has_permissionr  s     r7   _can_access_timeclockz!TenantStore._can_access_timeclock	  s8     55||77l4;V;VW^`k;llr6   c                    |sy 	 t        j                  |      }|j                   |j	                  t
        j                        }|S # t        $ r Y y w xY wNtzinfo)r   fromisoformatr>   r  replacer
   r4   r>  r   r   s      r7   _parse_iso_datetimezTenantStore._parse_iso_datetime	  sV    	++I6F == ^^8<<^8F	  		s   A 	AAc                \    | j                  |      }|y |j                  t                     S r2   )r  
astimezoner?   r  s      r7   _timeclock_local_datetimez%TenantStore._timeclock_local_datetime	  s/    )))4>  !7!9::r6   c                L    | j                  |      }||j                         S |S r2   )r	  r:   r>  r   local_datetimes      r7   _timeclock_local_isoz TenantStore._timeclock_local_iso	  s+    77	B-;-G~'')VYVr6   c                L    | j                  |      }||j                         S d S r2   )r	  r   r  s      r7   _timeclock_local_datez!TenantStore._timeclock_local_date	  s+    77	B(6(B~""$LLr6   Nreference_nowc                  | j                  |j                        }| j                  |j                        }|xs
 t               }|j                  }|.|,|xs |}t        t        ||z
  j                               d      }| j                  |j                        }i d|j                  d|j                  d|j                  d|j                  d|j                  d| j                  |j                        d| j                  |j                        d	|d
t        |xs ddz  d      d|j                  d u d|j                   d|j"                  d|j$                  d|d|j&                  d|j(                  S )Nr   r  r1  r  r  r  r  r  r  duration_hours  r  	is_activer  r  r\  
entry_dater  r  )r  r  r  rA   r  maxr   total_secondsr  r  r1  r  r  r  r  r  r  r  r\  r  r  )	r>  r  r  r  r  r3   r  end_referencer  s	            r7   _serialize_timeclock_entryz&TenantStore._serialize_timeclock_entry	  s   --f.?.?@
++FOO<+z|!22#
(>$OM"3
(B'Q'Q'S#TVWX//0A0AB

&))
v~~
 ))
 	

 &++
 $33F4E4EF
 11&//B
  0
 e%5%:d$BAF
 D0
 f33
 F//
 V\\
 *
 &++
  &++!
 	
r6   Fr4  )r1  r2  r3  include_active_onlyr8  c                   g }g }|r"|j                  d       |j                  |       |r"|j                  d       |j                  |       |r"|j                  d       |j                  |       |r|j                  d       |rddj                  |       nd}	t        dt        |d	            }
|s|rt        |
d	      n|
} j	                  |j
                        5 }|j                  d
|	 dg ||      j                         }d d d        D cg c]  } j                  |       }}|s|r||rt        j                  |      nd }|rt        j                  |      nd }g }|D ]A  } j                  |j                        }|!|||k  r)|||kD  r1|j                  |       C |}|j                   fdd       |d |
 S # 1 sw Y   xY wc c}w )Nzuser_id = ?z4date(substr(started_at, 1, 10)) >= date(?, '-1 day')z4date(substr(started_at, 1, 10)) <= date(?, '+1 day')zended_at IS NULLWHERE  AND ri   rm   r[  zX
                SELECT *
                FROM tenant_timeclock_entries
                zR
                ORDER BY started_at DESC
                LIMIT ?
                c                    j                  | j                        xs. t        j                  j	                  t
        j                        S r   )r  r  r   minr  r
   r4   )entryr>  s    r7   <lambda>z5TenantStore._read_timeclock_entries.<locals>.<lambda>M
  s6    d66u7G7GHuHLLL`L`hphthtL`Lu r6   Tr  reverse)rs   joinr  r   r  r  rO  rh  r  r   r  r  r  sort)r>  r  r1  r2  r3  r  r8  filtersr   	where_sqlrequested_limit	sql_limitrR  rowsrl  r  	start_dayend_dayfiltered_entriesr!  	entry_days   `                    r7   _read_timeclock_entriesz#TenantStore._read_timeclock_entries
  s     !NN=)MM'"NNQRMM*%NNQRMM(#NN-.8?fW\\'234R	aUD!122<C.o	**7+@+@A 
	Z%%   %&$)$	 hj 
	 CGG3411#6GG:D**:6$I6>d((2DG;=  / 66u7G7GH	$(Y-B&9w+> ''./ 'Gu 	 	
 '((?
	 
	 Hs    *GGGr3   c                  |xs
 t               }i }|D ]  }| j                  ||      }|j                  |j                  |j                  |j                  |j
                  |j                  dddd d      }t        |d         dz   |d<   t        |d         t        |d   xs d      z   |d<   |d   rt        |d	         dz   |d	<   |d
   t        |d         t        |d
         kD  s|d   |d
<    g }|j                         D ]8  }	t        |	d   xs d      }
|j                  i |	dt        |
dz  d      i       : |j                  d        |S )Nr  r   )r1  r  r  r  r  r  active_entrieslast_started_atr  rm   r  r  r  r3  r4  r  total_hoursr  r  c                    t        | j                  d      xs( | j                  d      xs | j                  d      xs d      j                         t        | j                  d      xs d      fS )Nr  r  r  ri   r1  )rj   r   rP   r   s    r7   r"  z6TenantStore._build_timeclock_summary.<locals>.<lambda>z
  s[    DHH[)aTXXj-AaTXXlE[a_abhhjDHHY'-2. r6   r  )rA   r  
setdefaultr1  r  r  r  r   rj   r   rs   r  r&  )r>  r  r3   r  groupedr!  
serializedbucket	summariesr   r  s              r7   _build_timeclock_summaryz$TenantStore._build_timeclock_summaryR
  s    +z|02 	EE88m8\J''$}}!& %"'"2"2 %&&''+	F !$F9$5 6 :F9&)&*A&BSTfIgIlklEm&mF?#+&+.v6F/G+H1+L'('(0C
<8P4QTWX^_pXqTr4r,6|,D())	E, .0	NN$ 	D_ 5 :;M!5)=q#A	 	 	 	
 r6   c                  i d|j                   d|j                  d|j                  d|j                  d|j                  d|j
                  d|j                  d|j                  d	|j                  d
|j                  d|j                  d|j                  d|j                  d|j                  d|j                  d|j                  }|r|j                   |d<   |S )Nr  r  r  r  r  r  r  rZ  r  r  r  r  r  r  r  r  r  )r  r  r  r  r  r  r  rZ  r  r  r  r  r  r  r  r  r  )r>  r  include_admin_noter  s       r7   _serialize_report_recordz$TenantStore._serialize_report_record
  s3   &
&))&
 7 7&
 V11&
  !9!9	&

 f33&
 V\\&
 6--&
 &
 &
 &
 fmm&
 ()I)I&
 %f&C&C&
 6--&
 &++&
  &++!&
$ $*$5$5GL!r6   c           
     n    |d   |d   |d   |d   |d   d|d   t        |d         |d	   |d
   |d   d	S )Nr  r1  r  r  r  )r  r  r  r  
last_errorr  r  )	r  r1  r  r  r  r  rC  r  r  )r   r  s     r7    _serialize_push_subscription_rowz,TenantStore._serialize_push_subscription_row
  sb    d)9~Jh-F l+C	N+l+l+l+
 	
r6   c                b    |j                  d|f      j                         }|t        d      |S )Nz1SELECT * FROM tenant_reports WHERE id = ? LIMIT 1Segnalazione non trovata.rO  r  r  )r>  rR  	report_idrl  s       r7   _read_report_rowzTenantStore._read_report_row
  s>      ?L
 (* 	 ;677
r6   c                x    |j                         j                         }|j                  dv ry||j                  v S N>   r   r   T)rQ   rP   r   r   )r>  r  r   rU   s       r7   r  z"TenantStore.session_has_permission
  s:    %%'--/
<<33W0000r6   c                x    |j                         j                         }|j                  dv ry||j                  v S rK  )rQ   rP   r   r+  )r>  r  scoperU   s       r7   session_has_assistant_scopez'TenantStore.session_has_assistant_scope
  s7    [[]((*
<<33W5555r6   c                F    |j                   dv xs | j                  |d      S )N>   r   r   r$   r   r  r  s     r7   _can_access_inventoryz!TenantStore._can_access_inventory
  s&    ||77l4;V;VW^`k;llr6   c                F    |j                   dv xs | j                  |d      S )N>   r   r   r%   rP  r  s     r7   _can_access_consumptionsz$TenantStore._can_access_consumptions
  s&    ||77o4;V;VW^`n;oor6   c                J    | j                  |      xs | j                  |      S r2   )rQ  rS  r  s     r7   +_can_access_inventory_data_for_consumptionsz7TenantStore._can_access_inventory_data_for_consumptions
  s$    ))'2\d6S6ST[6\\r6   c                     |j                   dk(  S Nr   r   r  s     r7    _can_manage_inventory_warehousesz,TenantStore._can_manage_inventory_warehouses
      ||},,r6   c                F    |j                   dv xs | j                  |d      S )N>   r   r   r&   rP  r  s     r7   _can_access_reportszTenantStore._can_access_reports
  s&    ||77j4;V;VW^`i;jjr6   c                    |j                   dv S r   rX  r  s     r7   _can_manage_reportszTenantStore._can_manage_reports
  s    ||777r6   c                F    |j                   dv xs | j                  |d      S )N>   r   r   r    rP  r  s     r7   _can_access_homemadez TenantStore._can_access_homemade
  s&    ||77k4;V;VW^`j;kkr6   c                F    |j                   dv xs | j                  |d      S )N>   r   r   r!   rP  r  s     r7   _can_manage_homemadez TenantStore._can_manage_homemade
  s&    ||77r4;V;VW^`q;rrr6   c                     |j                   dk(  S rW  rX  r  s     r7   %_can_manage_homemade_stock_warehousesz1TenantStore._can_manage_homemade_stock_warehouses
  rZ  r6   c                    t        |      }d| }d| d}|j                  dv xs& | j                  ||      xs | j                  ||      S )Ntips__manage>   r   r   r   r   r  )r>  r  areanormalized_areaaccess_permissionmanage_permissions         r7   _can_access_tipszTenantStore._can_access_tips
  sj    .t4#O#45#O#4G<LL44 G**74EFG**74EF	
r6   c                d    t        |      }|j                  dv xs | j                  |d| d      S )N>   r   r   rf  rg  rh  )r>  r  ri  rj  s       r7   _can_manage_tipszTenantStore._can_manage_tips
  s@    .t4||77 
4;V;VO$G,<
 	
r6   c                L    |j                  d|f      j                         }|d uS )NzHSELECT name FROM sqlite_master WHERE type = 'table' AND name = ? LIMIT 1rO  r  r>  rR  rw  rl  s       r7   r~  z TenantStore._tenant_table_exists
  s2      VM
 (* 	 $r6   c                    | j                  ||      s
t               S |j                  d| d      j                         D ch c]  }t	        |d          c}S c c}w )Nrr  rs  r'  )r~  r   rO  rh  rj   rr  s       r7   r  z!TenantStore._tenant_table_columns
  sb    ((Z@5L ")),>zl!*LMVVX
 F
 	
 
s   Ac           	         t        |d         t        |d         t        |d   xs d      t        |d   xs d      t        |d         t        |d         dS )	Nr  r'  product_countr   total_equivalent_unitsr  r  )r  r'  ru  rv  r  r  rj   r   r   r  s     r7   "_serialize_inventory_warehouse_rowz.TenantStore._serialize_inventory_warehouse_row
  sb    c$i.F$ _!5!:;&+C0H,I,NQ&Oc,/0c,/0
 	
r6   c           	         t        |d         t        |d         t        |d   xs d      t        |d   xs d      t        |d         t        |d         dS )	Nr  r'  recipe_countr   total_quantityr  r  )r  r'  rz  r{  r  r  rw  r  s     r7   '_serialize_homemade_stock_warehouse_rowz3TenantStore._serialize_homemade_stock_warehouse_row  sb    c$i.F$N 3 8q9#C(8$9$>Q?c,/0c,/0
 	
r6   c                2   t        d|j                         v rt        |d   xs d      nd      }t        |d         t        |d         t        |d   xs d      |t        |      t        t        |d   xs d      t        |d	         t        |d
         d	S )Nry  r-   r  r  recipe_nameri   rB  r   r  r  )	r  r  r~  ry  usage_scope_labelr   rB  r  r  )rc   r  rj   rf   ri  r   )r>  rl  ry  s      r7   "_serialize_homemade_stock_item_rowz.TenantStore._serialize_homemade_stock_item_row  s    51>#((*1LCM",f-RX
 c$i.S-.s=17R8&!<[!I 3c*o23c,/0c,/0

 
	
r6   c                4   t        |d         t        |d         t        |d   xs d      t        |d         t        |d   xs d      t        |d   xs d      t        |d   xs d	      t        |d
   xs d	      t        |d         t        |d         d
S )Nr  rf  warehouse_nameri   r`  labelcreated_by_nametotal_recipesr   r{  r  r  )
r  rf  r  r`  r  r  r  r{  r  r  rw  r  s     r7   %_serialize_homemade_stock_session_rowz1TenantStore._serialize_homemade_stock_session_row  s    c$i.N 34!#&6"7"=2>!#&6"78W+,"3'8#9#?R@ _!5!:;#C(8$9$>Q?c,/0c,/0
 	
r6   c                   d|j                         v r t        |d   xs d      j                         nd}d|j                         v r t        |d   xs d      j                         nd}d|j                         v rt        |d   xs d      nd}d|j                         v r t        |d   xs d      j                         nd}d|j                         v rt        |d   xs d      nd}|dkD  r
|dkD  r||z  nd }t	        d	|j                         v rt        |d	   xs d
      nd
      }i dt        |d         dt        |d         dt        |d   xs d      d	|dt        |      dt        |d   xs d      xs d d|xs d d|xs d d|dkD  r|nd d|xs d d|dkD  r|nd d|dt        |d   xs d      dt        |d   xs d      dt        |d   xs d      xs d dt        |d   xs d      xs d dt        |d   xs d      xs d t        |d   xs d      xs d t        |d   xs d      t        |d         t        |d         dS )Nr   ri   rV  r   r   r   r{  r   ry  r-   r  r'  r   r   r  r\  yield_ml_per_input_unittotal_partsingredient_countrU  rX  rY  rZ  r  r  r  )rZ  r  r  r  )r  rj   rQ   r   rc   rf   r   )	r>  rl  r   rV  r   r{  r   r  ry  s	            r7   _serialize_homemade_recipe_rowz*TenantStore._serialize_homemade_recipe_row*  s0   ?VZ]ZbZbZd?dC+,2399;jl 	 B[^a^f^f^hAhC-.4"5;;=np 	  G[^a^f^f^hFhU3';#<#ABnqI[_b_g_g_iIi3s#56<"=CCEoq@QUXU]U]U_@_%$5 6 ;!<eh "A%/A*= 00 	 
 61>#((*1LCM",f-RX

#c$i.
CF$
 C(:$;$Ct D
 ;	

  !<[!I
 SW+,4
 $%:%Bd
 &'>'F$
 !8JQ8N"4TX
  0 8D
 /A2E4
 &'>
 5]!3!8q9
 C(:$;$@q A
 C(:$;$Ar B Jd
  (S1L-M-SQS)T)\X\!
" (S1L-M-SQS)T)\X\#
$ -04R0S0YWY,Z,b^b"3'8#9#?R@c,/0c,/0+
 	
r6   c                   t        t        d|j                         v r|d   nd|j                         v r|d   nd            }d|j                         v r t        |d   xs d      j                         nd}d|j                         v r t        |d   xs d      j                         nd}|xs t        |d         }t	        |d   xs d	      }|d
v rd}d}	|}
n5t	        |xs d      }|d	kD  r||z  nt	        |d   xs d      }|dz  }	|dz  }
t        |d         ||rdnd|xs d |xs d ||||	|
|d
v rdndt        |d   xs d	      t        |d         t        |d         dS )Neffective_measurement_unitr   r   rt  ri   linked_recipe_namerr  r   r   r   r   share_ratiod   r5  r  reciper   	per_literproportional
sort_orderr  r  )r  rr  ingredient_modert  r  r   r   r  
percentageper_liter_quantitycalculation_moder  r  r  )r   rj   r  rQ   r   r   )r>  rl  reference_totalr   rt  r  effective_ingredient_namer   r  r  r  safe_reference_totals               r7   )_serialize_homemade_recipe_ingredient_rowz5TenantStore._serialize_homemade_recipe_ingredient_rowT  s   
 ?/388:= 010Bchhj0PS+,VZ
 J\_b_g_g_iIi3s#56<"=CCEoqMaehememeoMoS%9!:!@bAGGIuw$6$U#cBS>T:U!C.3!4AAKJ!,#()?C#@  (!+ 223}-45 
 %s*J!,t!3c$i.8+;x 0 8D"4"<& 0&$"4/?Ce/ekyc,/415c,/0c,/0
 	
r6   c                    t        |d         |d   t        |d         nd t        |d         t        |d         t        |d   xs d      t        |      |t        |d         t        |d         d		S )
Nr  rA  rS  rT  rv  r   r  r  	r  rA  rS  rT  rv  	lot_countlotsr  r  )rj   r   r   rq   )r>  rl  r  s      r7   #_serialize_inventory_stock_item_rowz/TenantStore._serialize_inventory_stock_item_row  s     c$i.474E4Q#c,/0W[N 34 _!56&+C0H,I,NQ&OTc,/0c,/0

 
	
r6   c                V   t        |d         t        |d         t        |d   xs d      t        |d         t        |d   xs d      t        |d   xs d      t        |d   xs d      t        |d	   xs d
      t        |d   xs d
      t        |d         t        |d         dS )Nr  rf  r  ri   r`  r  created_by_user_idr  total_productsr   rv  r  r  )r  rf  r  r`  r  r  r  r  rv  r  r  rw  r  s     r7    _serialize_inventory_session_rowz,TenantStore._serialize_inventory_session_row  s    c$i.N 34!#&6"7"=2>!#&6"78W+,"%c*>&?&E2"F"3'8#9#?R@!#&6"7"<1=&+C0H,I,NQ&Oc,/0c,/0
 	
r6   c                   t        |d         t        |d         t        |d   xs d      t        |d   xs d      t        |d   xs d      t        |d   xs d      t        |d	   xs d      t        |d
         t        |d         d	S )Nr  rc  r  ri   r  total_warehousesr   r  rv  r  r  )	r  rc  r  r  r  r  rv  r  r  rw  r  s     r7   !_serialize_inventory_snapshot_rowz-TenantStore._serialize_inventory_snapshot_row  s    c$i. _!56W+,"3'8#9#?R@ #C(:$;$@q A!#&6"7"<1=&+C0H,I,NQ&Oc,/0c,/0

 
	
r6   c                6    t        t        |xs d            dv S )Nri   >   ctcassacassecartonecartoni)r[   rj   )r>  rG  s     r7   &_inventory_lot_requires_units_per_packz2TenantStore._inventory_lot_requires_units_per_pack  s     X^!459gggr6   c                    |r| j                  |d      si S | j                  |d      }d|vri S i }t               }|D ]P  }t        |d   xs d      t        |d   xs d      f}|d   s-t        |d         }|||<   |j	                  |       R |si S i }	|j                  d      j                         }
|
D ]  }| j                  t        |d	   xs d            s%t        t        |d
   xs d            t        t        |d   xs d            f}||vr`t        |d   xs d      }|dk  rx|	j                  |      }|||kD  s||	|<    |j                         D ci c]  \  }}||	v r||	|    c}}S c c}}w )Nr}  rK  product_lookupri   supplier_lookupr   r  z
            SELECT product_name, supplier_name, lot_code, units_per_pack
            FROM ordini_products
            WHERE active = 1
              AND units_per_pack IS NOT NULL
              AND units_per_pack > 0
            rG  rS  rT  )r~  r  r   rj   r   rO  rh  r  r[   r   r   items)r>  rR  	item_rowsr  keys_by_item_idtarget_keysrl  r  item_idcatalog_by_keyr+  rK  currents                r7   ._inventory_catalog_units_per_pack_by_item_rowsz:TenantStore._inventory_catalog_units_per_pack_by_item_rows  s   
  9 9*FW XI44ZARS?2I68,/E 	!Cs+,23S=N9O9USU5VWCq6#d)nG'*OG$OOC 	! I79!!
 (* 	  	5C>>s3z?CXVX?YZ$S^)<)B%CDFWX[\_`o\p\vtvXwFxyC+%"3'7#8#=A>N"$((-G.7":&4s#	5 !0 5 5 7
n$ ^C((
 	
 
s   "E:c                   t        |      }t        |      }|r|sy||k(  ry| j                  |      sy|t        |      v S NFTrk   r  rx   )r>  stored_codescanned_coderG  normalized_storednormalized_scanneds         r7   _inventory_barcode_matchesz&TenantStore._inventory_barcode_matches  sR     9E9,G (: 22::8D $ABT$UUUr6   c                   t        |      }t        |      }|r|sy||k(  ry| j                  |      r| j                  |      sy|t        |      v S r  r  )r>  	left_codeleft_lot_code
right_coderight_lot_codenormalized_leftnormalized_rights          r7   _inventory_barcodes_conflictz(TenantStore._inventory_barcodes_conflict  s_     7yA7
C&6..77F;;NK"?@P"QQQr6   c                   t        |      }|dk  rt        d      |dk(  ry| j                  |      r)|t        |      dk  rt        d      |t        |      z  S |S )Nr   z$La quantita non puo essere negativa.r   zPer questo lotto manca il dato unita per pack nel catalogo prodotti. Completa prima il prodotto e poi aggiungilo in inventario.)r   r>   r  )r>  rG  rB  rK  safe_quantitys        r7   _inventory_effective_unitsz&TenantStore._inventory_effective_units  sw     h1CDDA66x@%~)>!)C Q  !5#888r6   c               x    |j                  d|f      j                         }|syt         fd|D        d       }|yt        |d   xs d      }|dk  ryt	        |d   xs d      xs d}t	        |d	   xs t        |            |D cg c]%  } j                  t	        |d   xs d
            s|' }	}|	rt	        |	d   d   xs d      nd}
t        |
      t        t        d |D              d      }t        |dz   |z        }t        ||z  d      }t        ||z
  d      }t        fd|D              }t        fd|D              }t        fd|D              }t        ||z
        dk  r8t        ||z
        dk  r't        ||z
        dk  rt        fd|D              ryt	        |d         }t        fd|	D        d       }|t	        |d         n dt        j                         j                   }|j                  d|||f       |dkD  r#|j                  d|t        |      ||||f       n|j                  d|f       |dkD  r6||j                  d|||
||||f       n.|j                  d|
||||f       n||j                  d|f       |j                  d|||f       yc c}w )Nz
            SELECT *
            FROM tenant_inventory_stock_lots
            WHERE item_id = ?
            ORDER BY lower(lot_code) ASC, created_at ASC
            Fc              3     K   | ]A  }j                  t        |d    xs d            r|d   t        |d   xs d      dkD  r| C yw)rG  ri   rK  Nr   )r  rj   r   ).0rl  r>  s     r7   	<genexpr>zCTenantStore._normalize_inventory_stock_item_lots.<locals>.<genexpr>5  sZ      >>s3z?CXVX?YZ()5#./4159	 s   AA
rK  r   rm   rG  r  
lot_lookupri   btc              3  @   K   | ]  }t        |d    xs d        yw)equivalent_unitsr   Nr  r  rl  s     r7   r  zCTenantStore._normalize_inventory_stock_item_lots.<locals>.<genexpr>N  s!     *cSV55G1H1MA+N*c   r  ư>c              3  l   K   | ]+  }t        |d    xs d      k(  rt        |d   xs d       - yw)r  ri   rB  r   Nrj   r   r  rl  pack_lot_lookups     r7   r  zCTenantStore._normalize_inventory_stock_item_lots.<locals>.<genexpr>S  s>      $
3|$*+> #j/&Q'$
   14c              3  l   K   | ]+  }t        |d    xs d      k(  rt        |d   xs d       - ywr  ri   r  r   Nr  r  s     r7   r  zCTenantStore._normalize_inventory_stock_item_lots.<locals>.<genexpr>X  s@      ,
3|$*+> #().Q/,
r  c              3  l   K   | ]+  }t        |d    xs d      k(  rt        |d   xs d       - ywr  r  r  rl  single_lot_lookups     r7   r  zCTenantStore._normalize_inventory_stock_item_lots.<locals>.<genexpr>]  sA      .
3|$*+/@@ #().Q/.
r  c              3  J   K   | ]  }t        |d    xs d      hv   ywr  ri   Nrj   )r  rl  r  r  s     r7   r  zCTenantStore._normalize_inventory_stock_item_lots.<locals>.<genexpr>f  s3       C%+,BS0TTs    #r  c              3  N   K   | ]  }t        |d    xs d      k(  s|  ywr  r  r  s     r7   r  zCTenantStore._normalize_inventory_stock_item_lots.<locals>.<genexpr>n  s)     pc#lBSBYWY>Z^o>op   %%inventory_lot_zNDELETE FROM tenant_inventory_stock_lots WHERE item_id = ? AND id NOT IN (?, ?)z
                UPDATE tenant_inventory_stock_lots
                SET lot_code = ?, lot_lookup = ?, quantity = ?, units_per_pack = ?, equivalent_units = ?, updated_at = ?
                WHERE id = ?
                4DELETE FROM tenant_inventory_stock_lots WHERE id = ?a  
                    INSERT INTO tenant_inventory_stock_lots (
                        id, item_id, lot_code, lot_lookup, quantity, units_per_pack, equivalent_units, created_at, updated_at
                    ) VALUES (?, ?, ?, ?, ?, NULL, ?, ?, ?)
                    z
                    UPDATE tenant_inventory_stock_lots
                    SET lot_code = ?, lot_lookup = ?, quantity = ?, units_per_pack = NULL, equivalent_units = ?, updated_at = ?
                    WHERE id = ?
                    z
            UPDATE tenant_inventory_stock_items
            SET total_equivalent_units = ?, updated_at = ?
            WHERE id = ?
            T)rO  rh  nextr   rj   r[   r  r  sumr   absr_   uuiduuid4r   )r>  rR  r  	timestamplot_rowspack_rowrK  pack_lot_coderl  single_rowssingle_lot_coderv  normalized_pack_quantity normalized_pack_equivalent_unitsnormalized_single_quantitycurrent_pack_quantitycurrent_pack_equivalent_unitscurrent_single_equivalent_unitskeep_pack_idkeep_single_rowkeep_single_idr  r  s   `                    @@r7   $_normalize_inventory_stock_item_lotsz0TenantStore._normalize_inventory_stock_item_lots!  s    %% J
 (* 	 # 	
 x(89>Q?QHZ08D9ATh|4X8I-8XY  
>>s3z?CXVX?YZ 
 

 FQ#k!nZ8@DAVZ-o>!&s*cZb*c'cef!g#&(>(E.'X#Y +01IN1Z\]+^(%*+ADd+dfg%h" # $
$
 !

 ), ,
,
 )
%
 +. .
.
 +
' %(@@ATI14TTUY]]36PPQUYY # 
 8D>*p{prvw7F7R_T23ZhimisisiuiyiyhzX{\lN3	
 $a' "#23"4 " UXdWfg%,&"" '')22!!	$ "" ()22!& (UXfWhi
 $Y8	
 a
s   *J7c                   |j                  d|f      j                         }d}|D ]&  }| j                  |t        |d         |      xs |}( |S )NzBSELECT id FROM tenant_inventory_stock_items WHERE warehouse_id = ?Fr  r  )rO  rh  r  rj   )r>  rR  rf  r  r  changedrl  s          r7   )_normalize_inventory_warehouse_stock_lotsz5TenantStore._normalize_inventory_warehouse_stock_lots  sn     &&PO
 (* 	  	|C??
CPSTXPYNfo?p{t{G	|r6   c                    |xs dj                         }|s&t               j                         j                         S 	 t        j                  |      j                         S # t
        $ r}t        d      |d }~ww xY w)Nri   z6Data inventario non valida. Usa il formato YYYY-MM-DD.)rQ   r8   r   r:   r  r>   )r>  rT   r   excs       r7   _normalize_inventory_datez%TenantStore._normalize_inventory_date  sp    [b'')	9>>#--//	`%%i0::<< 	`UV\__	`s   "A! !	A;*A66A;c                $    | j                  |      S r2   )r  )r>  rT   s     r7   "_normalize_inventory_snapshot_datez.TenantStore._normalize_inventory_snapshot_date  s    --e44r6   c                   |xs dj                         }|s
t               S |j                  d      r|d d dz   n|}	 t        j                  |      }|j                   |j                  d      j                         S |j                  t        j                        j                  d      j                         S # t
        $ r}t        d      |d }~ww xY w)Nri   Z+00:00z;Data consumo non valida. Usa un formato data/ora leggibile.r   )microsecond)rQ   r;   endswithr   r  r>   r  r  r:   r  r
   r4   )r>  rT   r   normalized_raw_valuer   r  s         r7   '_normalize_inventory_movement_timestampz3TenantStore._normalize_inventory_movement_timestamp  s    [b'')	:<E<N<Ns<Sy"~8Yb	e++,@AF == >>a>0::<<  .6616EOOQQ  	eZ[add	es   B; ;	CCCc                   t        |xs d      j                         }|sy |g}|j                  d      r|j                  |d d  d       |D ]d  }	 t	        j
                  |      }|j                   |j                  t        j                        }|j                  t        j                        c S  y # t        $ r Y sw xY w)Nri   r	  r
  r  r  )rj   rQ   r  rs   r   r  r>   r  r  r
   r4   r  )r>  rT   r   
candidatesr   r   s         r7   _parse_inventory_datetimez%TenantStore._parse_inventory_datetime  s    $**,	[
c"3B 078# 	3I!//	: }}$x||<$$X\\22	3   s   B66	CCc                   d }	 |d   }i dt        |d         dt        |d   xs d      dt        |xs d      d|d   t        |d         nd dt        |d   xs d      dt        |d   xs d      dt        |d   xs d      d	t	        |d	   xs d
      d|d   t	        |d         nd dt	        |d   xs d
      dt        |d   xs d      dt        |d   xs d      dt        |d   xs d      dt        |d   xs d      dt        |d   xs d      dt        |d   xs d      S # t         t        f$ r d }Y Ew xY w)Nr  r  rf  ri   rA  rS  rT  rG  rB  r   rK  r  movement_kindsource_typesource_labelrh  r  r  )r  
IndexErrorrj   r   r   )r>  rl  r  s      r7   !_serialize_inventory_movement_rowz-TenantStore._serialize_inventory_movement_row  s   	" !12N
#c$i.
CN 3 9r:
 c."6B7
 C4E4Q#c,/0W[	

 CN 3 9r:
 S_!5!;<
 C
O1r2
 c*o23
 cBR>S>_eC(8$9:ei
 c*<&=&B C
 S_!5!;<
 3s=17R8
 CN 3 9r:
 3s=17R8
 #c,/526
  #c,/526!
 	
 *% 	"!N	"s   D: :EEc                z    t        j                  | d| j                  d            j                         }d| S )N r   inventory_consumption_stat_)r   sha1rL   	hexdigest)r>  r  r  r   s       r7   _inventory_consumption_stat_idz*TenantStore._inventory_consumption_stat_id  s>     0?2CDKKGTU__a,VH55r6   c                   t        |d         |d   t        |d         nd t        |d   xs d      t        |d   xs d      t        |d   xs d      t        |d   xs d      t        |d	   xs d      t        |d
   xs d      t        |d   xs d      t        |d   xs d      t        |d   xs d      xs d t        |d   xs d      xs d t        |d   xs d      t        |d   xs d      t        |d   xs d      dS )Nr  rA  rS  ri   rT  total_consumed_unitsr   workdays_countconsumed_days_countaverage_daily_consumed_units*average_consumed_units_on_consumption_daysmovement_countfirst_consumption_datelast_consumption_datecalculation_sourcemanual_consumption_movementsr  r  )r  rA  rS  rT  r   r!  r"  r#  r$  r%  r&  r'  r(  r  r  rw  r  s     r7   1_serialize_inventory_consumption_product_stat_rowz=TenantStore._serialize_inventory_consumption_product_stat_row  s3   c$i.474E4Q#c,/0W[N 3 9r: _!5!;<$)#.D*E*J$K!#&6"7"<1=#&s+@'A'FQ#G,1#6T2U2ZYZ,[:?Dp@q@vuv:w!#&6"7"<1=&)#.F*G*M2&N&VRV%(-D)E)K%L%TPT"%c*>&?&aCa"bc,/526c,/526
 	
r6   c               F    |j                  d||f      j                         S )Nz
            SELECT *
            FROM tenant_inventory_consumption_product_stats
            WHERE product_lookup = ? AND supplier_lookup = ?
            LIMIT 1
            rq  )r>  rR  r  r  s       r7   ,_read_inventory_consumption_product_stat_rowz8TenantStore._read_inventory_consumption_product_stat_row(  s/     !! _-
 (*	r6   r   c               v   |xs
 t               }| j                  ||       |j                  d      j                         }| j	                  |      }i }g }|D ]|  }t        |d   xs d      t        |d   xs d      f}	t        |d   xs d      t        |d   xs d      f}
|j                  |
g       j                  |	       |j                  |	       ~ t        | j                  |||            }|j                  d	       |d
k  ry |j                  d      j                         }|D ]  }t        |d   xs d      j                         }t        |d   xs d      j                         }|r|sIt        t        |d   xs d
      d      }|d
k  rkt        t        |d   xs d
      d      }t        | j                  ||j                  ||fg       |            }|d
kD  rt        |t        |      z  d      nd}t        |t        |      z  d      }|j                  d| j!                  ||      |d   t        |d         nd ||t        |d   xs |      t        |d   xs |      |||||t        |d   xs d
      t        |d   xs d      xs d t        |d   xs d      xs d d||f        y )Nr   a]  
            SELECT DISTINCT
                product_lookup,
                supplier_lookup,
                period_start_date,
                period_end_date
            FROM tenant_inventory_estimated_consumptions
            WHERE consumed_units > 0
              AND period_start_date <> ''
              AND period_end_date <> ''
            period_start_dateri   period_end_dater  r  r?  z6DELETE FROM tenant_inventory_consumption_product_statsr   a?  
            SELECT
                MIN(product_id) AS product_id,
                product_lookup,
                supplier_lookup,
                MAX(product_name) AS product_name,
                MAX(supplier_name) AS supplier_name,
                ROUND(SUM(consumed_units), 6) AS total_consumed_units,
                COUNT(DISTINCT consumption_date) AS consumed_days_count,
                COUNT(*) AS movement_count,
                MIN(consumption_date) AS first_consumption_date,
                MAX(consumption_date) AS last_consumption_date
            FROM tenant_inventory_estimated_consumptions
            WHERE consumed_units > 0
              AND product_lookup <> ''
              AND supplier_lookup <> ''
              AND consumption_date <> ''
            GROUP BY product_lookup, supplier_lookup
            r   r  r"  rm   r   a8  
                INSERT INTO tenant_inventory_consumption_product_stats (
                    id,
                    product_id,
                    product_lookup,
                    supplier_lookup,
                    product_name,
                    supplier_name,
                    total_consumed_units,
                    workdays_count,
                    consumed_days_count,
                    average_daily_consumed_units,
                    average_consumed_units_on_consumption_days,
                    movement_count,
                    first_consumption_date,
                    last_consumption_date,
                    calculation_source,
                    created_at,
                    updated_at
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                rA  rS  rT  r%  r&  r'  :estimated_from_inventory_confirmed_orders_and_bar_calendar)r;   -_refresh_all_inventory_estimated_consumptionsrO  rh  _read_homemade_stock_settingsrj   r9  rs   rq   +_inventory_bar_operational_days_for_periodsrQ   r  r   r  r   r   r  )r>  rR  r  r  period_rowsr?  periods_by_productall_periods
period_rowperiodr  r!  r+  rl  r  r  r   r"  product_workdays_countaverage_dailyaverage_on_consumption_dayss                        r7   ,_refresh_inventory_consumption_product_statsz8TenantStore._refresh_inventory_consumption_product_stats9  s9    ,(*
:::Q[:\ ((

 (* 	 55jAKM-/% 	'J*%89?R@#jQbFcFigiBjkFz"239r:C
K\@]@cac<deC))#r299&Av&		'
 <<! = 
 	STQ!!
( (*) 	,  ?	C %5!6!<"=CCEN!#&7"8">B?EEGO!#(s3I/J/Oa)PRS#T #q("%c#.C*D*I&JA"N%(@@&**NO+LbQ% A &" *A- *U3I-JJAN 
 +00DuM`Ga0acd*e', 77X.1,.?.KCL)*QU"#N+=~>O,?@(*'!/,-2345;<D34:;CtP#-)-?	r6   c                b    |j                  d|f      j                         }|t        d      |S )Na  
            SELECT
                warehouses.*,
                COUNT(items.id) AS product_count,
                COALESCE(SUM(items.total_equivalent_units), 0) AS total_equivalent_units
            FROM tenant_inventory_warehouses AS warehouses
            LEFT JOIN tenant_inventory_stock_items AS items
                ON items.warehouse_id = warehouses.id
            WHERE warehouses.id = ?
            GROUP BY warehouses.id
            LIMIT 1
            zMagazzino non trovato.rG  r>  rR  rf  rl  s       r7   _read_inventory_warehouse_rowz)TenantStore._read_inventory_warehouse_row  sB       O
 (* 	 ;344
r6   c                b    |j                  d|f      j                         }|t        d      |S )Na-  
            SELECT
                warehouses.*,
                (
                    SELECT COUNT(*)
                    FROM tenant_homemade_recipes
                ) AS recipe_count,
                (
                    SELECT COALESCE(SUM(items.quantity), 0)
                    FROM tenant_homemade_stock_items AS items
                    WHERE items.warehouse_id = warehouses.id
                ) AS total_quantity
            FROM tenant_homemade_stock_warehouses AS warehouses
            WHERE warehouses.id = ?
            LIMIT 1
            z"Magazzino/frigo stock non trovato.rG  r?  s       r7   "_read_homemade_stock_warehouse_rowz.TenantStore._read_homemade_stock_warehouse_row  sB        O#
$ (*% 	& ;?@@
r6   c                b    |j                  d|f      j                         }|t        d      |S )Nz=SELECT * FROM tenant_inventory_snapshots WHERE id = ? LIMIT 1z"Fotografia inventario non trovata.rG  )r>  rR  snapshot_idrl  s       r7   _read_inventory_snapshot_rowz(TenantStore._read_inventory_snapshot_row  s>      KN
 (* 	 ;?@@
r6   c                b    |j                  d|f      j                         }|t        d      |S )Nz<SELECT * FROM tenant_inventory_sessions WHERE id = ? LIMIT 1z!Inventario magazzino non trovato.rG  r>  rR  
session_idrl  s       r7   _read_inventory_session_rowz'TenantStore._read_inventory_session_row  s>      JM
 (* 	 ;>??
r6   c                b    |j                  d|f      j                         }|t        d      |S )NzASELECT * FROM tenant_homemade_stock_sessions WHERE id = ? LIMIT 1zStock salvato non trovato.rG  rG  s       r7    _read_homemade_stock_session_rowz,TenantStore._read_homemade_stock_session_row  s>      OM
 (* 	 ;788
r6   c                D    |j                  d|f      j                         S )Nz
            SELECT *
            FROM tenant_inventory_sessions
            WHERE warehouse_id = ?
            ORDER BY inventory_date DESC, created_at DESC
            LIMIT 1
            rq  r>  rR  rf  s      r7   "_read_inventory_latest_session_rowz.TenantStore._read_inventory_latest_session_row   ,    
 !! O	
 (*		r6   c                D    |j                  d|f      j                         S )Nz
            SELECT *
            FROM tenant_homemade_stock_sessions
            WHERE warehouse_id = ?
            ORDER BY inventory_date DESC, created_at DESC
            LIMIT 1
            rq  rM  s      r7   '_read_homemade_stock_latest_session_rowz3TenantStore._read_homemade_stock_latest_session_row  rO  r6   c                   |j                  d|f      j                         }|D cg c]  }t        |d          }}| j                  ||      }|D ci c]  }|g  }}|rdj	                  d |D              }	|j                  d|	 dt        |            j                         }
|
D ]  }t        |d         }|j                  |g       j                  t        |d         t        |d         t        |d	   xs d
      |d   t        |d         nd |j                  |      t        |d   xs d
      t        |d         t        |d         d        ||fS c c}w c c}w )Nz
            SELECT *
            FROM tenant_inventory_stock_items
            WHERE warehouse_id = ?
            ORDER BY lower(supplier_name) ASC, lower(product_name) ASC
            r  , c              3      K   | ]  }d   yw?Nr5   r  _s     r7   r  z:TenantStore._load_inventory_stock_items.<locals>.<genexpr>3       $;QS$;   zm
                SELECT *
                FROM tenant_inventory_stock_lots
                WHERE item_id IN (O)
                ORDER BY lower(lot_code) ASC, created_at ASC
                r  rG  rB  r   rK  r  r  r  r  rG  rB  rK  catalog_units_per_packr  r  r  
rO  rh  rj   r  r%  ro   r9  rs   r   r   )r>  rR  rf  r  rl  item_idscatalog_units_per_pack_by_itemr  lots_by_itemplaceholdersr  s              r7   _load_inventory_stock_itemsz'TenantStore._load_inventory_stock_items   s   
 && O
 (* 	 /88sCD	N88)-)\)\]gir)s&W_;`GGRK;`;`99$;($;;L!))# $0. 1 h hj    c)n-''4;;!#d)n$'J$8$)#j/*>Q$?JMN^J_Jk%4D0E*Fqu2P2T2TU\2],1#6H2I2NQ,O&)#l*;&<&)#l*;&<	 ,&&9 9;`   E
E c                P    |j                  d|t        |f      j                         S )Na  
            SELECT
                COALESCE(items.id, 'homemade_stock_virtual_' || recipes.id) AS id,
                ? AS warehouse_id,
                recipes.id AS recipe_id,
                recipes.name AS recipe_name,
                recipes.name_lookup AS recipe_lookup,
                COALESCE(recipes.usage_scope, 'both') AS usage_scope,
                ? AS measurement_unit,
                COALESCE(items.quantity, 0) AS quantity,
                COALESCE(items.created_at, recipes.created_at) AS created_at,
                COALESCE(items.updated_at, recipes.updated_at) AS updated_at
            FROM tenant_homemade_recipes AS recipes
            LEFT JOIN tenant_homemade_stock_items AS items
                ON items.warehouse_id = ?
                AND (items.recipe_id = recipes.id OR items.recipe_lookup = recipes.name_lookup)
            ORDER BY lower(recipes.name) ASC, recipes.created_at ASC
            )rO  ri  rh  rM  s      r7   _load_homemade_stock_itemsz&TenantStore._load_homemade_stock_itemsM  s2    
 !!$ .='
( (*)	r6   c                   |j                  d|f      j                         }|D cg c]  }t        |d          }}| j                  ||      }|D ci c]  }|g  }}|rdj	                  d |D              }	|j                  d|	 dt        |            j                         }
|
D ]  }t        |d         }|j                  |g       j                  t        |d         t        |d         t        |d	   xs d
      |d   t        |d         nd |j                  |      t        |d   xs d
      t        |d         t        |d         d        ||fS c c}w c c}w )Nz
            SELECT *
            FROM tenant_inventory_session_items
            WHERE session_id = ?
            ORDER BY lower(supplier_name) ASC, lower(product_name) ASC
            r  rS  c              3      K   | ]  }d   ywrU  r5   rW  s     r7   r  z<TenantStore._load_inventory_session_items.<locals>.<genexpr>{  rY  rZ  zw
                SELECT *
                FROM tenant_inventory_session_lots
                WHERE session_item_id IN (r[  session_item_idrG  rB  r   rK  r  r  r  r\  r^  )r>  rR  rH  r  rl  r_  r`  r  ra  rb  r  s              r7   _load_inventory_session_itemsz)TenantStore._load_inventory_session_itemsh  s   
 && M
 (* 	 /88sCD	N88)-)\)\]gir)s&W_;`GGRK;`;`99$;($;;L!))+ ,8. 9 h hj    c"345''4;;!#d)n$'J$8$)#j/*>Q$?JMN^J_Jk%4D0E*Fqu2P2T2TU\2],1#6H2I2NQ,O&)#l*;&<&)#l*;&<	 ,&&9 9;`rd  c                D    |j                  d|f      j                         S )Na  
            SELECT
                session_items.*,
                COALESCE(recipes.usage_scope, 'both') AS usage_scope
            FROM tenant_homemade_stock_session_items AS session_items
            LEFT JOIN tenant_homemade_recipes AS recipes
                ON recipes.id = session_items.recipe_id
            WHERE session_items.session_id = ?
            ORDER BY lower(session_items.recipe_name) ASC, session_items.created_at ASC
            )rO  rh  )r>  rR  rH  s      r7   "_load_homemade_stock_session_itemsz.TenantStore._load_homemade_stock_session_items  s,    
 !!	 M
 (*	r6   c                   |j                  d|f      j                         }i }|D ]W  }t        |d         t        |d         f}t        |d   xs d      t        |d   xs d      t        |d   xs d      d	||<   Y |S )
Na  
            SELECT
                product_lookup,
                supplier_lookup,
                MIN(product_name) AS product_name,
                MIN(supplier_name) AS supplier_name,
                COALESCE(SUM(total_equivalent_units), 0) AS total_equivalent_units
            FROM tenant_inventory_snapshot_items
            WHERE snapshot_id = ?
            GROUP BY product_lookup, supplier_lookup
            r  r  rS  ri   rT  rv  r   rS  rT  rv  rO  rh  rj   r   )r>  rR  rD  r+  
aggregatedrl  r  s          r7   #_aggregate_inventory_snapshot_itemsz/TenantStore._aggregate_inventory_snapshot_items  s    
 !!
 N
 (* 	 @B
 	Cs+,-s37H3I/JKC #C$7$=2 >!$S%9%?R!@*/4L0M0RQR*SJsO	 r6   c                   |j                  d|f      j                         }i }|D ]W  }t        |d         t        |d         f}t        |d   xs d      t        |d   xs d      t        |d   xs d      d	||<   Y |S )
Na  
            SELECT
                product_lookup,
                supplier_lookup,
                MIN(product_name) AS product_name,
                MIN(supplier_name) AS supplier_name,
                COALESCE(SUM(total_equivalent_units), 0) AS total_equivalent_units
            FROM tenant_inventory_session_items
            WHERE session_id = ?
            GROUP BY product_lookup, supplier_lookup
            r  r  rS  ri   rT  rv  r   rn  ro  )r>  rR  rH  r+  rp  rl  r  s          r7   "_aggregate_inventory_session_itemsz.TenantStore._aggregate_inventory_session_items  s    
 !!
 M
 (* 	 @B
 	Cs+,-s37H3I/JKC #C$7$=2 >!$S%9%?R!@*/4L0M0RQR*SJsO	 r6   yearr?  c                   |xs  j                  |      } j                  |d      }t               j                         t	        d      z
  }|t        |dd      nd }|t        |dd      nd }	t               }
|D ]  \  }}	 t        j                  |      t	        d      z   t        t        j                  |      |      }|t        |      |	t        ||	      }|k  sj|rt         fd|D              r|
j                  j                                t	        d      z  |k  rK |
S # t        $ r Y w xY w)Nr,   rm   days      c              3  B   K   | ]  }j                  |        y wr2   _homemade_calendar_rule_matchesr  ruler  r>  s     r7   r  zJTenantStore._inventory_bar_operational_days_for_periods.<locals>.<genexpr>  s      'r`d(L(LWVZ(['r   )r3  "_homemade_calendar_rules_for_scoperA   r   r	   r   r  r   r>   r  anyr   r:   )r>  rR  periodsru  r?  active_settings	bar_rulescompleted_days_endallowed_startallowed_endoperational_daysraw_start_dateraw_end_datelastr  s   `             @r7   r4  z7TenantStore._inventory_bar_operational_days_for_periods  sM    #Td&H&H&T;;OUS	'\..09!3DD,0,<T1a($,0,<d4R($%(U,3 	-(NL,,^<ya?PP4--l;=OP (g}5&4-T/ C'rhq'r$r$(():):)<=9!,, T/	-    s   AD66	EEc                   	 t        j                  t        |d               }t        j                  t        |d               }||kD  r||}}||}}| j	                  |t        |d               }| j	                  |t        |d               }	| j                  |d         xs4 t        j                  |t        j                  t        j                        }
| j                  |d         xsA t        j                  |t        d      z   t        j                  t        j                        }|j                  d|f      j                         }i }i }|D ]  }| j                  |d	         }|
||
k  s||kD  r$t        |d
   xs d      t        |d   xs d      f}t        |d   xs d      dk(  r|n|}|j                  |t        |d   xs d      t        |d   xs d      dd      }t!        |d   xs d      t!        |d   xs d      z   |d<    t#        |      t#        |	      z  t#        |      z  t#        |      z  }g }|D ]  }|j%                  |      }|	j%                  |      }|j%                  |      }|j%                  |      }t        |xs |xs
 |xs |xs i j%                  d      xs d      }t        |xs |xs
 |xs |xs i j%                  d      xs d      }t!        |xs i j%                  d      xs d      }t!        |xs i j%                  d      xs d      }t!        |xs i j%                  d      xs d      }t!        |xs i j%                  d      xs d      }||z   |z
  |z
  } |dk(  r|dk(  r|dk(  r|dk(  rM|j'                  |d   |d   ||t)        |d      t)        |d      t)        |d      t)        |d      t)        | d      d	        |j+                  d        ||t!        t-        | j/                  ||j1                         |j1                         fg                  |dS # t        $ r}t        d      |d }~ww xY w)Nr`  ENon riesco a leggere correttamente le date degli inventari magazzino.r  r  r  rm   rw  aE  
            SELECT *
            FROM tenant_inventory_movements
            WHERE warehouse_id = ?
              AND (
                movement_kind = 'in'
                OR (movement_kind = 'out' AND source_type = 'warehouse_transfer_out')
              )
            ORDER BY occurred_at ASC, created_at ASC
            rh  r  ri   r  r  warehouse_transfer_outrS  rT  r   rn  rv  r   r  r  )	r  r  rS  rT  opening_unitsincoming_unitsoutgoing_transfer_unitsclosing_unitsconsumed_unitsc                    t        | d          t        | d         j                         t        | d         j                         fS Nr  rT  rS  r   rj   rP   r7  s    r7   r"  z[TenantStore._build_inventory_warehouse_consumption_items_between_sessions.<locals>.<lambda>e  F    t,-..D)*002D()//1 r6   r8  )start_inventory_dateend_inventory_dateperiod_daysr  )r   r  rj   r>   rs  r  r   combiner   r   r
   r4   r	   rO  rh  r9  r   r   r   rs   r  r&  rq   r4  r:   )!r>  rR  rf  start_session_rowend_session_rowr  r  r  opening_mapclosing_mapperiod_start_dtperiod_end_dtmovement_rowsincoming_mapoutgoing_transfer_maprl  rh  r  
target_mapr<  	item_keysr  openingclosingincomingoutgoing_transferrS  rT  r  r  r  r  r  s!                                    r7   =_build_inventory_warehouse_consumption_items_between_sessionszITenantStore._build_inventory_warehouse_consumption_items_between_sessions  s   	o#'#5#5c:KL\:]6^#_ !%!3!3CHX8Y4Z![  "441@BS7IK_"4 ==j#N_`dNeJfg==j#o^bNcJde889J<9XY 
]e]m]m HH<<^

 66|7TU 
YaYiYi!22HH<<Z

 #**	 O
 (* 	 BDJL  	BC88]9KLK"k_&DVcHcs+,23S=N9O9USU5VWC s=)/R04LL &! 
  **$'N(;(Ar$B%(_)=)C%D.1F 05V<T5U5ZYZ/[^cdghzd{  eA  @A  _B  0BF+,%	B( $s;'77#l:KKcRgNhh	)+ 	C!ooc*G!ooc*G#'',H 5 9 9# > Y7 Yh YBS YWY^^_mntrtuL!ZG!Zx!ZCT!ZXZ _ _`o p vtvwM!7=b"5"56N"O"TSTUM"HN#7#78P#Q#VUVWN&+->-D",I,IJb,c,hgh&i#!7=b"5"56N"O"TSTUM*^;>UUXeeN!n&9>UYZ>Z_lpq_qLL&)!f'*1v$0%2%*=!%<&+NA&>/45La/P%*=!%<&+NA&>
	8 	

 	 	
 %9"4 DD".88:<N<X<X<Z[\ 
 	
{  	odeknn	os   AQ 	Q!QQ!c                  | j                  ||      }t        |d   xs d      }|j                  d||f       |j                  d||f      j                         }|y| j	                  ||      }| j                  ||||      }	|	d   D 
cg c]  }
t        |
t              s|
 }}
|D ].  }
|j                  ddt        j                         j                   |t        |d	   xs d      ||	d
   j                         |	d   j                         t        |	d         t        |d         t        |d         t        |
d   xs d      t        |
d   xs d      t        |
d   xs d      t        |
d   xs d      t        |
d   xs d      t        |
d   xs d      t        |
d   xs d      t        |
d   xs d      t        |
d   xs d      ||f       1 t        |      S c c}
w )Nr`  ri   z
            DELETE FROM tenant_inventory_daily_consumptions
            WHERE warehouse_id = ? AND consumption_date = ?
            z
            SELECT *
            FROM tenant_inventory_sessions
            WHERE warehouse_id = ?
              AND inventory_date < ?
            ORDER BY inventory_date DESC, created_at DESC
            LIMIT 1
            r   r  al  
                INSERT INTO tenant_inventory_daily_consumptions (
                    id,
                    warehouse_id,
                    warehouse_name,
                    consumption_date,
                    period_start_date,
                    period_end_date,
                    period_days,
                    start_session_id,
                    end_session_id,
                    product_lookup,
                    supplier_lookup,
                    product_name,
                    supplier_name,
                    opening_units,
                    incoming_units,
                    outgoing_transfer_units,
                    closing_units,
                    consumed_units,
                    created_at,
                    updated_at
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                inventory_daily_consumption_r'  r  r  r  r  r  r  rS  rT  r  r  r  r  r  )rI  rj   rO  r  r@  r  r   rt   r  r  r   r:   r   rq   )r>  rR  rf  end_session_idr  r  consumption_dater  warehouse_rowr9  r   r  s               r7   0_refresh_inventory_daily_consumption_for_sessionz<TenantStore._refresh_inventory_daily_consumption_for_sessiony  s8    :::~V/?@FBG +,	
 '.. +,

 (* 	 $:::|TSS	
 #)/L$Zd5KLL 0	D2 34::<3C3C2DE f-34$12<<>/0::<&/0)$/0-.-.4"5./526^,23_-34$/415$/05A6$89>Q?$/415$/05A6)3/0	b 5ze Ms   G&!G&c                   |j                  d||f      j                         }|y| j                  ||t        |d         |      S )Nz
            SELECT *
            FROM tenant_inventory_sessions
            WHERE warehouse_id = ?
              AND inventory_date > ?
            ORDER BY inventory_date ASC, created_at ASC
            LIMIT 1
            r   r  r   )rO  r  r  rj   )r>  rR  rf  r`  r  next_session_rows         r7   4_refresh_next_inventory_daily_consumption_after_datez@TenantStore._refresh_next_inventory_daily_consumption_after_date  sj     &-- >*

 (* 	 #DD &'	 E 
 	
r6   c                   |j                  d      j                         }i }d}|D ]  }| j                  |t        |d         |      }|&|dz  }|j                  dt        |d         f      j                         }|D ]g  }	t        |	d   xs d      j	                         }
t        |	d   xs d      j	                         }|
r|sI|
|f}|j                  ||	d	   t        |	d	         nd t        |	d
   xs |
      t        |	d   xs |      dd      }|j                  d	      |	d	   t        |	d	         |d	<   t        |j                  d
      xs d      j	                         st        |	d
   xs |
      |d
<   t        |j                  d      xs d      j	                         st        |	d   xs |      |d<   t        |d   xs d      t        |	d   xs d      z   |d<   j  ||dS )Nz
            SELECT id
            FROM tenant_inventory_warehouses
            ORDER BY lower(name) ASC, created_at ASC
            r   r  rm   a  
                SELECT
                    MIN(product_id) AS product_id,
                    product_lookup,
                    supplier_lookup,
                    MAX(product_name) AS product_name,
                    MAX(supplier_name) AS supplier_name,
                    ROUND(SUM(total_equivalent_units), 6) AS total_equivalent_units
                FROM tenant_inventory_session_items
                WHERE session_id = ?
                GROUP BY product_lookup, supplier_lookup
                r  ri   r  rA  rS  rT  r   rA  rS  rT  rv  rv  )warehouse_countr  )	rO  rh  ._read_inventory_effective_session_row_for_daterj   rQ   r9  r   r   r   )r>  rR  r`  warehouse_rowsrp  r  r  session_rowr+  rl  r  r  r  r<  s                 r7   -_aggregate_inventory_latest_sessions_for_datez9TenantStore._aggregate_inventory_latest_sessions_for_date  sN   
 $++
 (* 	 @B
+ 0	MMMM$'(K
 "q O%% [&') hj   !$S)9%:%@b!A!G!G!I"%c*;&<&B"C"I"I"K%_%7#..@CL@Q@]c#l*;&<cg(+C,?,Q>(R),S-A-T_)U25	 ::l+3L8I8U+.s</@+AF<(6::n5;<BBD-0^1D1V-WF>*6::o6<"=CCE.1#o2F2Y/.ZF?+&!9:?a@C 89>Q?@ /0+10	f  /
 	
r6   c                  | j                  |d      r| j                  |d      si S | j                  |d      }d|v rdnd}d|v rdnd}| j                  |d      }|r| j                  |d      n	t               }|rd|v rd	nd}	|rd
nd}
|j                  d| d| d|	 d|
 d	||f      j	                         }i }|D ]#  }t        |d   xs d      j                         }t        |d   xs d      j                         }t        |      }t        |      }|r|s_t        |d   xs d      }|dk  rw|d   t        |d         nd }	 | j                  t        |d   xs d      ||      }|dk  r||f}|j                  ||d   t        |d         nd ||dd      }|j                  d      |d   t        |d         |d<   t        |d   xs d      |z   |d<   & |S # t        $ r |}Y w xY w)Nordini_itemsordini_batchesrA  zitems.product_idNULLrK  zitems.units_per_packr}  products.units_per_packzq
            LEFT JOIN ordini_products AS products
                ON products.id = items.product_id
            ri   z$
            SELECT
                z AS product_id,
                items.product_name,
                items.supplier_name,
                items.lot_code,
                items.quantity,
                COALESCE(rS  z) AS resolved_units_per_pack
            FROM ordini_items AS items
            JOIN ordini_batches AS batches
                ON batches.id = items.batch_id
            z
            WHERE substr(COALESCE(batches.confirmed_at, ''), 1, 10) > ?
              AND substr(COALESCE(batches.confirmed_at, ''), 1, 10) <= ?
            ORDER BY batches.confirmed_at ASC, items.id ASC
            rS  rT  rB  r   resolved_units_per_packrG  rG  rB  rK  r   r  rv  )r~  r  r   rO  rh  rj   rQ   r[   r   r  r>   r9  r   r   )r>  rR  r2  r3  item_columnsselect_item_product_idselect_item_units_per_packhas_products_schemar  select_product_units_per_packproducts_joinr+  rp  rl  rS  rT  r  r  rB  rK  r  r  r<  s                          r7   8_aggregate_inventory_confirmed_order_units_between_dateszDTenantStore._aggregate_inventory_confirmed_order_units_between_dates6  s    %%j.A))*6FGI11*nM7C|7S!3Y_?OS_?_%;ek""77
DUVWj$44ZARSpspu #'7?'J & 	& #	
  	 !!'( )
 55R8U7V W O   "#
$ (*% 	( @B
 #	Cs>28b9??ALO 4 :;AACM.|<N/>O!S_12H1}FIJcFdFpU3'@#ABvzN*!%!@!@ Z!6B7%#1 "A " "!?3C**<?<M<Y#c,&7"8_c$0%2.1	F zz,'/C4E4Q'*3|+<'=|$f56;!<~M +,C#	J )  *!)*s   	$G%%G32G3c                  | j                  |      }|j                  d|f       |j                  d|f      j                         }|y|j                  d|f      j                         }|t        |d   xs d      nd}|sy	 t	        j
                  |      }t	        j
                  |      }	| j                  ||      }| j                  ||      }| j                  |||      }t        |d	   t              r|d	   ni }t        |d	   t              r|d	   ni }t        |      t        |      z  t        |      z  }t        t        |d
   xs d      t        |d
   xs d            }t        t        | j!                  |||fg                  }d}|D ]  }t        |t              r|j#                  |      nd }t        |t              r|j#                  |      nd }|j#                  |      }|xs
 |xs |xs i }|\  }}t        |j#                  d      xs |      }t        |j#                  d      xs |      }|j#                  d      }t        |xs i j#                  d      xs d      }t        |xs i j#                  d      xs d      }t        |xs i j#                  d      xs d      } ||z   | z
  }!|dk(  r|dk(  r| dk(  r:|j                  ddt%        j&                         j(                   |||||t        |      nd t        |      t        |      |||t+        |d      t+        |d      t+        | d      t+        |!d      ||f       |dz  } |S # t        $ r}
t        d      |
d }
~
ww xY w)Nzt
            DELETE FROM tenant_inventory_estimated_consumptions
            WHERE consumption_date = ?
            a  
            SELECT 1
            FROM tenant_inventory_sessions AS current_sessions
            WHERE current_sessions.inventory_date = ?
              AND EXISTS (
                SELECT 1
                FROM tenant_inventory_sessions AS previous_sessions
                WHERE previous_sessions.warehouse_id = current_sessions.warehouse_id
                  AND previous_sessions.inventory_date < current_sessions.inventory_date
              )
            LIMIT 1
            r   z
            SELECT MAX(inventory_date) AS inventory_date
            FROM tenant_inventory_sessions
            WHERE inventory_date < ?
            r`  ri   z;Non riesco a leggere correttamente le date degli inventari.)r2  r3  r  r  rS  rT  rA  rv  a  
                INSERT INTO tenant_inventory_estimated_consumptions (
                    id,
                    consumption_date,
                    period_start_date,
                    period_end_date,
                    period_days,
                    product_id,
                    product_lookup,
                    supplier_lookup,
                    product_name,
                    supplier_name,
                    warehouse_count,
                    opening_units,
                    incoming_units,
                    closing_units,
                    consumed_units,
                    created_at,
                    updated_at
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                 inventory_estimated_consumption_r  rm   )r  rO  r  rj   r   r  r>   r  r  r   rt   r   r  r   r   rq   r4  r   r  r  r   r  )"r>  rR  r`  r  normalized_inventory_datehas_followup_inventory_rowprevious_date_rowprevious_inventory_dater  r  r  opening_periodclosing_periodr  r  r  r  r  r  written_countr  r  r  r  sourcer  r  rS  rT  rA  r  r  r  r  s"                                     r7   1_refresh_inventory_estimated_consumption_for_datez=TenantStore._refresh_inventory_estimated_consumption_for_date  s    %)$B$B>$R! '(	
 &0%7%7 '(&
 (* 	# &-&..
 '(
 (* 	 UfTq#&78H&I&OR"Pwy&	e#'#5#56M#N !%!3!34M!N KK#
 KK%
 TT.. U 
 2<N7<SUY1ZnW-`b1;N7<SUY1ZnW-`b$s;'77#l:KK	016Q7016Q7
 @@-/HIJ
  9	C.8d.Kkooc*QUG.8d.Kkooc*QUG#'',H9989rF.1+NOvzz.9K^LL

? ; NOML1J!7=b"5"56N"O"TSTUM"HN#7#78P#Q#VUVWN!7=b"5"56N"O"TSTUM*^;mKN!n&9mq>P, 7tzz|7G7G6HI-+-'1'=C
O4'( !#-+.!,-+.!,#-)T QMs9	t w  	eZ[add	es   *M! !	M;*M66M;c                   |j                  d|f      j                         }|y| j                  |t        |d   xs d      |      S )Nz
            SELECT inventory_date
            FROM tenant_inventory_sessions
            WHERE inventory_date > ?
            GROUP BY inventory_date
            ORDER BY inventory_date ASC
            LIMIT 1
            r   r`  ri   r   )rO  r  r  rj   )r>  rR  r`  r  next_date_rows        r7   8_refresh_next_inventory_estimated_consumption_after_datezDTenantStore._refresh_next_inventory_estimated_consumption_after_date  sj     #** 

 (* 	  EE./526 F 
 	
r6   c                   |xs
 t               }|j                  d      j                         }|D ]&  }| j                  |t	        |d   xs d      |       ( | j                  |       y )Nz
            SELECT inventory_date
            FROM tenant_inventory_sessions
            GROUP BY inventory_date
            ORDER BY inventory_date ASC
            r`  ri   r   )r;   rO  rh  r  rj   0_refresh_inventory_daily_consumption_period_days)r>  rR  r  r  	date_rowsrl  s         r7   r2  z9TenantStore._refresh_all_inventory_estimated_consumptions9  s     ,(*
&&
 (* 	  	CBBC()/R0$ C 	 	==jIr6   c           
     6   |j                  d      j                         }| j                  |      }|D ]d  }t        |d   xs d      }t        |d   xs d      }t	        t        | j                  |||fg|                  }|j                  d|||f       f y )Nz
            SELECT DISTINCT period_start_date, period_end_date
            FROM tenant_inventory_daily_consumptions
            WHERE period_start_date <> ''
              AND period_end_date <> ''
            r.  ri   r/  r0  z
                UPDATE tenant_inventory_daily_consumptions
                SET period_days = ?
                WHERE period_start_date = ?
                  AND period_end_date = ?
                )rO  rh  r3  rj   r   rq   r4  )r>  rR  r5  r?  rl  r.  r/  r  s           r7   r  z<TenantStore._refresh_inventory_daily_consumption_period_daysP  s     ((
 (* 	 55jA 	C #C(;$<$B C!#&7"8">B?ODD"+_=>!) E K  /A	r6   c                F    |j                  d||f      j                         S )Nz
            SELECT *
            FROM tenant_inventory_sessions
            WHERE warehouse_id = ? AND inventory_date <= ?
            ORDER BY inventory_date DESC, created_at DESC
            LIMIT 1
            rq  )r>  rR  rf  r`  s       r7   r  z:TenantStore._read_inventory_effective_session_row_for_datep  s/     !! >*	
 (*		r6   c               |   |j                  d      j                         }g }i }|D ]  }t        |d   xs d      }| j                  |||      }|,| j	                  |      }	|j                  |	       | j                  |t        |d               \  }
}|
D ]v  }t        |d         }t        |d   xs d      t        |d   xs d      f}|j                  |      }|dt        j                         j                   |d   t        |d         nd t        |d   xs d      t        |d	   xs d      d
i t        |d   xs d      t        |d   xs d      d}|||<   t        t        |d         t        |d   xs d      z   d      |d<   |d   }|j                  |g       D ]c  }t        |d   xs d      }t        |      }|j                  |      }|dt        j                         j                   |d
|d   t        |d         nd |j                  d      d
t        |d   xs	 |d   xs d      t        |d   xs	 |d   xs d      d}|||<   t        t        |d         t        |d   xs d      z   d      |d<   t        t        |d         t        |d   xs d      z   d      |d<   |d   |d   t        |d         |d<   |j                  d      =|j                  d      P|j                  d      |d<   f y  g }|j                         D ]  }t!        |j#                  d      j                               }|j%                  d        |j                  t        |d         |d   t        |d         t        |d	         t        |d         t'        |      |t        |d         t        |d         d	        |j%                  d        |j%                  d        |d| |t'        |      t        t)        d |D              d      t'        |      t'        |      |d S )!Nz
            SELECT *
            FROM tenant_inventory_warehouses
            ORDER BY lower(name) ASC, created_at ASC
            r  ri   r  r  inventory_total_item_rA  rS  rT  r   r  r  )r  rA  rS  rT  rv  lots_mapr  r  rv  r   r  r  rG  inventory_total_lot_rK  r]  r\  rB  r  c                :    t        | d         j                         S )NrG  rj   rP   )lots    r7   r"  zETenantStore._build_inventory_totals_detail_for_date.<locals>.<lambda>  s    c#j/&:&@&@&B r6   r8  r  c                r    t        | d         j                         t        | d         j                         fS )NrT  rS  r  r7  s    r7   r"  zETenantStore._build_inventory_totals_detail_for_date.<locals>.<lambda>  s2    So)>%?%E%E%GTR`MaIbIhIhIj$k r6   c                V    t        | d         j                         t        | d         fS )Nr  rf  r  r7  s    r7   r"  zETenantStore._build_inventory_totals_detail_for_date.<locals>.<lambda>  s,    s48H3I/J/P/P/RTWX\]kXlTm.n r6   zTotale locale al c              3  8   K   | ]  }t        |d            ywrv  Nr  r  r   s     r7   r  zFTenantStore._build_inventory_totals_detail_for_date.<locals>.<genexpr>       /hZ^d;S6T0U/h   )r`  r  r  r  rv  r  covered_warehousessource_sessions)rO  rh  rj   r  r  rs   rj  r   r  r  r   r   r  r   r[   r   r   popr&  rq   r  )r>  rR  r`  r  r  aggregated_itemsr  rf  r  serialized_sessionsession_item_rowssession_lots_by_itemitem_rowr  item_keyaggregated_itemr  lot_rowrG  r  aggregated_lotr  r  s                          r7   '_build_inventory_totals_detail_for_datez3TenantStore._build_inventory_totals_detail_for_date  s    $++
 (* 	 46EG+ @	iM}T28b9LMMjZfhvwK"!%!F!F{!S""#566:6X6XYcehituyize{6|33- 6ihtn-!128b9!239r: #3"6"6x"@"* 5djjl6F6F5GHEMlE[Egc(<*@&Amq(+H^,D,J(K),Xo-F-L")M25$&&)+l*C*Ir&J&)+l*C*Ir&J	'O 2A$X.<A/*BCDuXVnMoMtstGuu= 89
 +:6377D iG"7:#6#<"=H!28!<J%-\\*%=N%-$89I9I8J"K(0(+RYZjRkRweG<L4M.N  ~B6=kkBZ6[03*-gl.C.f{S_G`.fdf*g*-gl.C.f{S_G`.fdf*g	* 0>,16nZ89E'*BUBZYZ<[[2N:. :?n-?@AE'RdJeJjijDkk:N#56 &&67?GL\D]Di;@IYAZ;['78%))*BCKPWP[P[\tPu  QBCJ;;OgCh'?@9i56i@	iD *,/668 	O++J7>>@ADIIBICLLod34"1,"?$'(G$H%()I%J.3OD\4].^!$T "%ol&C"D"%ol&C"D
	" 	

k
l!no -((89!%j&+C/hbg/h,hjk&l #N 3"%o"6.	
 		
r6   c                   | j                  |      st        d      | j                  |j                        5 }|j	                  d      j                         }g }|D ]u  }t        |d   xs d      }|s| j                  ||      }|j                  |t        |d         t        |d         t        |d         t        |d	         |d
   d       w 	 d d d        t        |      dS # 1 sw Y   xY w)N-Il tuo account non puo accedere a Inventario.z
                SELECT DISTINCT inventory_date
                FROM tenant_inventory_sessions
                ORDER BY inventory_date DESC
                r`  ri   r`  r  r  r  rv  r  )r`  r  r  r  rv  r  )r  total_count)rQ  r>   r  r  rO  rh  rj   r  rs   r   r   rq   )r>  r  rR  r  r  date_rowr`  details           r7   list_inventory_totals_historyz)TenantStore.list_inventory_totals_history  s   ))'2LMM**7+@+@A 	Z"** hj  02G% !$X.>%?%E2!F%EEjaoEp*8!$VG_!5,/7I0J,K.1&9M2N.O27?W8X2Y+12C+D		4 #3w<@@5	 	s   BC++C4c               @   | j                  |      st        d      | j                  |      }| j                  |j                        5 }|j                  d|f      j                         }|t        d      | j                  ||      cd d d        S # 1 sw Y   y xY w)Nr  z
                SELECT 1
                FROM tenant_inventory_sessions
                WHERE inventory_date = ?
                LIMIT 1
                z+Storico totale non trovato per questa data.r  )	rQ  r>   r  r  r  rO  r  r  r  )r>  r  r`  normalized_daterR  existing_rows         r7   get_inventory_totals_detailz'TenantStore.get_inventory_totals_detail  s     ))'2LMM88H**7+@+@A 	lZ%-- !" hj  #LMM??
[j?k	l 	l 	ls   	ABBc                  |y| j                  |      }|gt        |xs d      j                         }|syt        j                  t        j                  |      t        j                  t        j                        }|j                  d|f      j                         }d}|D ]3  }| j                  |d         }	|	|	|k  r|t        |d   xs d      z  }5 t        |d      S )	Nr   ri   r  z
            SELECT occurred_at, equivalent_units
            FROM tenant_inventory_movements
            WHERE warehouse_id = ? AND movement_kind = 'in'
            ORDER BY occurred_at ASC, created_at ASC
            rh  r  r   r  )r  rj   rQ   r   r  r   r  r   r   r
   r4   rO  rh  r   r  )
r>  rR  rf  start_valuestart_dtr   r+  r   rl  rh  s
             r7   _sum_inventory_movements_sincez*TenantStore._sum_inventory_movements_since,  s     11+>K-2.446I''(:(:9(EtxxX`XdXdeH!! O
 (* 	  	9C88]9KLK"kH&<U3127a88E		9
 UAr6   c               ^   |D ci c]1  }t        t        |d               t        t        |d               f|3 }}|D ci c]1  }t        t        |d               t        t        |d               f|3 }}t        |      t        |      z  }g }|D ]  }|j                  |      }	|j                  |      }
t        |	xs |
xs i j                  d      xs d      }t        |	xs |
xs i j                  d      xs d      }t	        |	xs i j                  d      xs d      }t	        |
xs i j                  d      xs d      }||z
  }|dk(  r|dk(  r|j                  ||t        |d      t        |d      t        |d      d        |j                  d 	       |S c c}w c c}w )
NrS  rT  ri   rv  r   r  )rS  rT  theoretical_units
real_unitsdelta_unitsc                    t        t        | d                t        | d         j                         t        | d         j                         fS )Nr  rT  rS  )r  r   rj   rP   r7  s    r7   r"  z>TenantStore._build_inventory_item_comparison.<locals>.<lambda>r  sJ    U4./00D)*002D()//1 r6   r8  )r[   rj   r   r   r   rs   r  r&  )r>  current_items
real_itemsr   current_mapreal_mapr  r+  r  current_item	real_itemrS  rT  r	  r
  r  s                   r7    _build_inventory_item_comparisonz,TenantStore._build_inventory_item_comparisonM  s    &
 s4#789;LSQUVeQfMg;hikoo
 
 #
 s4#789;LSQUVeQfMg;hikoo
 
 ;#h-/(* 	C&??3/L S)I ?	 ?RDD^TZXZ[L!@!@b E Eo V \Z\]M %|'9r&>&>?W&X&]\] ^	R445MNSRSTJ+j8K A%*/KK$0%2)./@!)D"'
A"6#(a#8	( 			 	 	
 K

s   6F%6F*c           
        | j                  |      st        d      | j                  |j                        5 }|j	                  d      j                         }g }|D ]  }t        |d         }| j                  ||      }|| j                  |      nd }|j	                  d|f      j                         }	| j                  |      }
t        |d   xs d      |
d<   |t        |d   xs d      nd|
d	<   |t        |d
         nd |
d<   ||
d<   |	t        |	d   xs d      nd|
d<   | j                  ||||d   nd       |
d<   |j                  |
        	 d d d        t        |      t!        d |D              t!        d |D              t!        d |D              t!        d |D              t!        d |D              dS # 1 sw Y   lxY w)N7Il tuo account non puo accedere a Inventario o Consumi.a  
                SELECT
                    warehouses.*,
                    COUNT(items.id) AS product_count,
                    COALESCE(SUM(items.total_equivalent_units), 0) AS total_equivalent_units
                FROM tenant_inventory_warehouses AS warehouses
                LEFT JOIN tenant_inventory_stock_items AS items
                    ON items.warehouse_id = warehouses.id
                GROUP BY warehouses.id
                ORDER BY LOWER(warehouses.name) ASC, warehouses.created_at ASC
                r  zNSELECT COUNT(*) AS total FROM tenant_inventory_sessions WHERE warehouse_id = ?rv  r   "theoretical_total_equivalent_unitsr   real_total_equivalent_unitsr`  latest_inventory_datelatest_inventoryr   inventory_session_countr  r  0incoming_since_latest_inventory_equivalent_unitsc              3  8   K   | ]  }t        |d            yw)ru  Nr   r  s     r7   r  z8TenantStore.list_inventory_warehouses.<locals>.<genexpr>  s     !T#d?&;"<!Tr  c              3  8   K   | ]  }t        |d            ywr  r  r  s     r7   r  z8TenantStore.list_inventory_warehouses.<locals>.<genexpr>  s     )gTX%5M0N*O)gr  c              3  8   K   | ]  }t        |d            yw)r  Nr  r  s     r7   r  z8TenantStore.list_inventory_warehouses.<locals>.<genexpr>  s     5lpeDAe<f6g5r  c              3  8   K   | ]  }t        |d            yw)r  Nr  r  s     r7   r  z8TenantStore.list_inventory_warehouses.<locals>.<genexpr>  s     .q^buT:W5X/Y.qr  c              3  8   K   | ]  }t        |d            yw)r  Nr  r  s     r7   r  z8TenantStore.list_inventory_warehouses.<locals>.<genexpr>  s     *gTX3t4M/N+O*gr  )
warehousesr  r  rv  r  r  r  )rU  r>   r  r  rO  rh  rj   rN  r  r  rx  r   r   r  rs   rq   r  r>  r  rR  r+  r#  rl  rf  latest_session_rowr  session_count_rowwarehouse_summarys              r7   list_inventory_warehousesz%TenantStore.list_inventory_warehousesz  sP   ??HVWW**7+@+@A /	5Z%%
 hj  35J 5"3t9~%)%L%LZYe%f" *5 99:LM !
 %/$6$6d!O% (* " %)$K$KC$P!JOPSTlPmPrqrJs!"FG *5 ,-EFK!L ""?@ *5 *+;<= ""9:
 9I!"45gx  hEsCTU\C]Cbab?c  KL!";<X\X{X{DVDb 2< @hl Y| Y!"TU
 !!"34?5!/	5d %":!!T!TT&))g\f)g&g255t~52+..qfp.q+q'**g\f*g'g
 	
c/	5 /	5s   D GGc           	        | j                  |      st        d      |j                  j                         }t	        |      dk  rt        d      dt        j                         j                   }t               }| j                  5  | j                  |j                        5 }|j                  d|f      j                         }|t        d      |j                  d||||f       |j                          | j                  ||      }d d d        d d d        d| j!                        iS # 1 sw Y   $xY w# 1 sw Y   (xY w)	N<Solo il superadmin puo modificare i magazzini di Inventario.r  Dammi un nome magazzino valido.inventory_wh_zOSELECT id FROM tenant_inventory_warehouses WHERE lower(name) = lower(?) LIMIT 1(Esiste gia un magazzino con questo nome.z
                    INSERT INTO tenant_inventory_warehouses (id, name, created_at, updated_at)
                    VALUES (?, ?, ?, ?)
                    	warehouse)rY  r>   r'  rQ   rq   r  r  r   r;   r<  r  r  rO  r  rk  r@  rx  	r>  r  r  r'  rf  r  rR  r  rl  s	            r7   create_inventory_warehousez&TenantStore.create_inventory_warehouse  sG   
 44W=[\\||!!#t9q=>??&tzz|'7'7&89J	ZZ 	S..w/D/DE S)11eG  (*   +$%OPP"" "4I> !!#88\RS	S$ TDDSIJJ#S S	S 	S%   D9#A'D-
D9-D6	2D99Ec                   | j                  |      st        d      |j                  j                         }t	        |      dk  rt        d      t               }| j                  5  | j                  |j                        5 }| j                  ||      }|j                  d||f      j                         }|t        d      t        |d   xs d      j                         }	|	|k7  r%|j                  d|||f       |j                          | j                  ||      }
d d d        d d d        d	| j                  
      iS # 1 sw Y   $xY w# 1 sw Y   (xY w)
Nr*  r  r+  z[SELECT id FROM tenant_inventory_warehouses WHERE lower(name) = lower(?) AND id != ? LIMIT 1r-  r'  ri   z
                        UPDATE tenant_inventory_warehouses
                        SET name = ?,
                            updated_at = ?
                        WHERE id = ?
                        r.  )rY  r>   r'  rQ   rq   r;   r<  r  r  r@  rO  r  rj   rk  rx  r>  r  rf  r  r'  r  rR  r  r  current_nameupdated_rows              r7   update_inventory_warehousez&TenantStore.update_inventory_warehouse  sf    44W=[\\||!!#t9q=>??J	ZZ 	[..w/D/DE [ $ B B:| \)11q<(  (*   +$%OPP"=#8#>B?EEG4'&& y,7 %%'"@@\Z)[	[. TDD[QRR-[ [	[ 	[%   &EBE EE	EEc                r   | j                  |      st        d      | j                  5  | j                  |j                        5 }| j                  ||      }| j                  |      }|j                  d|f       |j                          d d d        d d d        ddS # 1 sw Y   xY w# 1 sw Y   xY w)Nr*  z4DELETE FROM tenant_inventory_warehouses WHERE id = ?Tdeletedr.  )	rY  r>   r<  r  r  r@  rx  rO  rk  r>  r  rf  rR  r  serialized_warehouses         r7   delete_inventory_warehousez&TenantStore.delete_inventory_warehouse  s    
 44W=[\\ZZ 	$..w/D/DE $ $ B B:| \'+'N'N}']$""J!O !!#$	$ -
 	
$ $	$ 	$$   B-AB!B-!B*	&B--B6c                   | j                  |      st        d      | j                  |j                        5 }| j	                  ||      }|j                  d|f      j                         }d d d        D cg c]  }| j                  |       }}| j                        |t        |      dS # 1 sw Y   ExY wc c}w )Nr  
                SELECT *
                FROM tenant_inventory_sessions
                WHERE warehouse_id = ?
                ORDER BY inventory_date DESC, created_at DESC
                r.  sessionsr  )
rQ  r>   r  r  r@  rO  rh  r  rx  rq   r>  r  rf  rR  r  r+  rl  rB  s           r7   !list_inventory_warehouse_sessionsz-TenantStore.list_inventory_warehouse_sessions  s    
 ))'2LMM**7+@+@A 
	Z >>z<XM%%  hj 
	 KOO3D99#>OO@@O x=
 	

	 
	 P   4B08B<0B9c                J   | j                  |      st        d      | j                  |j                        5 }| j	                  ||      }| j                  ||      }t        |d   xs d      |k7  rt        d      | j                  ||      \  }}d d d        D 	cg c]0  }	| j                  |	j                  t        |	d         g             2 }
}	| j                        | j                        |
t        |
      t        d |
D              dS # 1 sw Y   xY wc c}	w )Nr  rf  ri   9L'inventario richiesto non appartiene a questo magazzino.r  c              3  8   K   | ]  }t        |d            ywr  r  r  s     r7   r  zETenantStore.get_inventory_warehouse_session_detail.<locals>.<genexpr>M  s     )bTX%5M0N*O)br  )r.  r  r  r  rv  )rQ  r>   r  r  r@  rI  rj   rj  r  r   rx  r  rq   r  )r>  r  rf  rH  rR  r  r  r  ra  rl  r  s              r7   &get_inventory_warehouse_session_detailz2TenantStore.get_inventory_warehouse_session_detail4  s4    ))'2LMM**7+@+@A 	aZ >>z<XM:::zRK;~.4"5E !\]]&*&H&HU_&`#I|	a !
 44S,:J:J3sSWy>[]:^_
 

 @@O<<[I!%j&))b\a)b&b
 	
	a 	a
s   AD5D Dc                T   | j                  |      st        d      t               }| j                  5  | j	                  |j
                        5 }| j                  ||      }| j                  ||      }t        |d   xs d      |k7  rt        d      | j                  |      }| j                  |      }	| j                  ||      \  }
}|j                  d|f       |
D ]\  }dt        j                         j                   }|j                  d|||d   t!        |d         nd t        |d	   xs d      t        |d
   xs d      t        |d   xs d      t        |d   xs d      t#        |d   xs d      ||f
       |j%                  t        |d         g       D ]  }|j                  ddt        j                         j                   |t        |d   xs d      t'        t        |d   xs d            t#        |d   xs d      |d   t#        |d         nd t#        |d   xs d      ||f	        _ |j                  d||f       |j)                          d d d        d d d        | j+                  ||      }	|d<   |d<   |S # 1 sw Y   /xY w# 1 sw Y   3xY w)Nr  rf  ri   rG  ?DELETE FROM tenant_inventory_stock_items WHERE warehouse_id = ?inventory_item_?  
                        INSERT INTO tenant_inventory_stock_items (
                            id,
                            warehouse_id,
                            product_id,
                            product_name,
                            product_lookup,
                            supplier_name,
                            supplier_lookup,
                            total_equivalent_units,
                            created_at,
                            updated_at
                        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                        rA  rS  r  rT  r  rv  r   r  *  
                            INSERT INTO tenant_inventory_stock_lots (
                                id,
                                item_id,
                                lot_code,
                                lot_lookup,
                                quantity,
                                units_per_pack,
                                equivalent_units,
                                created_at,
                                updated_at
                            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
                            r  rG  rB  rK  r  
                    UPDATE tenant_inventory_warehouses
                    SET updated_at = ?
                    WHERE id = ?
                    loaded_sessionr.  )rQ  r>   r;   r<  r  r  r@  rI  rj   rx  r  rj  rO  r  r  r   r   r   r   r[   rk  get_inventory_warehouse_detail)r>  r  rf  rH  r  rR  r  r  r<  r  r  r  session_item_rowrm  r  r  s                   r7   +load_inventory_warehouse_session_into_stockz7TenantStore.load_inventory_warehouse_session_into_stockP  s    ))'2LMMJ	ZZ Q	$..w/D/DE P$ $ B B:| \">>z:V{>28b9\I$%`aa'+'N'N}']$%)%J%J;%W":>:\:\]gis:t7!#7""U!O
 ): 8$&5djjl6F6F5G$HM&& *(CST`CaCmC 0 >?sw 0 @ FBG 01A B HbI 0 A GRH 01B C IrJ!"23K"L"QPQR%%8 $8#;#;C@PQU@V<WY[#\ "**  #11A1A0B C - #GJ$7$=2 > 1#gj6I6OR2P Q %gj&9&>Q ?DKL\D]Dig.>&? @os %g.@&A&FQ G ) )
=8t ""
 - !!#aP$Q	$f 44WlK#5 2{kP$ P$Q	$ Q	$s$   JHJ$JJ	JJ'c                   | j                  |      st        d      t               }| j                  5  | j	                  |j
                        5 }| j                  ||       |j                  d|f       |j                  d||f       |j                          d d d        d d d        | j                  ||      S # 1 sw Y   #xY w# 1 sw Y   'xY w)Nr  rK  rO  )
rQ  r>   r;   r<  r  r  r@  rO  rk  rQ  )r>  r  rf  r  rR  s        r7   reset_inventory_warehouse_stockz+TenantStore.reset_inventory_warehouse_stock  s    
 ))'2LMMJ	ZZ 	$..w/D/DE $22:|L""U!O ""
 - !!#$	$" 227LII!$ $	$ 	$s$   CA
B;C;C	 CCc           
     X   | j                  |      st        d      t               }| j                  5  | j	                  |j
                        5 }| j                  ||      }| j                  ||      }t        |d   xs d      |k7  rt        d      | j                  |      }| j                  |      }	|j                  d|f       |j                  d|t        |d   xs d      f       | j                  ||t        |d   xs d      |       | j                  |t        |d   xs d      |       | j                  |t        |d   xs d      |       |j                  d	||f       |j                          d d d        d d d        d
	dS # 1 sw Y   xY w# 1 sw Y   xY w)Nr  rf  ri   rG  2DELETE FROM tenant_inventory_sessions WHERE id = ?z
                    DELETE FROM tenant_inventory_daily_consumptions
                    WHERE warehouse_id = ? AND consumption_date = ?
                    r`  r   rO  Tr:  r.  r  )rQ  r>   r;   r<  r  r  r@  rI  rj   rx  r  rO  r  r  r  rk  
r>  r  rf  rH  r  rR  r  r  r<  r  s
             r7   "delete_inventory_warehouse_sessionz.TenantStore.delete_inventory_warehouse_session  s    ))'2LMMJ	ZZ +	$..w/D/DE *$ $ B B:| \">>z:V{>28b9\I$%`aa'+'N'N}']$%)%J%J;%W"""HM "" "3{3C'D'J#KL II $45;<'	 J  FF$45;<' G 
 MM$45;<' N 
 ""
 - !!#U*$+	$\ -)
 	
Y*$ *$+	$ +	$s$   F D/F>F F	F  F)c                  | j                  ||      }|j                  xs |j                  xs |j                  xs dj	                         }dt        j                         j                   }d}	|j                  d||f      j                         }
|
!d}	|j                  dt        |
d         f       | j                  ||      \  }}t        d |D              }|j                  d	||t        |d
   xs d      |d|d
    d| |j                  |t        |      |||f       |D ]\  }dt        j                         j                   }|j                  d|||d   t        |d         nd t        |d   xs d      t        |d   xs d      t        |d   xs d      t        |d   xs d      t!        |d   xs d      ||f
       |j#                  t        |d         g       D ]  }|j                  ddt        j                         j                   |t        |d   xs d      t%        t        |d   xs d            t!        |d   xs d      |d   t!        |d         nd t!        |d   xs d      ||f	        _ | j'                  ||      }| j)                  ||||       | j+                  ||||       | j-                  |||       | j/                  |||       ||	fS )NUtente localeinventory_session_Fz
            SELECT id
            FROM tenant_inventory_sessions
            WHERE warehouse_id = ? AND inventory_date = ?
            LIMIT 1
            TrW  r  c              3  @   K   | ]  }t        |d    xs d        ywrv  r   Nr  r  s     r7   r  zKTenantStore._save_inventory_warehouse_session_from_stock.<locals>.<genexpr>*  s!     $dSVU3/G+H+MA%N$dr  a  
            INSERT INTO tenant_inventory_sessions (
                id,
                warehouse_id,
                warehouse_name,
                inventory_date,
                label,
                created_by_user_id,
                created_by_name,
                total_products,
                total_equivalent_units,
                created_at,
                updated_at
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            r'  ri   zInventario  del inventory_session_item_a  
                INSERT INTO tenant_inventory_session_items (
                    id,
                    session_id,
                    product_id,
                    product_name,
                    product_lookup,
                    supplier_name,
                    supplier_lookup,
                    total_equivalent_units,
                    created_at,
                    updated_at
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                rA  rS  r  rT  r  rv  r   a  
                    INSERT INTO tenant_inventory_session_lots (
                        id,
                        session_item_id,
                        lot_code,
                        lot_lookup,
                        quantity,
                        units_per_pack,
                        equivalent_units,
                        created_at,
                        updated_at
                    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
                    inventory_session_lot_rG  rB  rK  r  r   )r@  r  r  r  rQ   r  r  r   rO  r  rj   rc  r  r1  rq   r   r   r   r[   rI  r  r  r  r  )r>  rR  r  rf  r`  r  r  
actor_namerH  replaced_existingr  r  ra  rv  r  ri  r  saved_session_rows                     r7   ,_save_inventory_warehouse_session_from_stockz8TenantStore._save_inventory_warehouse_session_from_stock  s    :::|T''d7+;+;dw?Q?QdUdkkm
)$**,*:*:);<
!!)) >*
 (* 	 # $D\$'(*
 #'"B"B:|"\	<!$$dZc$d!d  M&)/R0mF34E.9IJI&!	
> " 8	H 7

8H8H7IJO $3;L3I3UC./[_06B7!128b917R8!239r:(#;<AB8 (++C,?D "" 11A1A0BC'GJ/526)#gj.A.GR*HIgj16Q7<CDT<U<ag&678gkg&89>Q?!!
=8	t !<<ZT==	 	> 	
 	AA	 	B 	
 	>> 	? 	

 	EE 	F 	

 !"333r6   c                  |j                  d|||f      j                         }|j                  d|||f      j                         }||t        |d         S d S |j                  d|||t        |d   xs d      f      j                         }||t        |d         S d S |dt        j                         j
                   }	|j                  d|	||d	   t        |d
   xs d      t        |d   xs d      t        |d   xs d      t        |d   xs d      t        |d   xs d      ||f
       nt        |d         }	|j                  d|d	   t        |d
   xs d      t        |d   xs d      t        |d   xs d      t        |d   xs d      t        |d   xs d      ||	f       |j                  d|	f       |j                  dt        |d         f      j                         }
|
D ]  }|j                  ddt        j                         j
                   |	t        |d   xs d      t        |d   xs d      t        |d   xs d      |d   t        |d         nd t        |d   xs d      ||f	        |	S )Nz
            SELECT *
            FROM tenant_inventory_stock_items
            WHERE warehouse_id = ? AND product_lookup = ? AND supplier_lookup = ?
            LIMIT 1
            a   
            SELECT
                session_items.*,
                sessions.created_at AS session_created_at
            FROM tenant_inventory_session_items AS session_items
            JOIN tenant_inventory_sessions AS sessions
                ON sessions.id = session_items.session_id
            WHERE sessions.warehouse_id = ?
              AND session_items.product_lookup = ?
              AND session_items.supplier_lookup = ?
            ORDER BY sessions.inventory_date DESC, sessions.created_at DESC
            LIMIT 1
            r  z
            SELECT 1
            FROM tenant_inventory_movements
            WHERE warehouse_id = ?
              AND product_lookup = ?
              AND supplier_lookup = ?
              AND created_at > ?
            LIMIT 1
            session_created_atri   rL  a  
                INSERT INTO tenant_inventory_stock_items (
                    id,
                    warehouse_id,
                    product_id,
                    product_name,
                    product_lookup,
                    supplier_name,
                    supplier_lookup,
                    total_equivalent_units,
                    created_at,
                    updated_at
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                rA  rS  r  rT  r  rv  r   a  
                UPDATE tenant_inventory_stock_items
                SET
                    product_id = ?,
                    product_name = ?,
                    product_lookup = ?,
                    supplier_name = ?,
                    supplier_lookup = ?,
                    total_equivalent_units = ?,
                    updated_at = ?
                WHERE id = ?
                9DELETE FROM tenant_inventory_stock_lots WHERE item_id = ?z
            SELECT *
            FROM tenant_inventory_session_lots
            WHERE session_item_id = ?
            ORDER BY lot_code ASC
            a  
                INSERT INTO tenant_inventory_stock_lots (
                    id,
                    item_id,
                    lot_code,
                    lot_lookup,
                    quantity,
                    units_per_pack,
                    equivalent_units,
                    created_at,
                    updated_at
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
                r  rG  r  rB  rK  r  )rO  r  rj   r  r  r   r   rh  )r>  rR  rf  r  r  r  r  latest_session_item_rowmovement_after_sessionrestored_item_idr  r  s               r7   1_restore_inventory_stock_item_from_latest_sessionz=TenantStore._restore_inventory_stock_item_from_latest_session  s8    ")) >?;
 (* 	 #-"4"4 >?;#
 (* 	   #*.:.F3|D)*PDP!+!3!3 +,@AGRH	"
  (*! 	" "-.:.F3|D)*PDP!01A1A0BC % +L9/?E2F/0@AGRH/@FBG/0ABHbI12JKPqQ:  #<#56 ,L9/?E2F/0@AGRH/@FBG/0ABHbI12JKPqQ$	0 Z]m\op%% (./1
 (* 	   	G %TZZ\%5%5$67$
+1r2-34'*-238?@P8Q8]E'"234cg'"45:;
	6  r6   c           	        | j                  |      st        d      | j                  |j                        }t	               }| j
                  5  | j                  |j                        5 }| j                  |||||      \  }}|j                          d d d        d d d        | j                        dS # 1 sw Y   %xY w# 1 sw Y   )xY w)Nr  r`  r  r  rd  )rQ  r>   r  r`  r;   r<  r  r  rf  rk  r  )	r>  r  rf  r  r`  r  rR  re  rd  s	            r7   "create_inventory_warehouse_sessionz.TenantStore.create_inventory_warehouse_session7  s     ))'2LMM778N8NOJ	ZZ 		$..w/D/DE $7;7h7h #1' 8i 84!#4 !!#$		$ <<=NO!2
 	
$ $		$ 		$s$   C**B8C8C	=CCc           
        |g S t        |t              rt        |dd       n"t        |t              r|j	                  d      nd }g }|xs g D ]  }t        |t              rt        |dd       n"t        |t              r|j	                  d      nd }t        |t              rt        |dd       n"t        |t              r|j	                  d      nd }t        |t              rt        |dd       n"t        |t              r|j	                  d      nd }	 |r:t        j                  t        |      j                               j                         nd }|r:t        j                  t        |      j                               j                         nd }	|r|	r||	kD  rt        d      t        |xs g D ch c]j  }t        |t              s6t        |      j                         j                  d      j                         r"dt        |      cxk  rd	k  rn nt        |      l c}      }|st        t!        d
            }|j#                  ||	|d        |S # t        $ r}
t        d      |
d }
~
ww xY wc c}w )Nr  r2  r3  r  z6Date calendario non valide. Usa il formato YYYY-MM-DD.zKLa data iniziale del calendario non puo essere successiva alla data finale.rH   r   r  r  )r2  r3  r  )r   r   getattrrt   r   r   r  rj   rQ   r:   r>   sortedr   lstriprp   r   rangers   )r>  calendar_payload	raw_rulesr  raw_ruler  r  raw_weekdaysr2  r3  r  r   r  s                r7   "_normalize_homemade_calendar_rulesz.TenantStore._normalize_homemade_calendar_rulesS  sF    #I *I6 $gt42<=Mt2T!%%g.Z^ 	
 *,!R (	H h	2 ,53=h3MX\\,/SW  h	2 *d31;Hd1KX\\*-QU  h	2 *d31;Hd1KX\\*-QU 
d\jT//N0C0I0I0KLVVXpt
Xd4--c,.?.E.E.GHRRTjn h:+@ !noo !- 2!$,D	0A0H0H0M0U0U0WCI** IH a>LL", ( (E(	R -  d !YZ`ccds    A<I/A/I2
	I/I**I/c                2   t        |xs d      j                         }|sg S 	 t        j                  |      }t        |t              r| j                  |      S t        |t              r| j                  d|i      S g S # t        j                  $ r g cY S w xY w)Nri   r  )	rj   rQ   r   r   r   r   rt   r{  r   )r>  rT   r   decodeds       r7   "_load_homemade_calendar_rules_jsonz.TenantStore._load_homemade_calendar_rules_json  s    $**,	I	jj+G gt$::7CCgt$::GW;MNN	 ## 	I	s   A> >BBc                   t        |      }t        |j                  d      t              r1t	        |j                  d      xs i j                  d      xs g       ng }t        |j                  d      t              r1t	        |j                  d      xs i j                  d      xs g       ng }|dk(  r|S |dk(  r|S g ||S )Nr  r  r  r,   r.   )rc   r   r   rt   r   )r>  r?  ry  normalized_scoper  restaurant_ruless         r7   r  z.TenantStore._homemade_calendar_rules_for_scope  s    
 ;;GU_`h`l`lm{`|  C  VDD(,,~6<"AA'JPbQ  JL	 (,,'<=tD (,,45;@@IORP 	
 u$|+##..-..r6   c                   t        |j                  d      xs d      j                         }t        |j                  d      xs d      j                         }|j                  d      xs g D ch c]%  }dt        |      cxk  rdk  sn nt        |      ' }}|r|j	                         |k  ry|r|j	                         |kD  ry|j                         |xs t        t        d            v S c c}w )	Nr2  ri   r3  r  r   r  Fr  )rj   r   rQ   r   r:   weekdayr   rv  )r>  target_dater  r2  r3  r   r  s          r7   r}  z+TenantStore._homemade_calendar_rule_matches  s    ,/526<<>
txx
+1r288:*.((:*>*D"\$SQUYH[Z[H[CI\\+//1J>--/(:""$)BSq]CC ]s   0C0
C0c               T   	  j                  ||      }|r|r|s|dfS 	 t        j                  |      	t        j                  |      }	|kD  r|dfS d}	|k  r/t	        	 fd|D              r|dz  }	t        d      z  		|k  r/t        ||      dfS # t        $ r |dfcY S w xY w)Nmovement_daysr   c              3  B   K   | ]  }j                  |        y wr2   r|  r~  s     r7   r  z?TenantStore._count_homemade_operational_days.<locals>.<genexpr>  s     Y4477FYr  rm   rw  operational_calendar)r  r   r  r>   r  r	   r  )
r>  r2  r3  r?  ry  fallback_daysr  r  countr  s
   `        @r7    _count_homemade_operational_daysz,TenantStore._count_homemade_operational_days  s     77+NJh /11	2((4G%%h/D T> /11oYSXYY
ya((G o 5-(*@@@  	2 /11	2s   *B B'&B'c                
   |j                  d      j                         }|| j                  |d         ng }|| j                  |d         ng }|t        |d   xs d      ndd|id|i|t	        |d   xs d	      d
S d d
S )Nz
            SELECT *
            FROM tenant_homemade_stock_settings
            WHERE id = 'default'
            LIMIT 1
            r]  r^  r  r   r   r  r  ri   )r  r  r  r  )rO  r  r~  r   rj   )r>  rR  rl  r  r  s        r7   r3  z)TenantStore._read_homemade_stock_settings  s      
 (* 	 ]`\kt>>sCV?WXqsX[XgD33C8R4STmo 	 LO?%,@(A(FQ"G`c$l3$+-@#A:=/#c,/526	
 	
 PT	
 	
r6   c       	        V   t        t        |      t        |      z
  d      }t        |      dk  ry |j                  xs |j                  xs |j
                  xs dj                         }|j                  ddt        j                         j                   t        |d   xs d      t        |d   xs d      |||t        t        t        |      d      t        t        |      d      |t        t        |d	      d      t        t        | d	      d      |	|
|j                  ||
f       y )
Nr  r  r\  a  
            INSERT INTO tenant_homemade_stock_movements (
                id,
                warehouse_id,
                warehouse_name,
                recipe_id,
                recipe_name,
                recipe_lookup,
                measurement_unit,
                quantity_before,
                quantity_after,
                delta_quantity,
                added_quantity,
                consumed_quantity,
                movement_type,
                occurred_at,
                created_by_user_id,
                created_by_name,
                created_at
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            homemade_stock_move_r  ri   r'  r   )r  r   r  r  r  r  rQ   rO  r  r  r   rj   ri  r  r1  )r>  rR  r  r  r  r~  recipe_lookupquantity_beforequantity_aftermovement_typer  delta_quantityrc  s                r7   _record_homemade_stock_movementz+TenantStore._record_homemade_stock_movement  s    u^4u_7MMqQ~)''d7+;+;dw?Q?QdUdkkm
, 'tzz|'7'7&89M$'-2.M&)/R0#eO,a0eN+Q/c.#.2c>/3/3#-)	
r6   c           	        | j                  |      st        d      | j                  |j                        5 }|j	                  d      j                         }g }|D ]  }t        |d   xs d      }| j                  ||      }|| j                  |      nd }|j	                  d|f      j                         }	| j                  |      }
|t        |d   xs d      nd |
d<   ||
d<   |	t        |	d	   xs d
      nd
|
d<   |j                  |
        	 d d d        t        |      t        d |D              t        t        d |D              d      dS # 1 sw Y   CxY w)N+Il tuo account non puo accedere a Homemade.a^  
                SELECT
                    warehouses.*,
                    (
                        SELECT COUNT(*)
                        FROM tenant_homemade_recipes
                    ) AS recipe_count,
                    COALESCE(SUM(items.quantity), 0) AS total_quantity
                FROM tenant_homemade_stock_warehouses AS warehouses
                LEFT JOIN tenant_homemade_stock_items AS items
                    ON items.warehouse_id = warehouses.id
                GROUP BY warehouses.id
                ORDER BY lower(warehouses.name) ASC, warehouses.created_at ASC
                r  ri   zSSELECT COUNT(*) AS total FROM tenant_homemade_stock_sessions WHERE warehouse_id = ?r`  r  r  r   r   r  c              3  8   K   | ]  }t        |d            yw)rz  Nr  r  s     r7   r  z=TenantStore.list_homemade_stock_warehouses.<locals>.<genexpr>P  s      RtT.%9!: Rr  c              3  8   K   | ]  }t        |d            ywr{  Nr  r  s     r7   r  z=TenantStore.list_homemade_stock_warehouses.<locals>.<genexpr>Q  s     ']$d3C.D(E']r  r  )r#  r  r  r{  )r`  r>   r  r  rO  rh  rj   rQ  r  r  r|  r   rs   rq   r  r  r$  s              r7   list_homemade_stock_warehousesz*TenantStore.list_homemade_stock_warehouses   s   ((1JKK**7+@+@A '	5Z%% hj " 35J 5"3t9?3%)%Q%QR\^j%k" *5 >>?QR !
 %/$6$6i!O% (* " %)$P$PQT$U! *5 *+;<BC ""9:
 9I!"45gx  hEsCTU\C]Cbab?c  KL!";<!!"34)5''	5T %":  Rz RR#C']R\']$]_`a	
 	
S'	5 '	5s   CEE c                   | j                  |      st        d      | j                  |j                        5 }|j	                  d      j                         }|j	                  d      j                         }d d d        i }D ]e  }|d    dt         }|j                  |g       j                  t        |d   xs d      t        |d   xs d      t        |d	   xs d
      d       g g }D ]  }|d    dt         }t        |d   xs d
      }	t        d|j                         v rt        |d   xs d      nd      }
|j                  |t        |d   xs d      t        |d   xs d      |
t        |
      t        t        |d   xs d
      |	|j                  |g       d	        |t!        |      t#        t%        d |D              d      dS # 1 sw Y   _xY w)Nr  a  
                SELECT
                    items.recipe_id,
                    items.recipe_name,
                    items.recipe_lookup,
                    COALESCE(MAX(recipes.usage_scope), 'both') AS usage_scope,
                    'pz' AS measurement_unit,
                    COUNT(DISTINCT items.warehouse_id) AS warehouse_count,
                    ROUND(SUM(items.quantity), 2) AS total_quantity
                FROM tenant_homemade_stock_items AS items
                LEFT JOIN tenant_homemade_recipes AS recipes
                    ON recipes.id = items.recipe_id
                WHERE items.quantity > 0
                GROUP BY items.recipe_lookup
                ORDER BY total_quantity DESC, lower(items.recipe_name) ASC
                a  
                SELECT
                    items.recipe_lookup,
                    'pz' AS measurement_unit,
                    items.recipe_id,
                    items.recipe_name,
                    items.warehouse_id,
                    warehouses.name AS warehouse_name,
                    ROUND(items.quantity, 2) AS quantity
                FROM tenant_homemade_stock_items AS items
                JOIN tenant_homemade_stock_warehouses AS warehouses
                    ON warehouses.id = items.warehouse_id
                WHERE items.quantity > 0
                ORDER BY lower(items.recipe_name) ASC, lower(warehouses.name) ASC
                r  ::rf  ri   r  rB  r   )rf  r  rB  r{  ry  r-   r  r~  r  )	r  r  r~  ry  r  r   r  r{  r#  c              3  8   K   | ]  }t        |d            ywr  r  r  s     r7   r  z9TenantStore.list_homemade_stock_totals.<locals>.<genexpr>  s     'X$d3C.D(E'Xr  r  )r  r  r{  )r`  r>   r  r  rO  rh  ri  r9  rs   rj   r   rc   r  rf   r   r   rq   r  r  )r>  r  rR  r+  breakdown_rowsbreakdowns_by_keyrl  r  r  r{  ry  s              r7   list_homemade_stock_totalsz&TenantStore.list_homemade_stock_totalsT  s   ((1JKK**7+@+@A $	Z%%" hj# & (//  hj! )$	L AC! 	C)*"-@,ABC((b188$'N(;(Ar$B&)#.>*?*E2&F %c*o&: ;	 *, 	C)*"-@,ABC"3'7#8#=A>N95Bchhj5PC&0&1V\K LL!$S%5%;!<#&s='9'?R#@#.)D[)Q(;'*3/@+A+FQ'G&4"3"7"7R"@
	* u:#C'XRW'X$XZ[\
 	
M$	 $	s   ?GGc           	        | j                  |      st        d      | j                  |j                        5 }| j	                  |      }|j                  d      j                         }|j                  d      j                         }d d d        D ci c]5  }t        |d   xs d      j                         rt        |d   xs d      |7 }}D ci c]5  }t        |d   xs d      j                         rt        |d   xs d      |7 }}t        d   xs d      }	t               j                         t        d	      z
  j                         }
g }t        t        |      t        |      z        D ]g  }|j!                  |      }|j!                  |      }|xs |t        |xs |d
   xs d      nd}|xs |t        |xs |d   xs |      n|}t#        |xs |t        |xs |d   xs d      nd      }|t        |d   xs d      nd}|t%        |d   xs d      nd}|t        |d   xs d      nd}|t        |d   xs d      nd}|t%        |d   xs d      nd}|t        |d   xs d      nd}|t        |d   xs d      nd}|t        |d   xs d      nd}|t        |d   xs d      nd}| j'                  |xs |xs d |
|||      \  }}|dkD  rt)        ||z  d      nd}t)        ||	z  d      }t)        t+        ||z
  d      d      }|dkD  rt)        ||z  d      nd } i d| dt,         d
|d|d|dt/        |      dt,        d|dt)        |d      dt)        |d      dt)        |d      d|d|d|xs d d |xs d d!|xs d d"|xs d d#|
||||| |	dkD  xr |dkD  xr |dkD  d$}!|j1                  |!       j |j3                  d% &       |D !cg c]  }!t5        |!d'         s|! }"}!|||"t7        |      t7        |"      d(d)S # 1 sw Y   xY wc c}w c c}w c c}!w )*Nr  as  
                SELECT
                    items.recipe_id,
                    items.recipe_name,
                    items.recipe_lookup,
                    COALESCE(recipes.usage_scope, 'both') AS usage_scope,
                    COUNT(DISTINCT items.warehouse_id) AS warehouse_count,
                    ROUND(SUM(items.quantity), 2) AS current_quantity
                FROM tenant_homemade_stock_items AS items
                LEFT JOIN tenant_homemade_recipes AS recipes
                    ON recipes.id = items.recipe_id
                WHERE quantity > 0
                GROUP BY items.recipe_lookup
                a  
                SELECT
                    movements.recipe_id,
                    movements.recipe_name,
                    movements.recipe_lookup,
                    COALESCE(recipes.usage_scope, 'both') AS usage_scope,
                    COUNT(DISTINCT CASE WHEN consumed_quantity > 0 THEN substr(occurred_at, 1, 10) END) AS workdays_count,
                    MIN(substr(occurred_at, 1, 10)) AS first_observed_date,
                    MAX(substr(occurred_at, 1, 10)) AS last_observed_date,
                    MIN(CASE WHEN consumed_quantity > 0 THEN substr(occurred_at, 1, 10) END) AS first_consumed_date,
                    MAX(CASE WHEN consumed_quantity > 0 THEN substr(occurred_at, 1, 10) END) AS last_consumed_date,
                    ROUND(SUM(consumed_quantity), 2) AS consumed_quantity,
                    ROUND(SUM(added_quantity), 2) AS added_quantity
                FROM tenant_homemade_stock_movements AS movements
                LEFT JOIN tenant_homemade_recipes AS recipes
                    ON recipes.id = movements.recipe_id
                WHERE consumed_quantity > 0 OR added_quantity > 0
                GROUP BY movements.recipe_lookup
                r  ri   r  r   rm   rw  r  r~  ry  r-   current_quantityr   r  consumed_quantityadded_quantityr!  first_observed_datelast_observed_datefirst_consumed_datelast_consumed_date)r2  r3  r?  ry  r  r  r  r  r  r   consumption_event_days_countobserved_start_dateobserved_end_datetracking_start_datetracking_last_movement_datecalculation_end_date)calculation_basisaverage_daily_consumptionminimum_required_quantityshortage_quantitycoverage_daysis_under_stockc                    t        | d         rdndt        | d   xs d       t        | d         j                         fS )Nr  r   rm   r  r~  )r   r   rj   r  r7  s    r7   r"  z;TenantStore.get_homemade_stock_understock.<locals>.<lambda>  sG    $/01qt/05A66D'(113 r6   r8  r  zmedia consumo = decrementi stock / giorni operativi della prep se configurati, altrimenti giorni con decrementi registrati; sotto-stock = stock corrente < media * giorni minimi)r?  r  under_stock_itemsr  under_stock_countr  )r`  r>   r  r  r3  rO  rh  rj   rQ   r   rA   r   r	   r:   rt  r   r   rc   r   r  r  r  ri  rf   rs   r&  r   rq   )#r>  r  rR  r?  
stock_rowsr  rl  stock_by_lookupmovement_by_lookupr  completed_days_end_dater  lookup	stock_rowmovement_rowr  r~  ry  r  r  r  r  r  r  r  r  r  r!  r  r  r  r  r  r   r  s#                                      r7   get_homemade_stock_understockz)TenantStore.get_homemade_stock_understock  s   ((1JKK**7+@+@A &	Z99*EH#++ hj   '..( hj) %&	T "
3'-2.446 O$*+S0
 
 %
3'-2.446 O$*+S0
 

 #8,@#A#FQG#-<#4#4#69J#J"U"U"W)+S1C8J4KKL 5	F'++F3I-11&9LPYPi]iOvY6,DJK|~IXaXqeqW~#y8L-HRFS  EKK9NWNg[gMtY.,>H&I  {AK MVLauY/A%B%GaHgjHQH]c),=">"C!DcdOQ]Qil3F&G&L1 MorKWKcU<0@#A#FQGilNWcWo3|<L/M/RQR+Suv(T`Tl#l3H&I&OR"PrtR^Rj\2F%G%M2!NprT`Tl#l3H&I&OR"PrtR^Rj\2F%G%M2!Npr040U0U.M2EM0!': 1V 1-N- YgijXj.?..PRS(Tps%(-.GJ\.\^_(`% %c*CFV*VX[&\^_ `VorsVsE"25N"NPQRy}M&$7#89Y { {	
 $%@%M #$7 "? #E*:A$> $U+<a%@ !%": !. /0L &':'Bd $%7%?4 &':'Bd  ./A/IT!" '(?#$ &7-F-F%6!."4q"8"t=VYZ=Z"t_pst_t/D2 LLk5	n 	

 	 	
 /4TdtDAQ<R7STTT !2u:!$%6!7 !S
 	
i&	 &	P


L Us$   AP.:P;:Q 9QQ.P8c                   | j                  |      st        d      t               }t        t	        |j
                  xs d      d      }| j                  5  | j                  |j                        5 }| j                  |      }|j                  | j                  |j                        n0t        |j                  d      xs i j                  d      xs g       }|j                  | j                  |j                        n0t        |j                  d      xs i j                  d      xs g       }|j                  d|t!        j"                  d|id	      t!        j"                  d|id	      ||f       |j%                          d d d        d d d        | j'                  |      S # 1 sw Y   "xY w# 1 sw Y   &xY w)
Nr  r   r  r  r  r  a  
                    INSERT INTO tenant_homemade_stock_settings (
                        id,
                        minimum_stock_days,
                        bar_calendar_json,
                        restaurant_calendar_json,
                        created_at,
                        updated_at
                    ) VALUES ('default', ?, ?, ?, ?, ?)
                    ON CONFLICT(id) DO UPDATE SET
                        minimum_stock_days = excluded.minimum_stock_days,
                        bar_calendar_json = excluded.bar_calendar_json,
                        restaurant_calendar_json = excluded.restaurant_calendar_json,
                        updated_at = excluded.updated_at
                    Fensure_ascii)r`  r>   r;   r  r   r  r<  r  r  r3  r  r{  r   r   r  rO  r   r   rk  r  )	r>  r  r  r  r  rR  current_settingsbar_calendar_rulesrestaurant_calendar_ruless	            r7   update_homemade_stock_settingsz*TenantStore.update_homemade_stock_settings%  s   
 ((1JKKJ	"5)C)C)Hq#I1MZZ %	$..w/D/DE $$#'#E#Ej#Q  ++7 ;;G<P<PQ/33NCIrNNwW][]^ # 22> ;;G<W<WX/334IJPbUUV]^dbde *
 ""  +

G-?#@uU

G-F#GV[\!!!0 !!#I$$%	$N 11'::M$$ $$%	$ %	$s%   F62DF*	F6*F3	/F66F?c           	        | j                  |      st        d      |j                  j                         }t	        |      dk  rt        d      dt        j                         j                   }t               }| j                  5  | j                  |j                        5 }|j                  d|f      j                         }|t        d      |j                  d||||f       |j                          | j                  ||      }d d d        d d d        d| j!                        iS # 1 sw Y   $xY w# 1 sw Y   (xY w)	N=Solo il superadmin puo modificare i magazzini/frigo di Stock.r  %Dammi un nome magazzino/frigo valido.homemade_stock_wh_zTSELECT id FROM tenant_homemade_stock_warehouses WHERE lower(name) = lower(?) LIMIT 1.Esiste gia un magazzino/frigo con questo nome.z
                    INSERT INTO tenant_homemade_stock_warehouses (id, name, created_at, updated_at)
                    VALUES (?, ?, ?, ?)
                    r.  )rd  r>   r'  rQ   rq   r  r  r   r;   r<  r  r  rO  r  rk  rB  r|  r/  s	            r7   create_homemade_stock_warehousez+TenantStore.create_homemade_stock_warehouseX  sG   
 99'B\]]||!!#t9q=DEE+DJJL,<,<+=>J	ZZ 	X..w/D/DE X)11jG  (*   +$%UVV"" "4I> !!#==j,WX	X$ TII#NOO#X X	X 	Xr1  c                   | j                  |      st        d      |j                  j                         }t	        |      dk  rt        d      t               }| j                  5  | j                  |j                        5 }| j                  ||      }|j                  d||f      j                         }|t        d      t        |d   xs d      j                         }	|	|k7  r%|j                  d|||f       |j                          | j                  ||      }
d d d        d d d        d	| j                  
      iS # 1 sw Y   $xY w# 1 sw Y   (xY w)
Nr  r  r  z
                    SELECT id
                    FROM tenant_homemade_stock_warehouses
                    WHERE lower(name) = lower(?) AND id != ?
                    LIMIT 1
                    r  r'  ri   z
                        UPDATE tenant_homemade_stock_warehouses
                        SET name = ?, updated_at = ?
                        WHERE id = ?
                        r.  )rd  r>   r'  rQ   rq   r;   r<  r  r  rB  rO  r  rj   rk  r|  r3  s              r7   update_homemade_stock_warehousez+TenantStore.update_homemade_stock_warehousez  sl    99'B\]]||!!#t9q=DEEJ	ZZ 	`..w/D/DE ` $ G G
T` a)11 <(  (*   +$%UVV"=#8#>B?EEG4'&&
 y,7 %%'"EEjR^_1`	`6 TII+VWW5` `	` 	`r7  c                r   | j                  |      st        d      | j                  5  | j                  |j                        5 }| j                  ||      }| j                  |      }|j                  d|f       |j                          d d d        d d d        ddS # 1 sw Y   xY w# 1 sw Y   xY w)Nr  z9DELETE FROM tenant_homemade_stock_warehouses WHERE id = ?Tr9  )	rd  r>   r<  r  r  rB  r|  rO  rk  r;  s         r7   delete_homemade_stock_warehousez+TenantStore.delete_homemade_stock_warehouse  s    
 99'B\]]ZZ 	$..w/D/DE $ $ G G
T` a'+'S'STa'b$""O!O !!#$	$ -
 	
$ $	$ 	$r>  c                   | j                  |      st        d      | j                  |j                        5 }| j	                  ||      }|j                  d|f      j                         }d d d        D cg c]  }| j                  |       }}| j                        |t        |      dS # 1 sw Y   ExY wc c}w )Nr  z
                SELECT *
                FROM tenant_homemade_stock_sessions
                WHERE warehouse_id = ?
                ORDER BY inventory_date DESC, created_at DESC
                rA  )
r`  r>   r  r  rB  rO  rh  r  r|  rq   rC  s           r7   &list_homemade_stock_warehouse_sessionsz2TenantStore.list_homemade_stock_warehouse_sessions  s    
 ((1JKK**7+@+@A 
	Z CCJP\]M%%  hj 
	 PTTD>>sCTTEEmT x=
 	

	 
	 UrE  c           
        | j                  |      st        d      | j                  |j                        5 }| j	                  ||      }| j                  ||      }t        |d   xs d      |k7  rt        d      | j                  ||      }d d d        D cg c]  }| j                  |       }	}| j                        | j                        |	t        |	      t        t        d |	D              d      dS # 1 sw Y   pxY wc c}w )Nr  rf  ri   ;Lo stock richiesto non appartiene a questo magazzino/frigo.c              3  8   K   | ]  }t        |d            ywrB  Nr  r  s     r7   r  zJTenantStore.get_homemade_stock_warehouse_session_detail.<locals>.<genexpr>  s     'RDd:.>(?'Rr  r  )r.  r  r  r  r{  )r`  r>   r  r  rB  rK  rj   rl  r  r|  r  rq   r  r  )
r>  r  rf  rH  rR  r  r  r  rl  r  s
             r7   +get_homemade_stock_warehouse_session_detailz7TenantStore.get_homemade_stock_warehouse_session_detail  s    ((1JKK**7+@+@A 	XZ CCJP\]M??
JWK;~.4"5E !^__??
JWI	X JSS#88=SSEEmTAA+N Z#C'RE'R$RTUV
 	
	X 	X Ts   AC>D
>Dc                   | j                  |      st        d      t               }| j                  5  | j	                  |j
                        5 }| j                  ||      }| j                  ||      }t        |d   xs d      |k7  rt        d      | j                  |      }| j                  |      }	| j                  ||      }
|j                  d|f       |
D ]  }|j                  ddt        j                         j                   |t        |d   xs d      t        |d	   xs d      t        |d
   xs d      t         t#        |d   xs d      ||f	        |j                  d||f       |j%                          d d d        d d d        | j'                  ||      }	|d<   |d<   |S # 1 sw Y   /xY w# 1 sw Y   3xY w)Nr  rf  ri   r  >DELETE FROM tenant_homemade_stock_items WHERE warehouse_id = ?   
                        INSERT INTO tenant_homemade_stock_items (
                            id,
                            warehouse_id,
                            recipe_id,
                            recipe_name,
                            recipe_lookup,
                            measurement_unit,
                            quantity,
                            created_at,
                            updated_at
                        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
                        homemade_stock_item_r  r~  r  rB  r   
                    UPDATE tenant_homemade_stock_warehouses
                    SET updated_at = ?
                    WHERE id = ?
                    rP  r.  )r`  r>   r;   r<  r  r  rB  rK  rj   r|  r  rl  rO  r  r  r   ri  r   rk  #get_homemade_stock_warehouse_detail)r>  r  rf  rH  r  rR  r  r  r<  r  r  rR  r  s                r7   0load_homemade_stock_warehouse_session_into_stockz<TenantStore.load_homemade_stock_warehouse_session_into_stock  s    ((1JKKJ	ZZ 3	$..w/D/DE 2$ $ G G
T` a"CCJPZ[{>28b9\I$%bcc'+'S'STa'b$%)%O%OP[%\"$($K$KJXb$c!""T!O
 ): $&& 34::<3C3C2DE( 0 = CD 0 ? E2F 0 A GRH/!"2:">"C!D%%
8 ""
 - !!#e2$3	$j 99'<P#5 2{o2$ 2$3	$ 3	$s$   F>D5F2F>2F;	7F>>Gc                   | j                  |      st        d      t               }|rt        |      nd }| j                  5  | j                  |j                        5 }| j                  ||       |dv r|j                  d||f       n|j                  d|f       |j                  d||f       |j                          d d d        d d d        | j                  ||      S # 1 sw Y   #xY w# 1 sw Y   'xY w)Nr     r,   r.   aF  
                        DELETE FROM tenant_homemade_stock_items
                        WHERE warehouse_id = ?
                          AND lower(COALESCE(
                            (
                                SELECT recipes.usage_scope
                                FROM tenant_homemade_recipes AS recipes
                                WHERE recipes.id = tenant_homemade_stock_items.recipe_id
                                LIMIT 1
                            ),
                            'both'
                          )) IN (?, 'both')
                        r  r  )r`  r>   r;   rc   r<  r  r  rB  rO  rk  r  )r>  r  rf  ry  r  normalized_usage_scoperR  s          r7   $reset_homemade_stock_warehouse_stockz0TenantStore.reset_homemade_stock_warehouse_stock4  s    ((1JKKJ	Q\!@!MbfZZ !	$..w/D/DE  $77
LQ)-BB&& &'=>" &&X% ""
 - !!#A $!	$F 77NNE $  $!	$ !	$s%   C/A#C#C/#C,	(C//C8c                6   | j                  |      st        d      t               }| j                  5  | j	                  |j
                        5 }| j                  ||      }| j                  ||      }t        |d   xs d      |k7  rt        d      | j                  |      }| j                  |      }	|j                  d|f       |j                  d||f       |j                          d d d        d d d        d	dS # 1 sw Y   xY w# 1 sw Y   xY w)	Nr  rf  ri   r  7DELETE FROM tenant_homemade_stock_sessions WHERE id = ?r  TrX  )r`  r>   r;   r<  r  r  rB  rK  rj   r|  r  rO  rk  rY  s
             r7   'delete_homemade_stock_warehouse_sessionz3TenantStore.delete_homemade_stock_warehouse_sessiond  s1    ((1JKKJ	ZZ 	$..w/D/DE $ $ G G
T` a"CCJPZ[{>28b9\I$%bcc'+'S'STa'b$%)%O%OP[%\"""MM ""
 - !!#'$	$. -)
 	
+$ $	$ 	$s$   DBD-DD	DDc                   | j                  |      st        d      | j                  |j                        }t	               }|j
                  xs |j                  xs |j                  xs dj                         }dt        j                         j                   }d}| j                  5  | j                  |j                        5 }	| j                  |	|      }
|	j!                  d||f      j#                         }|!d}|	j!                  dt%        |d         f       | j'                  |	|      }t)        t+        d	 |D              d
      }|	j!                  d||t%        |
d   xs d      |d|
d    d| |j,                  |t/        |      |||f       |D ]  }|	j!                  ddt        j                         j                   |t%        |d   xs d      t%        |d   xs d      t%        |d   xs d      t0        t3        |d   xs d      ||f	        |	j5                          | j7                  |	|      }d d d        d d d        | j9                        |dS # 1 sw Y   %xY w# 1 sw Y   )xY w)Nr  r\  homemade_stock_session_Fz
                    SELECT id
                    FROM tenant_homemade_stock_sessions
                    WHERE warehouse_id = ? AND inventory_date = ?
                    LIMIT 1
                    Tr  r  c              3  @   K   | ]  }t        |d    xs d        yw)rB  r   Nr  r  s     r7   r  zFTenantStore.create_homemade_stock_warehouse_session.<locals>.<genexpr>  s     *\35Z1EA+F*\r  r  a1  
                    INSERT INTO tenant_homemade_stock_sessions (
                        id,
                        warehouse_id,
                        warehouse_name,
                        inventory_date,
                        label,
                        created_by_user_id,
                        created_by_name,
                        total_recipes,
                        total_quantity,
                        created_at,
                        updated_at
                    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                    r'  ri   zStock r`  a  
                        INSERT INTO tenant_homemade_stock_session_items (
                            id,
                            session_id,
                            recipe_id,
                            recipe_name,
                            recipe_lookup,
                            measurement_unit,
                            quantity,
                            created_at,
                            updated_at
                        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
                        homemade_stock_session_item_r  r~  r  rB  r   rp  )r`  r>   r  r`  r;   r  r  r  rQ   r  r  r   r<  r  r  rB  rO  r  rj   rf  r  r  r1  rq   ri  r   rk  rK  r  )r>  r  rf  r  r`  r  rc  rH  rd  rR  r  r  r  r{  r  re  s                   r7   'create_homemade_stock_warehouse_sessionz3TenantStore.create_homemade_stock_warehouse_session  s    ((1JKK778N8NOJ	''d7+;+;dw?Q?QdUdkkm
.tzz|/?/?.@A
!ZZ Q	b..w/D/DE Pb $ G G
T` a)11 ">2  (*   +(,%&&Q\$/02
 !;;JU	!&s*\R[*\'\^_!`""  #$M&17R8& v!6 7u^<LM"I&!!!> !* H&& ;4::<;K;K:LM& 5 ;< 7 =2> 9 ?R@/!(:"6";!<%%
8 !!#$($I$I*V`$a!aPbQ	bh AABST!2
 	
ePb PbQ	b Q	bs%   +I-E6I!=I-!I*	&I--I6c                x   | j                  |      st        d      | j                  |j                        5 }| j	                  ||      }| j                  ||      }| j                  ||      }|| j                  |      nd }|| j                  |t        |d               ng }d d d        D 	cg c]  }	| j                  |	       }
}	D 	cg c]  }	| j                  |	       }}	| j                        |
|
|t        |
      t        t        d |
D              d      dS # 1 sw Y   xY wc c}	w c c}	w )Nr  r  c              3  8   K   | ]  }t        |d            ywr  r  r  s     r7   r  zBTenantStore.get_homemade_stock_warehouse_detail.<locals>.<genexpr>  s     'ZDd:.>(?'Zr  r  )r.  r  r  r  r  r  r{  )r`  r>   r  r  rB  rf  rQ  r  rl  rj   r  r|  rq   r  r  )r>  r  rf  rR  r  current_item_rowsr%  r  real_item_rowsrl  r  r  s               r7   r  z/TenantStore.get_homemade_stock_warehouse_detail  s\   
 ((1JKK**7+@+@A 	Z CCJP\]M $ ? ?
L Y!%!M!MjZf!g &1 ::;MN  &1 77
CHZ[_H`Dab 	 Rcc#@@EccN\]sd==cB]
]EEmT"*$ 0 /#C'ZM'Z$Z\]^
 	
#	 	 d]s   A.D&2D2D7&D/r  )ry  r8  c                  | j                  |      st        d      t        dt        t	        |      d            }t        |xs d      j                         j                         }|rt        |      nd }| j                  |j                        5 }d}	d}
|dv rd}	|f}
|r:d| d}| d}|j                  d	|	 d
|g|
|||      j                         }n)|j                  d|	 dg |
|      j                         }d d d        D cg c]  }t        |d   xs d      t        |d   xs d      t        t        d|j                         v rt        |d   xs d      nd      t        d|j                         v rt        |d   xs d      nd      t        |d   xs d      xs d t        |d   xs d      t        |d   xs d      d }}|t!        |      dS # 1 sw Y   xY wc c}w )Nr  rm   r  ri   r5   r  z8 AND lower(COALESCE(usage_scope, 'both')) IN (?, 'both')%z
                    SELECT id, name, measurement_unit, usage_scope, preparation_date, created_at, updated_at
                    FROM tenant_homemade_recipes
                    WHERE lower(name) LIKE ?
                    an  
                    ORDER BY
                        CASE
                            WHEN lower(name) = ? THEN 0
                            WHEN lower(name) LIKE ? THEN 1
                            ELSE 2
                        END,
                        lower(name) ASC,
                        created_at ASC
                    LIMIT ?
                    z
                    SELECT id, name, measurement_unit, usage_scope, preparation_date, created_at, updated_at
                    FROM tenant_homemade_recipes
                    WHERE 1 = 1
                    zn
                    ORDER BY lower(name) ASC, created_at ASC
                    LIMIT ?
                    r  r'  ry  r-   rU  r  r  )r  r'  r   ry  r  rU  r  r  )r  r  )r`  r>   r  r   r   rj   rQ   rP   rc   r  r  rO  rh  ri  r  rf   rq   )r>  r  queryry  r8  
safe_limitnormalized_queryr  rR  scope_clausescope_params
like_valueprefix_valuer+  rl  r  s                   r7   search_homemade_stock_recipesz)TenantStore.search_homemade_stock_recipes  sY    ((1JKKCE
C01
u{+11399;Q\!@!Mbf**7+@+@A '	ZL/1L%)>>Y 68 !1 2!4
"2!315!)) "N 
#	  [,[0@[,[PZ[!" (*# & ")) "N #	 0l/J/
 (* ;'	p 
  #d)/r*CK-2.$7>9F#((*9TCM*4f5Z`  &A9F#((*9TCM*4f5Z`& %(,>(?(E2$F$N$!#l"3"9r:!#l"3"9r:
 
" s5z::u'	 '	R
s   A3G$CG0$G-c                4   | j                  |      st        d      t               }t        |j                  xs d      j                         j                         }|dvrt        d      |dk(  r|j                  dk  rt        d      |dk(  r|j                  dk  rt        d      | j                  5  | j                  |j                        5 }| j                  ||      }| j                  ||j                        }t        |d	   xs d
      j                         }	t        |d   xs t        |	            }
t        }|j!                  d|t        |d   xs d
      |
f      j#                         }|t%        |d   xs d      nd}|dk(  rt%        |j                        n|t%        |j                        z   }|)|dk  r$| j'                  ||      cd d d        cd d d        S |K|j!                  ddt)        j*                         j,                   |t        |d   xs d
      |	|
||||f	       nZ|dk  r |j!                  dt        |d         f       n5|j!                  dt        |d   xs d
      |	|
|||t        |d         f       | j/                  |||t        |d   xs d
      |	|
||d| |
       |j!                  d||f       |j1                          d d d        d d d        | j'                  ||      S # 1 sw Y   #xY w# 1 sw Y   'xY w)Nr  r   >   r   r   zOperazione stock non valida.r   r   >Per aggiungere stock la quantita deve essere maggiore di zero.z*La quantita stock non puo essere negativa.r'  ri   name_lookupz
                    SELECT *
                    FROM tenant_homemade_stock_items
                    WHERE warehouse_id = ? AND (recipe_id = ? OR recipe_lookup = ?)
                    LIMIT 1
                    r  rB  r   r  r  z4DELETE FROM tenant_homemade_stock_items WHERE id = ?a  
                        UPDATE tenant_homemade_stock_items
                        SET
                            recipe_id = ?,
                            recipe_name = ?,
                            recipe_lookup = ?,
                            measurement_unit = ?,
                            quantity = ?,
                            updated_at = ?
                        WHERE id = ?
                        stock_)	r  r  r  r~  r  r  r  r  r  r  )r`  r>   r;   rj   rD  rQ   rP   rB  r<  r  r  rB  _read_homemade_recipe_rowr  r[   ri  rO  r  r   r  r  r  r   r  rk  )r>  r  rf  r  r  rD  rR  r  
recipe_rowr~  r  r   r  r  next_quantitys                  r7   add_homemade_stockzTenantStore.add_homemade_stock_  sy    ((1JKKJ	))2U399;AAC	N*;<<'"2"2a"7]^^'"2"2Q"6IJJZZ e	$..w/D/DE d$ $ G G
T` a!;;JHYHYZ
!*V"4":;AAC #J}$=$_ARS^A_ `#6 %-- "3z$'7'=2#>N (*  HPG[5*)=)B#Cad ;D;Mg&6&6 7Scfklsl|l|f}S}#(:CCG\Z+d$ d$e	$ e	$0 #&& 34::<3C3C2DE(
4 0 6B7'),)%%
4 #a'&&NXd^,.
 &&
  
4 0 6B7'),)%/. 44#"/!*T"2"8b9 +"/$4#0$*9+"6' 5  ""
 - !!#Id$e	$N 77NNMd$ d$e	$ e	$s2   5LDL	L'C9L LL	LL)start_session_idr  c                  | j                  |      st        d      | j                  |j                        5 }| j	                  ||      }|j                  d|f      j                         }|D cg c]  }| j                  |       }	}|s&| j                  |      |	d d dddddg ddcd d d        S |r|r||k(  rt        d      |r| j                  ||      }
nt        |      dk\  r|d   }
nd }
|r| j                  ||      }n|d   }|
 t        |
d	   xs d
      |k7  rt        d      | t        |d	   xs d
      |k7  rt        d      |
|`| j                  |      |	|
| j                  |
      nd || j                  |      nd ddd|t        |d   xs d      nddg ddcd d d        S 	 t        j                  t        |
d               }t        j                  t        |d               }||kD  r||
}}
||}}t        t        | j                  ||j!                         |j!                         fg                  }| j#                  |t        |
d               }| j#                  |t        |d               }| j%                  |
d         xs4 t'        j(                  |t*        j,                  t.        j0                        }| j%                  |d         xsA t'        j(                  |t3        d      z   t*        j,                  t.        j0                        }|j                  d|f      j                         }i }i }|D ]  }| j%                  |d         }|
||k  s||kD  r$t        |d   xs d
      t        |d   xs d
      f}t        |d   xs d
      dk(  r|n|}|j5                  |t        |d   xs d
      t        |d   xs d
      dd      }t        |d   xs d      t        |d   xs d      z   |d<    	 d d d        t7              t7              z  t7              z  t7              z  }g }|D ]  }|j9                  |      }|j9                  |      }|j9                  |      }|j9                  |      } t        |xs |xs
 |xs | xs i j9                  d      xs d
      }!t        |xs |xs
 |xs | xs i j9                  d      xs d
      }"t        |xs i j9                  d      xs d      }#t        |xs i j9                  d      xs d      }$t        | xs i j9                  d      xs d      }%t        |xs i j9                  d      xs d      }&|#|$z   |%z
  |&z
  }'|#dk(  r|$dk(  r|%dk(  r|&dk(  rM|j;                  |!|"t=        |#d      t=        |$d      t=        |%d      t=        |&d      t=        |'d      d        |j?                  d  !       | j                        	| j                  
      | j                        t=        tA        d" |D              d      t=        tA        d# |D              d      t=        tA        d$ |D              d      t=        tA        d% |D              d      |d&dS c c}w # t        $ r}t        d      |d }~ww xY w# 1 sw Y   xY w)'Nr  r@  r   zFconsumo = inventario iniziale + entrate registrate - inventario finale)r.  rB  start_sessionend_sessionr  total_opening_unitstotal_incoming_unitstotal_closing_unitsr   r  r  zOPer confrontare i consumi servono due inventari diversi dello stesso magazzino.r  rm   rf  ri   zDL'inventario iniziale selezionato non appartiene a questo magazzino.zBL'inventario finale selezionato non appartiene a questo magazzino.rv  r`  r  r  r  r  rw  ai  
                SELECT *
                FROM tenant_inventory_movements
                WHERE warehouse_id = ?
                  AND (
                    movement_kind = 'in'
                    OR (movement_kind = 'out' AND source_type = 'warehouse_transfer_out')
                  )
                ORDER BY occurred_at ASC, created_at ASC
                rh  r  r  r  r  rS  rT  r   rn  r  )rS  rT  r  r  r  r  r  c                    t        | d          t        | d         j                         t        | d         j                         fS r  r  r7  s    r7   r"  zATenantStore.get_inventory_warehouse_consumption.<locals>.<lambda>  r  r6   r8  c              3  8   K   | ]  }t        |d            ywr  Nr  r  s     r7   r  zBTenantStore.get_inventory_warehouse_consumption.<locals>.<genexpr>       ,\dU43H-I,\r  c              3  8   K   | ]  }t        |d            ywr  Nr  r  s     r7   r  zBTenantStore.get_inventory_warehouse_consumption.<locals>.<genexpr>       -^PTeD9I4J.K-^r  c              3  8   K   | ]  }t        |d            ywr  Nr  r  s     r7   r  zBTenantStore.get_inventory_warehouse_consumption.<locals>.<genexpr>  r  r  c              3  8   K   | ]  }t        |d            ywr  Nr  r  s     r7   r  zBTenantStore.get_inventory_warehouse_consumption.<locals>.<genexpr>  r  r  z]consumo = inventario iniziale + entrate registrate - trasferimenti usciti - inventario finale)!rQ  r>   r  r  r@  rO  rh  r  rx  rI  rq   rj   r   r   r  r4  r:   rs  r  r   r  r   r   r
   r4   r	   r9  r   r   rs   r  r&  r  )(r>  r  rf  r  r  rR  r  session_rowsrl  serialized_sessionsr  r  r  r  r  r  r  r  r  r  r  r  r  rh  r  r  r<  r  r  r  r  r  r  rS  rT  r  r  r  r  r  s(                                           r7   #get_inventory_warehouse_consumptionz/TenantStore.get_inventory_warehouse_consumption  s    ))'2LMM**7+@+@A 	FZ >>z<XM%--  hj  Zf"fRU4#H#H#M"f"f!%!H!H!W 3%)#'#$+,,-+,,-(p	F 	F6  N7G>7Y !rss$($D$DZQa$b!\"a'$0O!$(!"&"B"B:~"^".q/ ,5F~5V5\Z\1]am1m !ghh*s?>3R3XVX/Y]i/i !eff (O,C!%!H!H!W 3ara~T%J%JK\%]  EI]l]x4#H#H#Y  C#$+,,-dsd5AY1Z1_^_+`  FG,-(pa	F 	F|s'+'9'9#>OP`>a:b'c$%)%7%7OL\<]8^%_" $&885DFW?!;MOc&8$DD".88:<N<X<X<Z[\K AA*cRcdhRiNjkKAA*cRabfRgNhiK"<<=N|=\] aiaqaq$||bO
 !::?<;XY ]e]m]m"YA%66||^M
 '..	  hj  FHLNP!$ F"<<S=OP&+*HKZgLg3/06B7SAR=S=YWY9Z[ 3}-348PP *% 
 $..(+C,?,E2(F),S-A-GR)H25 49@X9Y9^]^3_bghkl~h  iE  DE  cF  4F/0%F[	FB $s;'77#l:KKcRgNhh	)+ 	C!ooc*G!ooc*G#'',H 5 9 9# > Y7 Yh YBS YWY^^_mntrtuL!ZG!Zx!ZCT!ZXZ _ _`o p vtvwM!7=b"5"56N"O"TSTUM"HN#7#78P#Q#VUVWN&+->-D",I,IJb,c,hgh&i#!7=b"5"56N"O"TSTUM*^;>UUXeeN!n&9>UYZ>Z_lpq_qLL$0%2%*=!%<&+NA&>/45La/P%*=!%<&+NA&>
	4 	

 	 	
 @@O+!BBCTU@@Q&#(,\V[,\)\^_#`$)#-^X]-^*^`a$b#(,\V[,\)\^_#`$)#-^X]-^*^`a$b 
 	
s #gl  s !hiorrsC	F 	FsJ   8Z:0Z Z:2C7Z:4AZ6HZ:Z:	Z7&Z22Z77Z::[r8  c                  | j                  |      st        d      t        dt        t	        |      d            }t        |xs d      j                         j                         t        fdt              D              }| j                  |j                        5 }| j                  |d      sdg dcd d d        S | j                  |d      }d	|v rd	nd
}d|v rdnd}	d|v rdnd}
rd d} d}dj                  d |D              }d}d}||||g}|rd| d}d| d}|j                  |       |j                  g||||       |j!                  d|	 d| d|
 d| d| dt        |            j#                         }n+|j!                  d|	 d| d|
 d|f      j#                         }d d d        D cg c]  }t	        |d         t        |d   xs d      t        |d   xs d      t        |d   xs d      t        |d   xs d      xs d |d	   t%        |d	         nd |d   t%        |d         nd | j'                  |d         d  }}d!|dS # 1 sw Y   xY wc c}w )"Nr  rm   r  ri   c              3  .   K   | ]  }|k7  s	|  y wr2   r5   )r  r  r  s     r7   r  z8TenantStore.search_inventory_products.<locals>.<genexpr>  s      '
RW[kRkE'
s   
r}  Fcatalog_availabler  rK  NULL AS units_per_packrO  NULL AS product_coderV  NULL AS final_price_vatr  rS  c              3      K   | ]  }d   ywrU  r5   rW  s     r7   r  z8TenantStore.search_inventory_products.<locals>.<genexpr>  s     5Zac5ZrZ  z
                        OR (
                            lower(lot_code) IN ('ct', 'cartone', 'cartoni', 'cassa', 'casse')
                            AND lower(COALESCE(product_code, '')) IN (z0)
                        )
                    z
                            WHEN lower(lot_code) IN ('ct', 'cartone', 'cartoni', 'cassa', 'casse')
                              AND lower(COALESCE(product_code, '')) IN (z) THEN 1
                    
                    SELECT
                        id,
                        product_name,
                        lot_code,
                        supplier_name,
                        ,
                        ac  
                    FROM ordini_products
                    WHERE active = 1
                      AND (
                        lower(product_name) LIKE ?
                        OR lower(supplier_name) LIKE ?
                        OR lower(lot_code) LIKE ?
                        OR lower(COALESCE(product_code, '')) LIKE ?
                        z
                      )
                    ORDER BY
                        CASE
                            WHEN lower(COALESCE(product_code, '')) = ? THEN 0
                            a  
                            WHEN lower(product_name) = ? THEN 2
                            WHEN lower(product_name) LIKE ? THEN 3
                            WHEN lower(supplier_name) LIKE ? THEN 4
                            ELSE 5
                        END,
                        lower(supplier_name) ASC,
                        lower(product_name) ASC,
                        lower(lot_code) ASC
                    LIMIT ?
                    z
                    FROM ordini_products
                    WHERE active = 1
                    ORDER BY lower(supplier_name) ASC, lower(product_name) ASC, lower(lot_code) ASC
                    LIMIT ?
                    r  rS  rG  rT  r  rS  rG  rT  rO  rK  rV  requires_units_per_packT)rU  r>   r  r   r   rj   rQ   rP   ro   rx   r  r  r~  r  r%  extendrO  rh  r   r  )r>  r  r  r8  r  carton_barcode_aliasesrR  r  select_units_per_packselect_product_codeselect_pricer  r  carton_alias_placeholderscarton_alias_filtercarton_alias_orderparamsr+  rl  r  r  s                       @r7   search_inventory_productsz%TenantStore.search_inventory_products  s    ??HVWWCE
C01
u{+11399;!& '
<=MN'
 "
 **7+@+@A _	Z,,Z9JK-2R@_	 _	 #88EVWO8HO8[$4ay!4Bo4U.[q0A_0T,ZsL !1 2!4
"2!315,0II5ZCY5Z,Z)&(#%'"	( */G HaFa b+'.IIbHc d*& MM"89(/ ) %	
 % #	 ")) -- ../ 0% ' -- .
 00 
1+@ &MC"D (*E H ")) -- ../ 0% '  M  (*! __	X 
  #d)n #C$7$=2 >J 526!$S%9%?R!@ #C$7$=2 > F$BEFVBWBc%,<(=">imDGHYDZDf5->)?#@lp+/+V+VWZ[eWf+g	
 
 &*E::]_	 _	B
s    I'C9I'BI3'I0c               v   | j                  |      st        d      | j                  |j                        5 }| j	                  |d      sdg dcd d d        S | j                  |d      }d|v rdnd}d|v rdnd}d	|v rd	nd
}|j                  d|f      j                         }|t        d      |j                  d| d| d| dt        |d   xs d      t        |d   xs d      f      j                         }	d d d        	D 
cg c]  }
t        |
d         t        |
d   xs d      t        |
d   xs d      t        |
d   xs d      t        |
d   xs d      xs d |
d   t        |
d         nd |
d	   t        |
d	         nd | j                  |
d         d }}
d|dS # 1 sw Y   xY wc c}
w )Nr  r}  Fr  rK  r  rO  r  rV  r  z
                SELECT id, product_name, supplier_name
                FROM ordini_products
                WHERE id = ? AND active = 1
                LIMIT 1
                )Prodotto non trovato nel catalogo attivo.z
                SELECT
                    id,
                    product_name,
                    lot_code,
                    supplier_name,
                    z,
                    aY  
                FROM ordini_products
                WHERE active = 1
                  AND lower(product_name) = lower(?)
                  AND lower(supplier_name) = lower(?)
                ORDER BY
                  CASE
                    WHEN lower(lot_code) IN ('bt', 'bottiglia') THEN 0
                    WHEN lower(lot_code) IN ('ct', 'cartone', 'cartoni', 'cassa', 'casse') THEN 1
                    WHEN lower(lot_code) IN ('pz', 'pezzo', 'pezzi') THEN 2
                    ELSE 3
                  END,
                  lower(lot_code) ASC,
                  id ASC
                rS  ri   rT  r  rG  r!  T)rQ  r>   r  r  r~  r  rO  r  r  rj   rh  r   r   r  )r>  r  rA  rR  r  r%  r&  r'  seed_rowr+  rl  r  s               r7   list_inventory_product_variantsz+TenantStore.list_inventory_product_variants  s:    ))'2LMM**7+@+@A -	Z,,Z9JK-2R@-	 -	 #88EVWO8HO8[$4ay!4Bo4U.[q0A_0T,ZsL!))  hj  JKK%% )) **+ ,!N #. Xn-34c(?:S:YWY6Z[12 hj3 )-	t 
  #d)n #C$7$=2 >J 526!$S%9%?R!@ #C$7$=2 > F$BEFVBWBc%,<(=">imDGHYDZDf5->)?#@lp+/+V+VWZ[eWf+g	
 
 &*E::y-	 -	^
s   F*B$F*	BF6*F3c                ,     j                  |      st        d      |j                  j                         }|j                  j                         |j
                  j                         }|rr|st        d      t        j                  ddt        |j                  xs d      j                               xs d  j                  5   j                  |j                        5 } j                  |d      st        d      r j                  |ddd        j                  |d      }|j!                  d	||f      j#                         rd|v r j%                        rt'              nf}|j!                  d
dj)                  d |D               dt+        |            j-                         }t/         fd|D        d       }	|	ot        dt        |	d   xs d      j                          dt        |	d   xs d      j                          dt        |	d   xs d      j                          d      i }
dfd|j0                  fd|j2                  fd|j4                  fd|j6                  fd|j8                  r|j8                  j                         nd fd|j:                  r|j:                  j                         nd fd|j<                  fd|j>                  ff	D ]  \  }}||v s|||
|<    d}d}g d}||g}|
jA                         D ]'  \  }}|jC                  |       |jC                  |       ) d |v r"|jC                  d        |jC                  d!       dj)                  d" |D              }|j!                  d#dj)                  |       d$| d%t+        |             ng }g }d |v r|jC                  d&       |
jA                         D ]*  \  }}|jC                  | d'       |jC                  |       , d(|v r|jC                  d)       |r>|j!                  d*dj)                  |       d+t+        |tE        d,         gz                |jG                           j                  |d      }d|v rdnd-}d|v rdnd.}d|v rdnd/}|j!                  d0| d1| d1| d2||f      j#                         }|tI        d3      	 d d d        d d d        dtE        d,         t        |d   xs d      t        |d   xs d      t        |d   xs d      t        |d   xs d      xs d |d   tK        |d         nd |d   tK        |d         nd  j%                  |d         d4d5S # 1 sw Y   xY w# 1 sw Y   xY w)6Nr  z)Compila nome prodotto, lotto e fornitore.rh   ri   r}  4Catalogo prodotti non disponibile per questo locale.rO  rF  a  
                    SELECT *
                    FROM ordini_products
                    WHERE lower(product_name) = lower(?)
                      AND lower(lot_code) = lower(?)
                      AND lower(supplier_name) = lower(?)
                    LIMIT 1
                    z
                        SELECT id, product_name, lot_code, supplier_name, product_code
                        FROM ordini_products
                        WHERE active = 1
                          AND lower(COALESCE(product_code, '')) IN (rS  c              3      K   | ]  }d   ywrU  r5   rW  s     r7   r  z7TenantStore.create_inventory_product.<locals>.<genexpr>  s     NvWXsNvrZ  z)
                        c              3     K   | ]@  }t        |d         t        d         k7  rj                  |d   |d         r| B y w)Nr  rO  rG  r  r  r  r  )r   r  )r  rl  r  rG  normalized_product_coder>  s     r7   r  z7TenantStore.create_inventory_product.<locals>.<genexpr>  sb      
 # , 4CI#l[_N`Ja8a $ A A*A.6+.~+>/2:	 !B !  
s   AA	!Questo barcode e gia assegnato a rS      · rT  rG  .rV  rW  rX  rY  rZ  r\  rK  r]  FT)rS  rG  rT  r  rm   c              3      K   | ]  }d   ywrU  r5   rW  s     r7   r  z7TenantStore.create_inventory_product.<locals>.<genexpr>       ,IQS,IrZ  INSERT INTO ordini_products (
) VALUES (rs  
active = 1 = ?r  updated_at = CURRENT_TIMESTAMPUPDATE ordini_products SET  WHERE id = ?r  r  r  r  r  r   a  
                    FROM ordini_products
                    WHERE lower(product_name) = lower(?)
                      AND lower(lot_code) = lower(?)
                      AND lower(supplier_name) = lower(?)
                    LIMIT 1
                    z)Prodotto non trovato dopo il salvataggio.r!  )okcreatedproduct)&rQ  r>   rS  rQ   rG  rT  rN   rO   rj   rO  r<  r  r  r~  rg  r  rO  r  r  rx   r%  ro   rh  r  rV  rW  rX  rY  rZ  r\  rK  r]  r  rs   r   rk  r  r   )r>  r  r  rS  rT  rR  r  target_barcode_candidatesconflicting_rowsconflicting_rowmetadata_valuesrx  rT   rD  insert_columnsinsert_valuesrb  assignmentsr   updated_columnsr%  r&  r'  r5  r  rG  r6  s   `                       @@@r7   create_inventory_productz$TenantStore.create_inventory_product]  sn   ))'2LMM++113##))+--3358=HII"$&&S9M9M9SQS5T5Z5Z5\"]"eaeZZ A	P..w/D/DE @P00=NO$%[\\*..z;Ln^de"&"<"<ZIZ"[)11 "8];
  (*  +~/P  FFxP 66MN57 .
 (2'9'9E FJYYNv\uNvEvDw x	 78( hj % '+
'7
 'O '2(?"?>#B#HbIOOQRRV"??#C#IrJPPRSSW"?:#>#D"EKKMNaQ  68#%<=&(?(?@!1!12 '"3"34('*C*CDW=M=M!1!1!7!7!9SWXw}}gmm113$O%w'='=>&(?(?@
+ =&K #o5%:K7<4=  '"G%RN3?=2YM.=.C.C.E 4*U&--k:%,,U34  ?2&--h7%,,Q/#'99,I.,I#IL&&7		.8Q7RR\]i\jjklm,
 .0K+-F?2#**<8.=.C.C.E -*U#**k]$+?@e,- $6#**+KL""**9$))K:P9QQ^_!&CT0B,C+D"DE !!#"&"<"<ZIZ"[<LP_<_(8e}%8F/8Yn_u#4E4X0^w(00 -- ../ 0% ' "8];!" (*# $ &"#NOO '@PA	PH +d+, #K$?$E2 FJ 7 =2>!$[%A%GR!H #K$?$E2 F N$JUVfJgJs%4D(E"Fy}LWXiLjLv55F)G#H  }A+/+V+VWbcmWn+o	
 	
E@P @PA	P A	Ps2   V
(HU>4U>7GU>V
>V	V

Vc               D     j                  |      st        d      t        j                  ddt	        |j
                  xs d      j                               t              dk  rt        d       j                  5   j                  |j                        5 } j                  |d      st        d       j                  |ddd	        j                  |d      }d
|v rd
nd}d|v rdnd}|j                  d|f      j                         }|t!        d      t	        |j"                  xs	 |d   xs d      j                         st        d       j%                        rt'              nf}	|j                  dt	        |d   xs d      j                         t	        |d   xs d      j                         f      j                         }
|
t)        |
d         n|}|j                  ddj+                  d |	D               d|g|	      j-                         }t/         fd|D        d       }|ot        dt	        |d   xs d      j                          dt	        |d   xs d      j                          dt	        |d   xs d      j                          d      d}|
Hd}g d }t	        |d   xs d      j                         t	        |d   xs d      j                         g}d!|v r"|j1                  d!       |j1                  d"       d#D ],  }||v s|j1                  |       |j1                  ||          . d
|v r[d } j%                        r& j%                  t	        |d   xs d            r|d
   }|j1                  d
       |j1                  |       dj+                  d$ |D              }|j                  d%dj+                  |       d&| d't3        |             nbd(g}g}d!|v r|j1                  d)       d*|v r|j1                  d+       |j                  d,dj+                  |       d-t3        ||gz                |j5                          |j                  d.| d/| d0t	        |d   xs d      j                         t	        |d   xs d      j                         f      j                         }|t!        d1      	 d d d        d d d        dt)        d         t	        |d   xs d      t	        |d   xs d      t	        |d   xs d      t	        |d   xs d      xs d |d
   t7        |d
         nd |d   t7        |d         nd  j%                  |d         d2d3S # 1 sw Y   xY w# 1 sw Y   xY w)4Nr  rh   ri   rI   z-Indicami un barcode valido prima di salvarlo.r}  r2  rO  rF  rK  r  rV  r  z
                    SELECT *
                    FROM ordini_products
                    WHERE id = ? AND active = 1
                    LIMIT 1
                    r.  rG  z<Seleziona il lotto da associare prima di salvare il barcode.a  
                    SELECT *
                    FROM ordini_products
                    WHERE lower(product_name) = lower(?)
                      AND lower(supplier_name) = lower(?)
                      AND lower(lot_code) = lower(?)
                    LIMIT 1
                    rS  rT  r  a  
                    SELECT id, product_name, lot_code, supplier_name, product_code
                    FROM ordini_products
                    WHERE active = 1
                      AND id != ?
                      AND lower(COALESCE(product_code, '')) IN (rS  c              3      K   | ]  }d   ywrU  r5   rW  s     r7   r  z<TenantStore.update_inventory_product_code.<locals>.<genexpr>?  s     JrST3JrrZ  z)
                    c              3  X   K   | ]!  }j                  |d    |d         r| # yw)rO  rG  r5  N)r  )r  rl  r6  r>  target_lot_codes     r7   r  z<TenantStore.update_inventory_product_code.<locals>.<genexpr>D  sA      	<<&=*9'*>':+.z?	 =  	s   '*r7  r8  r9  FT)rS  rG  rT  rO  r  rm   )rW  rZ  r\  r]  c              3      K   | ]  }d   ywrU  r5   rW  s     r7   r  z<TenantStore.update_inventory_product_code.<locals>.<genexpr>q  r;  rZ  r<  r=  rs  zproduct_code = ?r>  r  r@  rA  rB  z
                    SELECT
                        id,
                        product_name,
                        lot_code,
                        supplier_name,
                        product_code,
                        r   a  
                    FROM ordini_products
                    WHERE lower(product_name) = lower(?)
                      AND lower(supplier_name) = lower(?)
                      AND lower(lot_code) = lower(?)
                    LIMIT 1
                    z5Prodotto non trovato dopo il salvataggio del barcode.r!  )rC  created_variantrE  )rQ  r>   rN   rO   rj   rO  rQ   rq   r<  r  r  r~  rg  r  rO  r  r  rG  r  rx   r   r%  rh  r  rs   ro   rk  r   )r>  r  rA  r  rR  r  r%  r'  product_rowrF  
target_rowtarget_product_idrG  rH  rT  rJ  rK  rx  inherited_units_per_packrb  rL  r+  r5  r6  rR  s   `                      @@r7   update_inventory_product_codez)TenantStore.update_inventory_product_code  s    ))'2LMM"$&&S9M9M9SQS5T5Z5Z5\"]&'!+LMMZZ Q	\..w/D/DE P\00=NO$%[\\**:7H.Z`a"&"<"<ZIZ"[<LP_<_(8e}%4E4X0^w(00  M (*  &"#NOO"%g&6&6&W+j:Q&WUW"X"^"^"`&$%cdd BB?S 22IJ13 * (// K7=2>DDFK8>B?EEG' (*  >H=SC
4(8$9Yc!#-#5#5A
 BFJrXqJrAr@s t 'C)BC	$ (* ! #'	#3	 # #.$;~>D"EKKMNd?E2FLLNOtz:@bAGGIJ!M  #(%&*O%bNK7=2>DDF'K8>B?EEG/	3M  ?2&--h7%,,Q/'[ K&/9*11+>)00[1IJK (?:370FFW\`  ]H  ]HJ 7 =2>] 8CCS7T4&--.>?%,,-EF#'99,I.,I#IL&&7		.8Q7RR\]i\jjklm,
 $6"6K,C+DF?2#**<8#6#**+KL&&5dii6L5M][f(9'::; !!#(00 // 0% '  K7=2>DDFK8>B?EEG'!* (*+ , &"#Z[[ '_P\Q	\h .+d+, #K$?$E2 FJ 7 =2>!$[%A%GR!H #K$?$E2 F N$JUVfJgJs%4D(E"Fy}LWXiLjLv55F)G#H  }A+/+V+VWbcmWn+o	
 	
eP\ P\Q	\ Q	\s,   VJV
-F5V
#V
V	VVc                :   | j                  |      st        d      | j                  |j                        5 }|j	                  d      j                         }d d d        D cg c]  }| j                  |       }}|t        |      dS # 1 sw Y   5xY wc c}w )Nr  
                SELECT *
                FROM tenant_inventory_snapshots
                ORDER BY snapshot_date DESC, created_at DESC
                )	snapshotsr  )rQ  r>   r  r  rO  rh  r  rq   )r>  r  rR  r+  rl  r\  s         r7   list_inventory_snapshotsz$TenantStore.list_inventory_snapshots  s    ))'2LMM**7+@+@A 	Z%% hj 	 MQQST;;C@Q	Q&s9~FF	 	 Rs    B$BBc                   | j                  |      st        d      | j                  |j                        }t	               }|j
                  xs |j                  xs |j                  xs dj                         }dt        j                         j                   }d}| j                  5  | j                  |j                        5 }|j                  d|f      j!                         }	|	!d}|j                  dt#        |	d         f       |j                  d	      j%                         }
|j                  d
      j%                         }|D cg c]  }t#        |d          }}|D ci c]  }|g  }}|rydj'                  d |D              }|j                  d| dt)        |            j%                         }|D ]/  }|j+                  t#        |d         g       j-                  |       1 t/        d |D              }|j                  d||d| |j0                  |t3        |
      t3        |      |||f
       |D ]a  }dt        j                         j                   }|j                  d||t#        |d   xs d      t#        |d   xs d      t#        |d   xs d      t#        |d   xs d      t#        |d   xs d      t#        |d   xs d      t5        |d   xs d      ||f       |j7                  t#        |d         g       D ]  }|j                  ddt        j                         j                   |t#        |d    xs d      t#        |d!   xs d      t5        |d"   xs d      |d#   t5        |d#         nd t5        |d$   xs d      ||f	        d |j9                          | j;                  ||      }d d d        d d d        | j=                        |d%S c c}w c c}w # 1 sw Y   /xY w# 1 sw Y   3xY w)&Nr  r\  inventory_snapshot_FzISELECT id FROM tenant_inventory_snapshots WHERE snapshot_date = ? LIMIT 1Tz3DELETE FROM tenant_inventory_snapshots WHERE id = ?r  z
                    SELECT *
                    FROM tenant_inventory_warehouses
                    ORDER BY lower(name) ASC, created_at ASC
                    a  
                    SELECT
                        items.*,
                        warehouses.name AS warehouse_name
                    FROM tenant_inventory_stock_items AS items
                    JOIN tenant_inventory_warehouses AS warehouses
                        ON warehouses.id = items.warehouse_id
                    ORDER BY lower(warehouses.name) ASC, lower(items.supplier_name) ASC, lower(items.product_name) ASC
                    rS  c              3      K   | ]  }d   ywrU  r5   rW  s     r7   r  z8TenantStore.create_inventory_snapshot.<locals>.<genexpr>  s     ,CQS,CrZ  z
                        SELECT *
                        FROM tenant_inventory_stock_lots
                        WHERE item_id IN (z_)
                        ORDER BY lower(lot_code) ASC, created_at ASC
                        r  c              3  @   K   | ]  }t        |d    xs d        ywr_  r  r  s     r7   r  z8TenantStore.create_inventory_snapshot.<locals>.<genexpr>  s"     ,l[^U37O3P3UTU-V,lr  a  
                    INSERT INTO tenant_inventory_snapshots (
                        id,
                        snapshot_date,
                        label,
                        created_by_user_id,
                        created_by_name,
                        total_warehouses,
                        total_products,
                        total_equivalent_units,
                        created_at,
                        updated_at
                    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                    zInventario del inventory_snapshot_item_ar  
                        INSERT INTO tenant_inventory_snapshot_items (
                            id,
                            snapshot_id,
                            warehouse_id,
                            warehouse_name,
                            product_lookup,
                            supplier_lookup,
                            product_name,
                            supplier_name,
                            total_equivalent_units,
                            created_at,
                            updated_at
                        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                        rf  ri   r  r  r  rS  rT  rv  r   a6  
                            INSERT INTO tenant_inventory_snapshot_lots (
                                id,
                                snapshot_item_id,
                                lot_code,
                                lot_lookup,
                                quantity,
                                units_per_pack,
                                equivalent_units,
                                created_at,
                                updated_at
                            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
                            inventory_snapshot_lot_rG  r  rB  rK  r  )snapshotrd  )rQ  r>   r  rc  r;   r  r  r  rQ   r  r  r   r<  r  r  rO  r  rj   rh  r%  ro   r9  rs   r  r1  rq   r   r   rk  rE  r  )r>  r  r  rc  r  rc  rD  rd  rR  r  r  r  rl  r_  r  ra  rb  r  rv  r  snapshot_item_idr  snapshot_rows                          r7   create_inventory_snapshotz%TenantStore.create_inventory_snapshot  s   
 ))'2LMM??@U@UVJ	''d7+;+;dw?Q?QdUdkkm
+DJJL,<,<+=>!ZZ K	Z..w/D/DE JZ)11_"$  (*   +(,%&&M\$/02
 ",!3!3" (*  '..
 (*  7@@sCD	N@@Ya=bggrk=b=b#'99,C(,C#CL)11+ ,8. 9 h  hj   ( U$//C	N0CRHOOPSTU *-,lbk,l)l&"" $%)-9"N+I.!!: !* :H)A$**,BRBRAS'T$&&  -' 8 >B?)9 : @bA)9 : @bA): ; ArB 8 >B? 9 ?R@!(+C"D"IJ%%!< $0#3#3C4G#L "**  #:$**,:J:J9K L 0 #GJ$7$=2 > #GL$9$?R @ %gj&9&>Q ?DKL\D]Dig.>&? @os %g.@&A&FQ G ) )
A:x !!##@@[YUJZK	Z\ >>|L!2
 	
[ A=bAJZ JZK	Z K	ZsD   +P BO4O*#O4)
O/3IO4P *
O44O=	9P  P	)start_snapshot_idend_snapshot_idc                  | j                  |      st        d      | j                  |j                        5 }|j	                  d      j                         }|D cg c]  }| j                  |       }}|s|d d dddddg g ddcd d d        S |r|r||k(  rt        d      |r| j                  ||      }nt        |      dk\  r|d   }nd }|r| j                  ||      }	n|d   }	||	Q||| j                  |      nd |	| j                  |	      nd ddd|	t        |	d	   xs d      nddg g ddcd d d        S 	 t        j                  t        |d
               }
t        j                  t        |	d
               }|
|kD  r|	|}	}||
}}
t        t        | j                  ||
j                         |j                         fg                  }t        j                   |
t"        j$                  t&        j(                        }t        j                   |t+        d      z   t"        j$                  t&        j(                        }| j-                  |t        |d               }| j-                  |t        |	d               }i }g }| j/                  |d      xr | j/                  |d      }|r| j1                  |d      }d|v rdnd}|
j                         }|t+        d      z   j                         }|j	                  d| d||f      j                         }t3               }|D ]
  }| j5                  |d         }|
||k  s||k\  r%t        |d   xs d      j7                         }t        |d   xs d      j7                         }t        |d   xs d      j7                         }t        |d   xs d      }|dk  r|d   t        |d         nd } 	 | j9                  |||       }!t?        |      t?        |      f}#|jA                  |#||d d!      }$t        |$d	   xs d      |!z   |$d	<    t3        |      t3        |      z  t3        |      z  }%g }&|%D ]I  }#|jC                  |#      }'|jC                  |#      }(|jC                  |#      })t        |(xs
 |'xs |)xs i jC                  d      xs d      }t        |(xs
 |'xs |)xs i jC                  d      xs d      }t        |'xs i jC                  d	      xs d      }*t        |)xs i jC                  d	      xs d      }!t        |(xs i jC                  d	      xs d      }+|*|!z   |+z
  },|*dk(  r|!dk(  r|+dk(  r
|&j=                  ||tE        |*d      tE        |!d      tE        |+d      tE        |,d      d"       L 	 d d d        &jG                  d# $       tE        tI        d% |&D              d      }-tE        tI        d& |&D              d      }.tE        tI        d' |&D              d      }/tE        tI        d( |&D              d      }0| j                        | j                  	      |-|.|/|0|&ddS c c}w # t        $ r}t        d      |d }~ww xY w# t        $ r5 |}!|||f}"|"|vr&|j;                  |"       |j=                  |||d       Y w xY w# 1 sw Y   xY w))Nr  r[  r   zEconsumo = inventario iniziale + ordini confermati - inventario finale)r\  start_snapshotend_snapshotr  r  r  r  r   incoming_data_gapsr  r  z9Per confrontare i consumi servono due fotografie diverse.r  rm   rv  rc  zGNon riesco a leggere correttamente le date delle fotografie inventario.r  rw  r  r  r  r}  rK  r  r  z
                    SELECT
                        items.product_name,
                        items.supplier_name,
                        items.lot_code,
                        items.quantity,
                        COALESCE(items.units_per_pack, a  ) AS resolved_units_per_pack,
                        batches.confirmed_at
                    FROM ordini_items AS items
                    JOIN ordini_batches AS batches
                        ON batches.id = items.batch_id
                    LEFT JOIN ordini_products AS products
                        ON products.id = items.product_id
                    WHERE batches.confirmed_at >= ? AND batches.confirmed_at < ?
                    ORDER BY batches.confirmed_at ASC, items.id ASC
                    confirmed_atrS  ri   rT  rG  rB  r  r  )rS  rT  rG  r   rn  )rS  rT  r  r  r  r  c                    t        | d          t        | d         j                         t        | d         j                         fS r  r  r7  s    r7   r"  z@TenantStore.get_inventory_consumption_overview.<locals>.<lambda>  r  r6   r8  c              3  8   K   | ]  }t        |d            ywr
  r  r  s     r7   r  zATenantStore.get_inventory_consumption_overview.<locals>.<genexpr>       'Wd?.C(D'Wr  c              3  8   K   | ]  }t        |d            ywr  r  r  s     r7   r  zATenantStore.get_inventory_consumption_overview.<locals>.<genexpr>        (Y4t4D/E)F(Yr  c              3  8   K   | ]  }t        |d            ywr  r  r  s     r7   r  zATenantStore.get_inventory_consumption_overview.<locals>.<genexpr>!  rq  r  c              3  8   K   | ]  }t        |d            ywr  r  r  s     r7   r  zATenantStore.get_inventory_consumption_overview.<locals>.<genexpr>"  rs  r  )%rQ  r>   r  r  rO  rh  r  rE  rq   r   r   r  rj   r4  r:   r   r  r   r   r
   r4   r	   rq  r~  r  r   r  rQ   r  r   rs   r[   r9  r   r  r&  r  )1r>  r  rh  ri  rR  snapshot_rowsrl  serialized_snapshotsstart_snapshot_rowend_snapshot_rowstart_snapshot_dateend_snapshot_dater  r  r  r  r  r  r  rm  has_orders_schemar  r  broad_start_datebroad_end_date
order_rowsseen_gap_keysrn  rS  rT  rG  rB  r  r  gap_keyr  r<  r  r  r  r  r  r  r  r  r  r  r  r   s1                                                    r7   "get_inventory_consumption_overviewz.TenantStore.get_inventory_consumption_overview[  s    ))'2LMM**7+@+@A q	Z&.. hj  \i#iTWD$J$J3$O#i #i !5&*$(#$+,,-+,,-*,(oq	 q	0 !_9Jo9] !\]] %)%F%FzSd%e"]#q(%21%5"%)"#'#D#DZQ`#a #0#3 !)-=-E!5dv  eCd&L&LM_&`  IM`p`|D$J$JK[$\  CG#$+,,-eu  fB51ABZ1[1`_`+a  HI,-*,(oQq	 q	lu&*&8&8=OP_=`9a&b#$($6$6s;KO;\7]$^! #%667GI[$4"9JL_%6#DD"-779;L;V;V;XYZK '../BDHHU]UaUabO$,,->PQAR-RTXT\T\emeqeqrMBB:sSefjSkOlmKBB:sScdhSiOjkKEGL79 $ 9 9*n U  !RZ^ZsZst~  AQ  [R "&"<"<ZIZ"[M]apMp0Iv|-#6#@#@#B "3iQ6G"G!R!R!T'//8 9V7V 	W  &~6#$ (*% ( <?5% )uC#'#A#A#nBU#VL#+|o/MQ]anQn #&s>':'@b#A#G#G#IL$'O(<(B$C$I$I$KM"3z?#8b9??AH$S_%9:H1} ADE^A_Akc";<=qu ,)-)H)H%-%-+B *I *$ -\:<Mm<\]C)44,8-:69F 8=VD\=]=bab7cft7tF34S)uV K(3{+;;c,>OOI-/E  %//#.%//#.'++C0"9989r>>~NTRT  !$9989r>>OUSU! !&w}"&9&9:R&S&XWX Y!&B';';<T'U'ZYZ![ %w}"&9&9:R&S&XWX Y!.!?-!O A%.A*=-STBT(4)6).}a)@*/*B).}a)@*/*B	!qq	f 	

 	 	
 $C'WQV'W$WYZ[$S(YSX(Y%Y[\]#C'WQV'W$WYZ[$S(YSX(Y%Y[\]-"DDEWX BBCST&#6$8#6$8"4 g
 	
m $jb  u !jkqttuJ & )1#/"I"-7)--g6.554@5B08!"}q	 q	so   $[Y!4[B$[>AY& I[Z.F;[![&	Z /Y;;Z  [:[=[ [[[c                Z   | j                  |      st        d      | j                  5  | j                  |j                        5 }t               }| j                  ||      }| j                  |||      }|r"| j                  ||      }|j                          | j                  ||      \  }}| j                  ||      }	|	| j                  |	      nd }
g }i }|	!| j                  |t        |	d               \  }}|	| j                  |||	|	d   nd       nd}d d d        d d d        D cg c]0  }| j                  |j!                  t        |d         g             2 }}D cg c]0  }| j                  |j!                  t        |d         g             2 }}| j#                  ||      }| j%                        ||||
t'        |      t)        d |D              t)        d	 |D              t)        d
 |D              dS # 1 sw Y   xY w# 1 sw Y   xY wc c}w c c}w )Nr  r   r  r  r  r   )r  r  c              3  8   K   | ]  }t        |d            ywr  r  r  s     r7   r  z=TenantStore.get_inventory_warehouse_detail.<locals>.<genexpr>b  s     )jTX%5M0N*O)jr  c              3  8   K   | ]  }t        |d            ywr  r  r  s     r7   r  z=TenantStore.get_inventory_warehouse_detail.<locals>.<genexpr>c  s     5v`deDAY<Z6[5vr  c              3  8   K   | ]  }t        |d            ywr  r  r  s     r7   r  z=TenantStore.get_inventory_warehouse_detail.<locals>.<genexpr>d  s     .lY]uT:R5S/T.lr  )r.  r  r  r  comparison_itemsr  r  rv  r  r  r  )rU  r>   r<  r  r  r;   r@  r  rk  rc  rN  r  rj  rj   r  r  r   r  rx  rq   r  )r>  r  rf  rR  r  r  rU   r  current_lots_by_itemr%  r  r  real_lots_by_itemincoming_since_latestrl  r  r  r  s                     r7   rQ  z*TenantStore.get_inventory_warehouse_detail1  s   ??HVWWZZ 	=..w/D/DE =$J	 $ B B:| \!KKJXdpyKz
$($F$FzS_$`M%%':>:Z:Z[egs:t7!#7%)%L%LZYe%f" *5 99:LM !
 57HJ!%18<8Z8Z[egjk}  C  lD  hE  9F5N$5
 (3	 )-(K(K DVDb 2< @hl )L ) :=	 &%=	=6 )
 44S:N:R:RSVWZ[_W`Sace:fg
 
 &
 44S:K:O:OPSTWX\T]P^`b:cd

 
  @@'! A 

 @@O"*$ 0 0!-0&))j\i)j&j255vhu5v2v+..lak.l+l@U
 	
I= =	= 	=2

s0   HCHH05H#+5H(H	HH c                    | j                  |      st        d      t               }d }d}| j                  5  | j	                  |j
                        5 }| j                  ||      }| j                  |d      st        d      | j                  |d      }	d|	v rdnd}
|j                  d|
 d|j                  f      j                         }|t        d	      t        |d
   xs d      j                         }t        |d   xs d      j                         }t        |d   xs d      j                         }t        |j                  xs d      j                         }|xs |}|st        d      |j                  d|
 d|||f      j                         }||n|}t        |d
   xs d      j                         xs |}t        |d   xs d      j                         xs |}t!        |      }t!        |      }t!        |      }||d   t#        |d         nd }n/t!        |      t!        |      k(  r|d   t#        |d         nd }nd }|j$                  t#        |j$                        nd }||n|}t        |j&                  xs d      j                         j)                         }|dvrt        d      |dk(  r|j*                  dk  rt        d      |dk(  r|j*                  dk  rt        d      |j*                  dkD  r#| j-                  |      r||dk  rt        d      | j/                  ||j*                  |      }|&d|	v r"| |j                  d|t1        |d         f       |j                  d|||f      j                         }|t#        |d   xs d      nd}|t        |d         n dt3        j4                         j6                   }|j                  d||f      j                         } | t#        | d    xs d      nd}!| t#        | d!   xs d      nd}"|dk(  rt#        |j*                        }#|}$|$|"z
  }%n|!t#        |j*                        z   }#|"|z   }$|}%t9        ||%z   d"      }&t;        |&      d#k  rd}&|.|#dk  r)|&dk  r$| j=                  ||      cd d d        cd d d        S |Tdt3        j4                         j6                   }|j                  d$||t1        |d         ||||t?        |&d      ||f
       nG|&dk  r|j                  d%|f       n.|j                  d&t1        |d         ||t?        |&d      ||f       |&dk  r|j                  d'|f       n| @|#dkD  r|j                  d(d)t3        j4                         j6                   ||||#||$||f	       nM|#dk  s|$dk  r |j                  d*t        | d         f       n#|j                  d+|#||$|t        | d         f       | jA                  |||,       |j                  d-|f      j                         }'|'(t1        |'d.   xs d      dk(  r|j                  d%|f       |dk(  rm|dkD  rh|j                  d/d0t3        j4                         j6                   |t1        |d         ||||||t#        |j*                        ||d1d2d3|d4    |||f       |j                  d5||f       |jB                  r(| jE                  |||| jG                  d       |6      \  }}|jI                          d d d        d d d        | j=                  ||      }(|| jK                  |      |(d7<   ||(d8<   |(S # 1 sw Y   @xY w# 1 sw Y   DxY w)9Nr  Fr}  r2  rK  r  G
                    SELECT id, product_name, lot_code, supplier_name, 
                    FROM ordini_products
                    WHERE id = ? AND active = 1
                    LIMIT 1
                    r.  rS  ri   rT  rG  z/Seleziona un lotto per il conteggio inventario.'  
                    FROM ordini_products
                    WHERE active = 1
                      AND lower(product_name) = lower(?)
                      AND lower(supplier_name) = lower(?)
                      AND lower(lot_code) = lower(?)
                    LIMIT 1
                    r   >   r   r   z!Operazione inventario non valida.r   r  r   z-La quantita rilevata non puo essere negativa.;Per questo lotto devo sapere quante unita contiene il pack.r  z
                        UPDATE ordini_products
                        SET units_per_pack = ?
                        WHERE id = ?
                        r  
                    SELECT *
                    FROM tenant_inventory_stock_items
                    WHERE warehouse_id = ? AND product_lookup = ? AND supplier_lookup = ?
                    LIMIT 1
                    rv  r   rL  
                    SELECT *
                    FROM tenant_inventory_stock_lots
                    WHERE item_id = ? AND lot_lookup = ?
                    LIMIT 1
                    rB  r  r  r  rM  5DELETE FROM tenant_inventory_stock_items WHERE id = ?a  
                            UPDATE tenant_inventory_stock_items
                            SET
                                product_id = ?,
                                product_name = ?,
                                supplier_name = ?,
                                total_equivalent_units = ?,
                                updated_at = ?
                            WHERE id = ?
                            ri  rN  r  r    
                            UPDATE tenant_inventory_stock_lots
                            SET
                                quantity = ?,
                                units_per_pack = COALESCE(?, units_per_pack),
                                equivalent_units = ?,
                                updated_at = ?
                            WHERE id = ?
                            r   KSELECT COUNT(*) AS total FROM tenant_inventory_stock_lots WHERE item_id = ?r   a  
                        INSERT INTO tenant_inventory_movements (
                            id,
                            warehouse_id,
                            product_id,
                            product_name,
                            product_lookup,
                            supplier_name,
                            supplier_lookup,
                            lot_code,
                            lot_lookup,
                            quantity,
                            units_per_pack,
                            equivalent_units,
                            movement_kind,
                            source_type,
                            source_label,
                            occurred_at,
                            created_at,
                            updated_at
                        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                        inventory_movement_inmanual_stock_addzCarico manuale r'  rO  ro  inventory_sessioninventory_session_replaced)&rQ  r>   r;   r<  r  r  r@  r~  r  rO  rA  r  r  rj   rQ   rG  r[   r   rK  rD  rP   rB  r  r  r   r  r  r   r  r  rQ  r  r  rL  rf  r  rk  r  ))r>  r  rf  r  r  re  saved_session_replacedrR  r  r  r%  rU  seed_product_nameseed_supplier_nameseed_lot_coderequested_lot_coderG  matched_variant_rowresolved_product_rowrS  rT  r  r  r  r]  override_units_per_packrK  rD  target_equivalent_unitsr  current_item_totalr  r  current_lot_quantitycurrent_lot_equivalent_unitsnext_lot_quantitynext_lot_equivalent_unitsdelta_equivalent_unitsnext_item_totalremaining_lotsr  s)                                            r7   add_inventory_stockzTenantStore.add_inventory_stockh  s1	    ))'2LMMJ	04!&ZZ ~	$..w/D/DE }$ $ B B:| \00=NO$%[\\"&"<"<ZIZ"[<LP_<_(8e}%(00GG\F] ^ '') (*  &"#NOO$'N(C(Ir$J$P$P$R!%(_)E)K%L%R%R%T" #K
$;$Ar B H H J%()9)9)?R%@%F%F%H"->$%VWW&0&8&8GG\F] ^ '(:HE' (* $ ?R>]':cn$"#7#G#M2NTTVkZk #$8$I$OR P V V X n\n!2<!@"3M"B.x8
&2 //?@L 12BCD! +
 'x04Em4TTU`aqUrU~U;?O3P-Q  EI*-1*KRKaKaKm%0F0F*Gsw'<S<_!8e{ 1 1 :U;AACIIK	N2$%HII%'*:*:a*?$%eff%'*:*:Q*>$%TUU$$q(CCHM'/>Q3F$%bcc*.*I*I%$--#1 +J +' +6;K;^cv  dC&&
 1#6I$6O2PQ &-- ">?C (*  X`WkU84L+M+RQR%Sqt"191E#htn-_]a]g]g]i]m]m\nKo$,, j) (*  KRJ]uWZ-@-EA'Fcf$ZaZmuW=O5P5UTU/Vsv,%(-g.>.>(?%0G--FIe-e*(<uWEUEU?V(V%0LOf0f--D*"'(:=S(SUV"W'$.&)O#(9Q(>?VWCW>>wUy}$ }$~	$ ~	$~ # /

0@0@/ABG&& $( 4T :;(*)+5%%: '!+"**S$J
 #**	  !$$8$> ? , - #OS 9 ) '* #a'&&S 
 _(1,"**  #11A1A0B C ' ( * 1 . 9 ) )
6 )A-1Ja1O"**R /1
 #**  !2 . 9 ) #GDM 2& 99*gYb9c!+!3!3aJ" (*  "-#nW6M6RQR2SWX2X&&O 

 %*AA*E&&. 2$**,2B2B1CD( 4T :;(*)+$&!'"2"23*3 .-mF.C-DE%%%%/+Z ""
 - 11@D@q@q"$'+'E'Ed'K"+ Ar A=%'= !!#{	}$~	$@
 44WlK(*.*O*OPa*bF&'3IF/0G
}$ }$~	$ ~	$s1   ]4Q]((	]4;I.]()]4(]1	-]44]=c                >   | j                  |      st        d      t        |j                  xs d      j	                         }t        |j
                  xs d      j	                         }t        |j                  xs d      j	                         }t        |j                  xs d      j	                         }t        |j                  xs |j                  xs d      j	                         }|st        d      |st        d      ||k(  rt        d      |st        d      t               }|j                  r| j                  |j                        n|}	| j                  t        |	xs d      d d       }
dt        j                         j                   }| j                   5  | j#                  |j$                        5 }| j'                  ||      }| j'                  ||      }|j)                  d	||f      j+                         }|t        d
      t-        |      }t-        |      }|r|st        d      |j)                  d||f      j+                         }|t        d      t        |d   xs |      j	                         }|xs |}t-        |      }|d   t/        |d         nd }t        |d   xs d      j	                         }t        |d   xs d      j	                         }t        |d   xs t-        |            }t        |d   xs t-        |            }t1        |d   xs d      }t1        |d   xs d      }t1        |d   xs d      }|d   t1        |d         nd }|j2                  t1        |j2                        nd }||n|}| j5                  |      } | j5                  |      }!| r||dk  rt        d      |!r||dk  rt        d      | r|t1        |      z  n|}"|"|z
  }#| j7                  ||j8                  |      }$| r|$t1        |      z  n|$}%|%|dz   kD  rt        dt;        |"d       d      |$|"dz   kD  rt        d      t=        ||%z
  d       }&t=        |"|$z
  d       }'t=        t;        ||#z   |$z
  d!      d       }(|&dk  s|'dk  r |j)                  d"t        |d#         f       n#|j)                  d$|&||'|t        |d#         f       |j)                  d%|f      j+                         })|))t/        |)d&   xs d      dk(  r|j)                  d'|f       n,|j)                  d(||||(||f       | j?                  |||)       | jA                  |||||)       |j)                  d*|||f      j+                         }*|*t        |*d#         n d+t        j                         j                   }+|*t1        |*d   xs d      nd },d }-|*"|j)                  d,|+|f      j+                         }-|-t1        |-d   xs d      nd }.|-t1        |-d   xs d      nd }/|.t1        |j8                        z   }0|/|$z   }1t;        |,|$z   d!      }2|*|j)                  d-|+|||||||2||f
       n|j)                  d(||||2||+f       |-;|j)                  d.d/t        j                         j                   |+|||0||1||f	       n#|j)                  d$|0||1|t        |-d#         f       | j?                  ||+|)       d0|d1    d2|d1    d3| d4}3d5t        j                         j                   }4d5t        j                         j                   }5|4||||||||t1        |j8                        ||$d6d7|3|	||f|5||||||||t1        |j8                        ||$d8d9|3|	||fg}6|jC                  d:|6       |j)                  d;|||f       | jE                  ||||
|<      \  }7}8| jE                  ||||
|<      \  }9}:| j'                  ||      };| j'                  ||      }<|j)                  d=|4|5f      jG                         }=|jI                          d d d        d d d        =D >ci c]   }>t        |>d#         | jK                  |>      " }?}>|?4   |?5   | jM                  ;      | jM                  <      | jO                  7      | jO                  9      8:| jQ                  ||      | jQ                  ||      d>
S # 1 sw Y   xY w# 1 sw Y   xY wc c}>w )?Nr  ri   z Seleziona il magazzino sorgente.z'Seleziona il magazzino di destinazione.zEIl magazzino sorgente e quello di destinazione devono essere diversi.z"Seleziona il prodotto da spostare.r  inventory_transfer_z
                    SELECT *
                    FROM tenant_inventory_stock_items
                    WHERE id = ? AND warehouse_id = ?
                    LIMIT 1
                    z1Nel magazzino sorgente non trovo questo prodotto.zSeleziona il lotto da spostare.r  z.Nel magazzino sorgente non trovo questo lotto.rG  rA  rS  rT  r  r  rv  r   rB  r  rK  r  r  r  z Nel magazzino sorgente hai solo r  z unita disponibili.zALo spostamento supera lo stock disponibile del lotto selezionato.r   r  r  r  af  
                        UPDATE tenant_inventory_stock_lots
                        SET
                            quantity = ?,
                            units_per_pack = COALESCE(?, units_per_pack),
                            equivalent_units = ?,
                            updated_at = ?
                        WHERE id = ?
                        r  r   r    
                        UPDATE tenant_inventory_stock_items
                        SET
                            product_id = ?,
                            product_name = ?,
                            supplier_name = ?,
                            total_equivalent_units = ?,
                            updated_at = ?
                        WHERE id = ?
                        r   r  rL  z
                        SELECT *
                        FROM tenant_inventory_stock_lots
                        WHERE item_id = ? AND lot_lookup = ?
                        LIMIT 1
                        rM  a  
                        INSERT INTO tenant_inventory_stock_lots (
                            id,
                            item_id,
                            lot_code,
                            lot_lookup,
                            quantity,
                            units_per_pack,
                            equivalent_units,
                            created_at,
                            updated_at
                        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
                        r  zSpostamento r'  z -> z (rs  r  outr  r  warehouse_transfer_inB  
                    INSERT INTO tenant_inventory_movements (
                        id,
                        warehouse_id,
                        product_id,
                        product_name,
                        product_lookup,
                        supplier_name,
                        supplier_lookup,
                        lot_code,
                        lot_lookup,
                        quantity,
                        units_per_pack,
                        equivalent_units,
                        movement_kind,
                        source_type,
                        source_label,
                        occurred_at,
                        created_at,
                        updated_at
                    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                    z
                    UPDATE tenant_inventory_warehouses
                    SET updated_at = ?
                    WHERE id IN (?, ?)
                    ro  a  
                    SELECT
                        movements.*,
                        warehouses.name AS warehouse_name
                    FROM tenant_inventory_movements AS movements
                    JOIN tenant_inventory_warehouses AS warehouses
                        ON warehouses.id = movements.warehouse_id
                    WHERE movements.id IN (?, ?)
                    )
source_entrydestination_entrysource_warehousedestination_warehousesource_inventory_sessiondestination_inventory_session!source_inventory_session_replaced&destination_inventory_session_replacedsource_warehouse_detaildestination_warehouse_detail))rQ  r>   rj   rk  rQ   rl  rm  rG  rn  r;   rh  r  r  r  r  r   r<  r  r  r@  rO  r  r[   r   r   rK  r  r  rB  r  r  r  rm  executemanyrf  rh  rk  r  rx  r  rQ  )@r>  r  r  rk  rl  rm  r  requested_source_lot_coder  rh  transfer_inventory_datetransfer_idrR  source_warehouse_rowdestination_warehouse_rowsource_item_rowr  source_lot_lookupsource_lot_rowrn  rG  rA  rS  rT  r  r  current_source_item_totalcurrent_source_lot_quantity"stored_source_lot_equivalent_unitsstored_units_per_packr  rK  source_requires_units_per_packtarget_requires_units_per_pack#current_source_lot_equivalent_units"source_equivalent_units_correctionr  source_quantity_deltanext_source_lot_quantity next_source_lot_equivalent_unitsnext_source_item_totalsource_remaining_lotsdestination_item_rowdestination_item_idcurrent_destination_item_totaldestination_lot_row current_destination_lot_quantity(current_destination_lot_equivalent_unitsnext_destination_lot_quantity%next_destination_lot_equivalent_unitsnext_destination_item_totalr  source_movement_iddestination_movement_idmovement_valuessource_session_rowsource_session_replaceddestination_session_rowdestination_session_replacedupdated_source_warehouse_row!updated_destination_warehouse_rowr  rl  movements_by_ids@                                                                   r7   transfer_inventory_stockz$TenantStore.transfer_inventory_stock  s   
 ))'2LMM!'"="="CDJJL#&w'G'G'M2#N#T#T#V G117R8>>@ !1!1!7R8>>@$'(?(?(Y7CSCS(YWY$Z$`$`$b!"?@@'FGG"::deeABBJ	[b[n[ndBB7CVCVWt}"&"@"@[EVTVAWX[Y[A\"]+DJJL,<,<+=>ZZ U	$..w/D/DE T$'+'I'I*Vi'j$,0,N,Nz[s,t)","4"4 #$78# (*   #*$%XYY./AB
$56O$P!!):$%FGG!+!3!3 #$56" (*  ")$%UVV"%nZ&@&]D]"^"d"d"f-@.x8
CRS_C`ClS!>?rv
"?>#B#HbIOOQ #OO$D$J K Q Q S!$_5E%F%iJ[\hJi!j"%o6G&H&lL]^kLl"m,1/BZ2[2`_`,a).3N:4N4SRS.T+5:>J\;];bab5c2 &&67C .)9:; &
 LSKaKaKm%0F0F*Gsw'<S<_!8ez151\1\]l1m.151\1\]e1f.1~7MQ_cdQd$%bcc1~7MQ_cdQd$%bcc 6 0%2GG; 4
 6Y[}5}2*.*I*I%$--#1 +J +' 6 ,eN.CC0 &
 )+F+MM$:5Adfg;h:ii|}  +-PSW-WW$%hii+./JMb/bdg+h(367Z]t7tvy3z0),36XX[rrtuv*&
 ,t37W[_7_&&N^D124
 && 5*<%t 45& )3(:(:a"$) (* & )4=RSZ=[=`_`9aef9f&&O&(
 &&	 '()2%)( ==j-cl=mFF,"#' G  (2'9'9 .~O( (* % ,7 ,T23*4::<+;+;*<= $ ,7 ./GHMAN /
 '+#'3*4*<*< -j9+ hj ( +6 -j9>Q? 1 +6 -.@AFQG 9
 1QSXY`YiYiSj0j-8`cz8z5.34RUl4lno.p+'/&& 04&(*)+7%%: &&	 '()7%/* '.&& -TZZ\-=-=,>?/$&9*A%%
6 && :*A% 3D 9:& 99*FYen9o!-.B6.J-K4PijpPqOrrt  vA  uB  BC   D':4::<;K;K:L%M",?

@P@P?Q*R' ++"$&%' "g../&/0$#!!%* 00"$&%' "g../&//$#!!%+)#T &&, $/2 ""
  35MN ?C>o>o'#:' ?p ?;"$; IMHyHy,#:' Iz IE')E 04/Q/QR\^q/r,484V4VWac{4|1 * 2 2 ()@A! (*  !!#iT$U	$r %
 D	NDBB3GG
 

 ,,>?!01H!I $ G GHd e%)%L%LMn%o(,(M(MN`(a-1-R-RSj-k1H6R'+'J'J7Tg'h,0,O,OPWYq,r
 	
uT$ T$U	$ U	$n
s+   &dZdd,%dd	ddr;  )rf  r8  c               Z   | j                  |      st        d      t        dt        t	        |      d            }| j                  |j                        5 }d }g }ddg}|rE| j                  ||      }	| j                  |	      }|j                  d       |j                  |       |j                  ddj                  |       d	t        ||gz               j                         }
d d d        
D cg c]  }| j                  |       }}|t        |      d
S # 1 sw Y   6xY wc c}w )N*Il tuo account non puo accedere a Consumi.rm     zmovements.movement_kind = 'out'z,movements.source_type = 'manual_consumption'zmovements.warehouse_id = ?a?  
                SELECT
                    movements.*,
                    warehouses.name AS warehouse_name
                FROM tenant_inventory_movements AS movements
                JOIN tenant_inventory_warehouses AS warehouses
                    ON warehouses.id = movements.warehouse_id
                WHERE r  zx
                ORDER BY movements.occurred_at DESC, movements.created_at DESC
                LIMIT ?
                )r.  r  r  )rS  r>   r  r   r   r  r  r@  rx  rs   rO  r%  ro   rh  r  rq   )r>  r  rf  r8  r  rR  r<  r+  where_clausesr  r+  rl  r  s                r7   list_inventory_consumptionsz'TenantStore.list_inventory_consumptions}!  sB    ,,W5IJJCE
C01
**7+@+@A 	Z#' #%F>@noM $ B B:| \'+'N'N}']$$$%ABl+%% ||M23 4
 f
|+, hj 	2 KOO3499#>OO-w<
 	
5	 	2 Ps   BD3D(D%n  )ru  r  r8  c                  | j                  |      st        d      t        dt        t	        |      d            }t        |xs d      j                         j                         }ddg}g }|Ct        dt        t	        |      d            }	|j                  d	       |j                  |	d
       |r,d| d}
|j                  d       |j                  |
|
|
|
g       | j                  5  | j                  |j                        5 }| j                  |       |j                          |j                  ddj!                  |       dt#        ||gz               j%                         }d d d        d d d        D cg c]  }t        |d   xs d      t	        |d   xs d      t	        |d   xs d      t	        |d   xs d      t'        |d   xs d      t        |d   xs d      xs d t        |d   xs d      xs d d }}|t)        |      |t	        |      nd |xs d ddS # 1 sw Y   xY w# 1 sw Y   xY wc c}w )Nr  rm   r5  ri   consumed_units > 0consumption_date <> ''r[  4  "substr(consumption_date, 1, 4) = ?04dr  
                (
                    lower(product_name) LIKE ?
                    OR lower(supplier_name) LIKE ?
                    OR product_lookup LIKE ?
                    OR supplier_lookup LIKE ?
                )
                aH  
                    SELECT
                        consumption_date,
                        COUNT(*) AS movement_count,
                        MAX(warehouse_count) AS warehouse_count,
                        COUNT(DISTINCT product_lookup || char(0) || supplier_lookup) AS product_count,
                        ROUND(SUM(consumed_units), 6) AS total_equivalent_units,
                        MIN(updated_at) AS first_occurred_at,
                        MAX(updated_at) AS last_occurred_at
                    FROM tenant_inventory_estimated_consumptions
                    WHERE r  z
                    GROUP BY consumption_date
                    ORDER BY consumption_date DESC
                    LIMIT ?
                    r  r%  r   r  ru  rv  first_occurred_atlast_occurred_at)r   r%  r  ru  rv  r  r  zNconsumo stimato = inventario precedente + ordini confermati - nuovo inventario)rx  r  ru  r  r  )rS  r>   r  r   r   rj   rQ   rP   rs   r#  r<  r  r  r2  rk  rO  r%  ro   rh  r   rq   )r>  r  ru  r  r8  r  r  r  r+  	safe_yearr  rR  r+  rl  rx  s                  r7   list_inventory_consumption_daysz+TenantStore.list_inventory_consumption_days!  sx    ,,W5IJJCE
D12
u{+11399; $
  "D#c$i"67I  !EFMMYsO--.a0J  	 MM:z:zJKZZ 	..w/D/DE BB:N!!#!))
 #<<67 8 &J</0!" (*# 	B 
  C 239r:"%c*:&;&@q"A#&s+<'='B#C!$S%9%>Q!?*/4L0M0RQR*S%(-@)A)GR%H%PD$',>(?(E2$F$N$
 
 t9!%!1CIt%- p
 	
E 	 	.
s,   ,H7A"H+*H7>B	I+H4	0H77I c                   | j                  |      st        d      t               j                  }| j                  5  | j                  |j                        5 }| j                  |       |j                          |j                  d      j                         }|j                  d      j                         }|D cg c]&  }t        |d   xs d      t        |d   xs d      f( }}| j                  |      }|D ci c]>  }|d   7t        |d         t        | j                  ||t        |d         |            @ }	}d d d        d d d        D ci c]u  }|d   nt        |d         t        |d         	j!                  t        |d         d	      t        |d
   xs d	      t        |d   xs d	      t#        |d   xs d	      ddw }
}d|hD ]  }|
j%                  ||d	d	d	d	dd        t'        |
      D cg c]  }|
|   	 }}||ddS c c}w c c}w # 1 sw Y   xY w# 1 sw Y   xY wc c}w c c}w )Nr  a  
                    SELECT
                        CAST(substr(consumption_date, 1, 4) AS INTEGER) AS consumption_year,
                        COUNT(*) AS movement_count,
                        COUNT(DISTINCT consumption_date) AS day_count,
                        COUNT(DISTINCT product_lookup || char(0) || supplier_lookup) AS product_count,
                        ROUND(SUM(consumed_units), 6) AS total_equivalent_units
                    FROM tenant_inventory_estimated_consumptions
                    WHERE consumed_units > 0
                      AND consumption_date <> ''
                    GROUP BY substr(consumption_date, 1, 4)
                    ORDER BY consumption_year ASC
                    ,  
                    SELECT DISTINCT period_start_date, period_end_date
                    FROM tenant_inventory_estimated_consumptions
                    WHERE consumed_units > 0
                      AND period_start_date <> ''
                      AND period_end_date <> ''
                    r.  ri   r/  consumption_yearrt  r   r%  ru  rv  T)ru  	day_countr%  ru  rv  has_real_data  Fzhogni anno include i consumi stimati da inventari e ordini confermati; i giorni sono quelli operativi bar)yearscurrent_yearr  )rS  r>   rA   ru  r<  r  r  r2  rk  rO  rh  rj   r3  r   rq   r4  r   r   r9  rt  )r>  r  r  rR  r+  r5  rl  r  r?  operational_days_by_yearsummaries_by_yearrequired_yearru  r  s                 r7    list_inventory_consumption_yearsz,TenantStore.list_inventory_consumption_years!  s   ,,W5IJJ!|((ZZ ,	..w/D/DE +BB:N!!#!)) (*  )00 (*   + 017R8#cBS>T>ZXZ:[\   ==jI  $, -.: ./0#HH&#!$S);%<!=%-	 I 3 ,( ,A+,	n 
 %&2 &'(C 234599#cBT>U:VXYZ"%c*:&;&@q"A!$S%9%>Q!?*/4L0M0RQR*S!%+ 
 
 #L1 	M(()!"&'%&./%*
	 6<<M5NOT"4(OO( !K
 	
W
,A+ +,	 ,	\
0 PsP   H-A$H!=+H(H!?AHH!H-A:H9H>
H!!H*	&H--H6r  )r  r8  c                  | j                  |      st        d      t        dt        t	        |      d            }t        dt        t	        |      d            }t        |xs d      j                         j                         }g d}|dg}	|r,d	| d	}
|j                  d
       |	j                  |
|
|
|
g       | j                  5  | j                  |j                        5 }| j                  |       |j                          |j                  ddj!                  |       dt#        |	|gz               j%                         }|j                  ddj!                  |       dt#        |	            j%                         }i }|D ]i  }t        |d   xs d      t        |d   xs d      f}|j'                  |g       j                  t        |d   xs d      t        |d   xs d      f       k | j)                  |      }|j+                         D ci c]$  \  }}|t-        | j/                  ||||            & }}}d d d        d d d        D cg c]  }|d   t	        |d         nd t        |d   xs d      t        |d   xs d      t        |d   xs d      t        |d   xs d      t	        |d   xs d      t	        |d   xs d      j1                  t        |d   xs d      t        |d   xs d      fd      t3        |d   xs d      t3        |d   xs d      t5        t3        |d   xs d      t        |j1                  t        |d   xs d      t        |d   xs d      fd      d      z  d      t        |d   xs d      xs d t        |d   xs d      xs d t        |d    xs d      j7                  d!      D cg c]  }|r| c}d" }}}||t-        |      t5        t9        d# |D              d      |xs d d$d%S c c}}w # 1 sw Y   xY w# 1 sw Y   xY wc c}w c c}}w )&Nr  r[  r  rm   r5  ri   )r  r  r  r  r  r  a  
                    SELECT
                        product_lookup,
                        supplier_lookup,
                        MIN(product_id) AS product_id,
                        MAX(product_name) AS product_name,
                        MAX(supplier_name) AS supplier_name,
                        COUNT(*) AS movement_count,
                        MAX(warehouse_count) AS warehouse_count,
                        COUNT(DISTINCT consumption_date) AS day_count,
                        ROUND(SUM(consumed_units), 6) AS total_quantity,
                        ROUND(SUM(consumed_units), 6) AS total_equivalent_units,
                        MIN(consumption_date) AS first_consumption_date,
                        MAX(consumption_date) AS last_consumption_date,
                        '' AS lot_codes
                    FROM tenant_inventory_estimated_consumptions
                    WHERE r  z
                    GROUP BY product_lookup, supplier_lookup
                    ORDER BY total_equivalent_units DESC, lower(product_name) ASC, lower(supplier_name) ASC
                    LIMIT ?
                    a$  
                    SELECT DISTINCT
                        product_lookup,
                        supplier_lookup,
                        period_start_date,
                        period_end_date
                    FROM tenant_inventory_estimated_consumptions
                    WHERE zw
                      AND period_start_date <> ''
                      AND period_end_date <> ''
                    r  r  r.  r/  rt  rA  rS  rT  r%  r   r  r{  rv  r  r&  r'  	lot_codes,)rA  rS  rT  r  r  r%  r  r  r{  rv  r#  r&  r'  r  c              3  8   K   | ]  }t        |d            ywr  r  r  s     r7   r  zHTenantStore.list_inventory_consumption_product_totals.<locals>.<genexpr>"  r  r  zXtotali stimati da inventari e ordini confermati; medie divise per i giorni operativi bar)ru  r  r  rv  r  r  )rS  r>   r  r   r   rj   rQ   rP   rs   r#  r<  r  r  r2  rk  rO  r%  ro   rh  r9  r3  r  rq   r4  r   r   r  r   r  )r>  r  ru  r  r8  r  r  r  r  r+  r  rR  r+  r5  r6  r8  r  r?  r  workdays_by_productrl  rT   r  s                          r7   )list_inventory_consumption_product_totalsz5TenantStore.list_inventory_consumption_product_totalsH"  s    ,,W5IJJc#d)T23	CE
D12
u{+11399;

 $-S/3-.a0J  	 MM:z:zJKZZ ;	..w/D/DE :BB:N!!#!))  #<<67 8!* &J</0-. (*/ 0 )00 #<<67 8
 &M (*  TV""- Jz*:;ArBC
SdHeHkikDlmC&11#r:AAZ(;<BCSTeIfIljlEmn
  ==jI );(@(@(B
' %W HH&#!*%-	 I  
'# 
'a:;	B I%
 %
H E 9<L8I8Uc#l"34[_ #C$7$=2 >!$S%9%?R!@"%c*:&;&Ar"B#&s+<'='C#D"%c*:&;&@q"A#&s+<'='B#C044-.4"5s3?P;Q;WUW7XY #(,<(=(B"C*/4L0M0RQR*Sc":;@qA/33!$S)9%:%@b!A3sK\G]GcacCd e !  
 +.c2J.K.Qr*R*ZVZ),S1H-I-OR)P)XTX "%S%5%;!<!B!B3!G ;"%
 %
N u:&+C/hbg/h,hjk&l%- z
 	
e
'a: :;	 ;	v=%
sP   	P-%D2P )P
 P P-E<P?P:&P?P  P*	%P--P7:P?c          
     @   | j                  |      st        d      | j                  |      }| j                  5  | j	                  |j
                        5 }| j                  |       |j                          |j                  d|f      j                         }d d d        d d d        g }D cg c]  }i d|d   t        |d         nd dt        |d   xs d      dt        |d   xs d      dt        |d   xs d      dt        |d   xs d      d	t        |d	   xs d
      dt        |d   xs d
      dt        |d   xs d
      dt        |d   xs d
      dt        |d   xs d      j                  d      D cg c]  }|r| c}dt        |d   xs d      j                  d      D cg c]  }|r| c}dt        |d   xs d
      dt        |d   xs d
      dt        |d   xs d
      dt        |d   xs d
      dt        |d   xs d      xs d dt        |d   xs d      xs d  }	}}||	|t        |	      t        t!        d |	D              d      ddS # 1 sw Y   xY w# 1 sw Y   xY wc c}w c c}w c c}}w )Nr  a7  
                    SELECT
                        product_lookup,
                        supplier_lookup,
                        MIN(product_id) AS product_id,
                        MAX(product_name) AS product_name,
                        MAX(supplier_name) AS supplier_name,
                        COUNT(*) AS movement_count,
                        MAX(warehouse_count) AS warehouse_count,
                        ROUND(SUM(consumed_units), 6) AS total_quantity,
                        ROUND(SUM(consumed_units), 6) AS total_equivalent_units,
                        ROUND(SUM(opening_units), 6) AS opening_units,
                        ROUND(SUM(incoming_units), 6) AS incoming_units,
                        ROUND(SUM(closing_units), 6) AS closing_units,
                        MIN(period_start_date) AS period_start_date,
                        MAX(period_end_date) AS period_end_date,
                        '' AS lot_codes,
                        '' AS warehouse_ids
                    FROM tenant_inventory_estimated_consumptions
                    WHERE consumed_units > 0
                      AND consumption_date = ?
                    GROUP BY product_lookup, supplier_lookup
                    ORDER BY total_equivalent_units DESC, lower(product_name) ASC, lower(supplier_name) ASC
                    rA  rS  ri   rT  r  r  r%  r   r  r{  rv  r  r  warehouse_idsr  r  r  r  r.  r/  c              3  8   K   | ]  }t        |d            ywr  r  r  s     r7   r  zCTenantStore.get_inventory_consumption_day_detail.<locals>.<genexpr>$#  r  r  r  zVdettaglio dei consumi stimati da inventari e ordini confermati per la data selezionata)r   r  r  r  rv  r  )rS  r>   r  r<  r  r  r2  rk  rO  rh  r   rj   r   r   rq   r  r  )
r>  r  r  r  rR  r  r  rl  rT   r  s
             r7   $get_inventory_consumption_day_detailz0TenantStore.get_inventory_consumption_day_detail"  sU    ,,W5IJJ889IJZZ 	..w/D/DE BB:N!!#&..0 %&34 (*5 	@ ,.: !9
 
8 7L8I8Uc#l"34[_C$7$=2 >  S%9%?R!@ !#c*:&;&Ar"B	
 "3s+<'='C#D !#c*:&;&@q"A "3s+<'='B#C !%,<(=(B"C )%4L0M0RQR*S !$S%5%;!<!B!B3!G   !$S%9%?R!@!F!Fs!K" "(  s?';'@q!A)* !%,<(=(B"C+,  s?';'@q!A-. !%,D(E(J"K/0 $S-@)A)GR%H%PD12 "3s+<'='C#D#L3
 
> $u:&+C/hbg/h,hjk&l x
 	
} 	 	X
"!
sJ   JAI6J0CJ	J'J;JBJ6J 	;JJ
Jc                  | j                  |      st        d      t        |      }t        |      }|st        d      t        dt	        t        |      d            }| j                  5  | j                  |j                        5 }| j                  |       |j                          |j                  d||||f      j                         }	d d d        d d d        t        	      D 
cg c]Z  }
t        |
d   xs d      t        |
d   xs d	      t        |
d
   xs d	      t        |
d   xs d	      t        |
d   xs d	      d\ }}
|	rt        |	d	   d   xs |      n|}|	rt        |	d	   d   xs |      n|}|||t!        |      t#        t%        d |D              d      ddS # 1 sw Y   xY w# 1 sw Y   xY wc c}
w )Nr  z+Prodotto non valido per il grafico consumi.rm   r  ac  
                    SELECT
                        consumption_date,
                        MAX(product_name) AS product_name,
                        MAX(supplier_name) AS supplier_name,
                        COUNT(*) AS movement_count,
                        MAX(warehouse_count) AS warehouse_count,
                        ROUND(SUM(consumed_units), 6) AS total_quantity,
                        ROUND(SUM(consumed_units), 6) AS total_equivalent_units
                    FROM tenant_inventory_estimated_consumptions
                    WHERE consumed_units > 0
                      AND product_lookup = ?
                      AND (? = '' OR supplier_lookup = ?)
                      AND consumption_date <> ''
                    GROUP BY consumption_date
                    ORDER BY consumption_date DESC
                    LIMIT ?
                    r  ri   r%  r   r  r{  rv  )r   r%  r  r{  rv  rS  rT  c              3  8   K   | ]  }t        |d            ywr  r  )r  points     r7   r  zFTenantStore.get_inventory_consumption_product_trend.<locals>.<genexpr>c#  s     /k[`e<T6U0V/kr  r  zFsomma giornaliera dei consumi stimati da inventari e ordini confermati)rS  rT  pointsr  rv  r  )rS  r>   r[   r  r   r   r<  r  r  r2  rk  rO  rh  reversedrj   r   rq   r  r  )r>  r  rS  rT  r8  r  r  r  rR  r+  rl  r  display_productdisplay_suppliers                 r7   'get_inventory_consumption_product_trendz3TenantStore.get_inventory_consumption_product_trend(#  s    ,,W5IJJ*<8+M:JKKCE
C01
ZZ 	..w/D/DE BB:N!!#!))$ $_ozR'( (*) 	D  ~	
  C 239r:"%c*:&;&@q"A#&s+<'='B#C"',<(=(B"C*/4L0M0RQR*S	
 	
 KO#d1gn5EFT`MQ3tAw7H=IWd+-v;&+C/kdj/k,kmn&o h
 	
K 	 	4	
s,   +F5AF)F5*AG)F2	.F55F>r  X   )ru  workdaysr  r8  c                  | j                  |      st        d      t        dt        t	        |      d            }t        dt	        |            }t        dt        t	        |      d            }t        t        |xs d            }	| j                  |j                        5 }
| j                  |
d      r| j                  |
d      s||g d	d	d	d	d	d
g ddcd d d        S | j                  |
d      }d|v rdnd}|dd}|dz   dd}|
j                  d| d||f      j                         }|
j                  d      j                         }d d d        i }g }t               }D ]  }t        |d   xs d      j                         }t        |d   xs d      j                         }t        |d   xs d      j                         }t        |d   xs d	      }|r|d	k  r~|d   t        |d         nd }	 | j!                  |||      }t        |      t        |      f}|j'                  ||d   t	        |d         nd ||dd	d       }t        |d!   xs d	      |z   |d!<   t	        |d"   xs d	      dz   |d"<    D ci c]o  }t        |d#   xs d      t        |d$   xs d      f|d   t	        |d         nd t        |d   xs d      t        |d   xs d      t        |d%   xs d	      d&q }}t        |      }g } |D ]k  }|j)                  |      }!|j)                  |      }"t        |!xs i j)                  d!      xs d	      }t        |"xs i j)                  d%      xs d	      }#||#z
  }$t        |$d      }%t        |!xs |"xs i j)                  d      xs |d	         }t        |!xs |"xs i j)                  d      xs |d         }| j%                  |!xs |"xs i j)                  d      ||t+        |d'      t+        |#d'      t+        |$d'      t+        |%d'      t+        |%|z  d'      t+        t        |#|z
  d      d'      t	        |!xs i j)                  d"      xs d	      d(
       n | j-                  d) *       |	r| D &cg c]V  }&|	t        t        |&j)                  d      xs d            v s)|	t        t        |&j)                  d      xs d            v r|&X } }&|D &cg c]  }&|	t        t        |&j)                  d      xs d            v sR|	t        t        |&j)                  d      xs d            v s)|	t        t        |&j)                  d      xs d            v r|& }}&| d | }'t+        t/        d+ | D              d'      }(t+        t/        d, | D              d'      })t+        t/        d- | D              d'      }*|||'t1        |       |	xs d |(|)|*t+        |*|z  d'      d
|d.d/S # 1 sw Y   xY w# t        $ rQ |}t        |      t        |      t        |      f}||vr'|j#                  |       |j%                  |||dd       Y Uw xY wc c}w c c}&w c c}&w )0Nr  r[  r  rm   r5  ri   r  r  r   )ordered_unitscurrent_stock_unitsestimated_consumed_unitsaverage_daily_estimated_unitsz5storico ordini non disponibile per stimare il consumo)ru  r  r  r  totals	data_gapsr  r}  rK  r  r  r  z-01-01a
  
                SELECT
                    items.product_id,
                    items.product_name,
                    items.supplier_name,
                    items.lot_code,
                    items.quantity,
                    COALESCE(items.units_per_pack, a  ) AS resolved_units_per_pack,
                    batches.confirmed_at
                FROM ordini_items AS items
                JOIN ordini_batches AS batches
                    ON batches.id = items.batch_id
                LEFT JOIN ordini_products AS products
                    ON products.id = items.product_id
                WHERE batches.confirmed_at >= ? AND batches.confirmed_at < ?
                ORDER BY batches.confirmed_at ASC, items.id ASC
                a  
                SELECT
                    MIN(product_id) AS product_id,
                    product_lookup,
                    supplier_lookup,
                    MAX(product_name) AS product_name,
                    MAX(supplier_name) AS supplier_name,
                    ROUND(SUM(total_equivalent_units), 6) AS current_stock_units
                FROM tenant_inventory_stock_items
                WHERE total_equivalent_units <> 0
                GROUP BY product_lookup, supplier_lookup
                rS  rT  rG  rB  r  r  zRunits_per_pack mancante: per questa riga ho contato la quantita come unita singole)rS  rT  rG  reasonrA  r   )rA  rS  rT  r  order_line_countr  r  r  r  r  )rA  rS  rT  r  r  )
rA  rS  rT  r  r  raw_estimated_consumed_unitsr  r  stock_exceeds_purchases_unitsr  c                    t        | d          t        | d          t        | d         j                         t        | d         j                         fS )Nr  r  rT  rS  r  r7  s    r7   r"  zETenantStore.get_inventory_consumption_2025_estimate.<locals>.<lambda>$  sV    t6788tO,--D)*002D()//1	 r6   r8  c              3  8   K   | ]  }t        |d            yw)r  Nr  r  s     r7   r  zFTenantStore.get_inventory_consumption_2025_estimate.<locals>.<genexpr>$  rq  r  c              3  8   K   | ]  }t        |d            yw)r  Nr  r  s     r7   r  zFTenantStore.get_inventory_consumption_2025_estimate.<locals>.<genexpr>$  s     -cUYeD9N4O.P-cr  c              3  8   K   | ]  }t        |d            yw)r  Nr  r  s     r7   r  zFTenantStore.get_inventory_consumption_2025_estimate.<locals>.<genexpr>$  s     2m_c5>X9Y3Z2mr  zvstima = max(ordini confermati nell'anno - stock attuale, 0) / giorni lavorati; manca lo storico reale dei consumi 2025)ru  r  r  r  r  r  r  r  )rS  r>   r  r   r   r[   rj   r  r  r~  r  rO  rh  r   rQ   r   r  r   rs   r9  r   r  r&  r  rq   )+r>  r  ru  r  r  r8  target_yearsafe_workdaysr  r  rR  r  r  r2  r3  r  r  purchase_mapr  r  rl  rS  rT  rG  rB  r  r  r  r  r<  	stock_mapr  r  purchasestockr  r   r  r   limited_itemstotal_ordered_unitstotal_current_stock_unitstotal_estimated_consumed_unitss+                                              r7   'get_inventory_consumption_2025_estimatez3TenantStore.get_inventory_consumption_2025_estimateg#  su    ,,W5IJJ$CIt 45As8}-CE
D12
,S"-=>**7+@+@A 9	Z))*nE--j:JK ( -#$)*/0459:	 "$(_9	 9	( #88EVWOIY]lIl,Erx)',F3J%/#.f5H#++4 5R3R 	S" X&%& hj' ( $++ hj Y9	v BD-/	365 (	RCs>28b9??ALO 4 :;AACM3z?0b1779HS_12H8q=ORSlOmOyeC0I,J&K  @D# $ ? ?%%#: !@ !& %\24Em4TUC!,,<?<M<Y#c,&7"8_c$0%2%(()	F ',F?,C,Hq&IM&YF?#),V4F-G-L1)MPQ)QF%&Q(	Rb "
  %&,"-s37H3I3OR/PQ8;L8I8Uc#l"34[_ #C$7$=2 >!$S%9%?R!@',S1F-G-L1'M	T 
	 
 %	)+ 	C#'',HMM#&E!8>r"6"6"G"L1MM"'"(9(9:O(P(UTU"V+8;N+N('*+G'M$ 7E 7R<<^LVPSTUPVWL!8U!8b = =o N XRUVWRXYMLL#+#:u#:"?"?"M$0%2%*=!%<+01Da+H49:VXY4Z056NPQ0R5:;SVc;cef5g5:3?RUb?bdg;hjk5l(+X^,@,@AS,T,YXY(Z	0 	

 	 	
  "#'8TXXn=U=[Y[9\']]#'8TXXo=V=\Z\9]'^^ E  &#'8TXXn=U=[Y[9\']]#'8TXXo=V=\Z\9]'^^#'8TXXj=Q=WUW9X'YY	 I  kz*#C'WQV'W$WYZ[$)#-c]b-c*cef$g!).s2mgl2m/mop)q&%"u:%-!4'@,J167UXe7egh1i	 #:
 	
K9	 9	X   (,\:<Mm<\^opx^yz-/!%%g.$$,8-:(0&z	8
Xs;   #3W= A.W=:X
A4Y'AY,4BY1=X
AY$#Y$c                  | j                  |      st        d      t        dt        t	        |      d            }t        |xs d      j                         j                         }| j                  5  | j                  |j                        5 }| j                  |       g }d}|rd| d}	d}|j                  |	|	g       |j                  d| dt        ||gz               j                         }
|j                  d	      j                         }t!        | j#                  ||D cg c]&  }t        |d
   xs d      t        |d   xs d      f( c}            }|j%                          d d d        d d d        
D cg c]  }| j'                  |       c}t!        |
      ddS c c}w # 1 sw Y   CxY w# 1 sw Y   GxY wc c}w )Nr  rm   r  ri   r  z
                    WHERE lower(product_name) LIKE ?
                       OR lower(supplier_name) LIKE ?
                    zv
                    SELECT *
                    FROM tenant_inventory_consumption_product_stats
                    a  
                    ORDER BY
                        total_consumed_units DESC,
                        average_daily_consumed_units DESC,
                        lower(supplier_name) ASC,
                        lower(product_name) ASC
                    LIMIT ?
                    r  r.  r/  zYmedia giornaliera = consumo stimato totale / giorni operativi bar coperti dagli inventari)r  r  r!  r  )rS  r>   r  r   r   rj   rQ   rP   r<  r  r  r=  r#  rO  ro   rh  rq   r4  rk  r*  )r>  r  r  r8  r  r  rR  r+  where_clauser  r+  r5  rl  r!  s                 r7   (list_inventory_consumption_product_statsz4TenantStore.list_inventory_consumption_product_stats/$  s    ,,W5IJJCE
C01
u{+11399;ZZ -	$..w/D/DE ,$AA*M')!##$%5$6a!8J$L MM:z":;!)) "N #
 &J</0 (*  )00 (*  "%DD" (3 # !%8!9!?R@#cJ[F\Fb`bBcd" !!#Y,$-	$` ^bbVYdLLSQbt9,k
 	
M,$ ,$-	$ -	$` cs=   3F;BF/'+F*F/-F;G*F//F8	4F;;Gc                &   9  j                  |      st        d      t               }|j                  r j	                  |j                        n|} j                  t        |xs d      d d       }t        |j                  xs d      j                         }|st        d      d } j                  5   j                  |j                        5 } j                  ||      }	 j                  |	      }
 j                  |d      st        d       j                  |d      }d|v rdnd}|j!                  d	| d
|j"                  f      j%                         }|t'        d      t        |d   xs d      j                         }t        |d   xs d      j                         }t        |d   xs d      j                         }t        |j(                  xs d      j                         }|xs |}|st        d      |j!                  d	| d|||f      j%                         }||n|}t        |d   xs d      j                         xs |}t        |d   xs d      j                         xs |}t+        |      }t+        |      }t+        |      9||d   t-        |d         nd }n/t+        |      t+        |      k(  r|d   t-        |d         nd }nd }|j!                  d|||f      j%                         }|t        d      t        |d         }t-        |d   xs d      }|j!                  d|f      j/                         }t1        9fd|D        d       } j3                  |      }||rt        d      ||st        d      |j4                  t-        |j4                        nd } ||d   t-        |d         nd }!| | n|!}"|"|}"|r|"|"dk  rt        d       j7                  ||j8                  |"      }#|#|dz   kD  rt        dt;        |d       d      |rx|t        d      t-        |d    xs d      }$t-        |d!   xs d      }%|j8                  |$dz   kD  rt        dt;        |$d       d"| d#      |#|%dz   kD  rt        d$      ||#fg}&npd?9 fd%}'|#}(g }&t=        ||'&      D ]E  })|(dk  r n>t-        |)d!   xs d      }*|*dk  r"t?        |*|(      }+|&jA                  |)|+f       |(|+z  }(G |(dkD  rt        d'      |&D ]   \  },}-t-        |,d    xs d      }$t-        |,d!   xs d      }%t        |,d   xs d      }.|,d   t-        |,d         nd }/ j3                  |.      r/|/}0|0|0dk  r
|$dkD  r|%|$z  }0|0|0dk  rt        d(|. d)      |-|0z  }1n|/}0|-}1tC        |$|1z
  d*      }2tC        |%|-z
  d*      }3|2dk  s|3dk  r |j!                  d+t        |,d         f       |j!                  d,|2|0|3|t        |,d         f        tC        t;        ||#z
  d-      d*      }4 jE                  |||.       |j!                  d/|f      j%                         }5|5)tG        |5d0   xs d      dk(  r|j!                  d1|f       n$|j!                  d2tG        |d         |||4||f       d3tI        jJ                         jL                   }6|j!                  d4|6|tG        |d         |||||9t-        |j8                        |"|#d5d6d7|	d8    |||f       |j!                  d9||f        jO                  |||||:        jQ                  ||.        jS                  |||;      }7|7 jU                  |7      }|jW                          |j!                  d<|6f      j%                         }8|8t'        d=      	 d d d        d d d         jY                  8      
 j[                  ||      |d>S # 1 sw Y   7xY w# 1 sw Y   ;xY w)@Nr  ri   r  z7Seleziona il magazzino da cui stai scaricando la merce.r}  r2  rK  r  r  r  r.  rS  rT  rG  z-Seleziona un lotto per registrare il consumo.r  r  z4Nel magazzino selezionato non trovo questo prodotto.r  rv  r   z
                    SELECT *
                    FROM tenant_inventory_stock_lots
                    WHERE item_id = ?
                    ORDER BY lower(lot_code) ASC, created_at ASC
                    c              3  N   K   | ]  }t        |d    xs d      k(  s|  ywr  r  )r  rl  r  s     r7   r  z;TenantStore.create_inventory_consumption.<locals>.<genexpr>$  s)     f3s<?P?VTV;W[e;efr  z1Nel magazzino selezionato non trovo questo lotto.zJNel magazzino selezionato non trovo lotti disponibili per questo prodotto.r  r  r  zNel magazzino hai solo r  z3 unita equivalenti disponibili per questo prodotto.rB  r  rt  z! disponibili per questo prodotto.zAQuesto consumo supera lo stock disponibile del lotto selezionato.c                    t        | d   xs d      }t        | d   xs d      }|k(  rdndj                  |      rdnd|j                         fS )Nr  ri   rG  r   rm   )rj   r  rP   )rl  
row_lookuprow_lot_coder  r>  s      r7   allocation_priorityzETenantStore.create_inventory_consumption.<locals>.allocation_priority %  s_    %(\):)@b%A
'*3z?+@b'A!+z!9Aq!%!L!L\!ZA`a(..0  r6   r8  z?Questo consumo supera lo stock totale disponibile del prodotto.zPer scalare il lotto z+ devo sapere quante unita contiene il pack.r   r  r  r  r   r  r   r  r  r  r  r  manual_consumptionzConsumo manuale r'  rO  ro  )r  r  a  
                    SELECT
                        movements.*,
                        warehouses.name AS warehouse_name
                    FROM tenant_inventory_movements AS movements
                    JOIN tenant_inventory_warehouses AS warehouses
                        ON warehouses.id = movements.warehouse_id
                    WHERE movements.id = ?
                    LIMIT 1
                    z$Consumo salvato ma non ricaricabile.)r!  r.  warehouse_detailproduct_consumption_stat)rl  sqlite3.Rowr  ztuple[int, int, str]).rS  r>   r;   rh  r  r  rj   rf  rQ   r<  r  r  r@  rx  r~  r  rO  rA  r  r  rG  r[   r   rh  r  r  rK  r  rB  r  rt  r   rs   r  r  r   r  r  r   rf  r=  r,  r*  rk  r  rQ  ):r>  r  r  r  rh  consumption_inventory_daterf  r<  rR  r  r<  r  r%  rU  r  r  r  r  rG  r  r  rS  rT  r  r  r]  r  r  r  r  r  selected_lot_is_packr  r  rK  r  r  r  allocation_rowsr9  remaining_equivalent_unitscandidate_lot_rowcandidate_equivalent_unitsconsumed_from_lotallocated_lot_rowconsumed_equivalent_unitsallocated_lot_codeallocated_units_per_packquantity_divisorr  r  r  r  r  movement_idstat_rowr  r  s:   `                                                        @r7   create_inventory_consumptionz(TenantStore.create_inventory_consumptions$  sv
   
 ,,W5IJJJ	[b[n[ndBB7CVCVWt}%)%C%CCHYWYDZ[^\^D_%`"7//526<<>VWW=A ZZ |	K..w/D/DE {K $ B B:| \'+'N'N}']$00=NO$%[\\"&"<"<ZIZ"[<LP_<_(8e}%(00GG\F] ^ '') (*  &"#NOO$'N(C(Ir$J$P$P$R!%(_)E)K%L%R%R%T" #K
$;$Ar B H H J%()9)9)?R%@%F%F%H"->$%TUU&0&8&8GG\F] ^ '(:HE' (* $ ?R>]':cn$"#7#G#M2NTTVkZk #$8$I$OR P V V X n\n!2<!@"3M"B.x8
&2 //?@L 12BCD! +
 'x04Em4TTU`aqUrU~U;?O3P-Q  EI*-1*%-- ">?C (*  #$%[\\htn-%*84L+M+RQR%S"%-- J (*  fxfhlm'+'R'RS['\$?';$%XYY?8$%qrrKRKaKaKm%0F0F*Gsw' *w7G/H/T '"234 &
 =T<_!8ez!)%;N'^-C~YZGZ$%bcc*.*I*I%$--#1 +J +' +-?$-FF$1%8JA2N1O  PC  D  (()\]]+01D1I+J(38AS9T9YXY3Z0''*>*EE(5e<PRS6T5UUVW_V`  aB  C  /1MPT1TT()lmm(/1H'I&JO 2I.GIO-3HBU-V H)5=!5:;LM_;`;ede5f25:$,/0JLf,g)'..0ACT/UV26GG2H 2D8()jkkDS 0@%'@+01B:1N1SRS+T(389JK]9^9cbc3d0),->z-J-Pb)Q& --=>J /0@AB! -
 BBCUV+C(,48HA8MSgjkSk/KNb/b,+37G17L","78J7KKv w#  -FHX,X)+C(,E)(+,@CT,TVY(Z%034PSl4lnq0r-(D04MQU4U"**R !24!89;
 #**  !2 0 9 ) #$5d$; <?0d #&e,>AX,XZ[&\^a"b99*gYb9c!+!3!3aJ" (*  "-#nW6M6RQR2SWX2X&&O 

 &&	   4T :;()+%#* !4DJJL4D4D3EF"". $$067$&%' "g../&/,*=+@*AB#!!%/+X ""
 - AA #=' B  AA*XaAbLL#1$3 M 
 '/3/e/efn/o,!!#)11	 !N  (*   '"#IJJ (u	{K|	K~	 ;;LI- $ C CG\ Z(@	
 	
{	{K {K|	K |	Ks%   5`[3_;`;`	 ``c                b    |j                  d|f      j                         }|t        d      |S )Nz:SELECT * FROM tenant_homemade_recipes WHERE id = ? LIMIT 1zRicetta homemade non trovata.rG  )r>  rR  r  rl  s       r7   r  z%TenantStore._read_homemade_recipe_row%  s>      HL
 (* 	 ;:;;
r6   current_recipe_idc                   |j                  d|f      j                         }|t        d      |rt        |d         |k(  rt        d      |S )Nz
            SELECT id, name, name_lookup
            FROM tenant_homemade_recipes
            WHERE id = ?
            LIMIT 1
            z6La prep interna selezionata non esiste piu nel locale.r  z6Una ricetta non puo usare se stessa come prep interna.)rO  r  r>   rj   )r>  rR  rt  rO  rl  s        r7   #_resolve_homemade_linked_recipe_rowz/TenantStore._resolve_homemade_linked_recipe_row%  sd        
 (* 	 ;UVVSY3D!DUVV
r6   c          
        g }t        |      D ]  \  }}t        |j                  xs d      }|dk  r&t        t	        |dd            }t        t	        |dd      xs d      j                         xs d }	t        |j                  xs d      j                         }
|	r_| j                  ||	|      }|dvrt        d      t        |d	   xs d      j                         }
t        t        |d
   xs |
            }n|
st        |
      }|j                  |
||	|||d        |st        d      t        |      }t        d |D              }|dk  r|st        d      t        |      }||||fS )Nr   r   r   rt  ri   rN  >   r   r   z6Una prep interna puo essere dosata solo in ml o parti.r'  r  )rr  ingredient_lookuprt  r   r   r  z4Inserisci almeno un ingrediente con una dose valida.c              3  \   K   | ]$  }t        |j                  d       xs d      dv  & yw)r   r   r   Nr   r  s     r7   r  zDTenantStore._normalize_homemade_write_ingredients.<locals>.<genexpr>&  s2      .
 +,459[[.
s   *,z;La ricetta deve avere almeno una dose proporzionale valida.)	enumerater   r   r   rs  rj   rQ   rr  rQ  r>   r[   rs   r   r  r   )r>  rR  ingredients_payloadrO  normalized_ingredientsindexr   r   r   rt  rr  linked_recipe_rowrS  r  has_fixed_per_liter_ingredientsrecipe_measurement_units                   r7   %_normalize_homemade_write_ingredientsz1TenantStore._normalize_homemade_write_ingredients%  s    ;=$%89 !	KE4 0 0 5A6KaB74QceiCjk"741CR#H#NBOUUW_[_!$"6"6"<"=CCEO$($L$L$&7 %M %!
 $>9$%]^^"%&7&?&E2"F"L"L"N$5c:KM:Z:m^m6n$o!&$5o$F!"))'6):(8#.(8"'	1!	F &STT78NO*- .
..
 +
' !$CZ[["JKa"b%{4SUlllr6   c                  
 t        t        |dd      xs d      j                         }t        t        |dd      xs d      }t        t        |dd      xs d      }t        t        |dd      xs d      j                         j	                         }|dvrd}|s|dk  r|dk  rd d d d d d	S |r
|dk  s|dk  rt        d
      t        |      
t        
fd|D        d       }|t        
fd|D        d       }|t        d      t        |j                  d      xs d      j                         }t        |j                  d      xs d      j                         }	||	|||d	S )Nr   ri   r   r   r   r{  >   rz   r   rz   )r   rV  r   r{  r   z]Completa ingrediente guida, quantita base e resa finale stimata, oppure lascia la resa vuota.c              3  d   K   | ]'  }t        |j                  d       xs d      k(  r| ) ywrS  ri   Nr   r  r   rS  s     r7   r  z@TenantStore._normalize_homemade_yield_payload.<locals>.<genexpr>;&  s6      z~~&9:@bAEVV    -0c              3     K   | ]H  }rDt        |j                  d       xs d      v s t        |j                  d       xs d      v r| J ywr_  r   r`  s     r7   r  z@TenantStore._normalize_homemade_yield_payload.<locals>.<genexpr>D&  sV      "()S@S1T1ZXZ-[[z~~.ABHbIM^^ s   AAz=Scegli l'ingrediente guida tra gli ingredienti della ricetta.rr  rS  )	rj   rs  rQ   r   rP   r>   r[   r  r   )r>  r  rW  rr  r   r   
input_unitmatched_ingredientcanonical_ingredient_namecanonical_ingredient_lookuprS  s             @r7   !_normalize_homemade_yield_payloadz-TenantStore._normalize_homemade_yield_payload&  s   
 gg/FKQrRXXZWW.BAFK!L''+<a@EAF	*<bAGRHNNPVVX
[(J<1#4a)-+/&*$(#'  ,!"3yA~o  .o>!"8
 
 %!%&< " %\]]$'(:(>(>?P(Q(WUW$X$^$^$`!&)*<*@*@AT*U*[Y[&\&b&b&d#%>'B". *(
 	
r6   c           
     d   |D ci c]  }|g  }}|s|S dj                  d |D              }|j                  d| dt        |            j                         }|D ci c]  }|g  }}|D ]1  }t	        |d         }|j                  |g       j                  |       3 |j                         D ]  \  }}	|	r#t	        |	d   d   xs d      j                         nd}
|	rt        |	d   d	   xs d      nd
}|	rt        |	d   d   xs d      nd
}|	D cg c]N  }t        |d   xs d      t	        d|j                         v r|d   nd|j                         v r|d   nd      dP }}t        ||
||      }|	D cg c]  }| j                  ||       c}||<    |S c c}w c c}w c c}w c c}w )NrS  c              3      K   | ]  }d   ywrU  r5   rW  s     r7   r  z@TenantStore._load_homemade_recipe_ingredients.<locals>.<genexpr>d&  s      9 9rZ  a  
            SELECT
                ingredients.*,
                COALESCE(NULLIF(trim(ingredients.measurement_unit), ''), recipes.measurement_unit, 'ml') AS effective_measurement_unit,
                recipes.yield_ingredient_name AS recipe_yield_ingredient_name,
                recipes.yield_input_amount AS recipe_yield_input_amount,
                recipes.yield_output_ml AS recipe_yield_output_ml,
                linked_recipes.name AS linked_recipe_name
            FROM tenant_homemade_recipe_ingredients AS ingredients
            JOIN tenant_homemade_recipes AS recipes
              ON recipes.id = ingredients.recipe_id
            LEFT JOIN tenant_homemade_recipes AS linked_recipes
              ON linked_recipes.id = ingredients.linked_recipe_id
            WHERE recipe_id IN (zf)
            ORDER BY ingredients.sort_order ASC, lower(ingredients.ingredient_name) ASC
            r  r   recipe_yield_ingredient_nameri   recipe_yield_input_amountr   recipe_yield_output_mlr   r  r   r   )r   r   r   )r%  rO  ro   rh  rj   r9  rs   r  rQ   r   r  r   r  )r>  rR  
recipe_idsr  r:  rb  r+  rows_by_reciperl  recipe_rowsr   r   r   reference_ingredientsr  s                  r7   !_load_homemade_recipe_ingredientsz-TenantStore._load_homemade_recipe_ingredients\&  s*   
 Wa6ay"}6a6aNyy 9j 99!!! ". /  *#
$ (*% 	& Xb7b)	27b7b 	ACC,-I%%i4;;C@	A '5&:&:&< 	"I{U`KN#ABHbIOOQfh " ]h{1~6Q'R'WVW!XmpVaeKN3K$L$QPQRgjO '
%  $)]);)@q#A(+7388:E 898Jchhj8XS!34^b)
%! 
% G%&;#5 /	O '" >>sOT"GI/	6 m 7b. 8c
%""s   
F
F#AF(;F-c                j    | j                  |      }t        |      |d<   t        |      |d<   ||d<   |S )Nr  r  r   )r  r   rq   )r>  rl  r   recipe_payloads       r7   "_serialize_homemade_recipe_payloadz.TenantStore._serialize_homemade_recipe_payload&  sC    
 <<SA(I+(V}%-0-=)*(3}%r6   c                   | j                  |      st        d      | j                  |j                        5 }|j	                  d      j                         }|D cg c]  }t        |d          }}| j                  ||      }d d d        D cg c]0  }| j                  |j                  t        |d         g             2 }}|t        |      | j                  |      dS c c}w # 1 sw Y   gxY wc c}w )Nr  z
                SELECT *
                FROM tenant_homemade_recipes
                ORDER BY lower(name) ASC, created_at ASC
                r  )recipesr  
can_manage)r`  r>   r  r  rO  rh  rj   rq  rt  r   rq   rb  )r>  r  rR  ro  rl  rm  ingredients_by_reciperv  s           r7   list_homemade_recipesz!TenantStore.list_homemade_recipes&  s   ((1JKK**7+@+@A 		cZ$,, hj  5@@S#c$i.@J@$($J$J:Wa$b!		c #
 33C9N9R9RSVWZ[_W`Sace9fg
 

 w<33G<
 	
 A		c 		c
s#   $C+C&1C+5C7&C++C4c                ,   | j                  |      st        d      | j                  |j                        5 }| j	                  ||      }| j                  ||g      j                  |g       }d d d        d| j                        iS # 1 sw Y   xY w)Nr  r  )r`  r>   r  r  r  rq  r   rt  )r>  r  r  rR  r  r   s         r7   get_homemade_recipezTenantStore.get_homemade_recipe&  s    ((1JKK**7+@+@A 	mZ77
INJ@@i[Y]]^giklK	m $AA*kZ[[		m 	ms   6B

Bc                    | j                  |      st        d      t        |j                  xs d      j	                         }t        |      dk  rt        d      t        |j                  xs d      j	                         xs d }t        t        |dd            }t               }dt        j                         j                   }t        |      }| j                  5  | j                  |j                         5 }	|	j#                  d|f      j%                         }
|
t        d	      | j'                  |	|j(                        \  }}}}| j+                  ||      }t-        ||d
   |d   |d         }|	j#                  d|||||||d
   |d   |d   |d   |d   |t        |      |j.                  |j0                  xs |j2                  xs |j4                  ||f       |D ]  }t        |j7                  d      xs d      }|dvr|dkD  rt9        |d         |z  nd}|	j#                  ddt        j                         j                   ||d   |d   |j7                  d      t9        |d         |||dz  t;        |d         ||f        |	j=                          | j?                  |	|      }| jA                  |	|g      j7                  |g       }d d d        d d d        d| jC                        iS # 1 sw Y   %xY w# 1 sw Y   )xY w)N+Il tuo account non puo modificare Homemade.ri   r  !Inserisci un nome ricetta valido.ry  r-   homemade_recipe_zDSELECT id FROM tenant_homemade_recipes WHERE name_lookup = ? LIMIT 10Esiste gia una ricetta homemade con questo nome.r   r   r   r   aK  
                    INSERT INTO tenant_homemade_recipes (
                        id,
                        name,
                        name_lookup,
                        measurement_unit,
                        usage_scope,
                        notes,
                        yield_ingredient_name,
                        yield_ingredient_lookup,
                        yield_input_amount,
                        yield_input_unit,
                        yield_output_ml,
                        total_parts,
                        ingredient_count,
                        created_by_user_id,
                        created_by_name,
                        created_at,
                        updated_at
                    )
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                    rV  r{  r   r   r   r   r   r     
                        INSERT INTO tenant_homemade_recipe_ingredients (
                            id,
                            recipe_id,
                            ingredient_name,
                            ingredient_lookup,
                            linked_recipe_id,
                            part_amount,
                            measurement_unit,
                            share_ratio,
                            percentage,
                            sort_order,
                            created_at,
                            updated_at
                        )
                        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                        homemade_ing_rr  rS  rt  r  r  r  )"rb  r>   rj   r'  rQ   rq   r\  rc   rs  r;   r  r  r   r[   r<  r  r  rO  r  r\  r   rg  r   r1  r  r  r  r   r   r   rk  r  rq  rt  )r>  r  r  r~  r\  ry  r  r  r  rR  existingrW  r   _has_fixed_per_liter_ingredientsr[  yield_fieldsr  r   ingredient_unitr  r  r   s                         r7   create_homemade_recipez"TenantStore.create_homemade_recipe&  s}   
 ((1JKK',,,"-335{a@AAGMM'R(..08D5gg}V\6]^J	&tzz|'7'7&89	)+6ZZ k	q..w/D/DE jq%--Z"$ (*  '$%WXX >>z7K^K^_ o&5UWn  $EEgOef"J**67N*O'34H'I$01B$C	# "". "#%/#$%<=$%>?$%9:$%78$%67#23))SW-=-=SASAS!!#/*X #9 'J&)*..9K*L*TPT&UO +2TTYhklYl j78?J   
 &&$ ,DJJL,<,<+=>%&'89&':;&NN+=>!*]";<+''#-
< 89%%% 'R !!#!;;J	R
"DDZR[Q\]aabkmopUjqk	qZ $AA*kZ[[Yjq jqk	q k	qs%    L<GK8L8L	=LLc                   | j                  |      st        d      t        |j                  xs d      j	                         }t        |      dk  rt        d      t        |j                  xs d      j	                         xs d }t        t        |dd            }t               }t        |      }| j                  5  | j                  |j                        5 }	| j                  |	|       |	j                  d||f      j!                         }
|
t        d      | j#                  |	|j$                  |	      \  }}}}| j'                  ||      }t)        ||d
   |d   |d         }|	j                  d||||||d
   |d   |d   |d   |d   |t        |      ||f       |	j                  d|f       |D ]  }t        |j+                  d      xs d      }|dvr|dkD  rt-        |d         |z  nd}|	j                  ddt/        j0                         j2                   ||d   |d   |j+                  d      t-        |d         |||dz  t5        |d         ||f        |	j7                          | j                  |	|      }| j9                  |	|g      j+                  |g       }d d d        d d d        d| j;                        iS # 1 sw Y   %xY w# 1 sw Y   )xY w) Nr}  ri   r  r~  ry  r-   zPSELECT id FROM tenant_homemade_recipes WHERE name_lookup = ? AND id != ? LIMIT 1r  rN  r   r   r   r   a  
                    UPDATE tenant_homemade_recipes
                    SET name = ?,
                        name_lookup = ?,
                        measurement_unit = ?,
                        usage_scope = ?,
                        notes = ?,
                        yield_ingredient_name = ?,
                        yield_ingredient_lookup = ?,
                        yield_input_amount = ?,
                        yield_input_unit = ?,
                        yield_output_ml = ?,
                        total_parts = ?,
                        ingredient_count = ?,
                        updated_at = ?
                    WHERE id = ?
                    rV  r{  zBDELETE FROM tenant_homemade_recipe_ingredients WHERE recipe_id = ?r   r   r   r   r   r   r  r  rr  rS  rt  r  r  r  )rb  r>   rj   r'  rQ   rq   r\  rc   rs  r;   r[   r<  r  r  r  rO  r  r\  r   rg  r   r   r   r  r  r   r   rk  rq  rt  )r>  r  r  r  r~  r\  ry  r  r  rR  r  rW  r  r  r[  r  r  r   r  r  r  r   s                         r7   update_homemade_recipez"TenantStore.update_homemade_recipeH'  sq    ((1JKK',,,"-335{a@AAGMM'R(..08D5gg}V\6]^J	)+6ZZ k	q..w/D/DE jq..z9E%--f"I. (*  '$%WXX >>"++*3 ?  o&5UWn  $EEgOef"J**67N*O'34H'I$01B$C	# ""$ $%/#$%<=$%>?$%9:$%78$%67#23!!%"F ""XL #9 'J&)*..9K*L*TPT&UO +2TTYhklYl j78?J   
 &&$ ,DJJL,<,<+=>%&'89&':;&NN+=>!*]";<+''#-
< 89%%% 'R !!#!;;J	R
"DDZR[Q\]aabkmopUjqk	qZ $AA*kZ[[Yjq jqk	q k	qs%   ?KGK(KK	KK!c                   | j                  |      st        d      | j                  5  | j                  |j                        5 }| j                  ||      }| j                  ||g      j                  |g       }| j                  ||      }|j                  d|f       |j                          d d d        d d d        ddS # 1 sw Y   xY w# 1 sw Y   xY w)Nr}  z0DELETE FROM tenant_homemade_recipes WHERE id = ?T)r:  r  )rb  r>   r<  r  r  r  rq  r   rt  rO  rk  )r>  r  r  rR  r  r   serialized_recipes          r7   delete_homemade_recipez"TenantStore.delete_homemade_recipe'  s    
 ((1JKKZZ 	$..w/D/DE $!;;J	R
"DDZR[Q\]aabkmop$($K$KJXc$d!""#UXaWcd!!#$	$  +<==$ $	$ 	$s$   CA+C0CC	
CC)r  r  drive_updated_atc               ^   | j                  |      st        d      t               }| j                  5  | j	                  |j
                        5 }| j                  ||      }	|j                  d||xs t        |	d   xs d      xs d |xs t        |	d   xs d      xs d |xs |||f       |j                          | j                  ||      }
| j                  ||g      j                  |g       }d d d        d d d        d| j                  
      iS # 1 sw Y   %xY w# 1 sw Y   )xY w)Nr  af  
                    UPDATE tenant_homemade_recipes
                    SET preparation_date = ?,
                        preparation_drive_file_id = ?,
                        preparation_drive_web_url = ?,
                        preparation_drive_updated_at = ?,
                        updated_at = ?
                    WHERE id = ?
                    rX  ri   rY  r  )r`  r>   r;   r<  r  r  r  rO  rj   rk  rq  r   rt  )r>  r  r  r  r  r  r  r  rR  current_rowr  r   s               r7   set_homemade_recipe_preparationz+TenantStore.set_homemade_recipe_preparation'  sN    ((1JKKJ	ZZ 	q..w/D/DE q"<<ZS"" $%d[9T-U-[Y[)\d`d%d[9T-U-[Y[)\d`d(5I!!& !!#!;;J	R
"DDZR[Q\]aabkmop/q	q4 $AA*kZ[[3q q	q 	qs$   D#B$D3D#D 	D##D,c                    |j                  dt        f      j                         }t        |xr |d   j	                               S )N4SELECT password_hash FROM users WHERE id = ? LIMIT 1r  )rO  r  r  r   rQ   r>  rR  rl  s      r7   _is_super_admin_configuredz&TenantStore._is_super_admin_configured(  sG      B "
 (* 	 C8C066899r6   c                    |j                   dk7  ry d|j                  |j                  k7  |j                  |j                  |j                  |j                  |j
                  dS )Nr   T)is_super_adminis_impersonatingr  r  r  acting_tenant_idacting_tenant_slug)r   r  r  r  r  r  r  s     r7   _build_platform_admin_contextz)TenantStore._build_platform_admin_context(  sa    <<=( # ' 1 1W5K5K K%44 ' 8 8 ' 8 8 ' 1 1")"5"5
 	
r6   c                L   d|j                   |j                  |j                  |j                  |j                  |j
                  |j                  t        |j                        t        |j                        d| j                  |j                        | j                  |      dS )NT)r  r  r'  r  r  r   r   r+  )successsession_tokencurrent_usertenant_contextplatform_admin)r  r1  r  r  r  r  r   r   r   r+  get_tenant_contextr  r  s     r7   build_auth_responsezTenantStore.build_auth_response(  s    $]]oo$..))#,, ++#G$7$78$()A)A$B	 #55g6G6GH"@@I
 	
r6   c                v   | j                  |      }|d   xs dj                         }|d   xs dj                         }|d   xs dj                         }|d   xs dj                         }|d   xs dj                         }|d   }	|d   }
|r |r|r|r|j                  j                         st        d	      t	        |      }d
t        j                         j                   }dt        j                         j                   }dt        j                         j                   }t               }t        |j                  j                               }t        | j                  | dz        }|j                  d|f      j                         }|t        d      | j                  ||      xs | j                  ||      }|t        d      |j                  d|||||f       |j                  d|||||	|
|f       |j                  d||||||	|d|f	       t        D ]a  }|j                   dk(  rdnd}|rdnd}|j                  ddt        j                         j                   ||j"                  |||r|nd d f       c |j%                          | j'                  |||||||	|
||||	||       ||fS )Nr  ri   r  r  r  r  r	  r
  z6Compila tutti i campi obbligatori per creare il localetenant_venue_user_r  z$SELECT 1 FROM tenants WHERE slug = ?z@Esiste gia un locale con questo nome. Scegli un nome differente.z Username o email gia registrati.z|
            INSERT INTO tenants (id, name, slug, database_path, created_at)
            VALUES (?, ?, ?, ?, ?)
            z
            INSERT INTO venues (id, tenant_id, name, address, phone_number, whatsapp_number, created_at)
            VALUES (?, ?, ?, ?, ?, ?, ?)
            z
            INSERT INTO users (id, tenant_id, name, username, email, phone_number, password_hash, role, created_at)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
            r   r  rm   r   r  r  z
                INSERT INTO tenant_modules (id, tenant_id, module_key, enabled, plan_name, activated_at, expires_at)
                VALUES (?, ?, ?, ?, ?, ?, ?)
                tm_r  )r  rQ   r   r>   rW   r  r  r   r;   r   rj   r3  rO  r  r  r   r  r  rk  r  )r>  rR  r  normalized_payloadr  r  r  r  r  r	  r
  rV   r  venue_idr1  r  r  r  slug_existsr  r  r  r  s                          r7   _create_customer_tenantz#TenantStore._create_customer_tenant-(  s   
 "BB7K)-8>BEEG(6<"CCE
&z28b??A#G,299;%i06B==?).9,->?*HEQXQaQaQgQgQiUVV{+djjl../0	DJJL,,-.$**,**+,Z
&w'7'7'='='?@D,,$x/@@A (()ORVQXYbbd"_``11*hGy4KfKfgqsxKy"?@@ T=*E	
 	 y+woWab	
 	
 
	
$ % 	F!==H4a!G-4))I
 $**,**+,JJ")Jt	& 	$$'#"%+!#+'! 	% 	
  '!!r6   c                    | j                         5 }|j                  dt        f      j                         }t	        |xr |d         cd d d        S # 1 sw Y   y xY w)N3SELECT COUNT(*) AS total FROM tenants WHERE id != ?r   )rL  rO  r  r  r   r  s      r7   has_tenantszTenantStore.has_tenants(  s]    ##% 	.$$E&( hj  ,G-	. 	. 	.s   7AAc           
     :   | j                         5 }|j                  dt        f      j                         d   }|j                  dt        f      j                         d   }| j                  |      }dd||| | t        |sdnddcd d d        S # 1 sw Y   y xY w)	Nr  r   z1SELECT COUNT(*) AS total FROM users WHERE id != ?readyzsqlite-multi-tenantz-Imposta la password iniziale del super admin.zFAccedi come super admin e crea o gestisci i locali dal pannello admin.)r  modetenant_count
user_count	first_runsuper_admin_bootstrap_requiredsuper_admin_identifier	next_step)rL  rO  r  r  r  r  r  )r>  rR  r  r  super_admin_configureds        r7   r  zTenantStore.status(  s    ##% 	%--E&( hj"L $++C$& hj"J &*%D%DZ%P"!- ,(!776L2L*> 2 Da	 	 	s   A6BBc                    | j                   5  | j                         5 }| j                  ||      \  }}| j                  |||      cd d d        cd d d        S # 1 sw Y   nxY w	 d d d        y # 1 sw Y   y xY w)Nr  r1  )r<  rL  r  r  )r>  r  rR  r  r1  s        r7   registerzTenantStore.register(  s    ZZ 	^'') ^Z%)%A%A*g%V"	7++J)U\+]^ ^	^ 	^^ ^ ^	^ 	^ 	^s"   A0)A	A0A#	A00A9c           	     z   |j                   j                         }|j                  j                         }|r|st        d      | j                  5  | j                         5 }| j                  ||      }|st        d      d}|D ]b  }|d   xs dj                         }|d   dk(  r|sd}'|rt        ||      s6| j                  ||d	   |d
         c cd d d        cd d d        S  |r$t        |      dk(  r|d   d   dk(  rt        d      t        d      # 1 sw Y   nxY w	 d d d        y # 1 sw Y   y xY w)NzInserisci credenziali valide.zCredenziali non valide.Fr  ri   r   r   Tr  r  r  rm   r   zIPassword super admin non ancora impostata. Completa la prima attivazione.)
r  rQ   r   r>   r<  rL  r  r   r  rq   )	r>  r  r  r   rR  r  bootstrap_requireduserr   s	            r7   loginzTenantStore.login(  sc   ''--/
##))+<==ZZ 	<'') <Z!:::zR
!$%>??%*"& mD#'#8#>B"E"E"GKF|}4[-1* &.>x.U //
d;FWaefjak/ll< <	< 	<m &#j/Q*>:a=QWCX\iCi$%pqq !:;;%< < <	< 	< 	<s+   D1!A5D	D1)2DD$	 D11D:c                    | j                   5  | j                         5 }|j                  d|f       |j                          d d d        d d d        y # 1 sw Y   xY w# 1 sw Y   y xY w)N$DELETE FROM sessions WHERE token = ?)r<  rL  rO  rk  )r>  r  rR  s      r7   logoutzTenantStore.logout(  se    ZZ 	$'') $Z""#IE8T!!#$	$ 	$$ $	$ 	$s"   A$AAA	AA(c                n   | j                         5 }|j                  d|f      j                         }|
	 d d d        y t        j                  |d         }|t               k  r-|j                  d|f       |j                          	 d d d        y | j                  |      cd d d        S # 1 sw Y   y xY w)Nav  
                SELECT
                    sessions.token,
                    sessions.tenant_id,
                    sessions.user_id,
                    sessions.expires_at,
                    tenants.slug AS tenant_slug,
                    tenants.name AS tenant_name,
                    home_tenants.id AS home_tenant_id,
                    home_tenants.slug AS home_tenant_slug,
                    home_tenants.name AS home_tenant_name,
                    tenants.database_path AS database_path,
                    users.email AS user_email,
                    users.username AS username,
                    users.name AS user_name,
                    users.role AS role,
                    users.permissions_json AS permissions_json,
                    users.assistant_scopes_json AS assistant_scopes_json
                FROM sessions
                JOIN tenants ON tenants.id = sessions.tenant_id
                JOIN users ON users.id = sessions.user_id
                JOIN tenants AS home_tenants ON home_tenants.id = users.tenant_id
                WHERE sessions.token = ?
                LIMIT 1
                r  r  )rL  rO  r  r   r  r8   rk  r  )r>  r  rR  rl  r  s        r7   r  zTenantStore.get_session(  s    ##% '	8$$2 56 hj7 : {?'	8 '	8B "//L0ABJGI%""#IE8T!!#K'	8 '	8N 2237O'	8 '	8 '	8s   %B+A	B+B++B4c                   | j                         5 }|j                  d|f      j                         }d d d        y t        di dd| d|d   d|d   d|d   d|d   d|d   d	|d   d
|d
   d|d   d|d   d|d   d|d   dt	        |d   |d         dt        |d   |d   |d         d|d   dt               t        d      z   j                         S # 1 sw Y   xY w)Na  
                SELECT
                    tenants.id AS tenant_id,
                    tenants.slug AS tenant_slug,
                    tenants.name AS tenant_name,
                    tenants.database_path AS database_path,
                    users.id AS user_id,
                    users.email AS user_email,
                    users.username AS username,
                    users.name AS user_name,
                    users.role AS role,
                    users.permissions_json AS permissions_json,
                    users.assistant_scopes_json AS assistant_scopes_json
                FROM tenants
                JOIN users ON users.tenant_id = tenants.id
                WHERE tenants.id = ?
                ORDER BY
                    CASE users.role
                        WHEN 'owner' THEN 0
                        WHEN 'staff' THEN 1
                        ELSE 2
                    END,
                    users.created_at ASC
                LIMIT 1
                r  zservice:r  r  r  r  r  r  r1  r  r  r  r   r   r   r+  r   r  r  iB  rw  r5   )	rL  rO  r  r  r   r   r8   r	   r:   r>  r  rR  rl  s       r7   build_service_sessionz!TenantStore.build_service_session)  s   ##% 	$$2 56 hj7 	< ; 
YK(
+&
 M*
 M*	

 {+
 !/
 !/
 	N
 <(
 _
 +&
 V
 2#f+sCU?VW
 <F&'+,
& o.'
(  	I4$88CCE)
 	
C	 	s   "C%%C.c                $   | j                         5 }| j                  ||      \  }}}}d d d        t        d   |d   |d   |d         D cg c])  }t        |d   |d   |d   |d   |d   |d	   |d   
      + c}D cg c]-  }t	        |d   |d   |d   |d   |d   |d   |d   |d         / c}D cg c]2  }t        |d   |d   |d   t        |d         |d   |d   |d         4 c}dS # 1 sw Y   xY wc c}w c c}w c c}w )Nr  r'  rV   r  r  r'  rV   r  r  r  r	  r
  r  r  r'  r  r	  r
  r  r  r  r   r  r  r'  r  r  r	  r   r  
module_keyr  r  activated_atr  )r  r  r  r  r  r  r  )r  r  rE  tenant_modules)rL  r  r   r   r   r   r   )r>  r  rR  
tenant_row
venue_rows	user_rowsmodule_rowsrl  s           r7   r  zTenantStore.get_tenant_context@)  s   ##% 	g=A=O=OPZ\e=f:J
I{	g d#''%l3	  &  4y!+.V	N!$^!4$'(9$:"<0. %  4y!+.V _g,!$^!4V"<0	. '  4y!+."<0 Y0!+.!$^!4"<0A,
 ,	
	g 	gs   C7.D2D;7D
7D )r  r  r	  r
  c                  |j                   dvrt        d      g }g }g }g }	|X|j                         xs d }
|j                  d       |j                  |
       |j                  d       |	j                  |
       |X|j                         xs d }
|j                  d       |j                  |
       |j                  d       |	j                  |
       |X|j                         xs d }
|j                  d       |j                  |
       |j                  d       |	j                  |
       |X|j                         xs d }
|j                  d       |j                  |
       |j                  d       |	j                  |
       |sy | j                  5  | j                         5 }|j                  d|j                  f      j                         }|t        d	      |j                  |d
          |j                  ddj                  |       d|       |j                          d d d        d d d        |j                  rx|ru| j                  |j                        5 }|	j                  |j                         |j                  ddj                  |       d|	       |j                          d d d        y y y # 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   y xY w)N)r   managerr   *Operazione riservata all'admin del locale.name = ?zvenue_name = ?zaddress = ?phone_number = ?zwhatsapp_number = ?z1SELECT id FROM venues WHERE tenant_id = ? LIMIT 1z(Nessun locale trovato per questo tenant.r  zUPDATE venues SET rS  rB  zUPDATE tenant_profile SET z WHERE tenant_id = ?)r   r>   rQ   rs   r<  rL  rO  r  r  r  r%  rk  r  r  )r>  r  r  r  r	  r
  registry_assignmentsregistry_valuestenant_assignmentstenant_valuesvrR  	venue_rowtenant_conns                 r7   update_venue_infozTenantStore.update_venue_infor)  s    <<BBIJJ*,(*(*&(!  "*dA ''
3""1%%%&67  #'4A ''6""1%%%m4  ##""$,A ''(:;""1%%%&89  #&%%'/4A ''(=>""1%%%&;<  ##ZZ 	$'') $Z&..G&&( (*  $"#MNN&&y7""(3G)H(IW# !!#$	$   %7..w/D/DE %$$W%6%67##0;M1N0OOcd! ""$% % &8 $ $	$ 	$ % %s2   K*BK,K%AKK
	KKK"c                    | j                         5 }|j                  d|f      j                         }|
	 d d d        y 	 d d d        | j                  d         S # 1 sw Y   xY w)Nz-SELECT id FROM tenants WHERE slug = ? LIMIT 1r  )rL  rO  r  r  )r>  r  rR  r  s       r7   get_context_by_slugzTenantStore.get_context_by_slug)  sq    ##% 	''(WZeYghqqsF~	 		
 &&vd|44	 	s   %AA%c                <   |j                   j                         }|st        d      | j                  5  | j	                         5 }| j                  |      rt        d      t        |      }|j                  d|t        f       |j                          | j                  |t        t              }d d d        d d d        t        | j                  t         dz        }| j                  |      5 }|j                  dt        f      j!                         }|r|d   n	t#               }	| j%                  |t        t&        t        t&        dd d d	t(        t*        d |	
       |j                          d d d        S # 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   S xY w)Nz1Inserisci una password valida per il super admin.z'Il super admin e gia stato configurato./UPDATE users SET password_hash = ? WHERE id = ?r  r  ASELECT created_at FROM tenant_profile WHERE tenant_id = ? LIMIT 1r  ri   r  r  )r   rQ   r>   r<  rL  r  r   rO  r  rk  r  r  rj   r3  r  r  r  r;   r  r  r  r  )
r>  r  r   rR  r  r  admin_db_pathadmin_connectionprofiler  s
             r7   bootstrap_super_adminz!TenantStore.bootstrap_super_admin)  s   ##))+PQQZZ 	y'') 
yZ22:>$%NOO .x 8""E"$78 !!#..zEZdw.x
y	y D,,2I1J(/SST**=9 	&=M&..S&( hj  3:.xzJ'' /332! $(3-#'+% (   ##%-	&0 K
y 
y	y 	y	&0 s1   FA,E91F1A>F9F	>FFFc                J   |j                   dk7  rt        d      |j                  j                         }|j                  r|j                  j                         nd}|st        d      | j
                  5  | j                         5 }|j                  dt        f      j                         }|t        d      |d   xs dj                         }|r|st        d      |rt        ||      st        d	      t        |      }|j                  d
|t        f       |j                          d d d        d d d        t        | j                  t         dz        }	| j!                  |	      5 }
|
j                  dt"        f      j                         }|r|d   n	t%               }| j'                  |
t"        t(        t        t(        dd d |j*                  xs dt,        t.        d |       |
j                          d d d        y # 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   y xY w)Nr   $Operazione riservata al super admin.ri   $Inserisci una nuova password valida.r  z$Account super admin non disponibile.r  z.Inserisci la password attuale del super admin.#La password attuale non e corretta.r  r  r  r  r  r  )r   r>   r   rQ   r  r<  rL  rO  r  r  r   r   rk  rj   r3  r  r  r  r;   r  r  r  r  r  )r>  r  r  r   r  rR  rl  r   new_hashr  r  r  r  s                r7   update_super_admin_passwordz'TenantStore.update_super_admin_password)  s"   <<=(CDD++113?F?W?W73399;]_CDDZZ 	$'') $Z ((J(* (*  ;$%KLL"?39r@@B'7$%UVV'78H+'V$%JKK),7""E23 !!#'$	$, D,,2I1J(/SST**=9 	&=M&..S&( hj  3:.xzJ'' /332! $",,=3-#'&% (   ##%-	& 	&-$ $	$ 	$.	& 	&s2   6HB%H,H,BHH
	HHH"c                   |j                   dk(  r| j                  ||       y |j                  j                         }|j                  r|j                  j                         nd}|st        d      | j                  5  | j                         5 }|j                  d|j                  |j                  f      j                         }|t        d      |d   xs dj                         }|r|st        d      |rt        ||      st        d      t        |      }|j                  d	||j                  f       |j                          d d d        d d d        |j                   d
k(  r\| j                  |j                         5 }	|	j                  dt#               |j                  f       |	j                          d d d        y y # 1 sw Y   }xY w# 1 sw Y   xY w# 1 sw Y   y xY w)Nr   ri   r  z
                    SELECT password_hash
                    FROM users
                    WHERE id = ? AND tenant_id = ?
                    LIMIT 1
                    z#Account del locale non disponibile.r  zInserisci la password attuale.r  r  r   z
                    UPDATE tenant_profile
                    SET password_hash = ?, updated_at = ?
                    WHERE tenant_id = ?
                    )r   r  r   rQ   r  r>   r<  rL  rO  r1  r  r  r   r   rk  r  r  r;   )
r>  r  r  r   r  rR  rl  r   r  r  s
             r7   update_current_user_passwordz(TenantStore.update_current_user_password$*  s   <<=(,,Wg>++113?F?W?W73399;]_CDDZZ 	$'') $Z (( __g&7&78 (*  ;$%JKK"?39r@@B'7$%EFF'78H+'V$%JKK),7""Ew/ !!#1$	$6 <<7"..w/D/DE 	+IZ!))
 xz7+<+<= "((*	+ 	+ #5$ $	$ 	$8	+ 	+s1   >GB<GG8GG	GGG(c                D   d }|d   't        |d   |d   |d   |d   |d   |d   |d         }d }|d	   +t        |d	   |d   |d
   |d   |d   |d   |d   |d         }t        |d   |d   |d   |d         ||t        |d   xs d      t        |d   xs d      dS )Nr  r  r  venue_addressvenue_phone_numbervenue_whatsapp_numbervenue_created_atr  admin_user_idadmin_user_namer  r  r  
admin_roleadmin_created_atr  r'  rV   r  r  enabled_modulesr   total_modules)r  venue
admin_userr  r  )r   r   r   r   )r>  rl  venue_payloadadmin_payloads       r7   _tenant_summary_from_rowz$TenantStore._tenant_summary_from_rowU*  s   z?&!z?d)&O, !56 #$; <12M + 'd)*+-.-( !56&12	M t9[[|,	 #'"3'8#9#>Q? _!5!:;
 	
r6   c                    | j                         5 }t        |j                  dt        f            }d d d        D cg c]  }| j	                  |       c}S # 1 sw Y   (xY wc c}w )Na  
                    SELECT
                        tenants.*,
                        venues.id AS venue_id,
                        venues.name AS venue_name,
                        venues.address AS venue_address,
                        venues.phone_number AS venue_phone_number,
                        venues.whatsapp_number AS venue_whatsapp_number,
                        venues.created_at AS venue_created_at,
                        users.id AS admin_user_id,
                        users.name AS admin_user_name,
                        users.username AS admin_username,
                        users.email AS admin_email,
                        users.phone_number AS admin_phone_number,
                        users.role AS admin_role,
                        users.created_at AS admin_created_at,
                        COALESCE(module_stats.enabled_modules, 0) AS enabled_modules,
                        COALESCE(module_stats.total_modules, 0) AS total_modules
                    FROM tenants
                    LEFT JOIN venues ON venues.id = (
                        SELECT id
                        FROM venues AS first_venues
                        WHERE first_venues.tenant_id = tenants.id
                        ORDER BY first_venues.created_at ASC
                        LIMIT 1
                    )
                    LEFT JOIN users ON users.id = (
                        SELECT id
                        FROM users AS first_users
                        WHERE first_users.tenant_id = tenants.id
                        ORDER BY first_users.created_at ASC
                        LIMIT 1
                    )
                    LEFT JOIN (
                        SELECT
                            tenant_id,
                            SUM(CASE WHEN enabled = 1 THEN 1 ELSE 0 END) AS enabled_modules,
                            COUNT(*) AS total_modules
                        FROM tenant_modules
                        GROUP BY tenant_id
                    ) AS module_stats ON module_stats.tenant_id = tenants.id
                    WHERE tenants.id != ?
                    ORDER BY tenants.created_at DESC
                    )rL  r   rO  r  r  )r>  rR  r+  rl  s       r7   list_customer_tenantsz!TenantStore.list_customer_tenants|*  ss    ##% 1	""+X +,[.0D1	f ?CCs--c2CCg1	 1	f Ds   !AA%A"c                    | j                         5 }|j                  d|t        f      j                         }d d d        t	        d| d      | j                  |      S # 1 sw Y   +xY w)Na  
                SELECT
                    tenants.*,
                    venues.id AS venue_id,
                    venues.name AS venue_name,
                    venues.address AS venue_address,
                    venues.phone_number AS venue_phone_number,
                    venues.whatsapp_number AS venue_whatsapp_number,
                    venues.created_at AS venue_created_at,
                    users.id AS admin_user_id,
                    users.name AS admin_user_name,
                    users.username AS admin_username,
                    users.email AS admin_email,
                    users.phone_number AS admin_phone_number,
                    users.role AS admin_role,
                    users.created_at AS admin_created_at,
                    COALESCE(module_stats.enabled_modules, 0) AS enabled_modules,
                    COALESCE(module_stats.total_modules, 0) AS total_modules
                FROM tenants
                LEFT JOIN venues ON venues.id = (
                    SELECT id
                    FROM venues AS first_venues
                    WHERE first_venues.tenant_id = tenants.id
                    ORDER BY first_venues.created_at ASC
                    LIMIT 1
                )
                LEFT JOIN users ON users.id = (
                    SELECT id
                    FROM users AS first_users
                    WHERE first_users.tenant_id = tenants.id
                    ORDER BY first_users.created_at ASC
                    LIMIT 1
                )
                LEFT JOIN (
                    SELECT
                        tenant_id,
                        SUM(CASE WHEN enabled = 1 THEN 1 ELSE 0 END) AS enabled_modules,
                        COUNT(*) AS total_modules
                    FROM tenant_modules
                    GROUP BY tenant_id
                ) AS module_stats ON module_stats.tenant_id = tenants.id
                WHERE tenants.id = ? AND tenants.id != ?
                LIMIT 1
                r  r  )rL  rO  r  r  r  r  r  s       r7   get_customer_tenant_summaryz'TenantStore.get_customer_tenant_summary*  s    ##% /	$$+X 12[.\ hj] /	b ;WYK|<==,,S11g/	 /	s   'A""A+c                ,   | j                         5 }|j                  dt        f      j                         }| j	                  |      }d d d        t        d      |d   |d   |d   |d   |d   |d   d	| j                         d
S # 1 sw Y   CxY w)Nz
                SELECT id, name, username, email, role, created_at
                FROM users
                WHERE id = ?
                LIMIT 1
                zAccount super admin non trovator  r'  r  r  r   r  )r  r'  r  r  r   r  password_configured)r   tenants)rL  rO  r  r  r  r  r  )r>  rR  r   r  s       r7   get_admin_overviewzTenantStore.get_admin_overview*  s    ##% 
	N$,, %& hj  #'"A"A*"M
	N <== "$'#F+'
3$W-#F+),7': 113
 	

	N 
	Ns   7B

Bc                    | j                   5  | j                         5 }| j                  ||      \  }}d d d        d d d        | j                        S # 1 sw Y   "xY w# 1 sw Y   &xY wr2   )r<  rL  r  r  )r>  r  rR  r  _user_ids        r7   create_tenant_as_super_adminz(TenantStore.create_tenant_as_super_admin+  ss    ZZ 	X'') XZ&*&B&B:w&W#	8X	X //	::X X	X 	Xs!   A!AA!A	A!!A*c                    | j                  |      }|d   xs dj                         }|d   xs dj                         }|d   xs dj                         }|d   xs dj                         }|d   xs dj                         }|d   }	|d   }
|j                  r|j                  j                         nd }|t        k(  rt	        d	      |r|r|r|st	        d
      | j
                  5  | j                         5 }|j                  d|f      j                         }|t        d| d      |j                  d|f      j                         }|j                  d|f      j                         }||t	        d      |j                  dt        |      |d   f      j                         }|j                  dt        |      |d   f      j                         }||t	        d      |j                  d||f       |j                  d|||	|
|d   f       g d}||||	g}|d   }|r-t        |      }|j                  d       |j                  |       |j                  |d          |j                  ddj                  |       d|       |j                          | j                  |d   |||d   |||	|
||||	||d           d d d        d d d        | j!                  |      S # 1 sw Y   "xY w# 1 sw Y   &xY w)!Nr  ri   r  r  r  r  r	  r
  zFIl tenant di piattaforma non puo essere modificato da questo endpoint.z-Compila tutti i campi obbligatori del locale.z*SELECT * FROM tenants WHERE id = ? LIMIT 1r  r  zHSELECT * FROM venues WHERE tenant_id = ? ORDER BY created_at ASC LIMIT 1zGSELECT * FROM users WHERE tenant_id = ? ORDER BY created_at ASC LIMIT 1z-Tenant incompleto: venue o admin non trovati.BSELECT id FROM users WHERE lower(username) = ? AND id != ? LIMIT 1r  ?SELECT id FROM users WHERE lower(email) = ? AND id != ? LIMIT 14Username o email gia registrati da un altro account.z(UPDATE tenants SET name = ? WHERE id = ?z
                    UPDATE venues
                    SET name = ?, address = ?, phone_number = ?, whatsapp_number = ?
                    WHERE id = ?
                    )r  username = ?	email = ?r  r  password_hash = ?UPDATE users SET rS  rB  r  rV   r  r  )r  rQ   r   r  r>   r<  rL  rO  r  r  r[   r   rs   r%  rk  r  r  )r>  r  r  r  r  r  r  r  r  r	  r
  next_passwordrR  r  r  user_rowusername_conflictemail_conflictrL  r   r  s                        r7   update_tenant_adminzTenantStore.update_tenant_admin+  sz   !BB7K)-8>BEEG(6<"CCE
&z28b??A#G,299;%i06B==?).9,->?4;4D4D((..0$--eff*HELMMZZ L	'') KZ'//0\_h^jkttv
%"WYK|#DEE&..^L (*  &--]L (*  $(8$%TUU$.$6$6X&x0(4.A% (* " ",!3!3U&u-x~>" (*  %0N4N$%[\\""> ), ""
 !'<)TX/Z )3He\'R ( 9 $2=$AM&&':;MM-0htn-""'		+(>'?}M !!#,,",_"=' + *6 2*#!-$3)#+ %'3"/),7 - yKL	\ //	::[K KL	 L	s%   *K4;GK(K4(K1	-K44K=c                   | j                  |      st        d      | j                         5 }t        |j	                  d|j
                  f            }d d d        D cg c]]  }|d   |d   |d   |d   |d   |d   |d	   t        t        |d	   |d
               t        t        |d	   |d
   |d               |d   d
_ c}S # 1 sw Y   rxY wc c}w )Nr  a  
                    SELECT id, tenant_id, name, username, email, phone_number, role, permissions_json, assistant_scopes_json, created_at
                    FROM users
                    WHERE tenant_id = ?
                    ORDER BY
                        CASE role
                            WHEN 'owner' THEN 0
                            WHEN 'manager' THEN 1
                            WHEN 'staff' THEN 2
                            ELSE 3
                        END,
                        created_at ASC
                    r  r  r'  r  r  r	  r   r   r   r  )
r  r  r'  r  r  r	  r   r   r+  r  )r  r>   rL  r   rO  r  r   r   )r>  r  rR  r+  rl  s        r7   list_tenant_userszTenantStore.list_tenant_usersl+  s   //8IJJ##% 	"" &&(D	F 
  $i -F
OW #N 3F#$=c&k3OaKb$cd$(23v;DV@WY\]tYuv% ",/
 	
)	 	(
s   'C A"CCc                   | j                  |      st        d      | j                  |      }|d   xs dj                         }|d   xs dj                         }|d   xs dj                         }|d   }|j                  j                         }t        d|j                        }	t        d|j                        }
|r|r|r|st        d      | j                  5  | j                         5 }|j                  d	t        |      f      j                         }|j                  d
t        |      f      j                         }||t        d      dt        j                         j                    t#               }|j                  d|j$                  ||||t'        |      d|	|
|f       |j)                          d d d        d d d        t+        fd| j-                  |      D              S # 1 sw Y   4xY w# 1 sw Y   8xY w)Nr  r'  ri   r  r  r	  r   4Compila tutti i campi obbligatori del sotto-account.z6SELECT id FROM users WHERE lower(username) = ? LIMIT 1z3SELECT id FROM users WHERE lower(email) = ? LIMIT 1r  r  a  
                    INSERT INTO users (
                        id,
                        tenant_id,
                        name,
                        username,
                        email,
                        phone_number,
                        password_hash,
                        role,
                        permissions_json,
                        assistant_scopes_json,
                        created_at
                    )
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                    c              3  2   K   | ]  }|d    k(  r|  ywr  Nr5   r  r  r1  s     r7   r  z7TenantStore.create_tenant_staff_user.<locals>.<genexpr>+  %      
DzW$ 
   )r  r>   r  rQ   r   r   r   r   r+  r<  rL  rO  r[   r  r  r  r   r;   r  r   rk  r  r  )r>  r  r  r  r'  r  r  r	  r   r   r   rR  r  r  r  r1  s                  @r7   create_tenant_staff_userz$TenantStore.create_tenant_staff_user+  s   //8IJJ!AA'J"6*0b779&z28b??A#G,299;).9##))+6w@S@ST @'JbJb c85STTZZ .	$'') -$Z$.$6$6L&x02% (* " ",!3!3I&u-/" (*  %0N4N$%[\\!$**,"2"2!34%Z
"""  )) $&x0(-"#> !!#[-$.	$`  
..w7
 
 	
_-$ -$.	$ .	$s%   G=/CG1>G=1G:	6G==Hc                   | j                  |      st        d      | j                  |      }|d   xs dj                         }|d   xs dj                         }|d   xs dj                         }|d   }|j                  r|j                  j                         nd }	t        d|j                        }
t        d|j                        }r|r|r|st        d      | j                  5  | j                         5 }|j                  d	|j                  f      j                         }|t        d
      |d   dk7  rt        d      |j                  dt        |      f      j                         }|j                  dt        |      f      j                         }||t        d      g d}|||||
|g}|	r+|j!                  d       |j!                  t#        |	             |j!                         |j                  ddj%                  |       d|       |j'                          d d d        d d d        t)        fd| j+                  |      D              S # 1 sw Y   4xY w# 1 sw Y   8xY w)Nr  r'  ri   r  r  r	  r   r  
                    SELECT * FROM users
                    WHERE id = ? AND tenant_id = ?
                    LIMIT 1
                    Sotto-account non trovato.r   z0Puoi modificare solo i sotto-account del locale.r  r  r  )r  r  r  r  zpermissions_json = ?zassistant_scopes_json = ?r  r  rS  rB  c              3  2   K   | ]  }|d    k(  r|  ywr  r5   r  s     r7   r  z7TenantStore.update_tenant_staff_user.<locals>.<genexpr>,  r  r   )r  r>   r  rQ   r   r   r   r   r+  r<  rL  rO  r  r  r  r[   rs   r   r%  rk  r  r  )r>  r  r1  r  r  r'  r  r  r	  r  r   r   rR  r  r  r  rL  r   s     `               r7   update_tenant_staff_userz$TenantStore.update_tenant_staff_user+  s    //8IJJ!AA'J"6*0b779&z28b??A#G,299;).94;4D4D((..0$6w@S@ST @'JbJb cd(%STTZZ ,	$'') +$Z%--
 g//0 (*  #"#?@@F#w.$%WXX$.$6$6X&x0':% (* " ",!3!3U&u-w7" (*  %0N4N$%[\\ )-h|M]_t'u &&':;MM."?@g&""'		+(>'?}M !!#W+$,	$\  
..w7
 
 	
[+$ +$,	$ ,	$s%   ,I-=D1I!.I-!I*	&I--I6c                   | j                  |      st        d      |j                  |k(  rt        d      | j                  5  | j	                         5 }|j                  d||j                  f      j                         }|t        d      |d   dk7  rt        d      |j                  d|j                  |f       |j                  d	|j                  |f       |j                  d
|f       |j                  d|f       |j                          d d d        | j                  |j                        5 }|j                  d|f       |j                          d d d        d d d        y # 1 sw Y   YxY w# 1 sw Y   xY w# 1 sw Y   y xY w)Nr  z5Non puoi eliminare l'account con cui sei autenticato.r#  r$  r   r   z/Puoi eliminare solo i sotto-account del locale.z>DELETE FROM assistant_runs WHERE tenant_id = ? AND user_id = ?zADELETE FROM assistant_threads WHERE tenant_id = ? AND user_id = ?z&DELETE FROM sessions WHERE user_id = ?zDELETE FROM users WHERE id = ?z6DELETE FROM tenant_timeclock_entries WHERE user_id = ?)r  r>   r1  r<  rL  rO  r  r  r  rk  r  r  )r>  r  r1  rR  r  r  s         r7   delete_tenant_staff_userz$TenantStore.delete_tenant_staff_user%,  s   //8IJJ??g%TUUZZ 	+'') $Z%--
 g//0 (*  #"#?@@F#w.$%VWW""T&&0 ""W&&0 ""#KgZX""#CgZP!!#1$2 ..w/D/DE +IZ!))*beldno!((*+5	+ 	+$ $2+ +5	+ 	+s=   FB?E+#F6$E7F+E4	0F7F 	<FFc                *    t        |      }|dk(  rdS dS )Nr   z
Mance salaz	Mance Bar)r   )r>  ri  rj  s      r7   _tips_area_labelzTenantStore._tips_area_labelJ,  s    .t4.&8|IkIr6   c           
         t        |d         t        |d         t        |d         t        |d   xs d      t        |d   xs d      t        |d         t        |d         d	S )
Nr  ri  
staff_namer  r   r  r  r  )r  ri  r'  r  r  r  r  )rj   r   r   r  s     r7   _serialize_tips_roster_rowz&TenantStore._serialize_tips_roster_rowN,  sm    c$i.F$L)*3w<,1-c,/415c,/0c,/0
 	
r6   c                   d|j                         v r|d   n|d   }d|j                         v r|d   nd}t        |xs d      j                         }|dvrd}dddd	}i d
t        |d
         dt        |d         dt        |d         dt        |d   xs d      dt        |d   xs d      dt        |xs d      dt        |d   xs d      dt        |d   xs d      dt        |d   xs d      dt        |d   xs d      dt	        |d   xs d      dt	        |d   xs d      d|d||   dd|j                         v r|d   rt        |d   xs d      nd dd|j                         v r|d   rt        |d   xs d      nd dt        |d   xs d      xs d t        |d         t        |d         d}d|j                         v rt	        |d   xs d      |d<   |S )Nr_  ra  rc  r  >   carriedr  settledzDa consegnarezCaricata in altra giornata
Consegnata)r  r/  r0  r  ri  r  total_tip_amountr   rb  total_scorehistorical_total_amountpayable_total_amountpresent_staff_countabsent_staff_countpayout_status_labelrd  ri   rf  saved_by_namer  r  )r  r  entry_count)r  rj   rQ   r   r   )r>  rl  r_  raw_payout_statusrc  payout_status_labelsr  s          r7   _serialize_tips_run_summary_rowz+TenantStore._serialize_tips_run_summary_rowY,  s    *SXXZ7 *+%& 	!
 5Dsxxz4QC0W`-:;AAC AA%M&3# 


#c$i.
CF$
 C
O,
 c*<&=&B C	

 eC(8$9$>Q?
 '.F.K!(L
 uS):%;%@qA
 5]!3!8q9
 &uS1J-K-Pq'Q
 #E#.D*E*J$K
 "3s+@'A'FQ#G
 !#c*>&?&D1"E
 ]
 "#7#F
 ,#((*:TY\]iYj#c,/526pt
  DUY\YaYaYcDchkl}h~s3'8#9#?R@  EI!
" S_!5!;<D#
$ c,/0c,/0'
* CHHJ&%(]);)@q%AGM"r6   c                F   t        |d         t        |d         t        |d         t        |d         t        |d   xs d      t        |d         t        |d   xs d      t        |d	   xs d      t        |d
   xs d      t        |d         t        |d         dS )Nr  run_idri  r,  r  r   
is_presentamount_todayhistorical_amountr  r  r  )r  r?  ri  r'  r  r@  rA  rB  r  r  r  )rj   r   r   r  s     r7   _serialize_tips_run_entry_rowz)TenantStore._serialize_tips_run_entry_row,  s    c$i.#h-(F$L)*3w<,1-s<01!#n"5":;!&s+>'?'D1!E!#n"5":;c,/0c,/0
 	
r6   c                L   |D ci c]  }|g  }}|s|S dj                  d |D              }|j                  d| dt        |            j                         }|D ]@  }t	        |d         }|j                  |g       j                  | j                  |             B |S c c}w )NrS  c              3      K   | ]  }d   ywrU  r5   rW  s     r7   r  z5TenantStore._load_tips_run_entries.<locals>.<genexpr>,        6 6rZ  z\
            SELECT *
            FROM tenant_tips_run_entries
            WHERE run_id IN (zU)
            ORDER BY score DESC, lower(staff_name) ASC, created_at ASC
            r?  )r%  rO  ro   rh  rj   r9  rs   rC  r>  rR  run_idsr?  r:  rb  r+  rl  s           r7   _load_tips_run_entriesz"TenantStore._load_tips_run_entries,  s    
 QX6Xfvrz6X6XNyy 6g 66!! +^ , 'N
 (* 	  	[CX'Fvr*11$2T2TUX2YZ	[ # 7Ys   
B!c                d   |D ci c]  }|g  }}|s|S dj                  d |D              }|j                  d| dt        |            j                         }|D ]L  }t	        |d         }|j                  |g       j                  t	        |d         t	        |d         d       N |S c c}w )	NrS  c              3      K   | ]  }d   ywrU  r5   rW  s     r7   r  z=TenantStore._load_tips_run_history_sources.<locals>.<genexpr>,  rF  rZ  zd
            SELECT *
            FROM tenant_tips_run_history_sources
            WHERE run_id IN (zI)
            ORDER BY source_tip_date DESC, created_at DESC
            r?  source_run_idsource_tip_date)r  r  )r%  rO  ro   rh  rj   r9  rs   rG  s           r7   _load_tips_run_history_sourcesz*TenantStore._load_tips_run_history_sources,  s    
 QX6Xfvrz6X6XNyy 6g 66!! +^ , 'N
 (* 	  	CX'Fvr*11c/23 #C(9$: ;	 - 7Ys   
B-c                z    t        |      }|j                  d||f      j                         }|t        d      |S )Nal  
            SELECT
                runs.*,
                (
                    SELECT COUNT(*)
                    FROM tenant_tips_run_entries AS entries
                    WHERE entries.run_id = runs.id
                ) AS entry_count
            FROM tenant_tips_runs AS runs
            WHERE runs.area = ? AND runs.id = ?
            LIMIT 1
            zSalvataggio mance non trovato.)r   rO  r  r  )r>  rR  ri  r?  rj  rl  s         r7   _read_tips_run_rowzTenantStore._read_tips_run_row,  sQ     /t4   f%
 (* 	 ;;<<
r6   c           
        |D ci c]  }t        |d         d }}t        t        t        t	        |xs d      dz              d      }|D cg c]%  }t	        |j                  d      xs d      dkD  r|' }}t        d |D              }|dk  s|dk  s|s|S g }d}	|D ]{  }t        |d         }
t	        |j                  d      xs d      }||z  |z  }t        |      }|	|z  }	|j                  |
t        |j                  d      xs d      |||z
  d	       } ||	z
  }|j                  d
        t        t        |d            D ]4  }t        ||t        |      z     d         dz   ||t        |      z     d<   6 |D ],  }t        t        |d         dz  d      |t        |d         <   . |S c c}w c c}w )Nr  r   r   r  r  c              3  X   K   | ]"  }t        |j                  d       xs d       $ ywr  r   N)r   r   r  r!  s     r7   r  z5TenantStore._allocate_tips_amounts.<locals>.<genexpr>,  s#     WU%		' 2 7a8Ws   (*r'  ri   )r  r'  
base_cents	remainderc                r    t        | d          t        | d         j                         t        | d         fS )NrV  r'  r  r   rj   r  r7  s    r7   r"  z4TenantStore._allocate_tips_amounts.<locals>.<lambda>-  s:    tK())DL!**,DN# r6   r8  rU  rm   r  )rj   r  r   r  r   r   r  rs   r&  rv  rq   )r>  present_entriesr  r!  allocationstotal_centspositive_entriesr3  ranked_entriesassigned_centsr  r  exact_centsrU  missing_centsrX  s                   r7   _allocate_tips_amountsz"TenantStore._allocate_tips_amounts,  s7    )
 h #%
 
 #eE,*;!$<s$BCDaH.
UYYw',1-1 
 
 WFVWW!{a/7G24% 	Ex)F%))G,12E ;.+=K[)Jj(N!!$		& 1 7R8",!,z!9		 $n4 	 	
 3}a01 	KEHKN[`cfgucv[vLw  yE  MF  IG  JK  IKN53~#667E	K $ 	YE05c%:M6NQT6TVW0XKE(O,-	Y Y


s   F6*F;exclude_run_idc                  t        |      }| j                  |      }t        |j                  xs d      j	                         xs& t               j                         j                         }	 t        j                  |      j                         }|j                  d|f      j                         }
|
st        d| d      |
D cg c]  }| j                  |       }}|D ci c]  }t        t        |d               | }}|j                  D ch c]*  }t        |xs d      j	                         rt        |      , }}|D cg c]R  }t        t        |d               |vr7t        |d         t        t        |d               t        |d   xs d      d	T }}|st        d
      t!        t#        d |D              d      }|dk  rt        d      t!        t        |j$                  xs d      d      }t!        t        |j&                  xs d      d      }||kD  rt        d      t!        t        |j(                  |j(                  n|      d      }||kD  rt        d      t!        ||z
  d      }t!        ||z   d      }| j+                  ||      }g }t-               }|j.                  D ]H  }t        |xs d      j	                         }|r||v r'|j1                  |       |j3                  |       J g }i }|rWdj5                  d |D              }|j                  d| d|g|      j                         } | D cg c]&  }t        |d         |k7  rt        |d         |k7  r|( }!}|!D cg c]  }t        |d          }"}| j7                  ||"      }#|!D cg c]  }| j9                  |       }}|!D ]  }$|#j;                  t        |$d         g       D ]s  }t        t        |d               }%|j=                  |%t        |d         dd      }&t!        t        |&d   xs d      t        |d   xs	 |d   xs d      z   d      |&d<   u  i }'|D ]Z  }t        |d         }%t        |d         t        |d   xs d      dt!        t        |j;                  |%d            d      dd|'|%<   \ |j?                         D ]  \  }%}(|'j;                  |%      }|N|j;                  |%      })|)rt        |)d         nt        |(d         |)rt        |)d   xs d      nddddd}||'|%<   t!        t        |d    xs d      t        |(d   xs d      z   d      |d <    |'jA                         D cg c]  }t        |d         t        |d   xs d      tC        |d!         t!        t        |d   xs d      d      t!        t        |d    xs d      d      t!        t        |d   xs d      t        |d    xs d      z   d      d" }*}|*jE                  d# $       t!        t#        d% |*D              d      }+t!        ||+z   d      },|,dk  rt        d&      |||||||||+|,tG        |      tG        |      tG        |      z
  tG        |      |*|d'S # t        $ r}	t        d      |	d }	~	ww xY wc c}w c c}w c c}w c c}w c c}w c c}w c c}w c c}w )(Nri   'Inserisci una data valida per le mance.z
            SELECT *
            FROM tenant_tips_roster_entries
            WHERE area = ?
            ORDER BY sort_order ASC, lower(staff_name) ASC, created_at ASC
            z'Configura prima la lista dipendenti di r9  r'  r  r   )r'  r  r  z(Seleziona almeno un dipendente presente.c              3  @   K   | ]  }t        |d    xs d        ywrS  r  rT  s     r7   r  z2TenantStore._build_tips_preview.<locals>.<genexpr>E-  s     XueGn&9 :Xr  r  z4I dipendenti presenti non hanno un punteggio valido.r  z;Le mance POS non possono superare il totale della giornata.zBLe mance POS effettive non possono superare le mance POS inserite.rS  c              3      K   | ]  }d   ywrU  r5   rW  s     r7   r  z2TenantStore._build_tips_preview.<locals>.<genexpr>d-  s     $HQS$HrZ  a  
                SELECT
                    runs.*,
                    (
                        SELECT COUNT(*)
                        FROM tenant_tips_run_entries AS entries
                        WHERE entries.run_id = runs.id
                    ) AS entry_count
                FROM tenant_tips_runs AS runs
                WHERE runs.area = ?
                  AND runs.id IN (z)
                  AND COALESCE(runs.payout_status, 'pending') = 'pending'
                ORDER BY runs.tip_date DESC, runs.updated_at DESC
                r  r  r   )r'  amountrh  r  rA  r  T)r'  r  r@  rA  rB  FrB  r@  )r'  r  r@  rA  rB  r  c                `    t        | d   xs d       t        | d         j                         fS )Nr  r   r'  rX  r7  s    r7   r"  z1TenantStore._build_tips_preview.<locals>.<lambda>-  s.    U4=+=A%>$>DL@Q@Z@Z@\#] r6   r8  c              3  @   K   | ]  }t        |d    xs d        yw)rB  r   Nr  r  s     r7   r  z2TenantStore._build_tips_preview.<locals>.<genexpr>-  s!     +cVZE$7J2K2Pq,Q+cr  zXInserisci un importo mance oppure seleziona almeno una giornata arretrata da consegnare.)ri  
area_labelr  r2  ra  r_  rb  r3  r4  r5  r6  r7  roster_countr+  selected_history_runs)$r   r*  rj   r  rQ   r8   r   r:   r  r>   rO  rh  r-  r[   r  r   r  r  r  r  r  ra  r   r  rs   r   r%  rI  r=  r   r9  r  r   r   r&  rq   )-r>  rR  ri  r  rc  rj  rk  raw_tip_dater  r  roster_rowsrl  roster_entriesr!  roster_by_lookupr'  absent_lookupsrY  r3  r2  ra  r_  rb  effective_tip_amountdaily_allocationsrequested_history_idsseen_history_ids
raw_run_idr?  history_runshistory_amounts_by_lookuprb  history_rowsfiltered_history_rowsselected_run_idshistory_entries_by_runrun_rowr  r<  rows_by_lookuphistory_entryroster_entryr+  r4  r5  s-                                                r7   _build_tips_previewzTenantStore._build_tips_preview-  s    /t4**?;
7++1r288:Zginn>N>X>X>Z	Q)),7AACH !(( 
 (* 	 FzlRSTUUJUV3$99#>VV (
 c%-0158
 
  ,,
4:2$$& d#
 
 (
  U6]!34NJ E&M*+Cf,>?uW~23
 
 GHHCXXXZ[\!STT w';';'@q!A1EuW%7%7%<1=qA,,Z[[#('2N2N2Z'..`no$
  $n4abb 0> A1E$_7O%OQRS 77I]^+-%(U!11 	)J)r*002FV'77!((0  (	) 13BD! 99$H2G$HHL%--
# $0. 1 !9#89  hj! & (%s4y>^3C
O8LPX8X %! %
 ;PP3CIPP%)%@%@M]%^"Qfg#D@@EgLg0 
K377GDM8JBO 	KE.s5=/ABF6AA$'f$6&)F (-U6(3C3Hq-IERWXfRg  SFkpq  lA  SF  EF  MG  .G  IJ  (KF8$	K
K 8:$ 	Ex)FE&M*uW~23" %e,=,A,A&!,L&Mq Q%(&N6"	 &?%D%D%F 	|!FM $$V,C{/33F;9ECV 453}]cOdKeBNU<#8#=A>TW"'$'), *-v&',U37J3K3Pq-QTYZghpZqZvuvTw-wyz'{C#$	|. &,,.

  CK(s7|0q1"3|#45 %eC,?,D1&Eq I%*55H1I1NQ+OQR%S %eC,?,D1&EcReNfNkjkHl&lno p

 

 			]	^"'+c^b+c(cef"g$%9<S%SUVW1$wxx $$  0,(@.&'>$8#&#7"%n"5O8L"L/%1
 	
m  	QFGSP	Q W




z%
  QgL

sJ   -#Z Z;$[ /[A[
+[<[)[<B[	Z8'Z33Z8c           	        t        |      }| j                  ||      st        d| j                  |       d      | j	                  |j
                        5 }|j                  d|f      j                         }|j                  d|f      j                         }d d d        D cg c]  }| j                  |       }}D cg c]  }| j                  |       }	}|| j                  |      | j                  ||      |t        |      |	t        |	      t               j                         j                         dS # 1 sw Y   xY wc c}w c c}w )N"Questo account non puo accedere a r9  z
                SELECT *
                FROM tenant_tips_roster_entries
                WHERE area = ?
                ORDER BY sort_order ASC, lower(staff_name) ASC, created_at ASC
                a  
                SELECT
                    runs.*,
                    (
                        SELECT COUNT(*)
                        FROM tenant_tips_run_entries AS entries
                        WHERE entries.run_id = runs.id
                    ) AS entry_count
                FROM tenant_tips_runs AS runs
                WHERE runs.area = ?
                ORDER BY runs.tip_date DESC, runs.updated_at DESC
                LIMIT 120
                )ri  rk  rw  rosterrl  recent_runs	run_countcurrent_tip_date)r   rm  r>   r*  r  r  rO  rh  r-  r=  ro  rq   r8   r   r:   )
r>  r  ri  rj  rR  ro  run_rowsrl  r  r  s
             r7   get_tips_modulezTenantStore.get_tips_module-  s[   .t4$$Wo>A$BWBWXgBhAiijkll**7+@+@A 	Z$,, !" hj  ")) !" hj 	6 CNN3$11#6NNLTUSt;;C@UU#//@//IK&[) '	 0 : : <	
 		
;	 	6 OUs   AD>&E
E>Ec                   t        |      }| j                  ||      st        d| j                  |       d      g }t	               }t        |j                        D ]  \  }}t        |j                  xs d      j                         }	t        |	      dk  rt        d      t        |	      }
|
|v rt        |	 d      |j                  |
       |j                  |	|
t        t        |j                   xs d      d      |f        t#               }| j$                  5  | j'                  |j(                        5 }|j+                  d	|f       |D ]@  \  }	}
}}|j+                  d
dt-        j.                         j0                   ||	|
||||f       B |j3                          d d d        d d d        | j5                  ||      S # 1 sw Y   #xY w# 1 sw Y   'xY w)N"Questo account non puo modificare r9  ri   r  z*Ogni dipendente deve avere un nome valido.z& compare piu di una volta nella lista.r   r  z5DELETE FROM tenant_tips_roster_entries WHERE area = ?a  
                        INSERT INTO tenant_tips_roster_entries (
                            id,
                            area,
                            staff_name,
                            staff_lookup,
                            score,
                            sort_order,
                            created_at,
                            updated_at
                        )
                        VALUES (?, ?, ?, ?, ?, ?, ?, ?)
                        tips_roster_)r   ro  r>   r*  r   rU  r  rj   r'  rQ   rq   r[   r   rs   r  r   r  r;   r<  r  r  rO  r  r  r   rk  r  )r>  r  ri  r  rj  normalized_entriesseen_lookupsrX  	raw_entryr'  r  r  rR  r  r  s                  r7   replace_tips_rosterzTenantStore.replace_tips_roster-  s    /t4$$Wo>A$BWBWXgBhAiijkll@B!$ )'// : 	dE9y~~+,224D4y1} !MNN&t,F% D6)O!PQQV$%%tVU5AUTU;VXY5Z\a&bc	d J	ZZ  	$..w/D/DE $""K$& 8J 3D&%&& +4::<+;+;*<=+ "!&%%	4 !!#?$ 	$D ##G_==C$ $ 	$  	$s%   G;A)G$GG	GGc                    t        |      }| j                  ||      st        d| j                  |       d      | j	                  |j
                        5 }| j                  |||      cd d d        S # 1 sw Y   y xY w)Nr  r9  )r   rm  r>   r*  r  r  r  )r>  r  ri  r  rj  rR  s         r7   preview_tips_distributionz%TenantStore.preview_tips_distribution/.  s     /t4$$Wo>A$BWBWXgBhAiijkll**7+@+@A 	RZ++JQ	R 	R 	Rs   A44A=c                   t        |      }| j                  ||      st        d| j                  |       d      t	               }| j
                  5  | j                  |j                        5 }t        |j                  xs d      j                         xs& t               j                         j                         }	 t        j                  |      j                         }|j                  d||f      j!                         }
t#        |j$                  xs d      dk  xr t'        d |j(                  D              }|
"|r t#        |
d   xs d      dkD  rt        d	      t+               }|
F|j                  d
t        |
d         f      j-                         D ch c]  }t        |d          }}| j/                  ||||
t        |
d         nd       }|
t        |
d         n dt1        j2                         j4                   }|j6                  rdnd}|j6                  r|nd }|j6                  r|j8                  nd }|j6                  r(|j:                  xs |j<                  xs |j>                  nd }|
u|j                  d|||d   |d   |d   |d   |d   |d   |d   |d   |d   |d   |||||j8                  |j:                  xs |j<                  xs |j>                  ||f       n|j                  d|d   |d   |d   |d   |d   |d   |d   |d   |d   |||||j8                  |j:                  xs |j<                  xs |j>                  ||f       |j                  d|f       |j                  d|f       |d   D ]W  }tA        |tB              r|ni }|j                  dd t1        j2                         j4                   ||t        |jE                  d!      xs d      j                         tG        t        |jE                  d!      xs d            tI        t#        |jE                  d"      xs d      d#      tK        |jE                  d$            rd%ndtI        t#        |jE                  d&      xs d      d'      tI        t#        |jE                  d(      xs d      d'      tI        t#        |jE                  d)      xs d      d'      ||f       Z t+               }|d*   D ]  }tA        |tB              r|ni }t        |jE                  d      xs d      j                         }t        |jE                  d      xs d      j                         }|r|st|jM                  |       |j                  d+d,t1        j2                         j4                   |||||f        |rCtO        ||z
        }|r3d-jQ                  d. |D              }|j                  d/| d0||g|       |rd-jQ                  d1 |D              }|j6                  rX|j                  d2| d0||j8                  |j:                  xs |j<                  xs |j>                  ||gtO        |             n$|j                  d3| d0||gtO        |             |jS                          d d d        | jU                  ||      cd d d        S # t        $ r}	t        d      |	d }	~	ww xY wc c}w # 1 sw Y   GxY w# 1 sw Y   y xY w)4Nr  r9  ri   re  z
                    SELECT *
                    FROM tenant_tips_runs
                    WHERE area = ? AND tip_date = ?
                    LIMIT 1
                    r   c              3  V   K   | ]!  }t        |xs d       j                          # yw)ri   N)rj   rQ   )r  r?  s     r7   r  z5TenantStore.save_tips_distribution.<locals>.<genexpr>W.  s,      U "%++-Us   ')r2  zPer consegnare solo arretrati scegli una data senza mance giornaliere gia salvate, cosi non sovrascrivo una divisione esistente.z
                            SELECT source_run_id
                            FROM tenant_tips_run_history_sources
                            WHERE run_id = ?
                            r  rL  rb  	tips_run_r0  r  a4  
                        INSERT INTO tenant_tips_runs (
                            id,
                            area,
                            tip_date,
                            total_tip_amount,
                            tip_pos_amount,
                            tip_pos_effective_amount,
                            tip_cash_amount,
                            total_score,
                            historical_total_amount,
                            payable_total_amount,
                            present_staff_count,
                            absent_staff_count,
                            payout_status,
                            settled_at,
                            settled_by_user_id,
                            settled_by_name,
                            saved_by_user_id,
                            saved_by_name,
                            created_at,
                            updated_at
                        )
                        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                        r  ra  r_  rb  r3  r4  r5  r6  r7  a  
                        UPDATE tenant_tips_runs
                        SET total_tip_amount = ?,
                            tip_pos_amount = ?,
                            tip_pos_effective_amount = ?,
                            tip_cash_amount = ?,
                            total_score = ?,
                            historical_total_amount = ?,
                            payable_total_amount = ?,
                            present_staff_count = ?,
                            absent_staff_count = ?,
                            payout_status = ?,
                            settled_at = ?,
                            settled_by_user_id = ?,
                            settled_by_name = ?,
                            saved_by_user_id = ?,
                            saved_by_name = ?,
                            updated_at = ?
                        WHERE id = ?
                        z4DELETE FROM tenant_tips_run_entries WHERE run_id = ?z<DELETE FROM tenant_tips_run_history_sources WHERE run_id = ?r+  a  
                        INSERT INTO tenant_tips_run_entries (
                            id,
                            run_id,
                            area,
                            staff_name,
                            staff_lookup,
                            score,
                            is_present,
                            amount_today,
                            historical_amount,
                            total_amount,
                            created_at,
                            updated_at
                        )
                        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                        tips_entry_r'  r  r  r@  rm   rA  r  rB  r  rm  a  
                        INSERT INTO tenant_tips_run_history_sources (
                            id,
                            run_id,
                            source_run_id,
                            source_tip_date,
                            created_at,
                            updated_at
                        )
                        VALUES (?, ?, ?, ?, ?, ?)
                        tips_history_rS  c              3      K   | ]  }d   ywrU  r5   rW  s     r7   r  z5TenantStore.save_tips_distribution.<locals>.<genexpr>#/  s     0R0RrZ  a  
                            UPDATE tenant_tips_runs
                            SET payout_status = 'pending',
                                settled_at = NULL,
                                settled_by_user_id = NULL,
                                settled_by_name = NULL,
                                updated_at = ?
                            WHERE area = ?
                              AND payout_status = 'carried'
                              AND id IN ()
                            c              3      K   | ]  }d   ywrU  r5   rW  s     r7   r  z5TenantStore.save_tips_distribution.<locals>.<genexpr>4/  s     ,NQS,NrZ    
                            UPDATE tenant_tips_runs
                            SET payout_status = 'settled',
                                settled_at = COALESCE(settled_at, ?),
                                settled_by_user_id = COALESCE(settled_by_user_id, ?),
                                settled_by_name = COALESCE(settled_by_name, ?),
                                updated_at = ?
                            WHERE area = ?
                              AND id IN (a  
                            UPDATE tenant_tips_runs
                            SET payout_status = 'carried',
                                settled_at = NULL,
                                settled_by_user_id = NULL,
                                settled_by_name = NULL,
                                updated_at = ?
                            WHERE area = ?
                              AND payout_status = 'pending'
                              AND id IN ()+r   rm  r>   r*  r;   r<  r  r  rj   r  rQ   r8   r   r:   r  rO  r  r   r  r  r  r   rh  r  r  r  r   r  r1  r  r  r  r   rt   r   r[   r  r   r   rt  r%  rk  get_tips_run_detail)r>  r  ri  r  rj  r  rR  rn  normalized_tip_dater  existing_run_rowhistory_only_requestprevious_source_idsrl  previewr?  rc  rd  re  rf  row_payloadselected_source_idsr  source_payloadrL  rM  released_source_idsrb  s                               r7   save_tips_distributionz"TenantStore.save_tips_distribution<.  s    /t4$$Wo>A$BWBWXgBhAiijkllJ	ZZ U	N..w/D/DE R$"7#3#3#9r:@@BbginnFVF`F`FbY*.*<*<\*J*T*T*V' $.#5#5 %&9:$ (* ! (-W-A-A-FQ'G1'L (QT U")"9"9U R$
 %0,./ABGaH1L$H 
 14##/ $.#5#5 
 !!1$!78:$ #(*
+ C01
+' 
+ 22#BRB^3'7'=#>dh	 3  9I8T-d34\efjfpfpfrfvfvewZx-4-F-F	I*1*C*CY
8?8Q8QW__W["cjc|c|7#4#4#^8H8H#^GL^L^  CG#+&&4 #+#J/#$67#$45#$>?#$56#M2#$=>#$:;#$9:#$89)&.+#OO#--W1A1AWWEWEW%%)50d &&* $$67#$45#$>?#$56#M2#$=>#$:;#$9:#$89)&.+#OO#--W1A1AWWEWEW%"#+(R &&']`f_hi&&'ehngpq"6? "C)3C)>#BK&&$ *$**,*:*:);<"+ 7 =2>DDF-c+//&2I2OR.PQ!%(@(EA"FJ!%kool&C!DA!!%(G(L1"MqQ!%8K(L(QPQ"RTUV!%(G(L1"MqQ%%% "H 14#%&=> F/9&$/GVRN$'(:(:4(@(FB$G$M$M$OM&).*<*<Z*H*NB&O&U&U&WO( '++M:&&
 ,DJJL,<,<+=>")+%%: '*01DGZ1Z*['*'+yy0R>Q0R'R"**	!* +7 8
  'N:MN '#'99,N:M,N#NL00"**!* +7 8	  !* ' ' 1 1 [W5E5E [I[I[ ) / "((;!<* #**	!* +7 8
  'V&AT:UV !!#eR$h ++G_fMkU	N U	N
 " Y$%NOUXXY6
+?R$ R$U	N U	NsW   [;.A[/?#["B;[/[*2S7[/)[;	['[""[''[//[8	4[;;\c                  t        |      }| j                  ||      st        d| j                  |       d      t	               }|rdnd}|r|nd }|r|j
                  nd }	|r(|j                  xs |j                  xs |j                  nd }
| j                  5  | j                  |j                        5 }| j                  |||       |j                  d|||	|
|||f       |j                  d|f      j                         D cg c]  }t        |d          }}|rdj!                  d	 |D              }|rO|j                  d
| d||j
                  |j                  xs |j                  xs |j                  ||g|       n|j                  d| d||g|       |j#                          d d d        d d d        | j%                  |||      S c c}w # 1 sw Y   )xY w# 1 sw Y   -xY w)Nr  r9  r0  r  aF  
                    UPDATE tenant_tips_runs
                    SET payout_status = ?,
                        settled_at = ?,
                        settled_by_user_id = ?,
                        settled_by_name = ?,
                        updated_at = ?
                    WHERE area = ? AND id = ?
                    z
                        SELECT source_run_id
                        FROM tenant_tips_run_history_sources
                        WHERE run_id = ?
                        rL  rS  c              3      K   | ]  }d   ywrU  r5   rW  s     r7   r  z9TenantStore.set_tips_run_payout_status.<locals>.<genexpr>/  s     ,EQS,ErZ  r  r  a  
                            UPDATE tenant_tips_runs
                            SET payout_status = 'carried',
                                settled_at = NULL,
                                settled_by_user_id = NULL,
                                settled_by_name = NULL,
                                updated_at = ?
                            WHERE area = ?
                              AND id IN ()r   ro  r>   r*  r;   r1  r  r  r  r<  r  r  rP  rO  rh  rj   r%  rk  r  )r>  r  ri  r?  r  rj  r  rc  rd  re  rf  rR  rl  
source_idsrb  s                  r7   set_tips_run_payout_statusz&TenantStore.set_tips_run_payout_status^/  sQ    /t4$$Wo>A$BWBWXgBhAiijkllJ	%.	I"+Y
09W__t[d7,,V0@0@VGDVDVjnZZ K	$..w/D/DE J$''
OVL"" &"*'!',  *11
  	  hj
 O,-

 
 #'99,E*,E#EL "**!* +7 8	  !* ' ' 1 1 [W5E5E [I[I[ ) / ",* #**!* +7 8	  !* / "," !!#UJ$K	$Z ''&IIm
-J$ J$K	$ K	$s8   G'6AGGBG3G'GG$	 G''G0c                   t        |      }| j                  ||      st        d| j                  |       d      | j	                  |j
                        5 }| j                  |||      }| j                  ||g      j                  |g       }| j                  ||g      j                  |g       }d d d        || j                  |      | j                  ||      | j                        dS # 1 sw Y   @xY w)Nr  r9  )ri  rk  rw  runr  history_sources)r   rm  r>   r*  r  r  rP  rI  r   rN  ro  r=  )	r>  r  ri  r?  rj  rR  r~  r  r  s	            r7   r  zTenantStore.get_tips_run_detail/  s
    /t4$$Wo>A$BWBWXgBhAiijkll**7+@+@A 	hZ--j/6RG11*vhGKKFTVWG"AA*vhW[[\bdfgO	h $//@//I77@.
 	
	h 	hs   AC00C9)status_filterr8  c                  | j                  |      st        d      | j                  |      }t        dt	        t        |      d            }|xs dj                         j                         }h d}g }g }	|r6|dk7  r1||vrt        d      |j                  d       |	j                  |       |s,|j                  d	       |	j                  |j                         |rd
dj                  |       nd}
| j                  |j                        5 }|j                  d|
 dt        |	            j                         }|j                  d|
 dt        |	|gz               j!                         }d d d        D cg c]$  }| j#                  | j%                  |      |      & }}|t        |d   xs d      |dS t'        |      |dS # 1 sw Y   ^xY wc c}w )N/Questo account non puo accedere a Segnalazioni.rm   r  ri   >   r  resolvedreviewedin_progressreported_to_ownerr_   zStato segnalazione non valido.
status = ?zreporter_user_id = ?r  r  z^
                SELECT COUNT(*) AS total
                FROM tenant_reports
                z
                zN
                SELECT *
                FROM tenant_reports
                zR
                ORDER BY created_at DESC
                LIMIT ?
                r@  r   r   )r&   r  rw  )r\  r>   r^  r  r   r   rQ   rP   rs   r1  r%  r  r  rO  ro   r  rh  rA  r  rq   )r>  r  r  r8  rw  r  normalized_statusallowed_statusesr'  r   r(  rR  total_count_rowr+  rl  r&   s                   r7   list_reportszTenantStore.list_reports/  s    ''0NOO--g6
CE
C01
*0b779??A^!!2e!; (88 !ABBNN<(MM+,NN12MM'//*8?fW\\'234R	**7+@+@A 	Z(00  
 f hj  %%   f
|+,	 hj 	, 
 ))$*?*?*DYc)d
 

 APA\3w7<1=$
 	
befmbn$
 	
1	 	(
s   AG;)GGc               P   t        dt        t        |      d            }| j                  |j                        5 }|j                  d|df      j                         }d d d        D cg c]$  }| j                  | j                  |      d      & c}S # 1 sw Y   9xY wc c}w )Nr[  r  z
                SELECT *
                FROM tenant_reports
                WHERE substr(created_at, 1, 4) = ?
                ORDER BY created_at ASC, id ASC
                r  Tr  )	r  r   r   r  r  rO  rh  rA  r  )r>  r  ru  r  rR  r+  rl  s          r7   list_reports_for_google_sheetz)TenantStore.list_reports_for_google_sheet0  s    c#d)T23	**7+@+@A 		Z%% c?% hj 		 
 ))$*?*?*DY])^
 	
		 		
s   $B+)B#B c                f   | j                  |      st        d      |j                  j                         }|j                  j                         }|j
                  r|j
                  j                         nd }|r|st        d      dt        j                         j                   }t               }| j                  5  | j                  |j                        5 }|j                  d||j                  |j                  |j                   |j"                  |||j$                  ||j&                  ||f       |j)                          | j+                  ||      }	d d d        d d d        d| j-                  | j/                  	      | j1                  |            iS # 1 sw Y   DxY w# 1 sw Y   HxY w)Nr  z9Titolo e descrizione della segnalazione sono obbligatori.report_a$  
                    INSERT INTO tenant_reports (
                        id,
                        reporter_user_id,
                        reporter_name,
                        reporter_username,
                        reporter_email,
                        title,
                        description,
                        category,
                        location,
                        priority,
                        status,
                        admin_note,
                        status_updated_by_user_id,
                        status_updated_by_name,
                        resolved_at,
                        created_at,
                        updated_at
                    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'new', NULL, NULL, NULL, NULL, ?, ?)
                    reportr  )r\  r>   r  rQ   r  r  r  r  r   r;   r<  r  r  rO  r1  r  r  r  rZ  r  rk  rI  rA  r  r^  )
r>  r  r  r  r  r  rH  r  rR  rl  s
             r7   create_reportzTenantStore.create_report!0  s   ''0NOO##%))//1/6/?/?7##))+TKXYYdjjl../0	J	ZZ (	C..w/D/DE 'C"", "))((**#(( ((!!-$J !!#++J	BO'C(	CV d33%%c*#'#;#;G#D 4 
 	
S'C 'C(	C (	Cs%   ?F'A=FF'F$	 F''F0c                   | j                  |      st        d      |j                         }|st        d      t	               }|j
                  r|j
                  j                         nd }|j                  dk(  r|nd }|j                  xs |j                  xs |j                  xs dj                         }| j                  5  | j                  |j                        5 }	| j                  |	|       |	j                  d|j                  ||j                  ||||f       |	j!                          | j                  |	|      }
d d d        d d d        d| j#                  | j%                  
      d      iS # 1 sw Y   5xY w# 1 sw Y   9xY w)	Nz7Solo l'admin del locale puo aggiornare le segnalazioni.rF  r  zAdmin localea  
                    UPDATE tenant_reports
                    SET
                        status = ?,
                        admin_note = ?,
                        status_updated_by_user_id = ?,
                        status_updated_by_name = ?,
                        resolved_at = ?,
                        updated_at = ?
                    WHERE id = ?
                    r  Tr  )r^  r>   rQ   r  r;   r  r  r  r  r  r<  r  r  rI  rO  r1  rk  rA  r  )r>  r  rH  r  normalized_report_idr  r  r  r  rR  rl  s              r7   update_report_statusz TenantStore.update_report_status^0  s    ''0VWW(0#677J	3:3E3EW''--/4
#*>>Z#?iT")"3"3"ow7G7G"o7K]K]"oao!v!v!xZZ 	N..w/D/DE N%%j2FG""
  ".#!,, !!#++J8LM3N	N: d33%%c*#' 4 
 	
7N N	N 	Ns%   F!A"E7F7F 	<FFc                R   | j                  |      st        d      |j                  j                         }|j                  j
                  j                         }|j                  j                  j                         }|j                  r|j                  j                         nd }|r|r|st        d      t               }dt        j                         j                   }| j                  5  | j                  |j                        5 }	|	j                  d||j                   ||||||f       |	j#                          |	j                  d|f      j%                         }
d d d        d d d        
t        d      d| j'                  |
      iS # 1 sw Y   1xY w# 1 sw Y   5xY w)Nz7Solo l'admin del locale puo attivare le notifiche push.zSottoscrizione push incompleta.push_al  
                    INSERT INTO tenant_push_subscriptions (
                        id,
                        user_id,
                        endpoint,
                        p256dh,
                        auth,
                        user_agent,
                        enabled,
                        last_error,
                        created_at,
                        updated_at
                    ) VALUES (?, ?, ?, ?, ?, ?, 1, NULL, ?, ?)
                    ON CONFLICT(endpoint) DO UPDATE SET
                        user_id = excluded.user_id,
                        p256dh = excluded.p256dh,
                        auth = excluded.auth,
                        user_agent = excluded.user_agent,
                        enabled = 1,
                        last_error = NULL,
                        updated_at = excluded.updated_at
                    zBSELECT * FROM tenant_push_subscriptions WHERE endpoint = ? LIMIT 1z Sottoscrizione push non salvata.subscription)r^  r>   r  rQ   r  r  r  r  r;   r  r  r   r<  r  r  rO  r1  rk  r  rD  )r>  r  r  r  r  r  r  r  subscription_idrR  rl  s              r7   upsert_push_subscriptionz$TenantStore.upsert_push_subscription0  s   ''0VWW##))+**11779((--3353:3E3EW''--/4
vT>??J	!$**,"2"2!34ZZ (	..w/D/DE '"". ( "!!	/!D !!# ((XK (* I'(	T ;?@@ E Ec JKKW' '(	 (	s%   /FAF!FF	FF&c                \   |j                         }|st        d      | j                  5  | j                  |j                        5 }|j                  d||j                  f      }|j                          d d d        d d d        dj                  dkD  iS # 1 sw Y   "xY w# 1 sw Y   &xY w)NzEndpoint push mancante.z
                    DELETE FROM tenant_push_subscriptions
                    WHERE endpoint = ? AND user_id = ?
                    r:  r   )	rQ   r>   r<  r  r  rO  r1  rk  rowcount)r>  r  r  normalized_endpointrR  cursors         r7   delete_push_subscriptionz$TenantStore.delete_push_subscription0  s    &nn."677ZZ 		$..w/D/DE $#++ )'//: !!#$		$ 6??Q.//$ $		$ 		$s#   B"/B5B"B	B""B+c                J   t               }| j                  |      r|j                  |j                         | j	                         5 }|j                  d|j                  f      j                         }d d d        |j                  d D               | j	                         5 }|j                  d|j                  f      j                         }d d d        |j                  d |D               |sg S dj                  d |D              }| j                  |j                        5 }|j                  d| dt        |            j                         }d d d        |D cg c]  }| j                  |       c}S # 1 sw Y   
xY w# 1 sw Y   xY w# 1 sw Y   AxY wc c}w )	Nz
                SELECT id
                FROM users
                WHERE tenant_id = ?
                  AND role IN ('owner', 'super_admin')
                c              3  D   K   | ]  }|d    s	t        |d            ywr  r  r  s     r7   r  z<TenantStore.list_admin_push_subscriptions.<locals>.<genexpr>0  s     JD	c#d)nJ   
  z
                SELECT user_id
                FROM tenant_admin_push_recipients
                WHERE tenant_id = ?
                  AND enabled = 1
                c              3  D   K   | ]  }|d    s	t        |d            yw)r1  Nr  r  s     r7   r  z<TenantStore.list_admin_push_subscriptions.<locals>.<genexpr>0  s     TcS^c#i.1Tr  rS  c              3      K   | ]  }d   ywrU  r5   rW  s     r7   r  z<TenantStore.list_admin_push_subscriptions.<locals>.<genexpr>0  s      = =rZ  z
                SELECT *
                FROM tenant_push_subscriptions
                WHERE enabled = 1
                  AND user_id IN (z;)
                ORDER BY updated_at DESC
                )r   r^  r   r1  rL  rO  r  rh  updater%  r  r  ro   rD  )r>  r  admin_user_idsrR  r+  rb  rl  s          r7   list_admin_push_subscriptionsz)TenantStore.list_admin_push_subscriptions0  s   #&5##G,w/##% 		%% ""$ hj 		 	JJJ##% 		%% ""$ hj 		 	TTTTIyy =n ==**7+@+@A 
	Z%%# $0. 1	 n%	 hj 
	 GKKs55c:KKK		 				 		
	 
	 Ls*   ,E;#,F&.F F ;FFF)disablec          	     :   |j                         }|sy t               }| j                  5  | j                  |j                        5 }|j                  d|rdnd|d d ||f       |j                          d d d        d d d        y # 1 sw Y   xY w# 1 sw Y   y xY w)Na  
                    UPDATE tenant_push_subscriptions
                    SET
                        enabled = CASE WHEN ? THEN 0 ELSE enabled END,
                        last_error = ?,
                        updated_at = ?
                    WHERE endpoint = ?
                    rm   r   r5  )rQ   r;   r<  r  r  rO  rk  )r>  r  r  r  r  r  r  rR  s           r7   mark_push_subscription_errorz(TenantStore.mark_push_subscription_error
1  s     'nn."J	ZZ 	$..w/D/DE $"" "Qq,u*=yJ]^
 !!#$	$ 	$$ $	$ 	$s#   B.B4BB	
BBc                     j                  |      st        d      t               j                         j	                         }j                         t        j                               z
  j	                         } j                  ||j                  d      }t        d |D        d       } j                  ||j                  ||d      } j                  ||j                  ||d      }t         fd	|D              }t         fd
|D              }	|j                  dv | j                  |      nd |t        |dz  d      |	t        |	dz  d      |D 
cg c]  }
 j                  |
       c}
dS c c}
w )N(Questo account non puo accedere a Turni.rw  r  )r1  r8  c              3  :   K   | ]  }|j                   |  y wr2   )r  rT  s     r7   r  z3TenantStore.get_timeclock_status.<locals>.<genexpr>/1  s     Yu%..BXUYs   r  r1  r2  r3  r8  r5  c              3  d   K   | ]'  }t        j                  |       d   xs d       ) ywr  r  r   Nr   r  r  r!  r3   r>  s     r7   r  z3TenantStore.get_timeclock_status.<locals>.<genexpr>91  s:       Px}C ? ?UX ? YZl m rqrs  Pra  c              3  d   K   | ]'  }t        j                  |       d   xs d       ) ywr  r  r  s     r7   r  z3TenantStore.get_timeclock_status.<locals>.<genexpr>:1  s:       Nw|3t>>uTW>XYklqpqr  Nra  >   r   r   r  r  r  )can_manage_teamactive_entrytoday_secondstoday_hoursweek_seconds
week_hoursrecent_entries)r  r>   rA   r   r:   r	   r  r0  r1  r  r  r   r  r  )r>  r  today
week_startr  r  today_entriesweek_entriesr  r  r!  r3   s   `          @r7   get_timeclock_statusz TenantStore.get_timeclock_status&1  s   ))'2GHHl
$$&hhj9#++-#@@KKM
55gw^`5aYY[_`44Wgoobgrw  @C4  D33OO! 4 
   P  BO  P  P  N  AM  N  N  '||/GG`l`xD;;LX[;\  C* !5q9(t 3Q7ftu]bt>>uTW>Xu
 	
 vs    E?c                   | j                  |      st        d      t               }| j                  5  | j	                  |j
                        5 }|j                  d|j                  f      j                         }|t        d      dt        j                         j                   }|j                  d||j                  |j                  |j                  |j                  |||f       |j                          |j                  d|f      j                         }d d d        d d d        t        d      d| j!                  | j#                  |            iS # 1 sw Y   @xY w# 1 sw Y   DxY w)	Nr  
                    SELECT *
                    FROM tenant_timeclock_entries
                    WHERE user_id = ? AND ended_at IS NULL
                    ORDER BY started_at DESC
                    LIMIT 1
                    z.Esiste gia un turno attivo per questo account.
timeclock_ar  
                    INSERT INTO tenant_timeclock_entries (
                        id,
                        user_id,
                        user_name,
                        username,
                        user_email,
                        started_at,
                        ended_at,
                        duration_seconds,
                        started_source,
                        ended_source,
                        notes,
                        created_at,
                        updated_at
                    ) VALUES (?, ?, ?, ?, ?, ?, NULL, NULL, 'portal', NULL, NULL, ?, ?)
                    ;SELECT * FROM tenant_timeclock_entries WHERE id = ? LIMIT 1z%Turno avviato ma non piu disponibile.r!  )r  r>   rC   r<  r  r  rO  r1  r  r  r  r   r  r  r  rk  r  r  )r>  r  r  rR  
active_rowentry_idrl  s          r7   start_timeclock_shiftz!TenantStore.start_timeclock_shiftF1  sw   ))'2GHH#%
ZZ 1	..w/D/DE 0'// __&	 (*  )$%UVV'

(8(8'9:""$ !))((**"""	%: !!# ((QK (* [01	f ;DEE889W9WX[9\]^^i0 01	 1	s$   E'CEE'E$	 E''E0c           	     @   | j                  |      st        d      t               }|j                         }| j                  5  | j                  |j                        5 }|j                  d|j                  f      j                         }|t        d      | j                  |      }| j                  |j                        }d}|&t        t        ||z
  j                               d      }|j                  d||||j                   f       |j#                          |j                  d|j                   f      j                         }	d d d        d d d        	t        d      d| j%                  | j                  |	            iS # 1 sw Y   @xY w# 1 sw Y   DxY w)	Nr  r  z8Non vedo un turno attivo da chiudere per questo account.r   z
                    UPDATE tenant_timeclock_entries
                    SET ended_at = ?, duration_seconds = ?, ended_source = 'portal', updated_at = ?
                    WHERE id = ?
                    r  z$Turno chiuso ma non piu disponibile.r!  )r  r>   rA   r:   r<  r  r  rO  r1  r  r  r  r  r  r   r  r  rk  r  )
r>  r  
stopped_atstopped_at_isorR  r  r  r  r  rl  s
             r7   stop_timeclock_shiftz TenantStore.stop_timeclock_shift1  s   ))'2GHH\
#--/ZZ !	..w/D/DE  '// __&	 (*  %$%_``#==jI!55l6M6MN
#$ )'*3
Z0G/V/V/X+Y[\']$""
 $%5~|W !!# ((Q!__& (* ; !	F ;CDD889W9WX[9\]^^I   !	 !	s%   FC*F	FF	FFc                H   | j                  |      st        d      |j                  r|j                  j                         nd }|j                  dv }|s|j                  }t               }| j                  |||j                  r|j                  j                         nd |j                  r|j                  j                         nd |j                        }| j                  ||dd      }|s)|D cg c]  }|j                  |j                  k(  s|  }}|||j                  |j                  |j                  d|D cg c]  }| j                  ||       c}| j                  |r|n||      |D cg c]  }| j                  ||       c}d	S c c}w c c}w c c}w )
Nr  >   r   r   r  Tr4  )r1  r  r8  r  r1  )r  r'  r  summary_by_userr3  )r  r>   r1  rQ   r   rA   r0  r2  r3  r8  r  r>  )	r>  r  r  effective_user_idr  r3   r  r3  r!  s	            r7   get_timeclock_overviewz"TenantStore.get_timeclock_overview1  s   ))'2GHH5:]]EMM//1!,,*BB 'l..%383C3Cu''--//4~~U^^))+4++ / 
 55% $	 6 
 1?d5==T[TcTcCcedNd  /,#..!NN	 `ggV[77S7Qg#<<WUcil<mftu]bt>>uTW>Xu
 	
 e hus   2FF F6Fc                   |j                   dk7  rt        d      |t        k(  rt        d      | j                  5  | j	                         5 }|j                  d|f      j                         }|t        d      | j                  ||t              cd d d        cd d d        S # 1 sw Y   nxY w	 d d d        y # 1 sw Y   y xY w)Nr   r  z2Usa la sessione piattaforma per il pannello admin.r  zLocale non trovato.r  )	r   r>   r  r<  rL  rO  r  r  r  )r>  r  r  rR  r  s        r7   impersonate_tenantzTenantStore.impersonate_tenant1  s    <<=(CDD--QRRZZ 	j'') jZ#++AL (*  >$%:;;++J)Uh+ij j	j 	jj j j	j 	j 	js$   B;AB%	B;%B.	*B;;Cc                J   | j                         5 }|j                  d||f      j                         }d d d        i d fS 	 t        j                  |d         }t        |t              si |d   fS ||d   fS # 1 sw Y   FxY w# t        j
                  $ r
 i |d   fcY S w xY w)Nz
                SELECT config_json, updated_at
                FROM tenant_llm_settings
                WHERE tenant_id = ? AND scope = ?
                LIMIT 1
                config_jsonr  )rL  rO  r  r   r   r   r   rt   )r>  r  rM  rR  rl  r  s         r7   get_llm_settingszTenantStore.get_llm_settings1  s    ##% 		$$ E" hj 		 ;t8O	)jj]!34G '4(s<(((L)))-		 		  ## 	)s<(((	)s   #A9B 9BB"!B"c           	     "   t               }t        j                  |d      }| j                  5  | j	                         5 }|j                  d||||f       |j                          d d d        d d d        |S # 1 sw Y   xY w# 1 sw Y   |S xY w)NTr  aR  
                    INSERT INTO tenant_llm_settings (tenant_id, scope, config_json, updated_at)
                    VALUES (?, ?, ?, ?)
                    ON CONFLICT(tenant_id, scope) DO UPDATE SET
                        config_json = excluded.config_json,
                        updated_at = excluded.updated_at
                    )r;   r   r   r<  rL  rO  rk  )r>  r  rM  configr  r;  rR  s          r7   upsert_llm_settingszTenantStore.upsert_llm_settings1  s    Z
ZZT:
ZZ 	$'') $Z"" z:>	 !!#$	$ $ $	$ s"   B'A8&B8B	=BBc                J    | j                   |z  }|j                  dd       |S NTrB  )r5  rK  r>  r  targets      r7   menu_assets_directoryz!TenantStore.menu_assets_directory2  s'    &&2TD1r6   c                J    | j                   |z  }|j                  dd       |S r	  )r7  rK  r	  s      r7   fiscal_documents_directoryz&TenantStore.fiscal_documents_directory2  s'    ++i7TD1r6   ri   )r  r  r  c               L   t               }| j                  5  | j                         5 }|j                  d|||||||||
||	|||f       |j	                          d d d        d d d        | j                  ||      }|t        d| d      |S # 1 sw Y   6xY w# 1 sw Y   :xY w)Na  
                    INSERT INTO tenant_menu_assets (
                        id,
                        tenant_id,
                        original_name,
                        display_name,
                        mime_type,
                        kind,
                        file_size_bytes,
                        storage_path,
                        extracted_text,
                        analysis_text,
                        status,
                        error_detail,
                        created_at,
                        updated_at
                    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                    Asset menu r  r;   r<  rL  rO  rk  get_menu_assetr  )r>  asset_idr  r  r  r  r  r  r  r  r  r  r  r  rR  r  s                   r7   create_menu_assetzTenantStore.create_menu_asset2  s      J	ZZ &	$'') %$Z""& !!%$!'$&%$!!'#H !!#K%$&	$P $$Y9>[
,?@@U%$ %$&	$ &	$s"   B1BBB	BB#c                    | j                         5 }|j                  d||f      j                         }d d d        y | j                  |      S # 1 sw Y   xY w)Nz
                SELECT *
                FROM tenant_menu_assets
                WHERE tenant_id = ? AND id = ?
                LIMIT 1
                )rL  rO  r  r  )r>  r  r	  rR  rl  s        r7   r	  zTenantStore.get_menu_asset[2  sk    ##% 		$$ H% hj 		 ;((--		 		   #AAc                    | j                         5 }t        |j                  d|f            }d d d        D cg c]  }| j                  |       c}S # 1 sw Y   (xY wc c}w )Nz
                    SELECT *
                    FROM tenant_menu_assets
                    WHERE tenant_id = ?
                    ORDER BY created_at DESC, id DESC
                    )rL  r   rO  r  )r>  r  rR  r+  rl  s        r7   list_menu_assetszTenantStore.list_menu_assetsl2  sl    ##% 	"" L
D	 ;??3))#.??	 	 @s   AA!A)r  r  r  r  r  c                  dg}t               g}	|"|j                  d       |	j                  |       |"|j                  d       |	j                  |       |"|j                  d       |	j                  |       |"|j                  d       |	j                  |       |"|j                  d       |	j                  |       |	j                  ||g       | j                  5  | j	                         5 }
|
j                  ddj                  |       d	|	       |
j                          d d d        d d d        | j                  ||      }|t        d
| d      |S # 1 sw Y   6xY w# 1 sw Y   :xY w)Nupdated_at = ?zdisplay_name = ?zextracted_text = ?zanalysis_text = ?r  zerror_detail = ?zG
                    UPDATE tenant_menu_assets
                    SET rS  H
                    WHERE tenant_id = ? AND id = ?
                    r	  r  )
r;   rs   r#  r<  rL  rO  r%  rk  r	  r  )r>  r  r	  r  r  r  r  r  rL  r   rR  r  s               r7   update_menu_assetzTenantStore.update_menu_asset|2  sz    #3!3 (
|#12MM,'%34MM.)$23MM-(|,MM&!#12MM,'y(+,ZZ 
	$'') 	$Z"";/0 1
  !!#	$
	$ $$Y9>[
,?@@	$ 	$
	$ 
	$s$   "E*36E)E*E'	#E**E3c                6   t               }| j                  5  | j                         5 }|j                  d|||f       |j	                          d d d        d d d        | j                  ||      }|t        d| d      |S # 1 sw Y   6xY w# 1 sw Y   :xY w)Nz
                    UPDATE tenant_menu_assets
                    SET error_detail = NULL, updated_at = ?
                    WHERE tenant_id = ? AND id = ?
                    r	  r  r	  )r>  r  r	  r  rR  r  s         r7   clear_menu_asset_errorz"TenantStore.clear_menu_asset_error2  s    J	ZZ 
	$'') 	$Z""
 	84 !!#	$
	$ $$Y9>[
,?@@	$ 	$
	$ 
	$s"   B&BBB	BBc                   | j                  ||      }|y | j                  5  | j                         5 }|j                  d||f       |j	                          d d d        d d d        |S # 1 sw Y   xY w# 1 sw Y   |S xY w)Nz=DELETE FROM tenant_menu_assets WHERE tenant_id = ? AND id = ?)r	  r<  rL  rO  rk  )r>  r  r	  r  rR  s        r7   delete_menu_assetzTenantStore.delete_menu_asset2  s    &&y(;ZZ 	$'') $Z""S) !!#$	$ $ $	$ s"   A6%A*A6*A3	/A66B r  
processingr  r  )r  r  r  rT  r  r  r  r  r  r  r  r  r  c               `   t               }| j                  5  | j                         5 }|j                  d|||||||||	|
||||||||||||||f       |j	                          d d d        d d d        | j                  ||      }|t        d| d      |S # 1 sw Y   6xY w# 1 sw Y   :xY w)Na+  
                    INSERT INTO tenant_fiscal_documents (
                        id,
                        tenant_id,
                        original_name,
                        display_name,
                        mime_type,
                        kind,
                        file_size_bytes,
                        file_hash,
                        storage_path,
                        document_type,
                        document_number,
                        document_date,
                        supplier_name,
                        total_amount,
                        currency,
                        summary_text,
                        extracted_text,
                        preview_text,
                        status,
                        matching_status,
                        review_status,
                        error_detail,
                        created_at,
                        updated_at
                    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                    Documento fiscale r  )r;   r<  rL  rO  rk  get_fiscal_documentr  )r>  r  r  r  r  r  r  r  r  r  r  r  r  rT  r  r  r  r  r  r  r  r  r  r  rR  r  s                             r7   create_fiscal_documentz"TenantStore.create_fiscal_document2  s    4 J	ZZ :	$'') 9$Z"": $!%$!'!$%'%%$ $&$'%$!!1;7p !!#s9$:	$x )))[A>/}LIJJ}9$ 9$:	$ :	$s"   B$;B#B$B!	B$$B-c                    | j                         5 }|j                  d||f      j                         }d d d        y | j                  |      S # 1 sw Y   xY w)Nz
                SELECT *
                FROM tenant_fiscal_documents
                WHERE tenant_id = ? AND id = ?
                LIMIT 1
                rL  rO  r  r  )r>  r  r  rR  rl  s        r7   r"	  zTenantStore.get_fiscal_document/3  sk    ##% 		$$ K( hj 		 ;--c22		 		r	  c                    | j                         5 }|j                  d||f      j                         }d d d        y | j                  |      S # 1 sw Y   xY w)Nz
                SELECT *
                FROM tenant_fiscal_documents
                WHERE tenant_id = ? AND file_hash = ?
                ORDER BY created_at DESC
                LIMIT 1
                r%	  )r>  r  r  rR  rl  s        r7   get_fiscal_document_by_hashz'TenantStore.get_fiscal_document_by_hash?3  sk    ##% 
	$$ I&	 hj 
	 ;--c22
	 
	r	  c                   | j                         5 }t        |j                  d|f            }d d d        D cg c]  }| j                  |       }}g }t	               }t	               }|D ]r  }	| j                  |	      }
|
|v r|	j                  xs dj                         }|r||v r>|j                  |
       |r|j                  |       |j                  |	       t |S # 1 sw Y   xY wc c}w )Nz
                    SELECT *
                    FROM tenant_fiscal_documents
                    WHERE tenant_id = ?
                    ORDER BY created_at DESC, id DESC
                    ri   )
rL  r   rO  r  r   r  r  rQ   r   rs   )r>  r  rR  r+  rl  recordsdedupedseen_semantic_keysseen_hashesr  semantic_keyr  s               r7   list_fiscal_documentsz!TenantStore.list_fiscal_documentsP3  s   ##% 	"" L
D	 CGG3411#6GG.069e # 
	#F::6BL11))/R668IY+5""<0	*NN6"
	# 7	 	 Hs   C#C/#C,c                    | j                         5 }t        |j                  d||f            }d d d        D cg c]  }| j                  |       c}S # 1 sw Y   (xY wc c}w )Nz
                    SELECT *
                    FROM tenant_fiscal_document_items
                    WHERE tenant_id = ? AND document_id = ?
                    ORDER BY line_index ASC, created_at ASC, id ASC
                    )rL  r   rO  r  )r>  r  r  rR  r+  rl  s         r7   list_fiscal_document_itemsz&TenantStore.list_fiscal_document_itemsn3  sq    ##% 	"" ,
D	 JNN#88=NN	 	 O   AA"Ac                
   t               }| j                  5  | j                         5 }|j                  d||f       t	        |      D ]o  \  }}|j                  ddt        j                         j                   ||t        |j                  d      xs |      |j                  d      |j                  d      t        |j                  d      xs d      j                         |j                  d	      |j                  d
      |j                  d      |j                  d      |j                  d      |j                  d      |j                  d      |j                  d      |j                  d      |j                  d      t        |j                  d      xs d      j                         ||f       r |j                          d d d        d d d        | j                  ||      S # 1 sw Y   #xY w# 1 sw Y   'xY w)NPDELETE FROM tenant_fiscal_document_items WHERE tenant_id = ? AND document_id = ?a  
                        INSERT INTO tenant_fiscal_document_items (
                            id,
                            tenant_id,
                            document_id,
                            line_index,
                            product_code,
                            iso_code,
                            description,
                            category_code,
                            unit_code,
                            pack_count,
                            quantity,
                            gross_quantity,
                            tare_quantity,
                            net_quantity,
                            unit_price,
                            line_total,
                            vat_code,
                            raw_row_text,
                            created_at,
                            updated_at
                        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                        	fdocitem_r  rO  r  r  ri   r  r  r  rB  r  r  r  r  r  r  r  )r;   r<  rL  rO  rU  r  r  r   r   r   rj   rQ   rk  r0	  )r>  r  r  r  r  rR  rX  r   s           r7   replace_fiscal_document_itemsz)TenantStore.replace_fiscal_document_items}3  s    J	ZZ 7	$'') 6$Z""f, $-U#3 0KE4&&2 (

(8(8'9:%' 6 ?%@ HH^4 HHZ0 7 =2>DDF HH_5 HH[1 HH\2 HHZ0 HH%56 HH_5 HH^4 HH\2 HH\2 HHZ0 8 >B?EEG%%)3/0b !!#m6$7	$p ..y+FFo6$ 6$7	$ 7	$s#   G9F#G-G9-G6	2G99Hc                    | j                         5 }|j                  d|f      j                         }d d d        t        |d d       S | j	                  |      S # 1 sw Y   *xY w)Nz
                SELECT tenant_id, inbound_email, updated_at
                FROM tenant_fiscal_document_settings
                WHERE tenant_id = ?
                LIMIT 1
                r  )rL  rO  r  r   r  r  s       r7   get_fiscal_document_settingsz(TenantStore.get_fiscal_document_settings3  sv    ##% 		$$  hj 		 ;/)SWdhii66s;;		 		s   "AA%c                  t               }| j                  5  | j                         5 }|j                  d|||f       |j	                          d d d        d d d        | j                  |      S # 1 sw Y   "xY w# 1 sw Y   &xY w)NaS  
                    INSERT INTO tenant_fiscal_document_settings (tenant_id, inbound_email, updated_at)
                    VALUES (?, ?, ?)
                    ON CONFLICT(tenant_id) DO UPDATE SET
                        inbound_email = excluded.inbound_email,
                        updated_at = excluded.updated_at
                    )r;   r<  rL  rO  rk  r7	  )r>  r  r  r  rR  s        r7   upsert_fiscal_document_settingsz+TenantStore.upsert_fiscal_document_settings3  s    Z
ZZ 	$'') $Z"" z:	 !!#$	$ 00;;$ $	$ 	$s"   A;&A/A;/A8	4A;;B)r  r  r  r  r  rT  r  r  r  r  r  r  r  r  r  r  r  r  c               T   dg}t               g}d|fd|fd|fd|fd|fd|fd|	fd	|
fd
|fd|fd|fd|fd|fd|fd|fd|fd|fd|ffD ]-  \  }}|	|j                  | d       |j                  |       / |j                  ||g       | j                  5  | j	                         5 }|j                  ddj                  |       d|       |j                          d d d        d d d        | j                  ||      }|t        d| d      |S # 1 sw Y   6xY w# 1 sw Y   :xY w)Nr	  r  r  r  r  r  rT  r  r  r  r  r  r  r  r  r  r  r  r  r?  zL
                    UPDATE tenant_fiscal_documents
                    SET rS  r	  r!	  r  )
r;   rs   r#  r<  rL  rO  r%  rk  r"	  r  )r>  r  r  r  r  r  r  r  rT  r  r  r  r  r  r  r  r  r  r  r  r  rL  r   columnrT   rR  r  s                              r7   update_fiscal_documentz"TenantStore.update_fiscal_document3  s   0 #3!3 (
| \*)$m,0m,m,\*"\*~.\*m,m, "34v0m,\*%
 	%MFE(  ""fXT?3e$-	%0 	y+./ZZ 
	$'') 	$Z"";/0 1
  !!#	$
	$ )))[A>/}LIJJ	$ 	$
	$ 
	$s$   D'6DDD	DD'c           	     6   | j                  ||      }|y|j                  xs dj                         rt        |j                        nd }| j                  5  | j                         5 }|j                  ddt               ||f       |j                  d||f       |j                  d||f       |j                          d d d        d d d        |"	 |j                         r|j                          yy# 1 sw Y   6xY w# 1 sw Y   :xY w# t        $ r Y yw xY w)NFri   a  
                    UPDATE tenant_fiscal_document_inbox_items
                    SET document_id = NULL,
                        error_detail = ?,
                        updated_at = ?
                    WHERE tenant_id = ? AND document_id = ?
                    z*Documento eliminato dall'archivio fiscale.r3	  zBDELETE FROM tenant_fiscal_documents WHERE tenant_id = ? AND id = ?T)r"	  r  rQ   r   r<  rL  rO  r;   rk  existsunlinkOSError)r>  r  r  r  r  rR  s         r7   delete_fiscal_documentz"TenantStore.delete_fiscal_document&4  s-   )))[A>5;5H5H5NB4U4U4WtF//0]aZZ 	$'') $Z"" B8:yZef	 ""f, ""X, !!#'$	$, #&&( '') 9$ $	$ 	$4  s7   D (AC4?D  D 4C=	9D  D		DDc                   | j                  |      }| j                  |d      }|D cg c]5  }|j                  xs dj                         rt	        |j                        7 }}| j
                  5  | j                         5 }|j                  d|f       |j                  d|f       |j                  d|f       |j                          d d d        d d d        |D ]#  }	 |j                         r|j                          % t        |      t        |      fS c c}w # 1 sw Y   TxY w# 1 sw Y   XxY w# t        $ r Y dw xY w)N  r  ri   z<DELETE FROM tenant_fiscal_document_items WHERE tenant_id = ?zBDELETE FROM tenant_fiscal_document_inbox_items WHERE tenant_id = ?z7DELETE FROM tenant_fiscal_documents WHERE tenant_id = ?)r.	   list_fiscal_document_inbox_itemsr  rQ   r   r<  rL  rO  rk  r>	  r?	  r@	  rq   )r>  r  r   inbox_itemsdocumentstorage_pathsrR  r  s           r7   clear_fiscal_documents_archivez*TenantStore.clear_fiscal_documents_archiveL4  sZ   ..y9	;;IT;R &
%%+224 &&'
 
 ZZ 	$'') $Z""RL ""XL ""ML !!#$	$  * 	L&&( '')	 9~s;///;
$ $	$ 	$(  s<   :D1D+A
DD+" D7D(	$D++D47	EEc                   | j                         5 }|j                  d|||f      j                         }d d d        y | j                  |      S # 1 sw Y   xY w)Nz
                SELECT *
                FROM tenant_fiscal_document_inbox_items
                WHERE tenant_id = ? AND message_id = ? AND attachment_id = ?
                LIMIT 1
                )rL  rO  r  r  )r>  r  r  r  rR  rl  s         r7   get_fiscal_document_inbox_itemz*TenantStore.get_fiscal_document_inbox_itemn4  so     ##% 		$$ J6 hj 		 ;88==		 		s   $AArE  c          	        t        t        t        d|      dz  t        d|            d      }| j                         5 }t        |j	                  d||f            }d d d        D cg c]  }| j                  |       }}i }|D ]  }	| j                  |	      }
|j                  |
      }|g| j                  |      }| j                  |	      }||k  rO||k(  r:|j                  xs d|j                  f|	j                  xs d|	j                  fk\  r|	||
<    t        |j                         d d      }|d t        d|       S # 1 sw Y   xY wc c}w )	Nrm   r  rC	  z
                    SELECT *
                    FROM tenant_fiscal_document_inbox_items
                    WHERE tenant_id = ?
                    ORDER BY created_at DESC, id DESC
                    LIMIT ?
                    ri   c                :    | j                   xs d| j                  fS r   )r  r  )r  s    r7   r"  z>TenantStore.list_fiscal_document_inbox_items.<locals>.<lambda>4  s     1 1 7RC r6   Tr#  )r   r  rL  r   rO  r  r  r   r  r  r  rt  r   )r>  r  r8  	raw_limitrR  r+  rl  r)	  deduped_by_keyr  	dedup_keyr  current_priorityrecord_priorityr*	  s                  r7   rD	  z,TenantStore.list_fiscal_document_inbox_items4  s   C5MC/Q?F	##% 	"" 	*	D	 NRRc4<<SARRRT 
	/F==fEI$((3G"#'#M#Mg#V "&"L"LV"T#o5#6G<N<N<TRTV]V`V`;aflfwfwf}{}  @F  @I  @I  fJ  <J(.N9%
	/ !!#C

 Q''A	 	 Ss   E(EEc                    |xs dj                         }|sy | j                         5 }|j                  d||f      j                         }d d d        y | j	                  |      S # 1 sw Y   xY w)Nri   z
                SELECT *
                FROM tenant_fiscal_document_inbox_items
                WHERE tenant_id = ? AND file_hash = ?
                ORDER BY created_at DESC
                LIMIT 1
                )rQ   rL  rO  r  r  )r>  r  r  normalized_hashrR  rl  s         r7   &get_fiscal_document_inbox_item_by_hashz2TenantStore.get_fiscal_document_inbox_item_by_hash4  s    $?113##% 
	$$ O,	 hj 
	 ;88==
	 
	s   #A''A0c               0   |xs dj                         }|xs dj                         j                         }|r|sy | j                         5 }|j                  d|||f      j	                         }d d d        y | j                  |      S # 1 sw Y   xY w)Nri   a  
                SELECT *
                FROM tenant_fiscal_document_inbox_items
                WHERE tenant_id = ?
                    AND message_id = ?
                    AND lower(attachment_name) = ?
                ORDER BY
                    CASE
                        WHEN sync_status = 'imported' AND document_id IS NOT NULL AND document_id <> '' THEN 0
                        WHEN sync_status = 'imported' THEN 1
                        WHEN file_hash IS NOT NULL AND file_hash <> '' THEN 2
                        WHEN sync_status = 'unsupported' THEN 3
                        WHEN sync_status = 'error' THEN 4
                        ELSE 5
                    END,
                    created_at DESC,
                    id DESC
                LIMIT 1
                )rQ   r  rL  rO  r  r  )r>  r  r  r	  normalized_message_idnormalized_attachment_namerR  rl  s           r7   9get_fiscal_document_inbox_item_by_message_attachment_namezETenantStore.get_fiscal_document_inbox_item_by_message_attachment_name4  s     ",!1r 8 8 :&5&;%B%B%D%M%M%O"$,F##% 	$$& 13MN)* hj+ 	0 ;88==5	 	s   $BB)r  r  r  c               R   t               }| j                  5  | j                         5 }|j                  d|||||||||	|
|||||f       |j	                          d d d        d d d        | j                  |||      }|t        d| d      |S # 1 sw Y   8xY w# 1 sw Y   <xY w)Na  
                    INSERT INTO tenant_fiscal_document_inbox_items (
                        id,
                        tenant_id,
                        message_id,
                        attachment_id,
                        file_hash,
                        subject,
                        sender,
                        received_at,
                        attachment_name,
                        mime_type,
                        sync_status,
                        document_id,
                        error_detail,
                        created_at,
                        updated_at
                    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                    r  r  Elemento inbox fiscale r  )r;   r<  rL  rO  rk  rJ	  r  )r>  r  r  r  r  r  r  r  r  r	  r  r
  r  r  r  rR  r  s                    r7   !create_fiscal_document_inbox_itemz-TenantStore.create_fiscal_document_inbox_item4  s    " J	ZZ (	$'') '$Z""(  !"%!#'!##$!!)%L !!#O'$(	$T 44Y:er4s>4WI\JKKY'$ '$(	$ (	$s"   B2BBB	BB&)	r  r  r  r  r	  r  r
  r  r  c          
     *   dg}t               g}d|fd|fd|fd|fd|fd|	fd|
fd	|fd
|ff	D ]-  \  }}|	|j                  | d       |j                  |       / |j                  |||g       | j                  5  | j	                         5 }|j                  ddj                  |       d|       |j                          d d d        d d d        | j                  |||      }|t        d| d| d      |S # 1 sw Y   ;xY w# 1 sw Y   ?xY w)Nr	  r  r  r  r  r	  r  r
  r  r  r?  zW
                    UPDATE tenant_fiscal_document_inbox_items
                    SET rS  zf
                    WHERE tenant_id = ? AND message_id = ? AND attachment_id = ?
                    rZ	  r[	  /r  )
r;   rs   r#  r<  rL  rO  r%  rk  rJ	  r  )r>  r  r  r  r  r  r  r  r	  r  r
  r  r  rL  r   r;	  rT   rR  r  s                      r7   !update_fiscal_document_inbox_itemz-TenantStore.update_fiscal_document_inbox_item$5  so     #3!3 (
| )$ vK(0)$K(K(\*

 	%MFE  ""fXT?3e$	% 	y*m<=ZZ 
	$'') 	$Z"";/0 1
  !!#	$
	$ 44Y:er4s>4ZL-P\]^^	$ 	$
	$ 
	$s$   <D	6C=D	=D	D		Dc                    | j                         5 }|j                  d|f      j                         }d d d        y | j                  |      S # 1 sw Y   xY w)N4SELECT * FROM assistant_threads WHERE id = ? LIMIT 1)rL  rO  r  r  )r>  r"  rR  rl  s       r7   get_assistant_threadz TenantStore.get_assistant_threadX5  sd    ##% 	$$F hj 	
 ;..s33	 	s   "AAhomec                    | j                         5 }|j                  d|j                  |j                  |f      j	                         }d d d        y | j                  |      S # 1 sw Y   xY w)Nz
                SELECT *
                FROM assistant_threads
                WHERE tenant_id = ? AND user_id = ? AND surface = ? AND status = 'active'
                ORDER BY updated_at DESC, created_at DESC
                LIMIT 1
                )rL  rO  r  r1  r  r  )r>  r  r  rR  rl  s        r7   get_active_assistant_threadz'TenantStore.get_active_assistant_threadb5  sw    ##% 
	$$ ""GOOW=	 hj 
	 ;..s33
	 
	s   8A%%A.P   c                   | j                         5 }t        |j                  d||f            }d d d        D cg c]  }| j                  |       c}S # 1 sw Y   (xY wc c}w )Na]  
                    SELECT *
                    FROM (
                        SELECT *
                        FROM assistant_messages
                        WHERE thread_id = ?
                        ORDER BY sort_index DESC
                        LIMIT ?
                    )
                    ORDER BY sort_index ASC
                    )rL  r   rO  r  r>  r"  r8  rR  r+  rl  s         r7   list_assistant_messagesz#TenantStore.list_assistant_messagesr5  sq    ##% 	""
 &D	" BFF#005FF#	 	" Gr1	  c                   dt        j                         j                   }t               }| j                  5  | j                         5 }|j                  d||j                  |j                  |d||f       |j                          d d d        d d d        | j                  |      }|t        d| d      |S # 1 sw Y   5xY w# 1 sw Y   9xY w)Nath_z
                    INSERT INTO assistant_threads (id, tenant_id, user_id, surface, status, state_json, created_at, updated_at)
                    VALUES (?, ?, ?, ?, 'active', ?, ?, ?)
                    z{}Thread assistente z non creato)r  r  r   r;   r<  rL  rO  r  r1  rk  rb	  r  )r>  r  r  r"  r  rR  r  s          r7   create_assistant_threadz#TenantStore.create_assistant_thread5  s    4::<++,-	J	ZZ 		$'') $Z""  1 17??GTS\^gh !!#$		$ **95>/	{+FGG$ $		$ 		$s#   C	>B;C;C	 CC)r  r"  c                   |re| j                  |      }|R|j                  dk(  rC|j                  |j                  k(  r*|j                  |j                  k(  r|j                  |k(  r|S | j                  ||      }||S | j                  ||      S )Nr  )rb	  r  r  r1  r  re	  rm	  )r>  r  r  r"  r  s        r7   ensure_assistant_threadz#TenantStore.ensure_assistant_thread5  s     //	:G#NNh.%%):)::OOw6OOw.227GDN++GW==r6   c                   |D cg c]  }t        |j                  d      xs d      j                         dv rt        |j                  d      xs d      j                         rYt        |j                  d      xs d      j                         t        |j                  d      xs d      j                         d }}|s&| j                  |      }|t	        d| d      |g fS t               }g }| j                  5  | j                         5 }|j                  d|f      j                         }	|	t	        d| d      |j                  d	|f      j                         d
   }
t        |
xs d
      }|D ]l  }|dz  }dt        j                         j                   }|j                  d|||d   |d   ||f       |j                  t        |||d   |d   ||             n |j                  d||f       |j!                          d d d        d d d        | j                  |      }|t	        d| d      ||fS c c}w # 1 sw Y   <xY w# 1 sw Y   @xY w)Nr   ri   >   r  r   r#  )r   r#  rl	  r  ra	  zOSELECT COALESCE(MAX(sort_index), 0) FROM assistant_messages WHERE thread_id = ?r   rm   ams_z
                        INSERT INTO assistant_messages (id, thread_id, role, content, created_at, sort_index)
                        VALUES (?, ?, ?, ?, ?, ?)
                        r  z
                    UPDATE assistant_threads
                    SET updated_at = ?, status = 'active'
                    WHERE id = ?
                    )rj   r   rQ   rb	  r  r;   r<  rL  rO  r  r   r  r  r   rs   r!  rk  )r>  r"  messagesr  	sanitizedthreadr  rD  rR  
thread_rowmax_sort_indexr$  r  s                r7   append_assistant_messagesz%TenantStore.append_assistant_messages5  s    $

 7;;v&,"-3359NNSVW^WbWbclWmWsqsStSzSzS|	 GKK/526<<>w{{95;<BBD
	 
 ..y9F~!3I;lKLL2:J	02ZZ ,	$'') +$Z'//JL (*  %"%7	{,#OPP!+!3!3eL" (*Q"  !!415
( G!OJ#'

(8(8'9!:J&& $YASU^`jk NN.)&/!($+I$6'0'1	* ""
 	* !!#W+$,	$\ **95>/	{,GHHwC
"+$ +$,	$ ,	$s+   B:IIC;III	IIc          
        | j                   5  | j                         5 }|r4|j                  dt               ||j                  |j
                  |f       n2|j                  dt               |j                  |j
                  |f       |j                          d d d        d d d        y # 1 sw Y   xY w# 1 sw Y   y xY w)Nz
                        UPDATE assistant_threads
                        SET status = 'archived', updated_at = ?
                        WHERE id = ? AND tenant_id = ? AND user_id = ? AND surface = ?
                        a  
                        UPDATE assistant_threads
                        SET status = 'archived', updated_at = ?
                        WHERE id IN (
                            SELECT id
                            FROM assistant_threads
                            WHERE tenant_id = ? AND user_id = ? AND surface = ? AND status = 'active'
                            ORDER BY updated_at DESC, created_at DESC
                            LIMIT 1
                        )
                        )r<  rL  rO  r;   r  r1  rk  )r>  r  r  r"  rR  s        r7   archive_assistant_threadz$TenantStore.archive_assistant_thread5  s     ZZ 	$'') $Z&&
 "Y0A0A7??T[\ &&
 "W%6%6Q !!#3$	$ 	$$ $	$ 	$s#   B4A9B(B4(B1	-B44B=c                    | j                  |      }||j                  si S 	 t        j                  |j                        }t        |t              r|S i S # t        j                  $ r i cY S w xY wr2   )rb	  r  r   r   r   r   rt   )r>  r"  rt	  r  s       r7   get_assistant_thread_statez&TenantStore.get_assistant_thread_state6  so    **95>!2!2I	jj!2!23G %Wd3w;; ## 	I	s   A A.-A.c                t   t        j                  |xs i dt              }t               }| j                  5  | j                         5 }|j                  d|||f       |j                          d d d        d d d        | j                  |      }|t        d| d      |S # 1 sw Y   5xY w# 1 sw Y   9xY w)NFr  r  z
                    UPDATE assistant_threads
                    SET state_json = ?, updated_at = ?
                    WHERE id = ?
                    rl	  r  )
r   r   rj   r;   r<  rL  rO  rk  rb	  r  )r>  r"  stateserialized_stater  rR  r  s          r7   update_assistant_thread_statez)TenantStore.update_assistant_thread_state#6  s    ::ekrsSJ	ZZ 
	$'') 	$Z""
 &y)< !!#	$
	$ **95>/	{,GHH	$ 	$
	$ 
	$s#   B.&B".B."B+	'B..B7c               n   dt        j                         j                   }	t               }
t	        j
                  |dt              }| j                  5  | j                         5 }|j                  d|	||j                  |j                  |||||||
f       |j                          d d d        d d d        | j                         5 }|j                  d|	f      j                         }d d d        t        d|	 d      | j                  |      S # 1 sw Y   mxY w# 1 sw Y   qxY w# 1 sw Y   CxY w)Narn_Fr}	  a%  
                    INSERT INTO assistant_runs (
                        id, thread_id, tenant_id, user_id, surface, route, model,
                        user_message, assistant_reply, trace_json, created_at
                    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                    z1SELECT * FROM assistant_runs WHERE id = ? LIMIT 1zRun assistente r  )r  r  r   r;   r   r   rj   r<  rL  rO  r  r1  rk  r  r  r  )r>  r"  r  r  r'  r(  r)  r*  tracer?  r  serialized_tracerR  r  s                 r7   create_assistant_runz TenantStore.create_assistant_run76  s<    

(()*Z
::e%MZZ 	$'') $Z"" !))$'("* !!#-$	$0 ##% 	s''([^d]fgpprF	s>_VHLABB++F337$ $	$ 	$0	s 	ss1   D%AD'D"D+D	DD(+D4c                   | j                         5 }t        |j                  d||f            }d d d        D cg c]  }| j                  |       c}S # 1 sw Y   (xY wc c}w )Nz
                    SELECT *
                    FROM assistant_runs
                    WHERE thread_id = ?
                    ORDER BY created_at DESC
                    LIMIT ?
                    )rL  r   rO  r  rh	  s         r7   list_assistant_runszTenantStore.list_assistant_runsd6  sp    ##% 	"" &	D	 >BBc,,S1BB	 	 Cr1	  )r  None)rR  sqlite3.Connectionr  r	  )
rR  r	  rw  rj   rx  rj   ry  rj   r  r	  )r  rj   )rR  r	  r  rj   r  rj   r  rj   r  rj   r  rj   r	  r  r
  r  r  rj   r  rj   r  rj   r  r  r  rj   r  rj   r  r	  )r  rj   r  rj   r  rj   r  rj   r  rj   r  rj   r	  r  r
  r  r  rj   r  rj   r  rj   r  r  r  rj   r  rj   r  r	  )rl  r=  r  r  )rl  r=  r  r  )rl  r=  r  r  )rl  r=  r  r  )r  r  r  tuple[object, ...])r  r  r  r	  )r  r  r  r   )rl  r=  r  r  )rl  r=  r  r!  )rl  r=  r  r&  )rl  r=  r  r   )rl  r=  r  r  )rl  r=  r  r  )rl  r=  r  r  )rR  r	  r  rj   r1  rj   r  r  )rR  r	  r  rj   r  sqlite3.Row | None)rR  r	  r  rj   r  list[sqlite3.Row])rR  r	  r  rj   r  zKtuple[sqlite3.Row, list[sqlite3.Row], list[sqlite3.Row], list[sqlite3.Row]])r  zPRegisterTenantPayload | AdminCreateTenantPayload | AdminUpdateTenantAdminPayloadr  dict[str, str | None])r  z;TenantStaffUserCreatePayload | TenantStaffUserUpdatePayloadr  r	  )r  r  r  r   )r   r  r  datetime | None)r   r  r  r  )r   r  r  zdate | None)r  r  r  r	  r  dict[str, object])r  r  r1  r  r2  r  r3  r  r  r   r8  r   r  list[TimeclockEntryRecord])r  r	  r3   r	  r  list[dict[str, object]])r  r  r@  r   r  r	  )rl  r=  r  r	  )rR  r	  rH  rj   r  r=  )r  r  r   rj   r  r   )r  r  rM  rj   r  r   )r  r  ri  rj   r  r   )rR  r	  rw  rj   r  r   )rR  r	  rw  rj   r  set[str]r2   )rl  r=  r  rJ  r  r	  )rl  r=  r  r	  r  r	  )rG  r  r  r   )rR  r	  r  r	  r  dict[str, float])r  r  r  r  rG  r  r  r   )
r  r  r  r  r  r  r  r  r  r   )rG  r  rB  r   rK  rJ  r  r   )rR  r	  r  rj   r  rj   r  r   )rR  r	  rf  rj   r  rj   r  r   rT   r  r  rj   )rT   object | Noner  r	  )r  rj   r  rj   r  rj   )rR  r	  r  rj   r  rj   r  r	  )rR  r	  r  r  r  r	  )rR  r	  rf  rj   r  r=  )rR  r	  rD  rj   r  r=  )rR  r	  rH  rj   r  r=  )rR  r	  rf  rj   r  r	  )rR  r	  rf  rj   r  <tuple[list[sqlite3.Row], dict[str, list[dict[str, object]]]])rR  r	  rf  rj   r  r	  )rR  r	  rH  rj   r  r	  )rR  r	  rH  rj   r  r	  )rR  r	  rD  rj   r  (dict[tuple[str, str], dict[str, object]])rR  r	  rH  rj   r  r	  )
rR  r	  r  zlist[tuple[str, str]]ru  r  r?  dict[str, object] | Noner  r	  )
rR  r	  rf  rj   r  r=  r  r=  r  r	  )
rR  r	  rf  rj   r  rj   r  rj   r  r   )
rR  r	  rf  rj   r`  rj   r  rj   r  r   )rR  r	  r`  rj   r  r	  )rR  r	  r2  rj   r3  rj   r  r	  )rR  r	  r`  rj   r  rj   r  r   )rR  r	  rf  rj   r`  rj   r  r	  )r  r  r  r	  )r  r  r`  rj   r  r	  )rR  r	  rf  rj   r  r	  r  r   )r  r	  r  r	  r  r	  )r  r  r  r:  r  r	  )r  r  rf  rj   r  r:  r  r	  )r  r  rf  rj   r  r	  )r  r  rf  rj   rH  rj   r  r	  )rR  r	  r  r  rf  rj   r`  rj   r  rj   r  ztuple[sqlite3.Row, bool])rR  r	  rf  rj   r  rj   r  rj   r  rj   r  r  )r  r  rf  rj   r  r_  r  r	  )rw  z=HomemadeOperationalCalendarPayload | dict[str, object] | Noner  r	  )rT   r	  r  r	  )r?  r	  ry  r  r  r	  )r  r   r  r	  r  r   )r2  r  r3  r  r?  r	  ry  r  r  r   r  ztuple[int, str])rR  r	  r  r	  )rR  r	  r  r  r  r=  r  rj   r~  rj   r  rj   r  r   r  r   r  rj   r  rj   r  r	  )r  r  r  r  r  r	  )r  r  rf  rj   ry  r  r  r	  )
r  r  r  r  ry  r  r8  r   r  r	  )r  r  rf  rj   r  r  r  r	  )
r  r  rf  rj   r  r  r  r  r  r	  )r  r  r  r  r8  r   r  r	  )r  r  rA  r   r  r	  )r  r  r  rQ  r  r	  )r  r  rA  r   r  rN  r  r	  )r  r  r  rb  r  r	  )r  r  rh  r  ri  r  r  r	  )r  r  rf  rj   r  r?  r  r	  )r  r  r  rj  r  r	  )r  r  rf  r  r8  r   r  r	  )
r  r  ru  r  r  r  r8  r   r  r	  )
r  r  ru  r   r  r  r8  r   r  r	  )r  r  r  rj   r  r	  )
r  r  rS  rj   rT  rj   r8  r   r  r	  )r  r  ru  r   r  r   r  r  r8  r   r  r	  )r  r  r  re  r  r	  )rR  r	  r  rj   r  r=  )rR  r	  rt  rj   rO  r  r  r=  )rR  r	  rV  r}  rO  r  r  z?tuple[list[dict[str, object]], float, bool, Literal['ml', 'g']])r  rv  rW  r	  r  zdict[str, object | None])rR  r	  rm  r*  r  "dict[str, list[dict[str, object]]])rl  r=  r   r	  r  r	  )r  r  r  rj   r  r	  )r  r  r  rv  r  r	  )r  r  r  rj   r  rv  r  r	  )r  r  r  rj   r  rj   r  r  r  r  r  r  r  r	  )rR  r	  r  r   )r  r  r  r	  )rR  r	  r  z0RegisterTenantPayload | AdminCreateTenantPayloadr  ztuple[str, str])r  r   )r  r	  )r  r  r  r  )r  r  r  r  )r  rj   r  r	  )r  rj   r  SessionIdentity | None)r  rj   r  r	  )r  rj   r  r	  )r  r  r  r  r  r  r	  r  r
  r  r  r	  )r  rj   r  r	  )r  r  r  r  )r  r  r  r  r  r	  )r  r	  )r  r"  r  r	  )r  rj   r  r$  r  r	  )r  r  r  r	  )r  r  r  r&  r  r	  )r  r  r1  rj   r  r.  r  r	  )r  r  r1  rj   r  r	  )ri  rj   r  rj   )rR  r	  rH  r*  r  r	  )rR  r	  ri  rj   r?  rj   r  r=  )rY  r	  r  r   r  r	  )
rR  r	  ri  rj   r  r  rc  r  r  r	  )r  r  ri  rj   r  r	  )r  r  ri  rj   r  r  r  r	  )r  r  ri  rj   r  r  r  r	  )
r  r  ri  rj   r?  rj   r  r   r  r	  )r  r  ri  rj   r?  rj   r  r	  )r  r  r  r  r8  r   r  r	  )r  r  ru  r   r  r	  )r  r  r  r  r  r	  )r  r  rH  rj   r  r  r  r	  )r  r  r  r  r  r	  )r  r  r  rj   r  r	  )
r  r  r  rj   r  rj   r  r   r  r	  )r  r  r  r0  r  r	  )r  r  r  rj   r  r  )r  rj   rM  rj   r  z$tuple[dict[str, object], str | None])r  rj   rM  rj   r	  r	  r  rj   )r  rj   r  r   )r	  rj   r  rj   r  rj   r  rj   r  rj   r  rj   r  r   r  rj   r  rj   r  rj   r  rj   r  r  r  r  )r  rj   r	  rj   r  zMenuAssetRecord | None)r  rj   r  zlist[MenuAssetRecord])r  rj   r	  rj   r  r  r  r  r  r  r  r  r  r  r  r  )r  rj   r	  rj   r  r  ).r  rj   r  rj   r  rj   r  rj   r  rj   r  rj   r  r   r  r  r  rj   r  rj   r  r  r  r  rT  r  r  rJ  r  rj   r  rj   r  rj   r  rj   r  rj   r  rj   r  rj   r  r  r  r  )r  rj   r  rj   r  FiscalDocumentRecord | None)r  rj   r  rj   r  r	  )r  rj   r  zlist[FiscalDocumentRecord])r  rj   r  rj   r  "list[FiscalDocumentLineItemRecord])r  rj   r  rj   r  r	  r  r	  )r  rj   r  r   )r  rj   r  r  r  r   )*r  rj   r  rj   r  r  r  r  r  r  r  r  r  r  rT  r  r  rJ  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  )r  rj   r  rj   r  r   )r  rj   r  ztuple[int, int])r  rj   r  rj   r  rj   r  $FiscalDocumentInboxItemRecord | None)r  rj   r8  r   r  z#list[FiscalDocumentInboxItemRecord])r  rj   r  rj   r  r	  )r  rj   r  rj   r	  rj   r  r	  )r  rj   r  rj   r  rj   r  rj   r  r  r  rj   r  r  r  r  r	  rj   r  rj   r
  rj   r  r  r  r  r  r  )r  rj   r  rj   r  rj   r  r  r  r  r  r  r  r  r	  r  r  r  r
  r  r  r  r  r  r  r  )r"  rj   r  AssistantThreadRecord | None)rc	  )r  r  r  rj   r  r	  )r"  rj   r8  r   r  zlist[AssistantStoredMessage])r  r  r  rj   r  r  )r  r  r  rj   r"  r  r  r  )r"  rj   rr	  zlist[dict[str, str]]r  z:tuple[AssistantThreadRecord, list[AssistantStoredMessage]])r  r  r  rj   r"  r  r  r	  )r"  rj   r  r	  )r"  rj   r~	  r	  r  r  )r"  rj   r  r  r  rj   r'  rj   r(  rj   r)  rj   r*  rj   r	  r	  r  r&  )r"  rj   r8  r   r  zlist[AssistantRunRecord](  r  r  r  r@  r=  ro  rN  rg  rj  r   rL  r  r  rP  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r	  r  r  r  r0  r>  rA  rD  rI  r  rN  rQ  rS  rU  rY  r\  r^  r`  rb  rd  rm  ro  r~  r  rx  r|  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r*  r,  r=  r@  rB  rE  rI  rK  rN  rQ  rc  rf  rj  rl  rq  rs  r4  r  r  r  r  r  r  r  r2  r  r  r  r  r  r  r  r(  r0  r6  r=  rD  rI  rS  rU  rZ  rf  rm  rq  r{  r~  r  r}  r  r3  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r   r  r,  r0  rN  rY  r]  rg  r  rQ  r  r  r  r  r  r  r  r  r0  r3  rL  r  rQ  r\  rg  rq  rt  ry  r{  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r
  r  r  r!  r&  r(  r*  r-  r=  rC  rI  rN  rP  ra  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r	  r	  r
	  r	  r	  r	  r	  r	  r	  r	  r#	  r"	  r'	  r.	  r0	  r5	  r7	  r9	  r<	  rA	  rH	  rJ	  rD	  rT	  rX	  r\	  r_	  rb	  re	  ri	  rm	  ro	  rw	  ry	  r{	  r	  r	  r	  r5   r6   r7   r-  r-    s-   P9dWr  =
~   
 
5
&5
 	5

 5
 5
 5
 5
 !5
 $5
 5
 5
 5
 '5
 5
  !5
" 
#5
nX't:  :  	: 
 :  :  :  :  !:  $:  :  :  :  ':  :   !: " 
#: x
0
$
@
6	
!





&
$
*'"
4.
a
 

	
L	
 
	
8
m
	;WM lp 
@ #!%#$):) :) 	:)
 :) :) ":) :) 
$:)@  $	-+- 	-
 
!-^.
 16mp]-k8ls-





 
(
Z )-,
,
 &,
 
	,
\

 &
 
	
"

h2
&2
 %2
 
	2
hV  V !	V
 V 
V"R R "	R
 R #R 
R*  	
 % 
*V&V V
 V 
Vp& 
  
 `5R&
46
&& 	
  
* !%	~&~ 	~
 
~@(0&  
	 &  
	 +'&+' +' 
F	+'Z&  
	6+'&+' +' 
F	+'Z&  
	&&  
2	<&  
2	F  -1 &  ' 
   +  
 @s
&s
 s
 '	s

 %s
 
s
jY&Y Y 	Y Y 
Yv
&
 
 	
 
 

8D
&D
 D
 
	D
LY&Y 	Y
 Y 
2YvL&L L
 L 
L\
&
 

 
 

< !%	J&J 	J
 
J.@&  	
 
"q
&q
 	q

 
q
fA@l l 	l
 
l0& 
 # 
B+ /+ ,	+
 
!+Z=
~ K  K 1 K 
	 KD%S %S %S 1	%S
 
%SN
 
 
 
	
.
 
 
 
	
6
 
 
 	

 

8` ` ` 	`
 
`DJ J J 
	J8;
 ;
 ;
 	;

 
;
zP4&P4 !P4 	P4 P4 P4 
"P4dX &X  X  	X 
 X  X  
X t
 
 
 /	

 

85W5 
!5n/#/  / 
!	/$DA A 	A
 $A  A A 
A6
(;
&;
 !	;

 #;
 ;
 ;
 ;
 ;
 ;
 ;
 ;
 
;
z2
hN
`
B1; 1; .1; 
	1;f P  P 1 P 
	 PD)X )X )X 1	)X
 
)XV
 
 
 
	
.
 
 
 
	
6
 
 
 	

 

2B B B 	B
 
BP #'	.O .O .O  	.O
 
.O`$
 $
 $
 	$

 
$
Le
 e
 e
 /	e

 
e
N!
 !
 !
 
	!
P #'I; I; 	I;
  I; I; 
I;VyO yO yO +	yO
 
yO@ (,%){
 {
 {

 %{
 #{
 
{
D }; }; 	};
 }; 
};~E; E; 	E;
 
E;N\
|n
 n
 	n

 2n
 
n
`G ^
 ^
 0^
 
	^
H )-&*T
 T
 &	T

 $T
 
T
l5
nP P P ,	P
 
Pd
A
 A
 /A
 
	A
N $()
 )
 !	)

 )
 
)
^   L
 L
 	L

 L
 L
 
L
\P
n !L
 L
 	L

 L
 L
 
L
\P
 P
 	P

 
P
p =
 =
 	=

 =
 =
 
=
F  F
 F
 	F

 F
 F
 F
 
F
X !B
 B
 	B

 B
 
B
HS
 S
 2S
 
	S
j
 )-& 
 & 
6 )-6m&6m C6m
 &6m 
I6mp;
+;
 !8;
 
"	;
z;&; ; 
,	;z		 -	 
		
2\\ \ ,\ 
	\B\ \ \ ,	\
 
\B> > > 
	>0 %)$('+)\ )\ )\
 )\ ")\ ")\ %)\ 
)\V:

$e"&e" Be" 
	e"N.4^<6$(8T7
r0
l "&"#'&*C% C% 	C%
 C% !C% $C% 
C%J5+Z6&p/+b%
N4Dl42l
:;^;@(
TD
LG
 G
 G
 .	G

 
G
R#+JJ	
&P
&  
,	0&  
,	:&  	
 
4101 1 
	1r &*s
&s
 s
 '	s
 #s
 
s
j+
Z9> 9> 9> (	9>
 
9>vR R R '	R
 
R`N `N `N '	`N
 
`ND	_J _J _J 	_J _J 
_JB
 
 
 	

 

8 %)6
 6
 "	6

 6
 
6
p
$;
z2
 2
 2
 +	2

 
2
h9Lv0"*Ld $ $ $ 	$ $ 
$8
@:_x+_Z%
Nj *2(
" !#'< < 	<
 < < < < < < < < < !< 
<|."@* $(%)$(!#'33 3
 !3 #3 "3 3 !3 
3j&2 !% '+$($(%) "((#'1Z Z 	Z
 Z Z Z Z Z Z Z Z $Z "Z "Z  #!Z" #Z$ %Z& 'Z( )Z* +Z, -Z. /Z0 !1Z2 
3Zx3 3"<O?G?G ?G '	?G
 
,?GB<"<. $( $$(&*$($(%)##'%)#'$($((,!&*$(#'-CC C
 !C C "C $C "C "C #C C !C #C !C  "!C" "#C$ &%C& 'C( $)C* "+C, !-C. 
/CJ$L 0D>> 	>
 > 
.>, PR "( "(H>(%>%> 	%>
 %> 
.%>\ !% #'#'? ? 	?
 ? ? ? ? ?  ? ? ? ?  ? !?  
'!?N !%"!"&&* $"&"&#'22 	2
 2 2 2 2  2 $2 2  2  2 !2 
'2h44 4  GI G G( ,  $> > 	>
 > 
>,FF 'F 
D	FX  $!$ !$ 	!$
 !$ 
!$F<(+4 +4 !	+4
 +4 +4 +4 +4 +4 !+4 
+4Z CE C Cr6   r-  zTenantStore | None_tenant_storec                 .    t         
t               a t         S r2   )r	  r-  r5   r6   r7   get_tenant_storer	  x6  s    #r6   )r  r   )r  rj   )r  r   )rT   rj   r  rj   )rT   r  r  rx  r	  )rT   r  r  r  )rT   r  r  rs  )r   r	  r  rw  )r   r	  r  r   )r   r	  r   r	  r   r	  r  r   )
r   r	  r   r	  r   r	  r   r	  r  r   )rT   r  r  zLiteral['sala', 'bar'])r   rj   r  rj   )r   rj   r   rj   r  r   )r   "list[str] | tuple[str, ...] | Noner  r  )r   rj   r   r  r  r  )r   rj   r   r	  r  r  )r   r	  r  r  )r   rj   r   r  r   r  r  r  )r   rj   r   r	  r  r  )r  r-  )
__future__r   
contextlibr   dataclassesr   r   r   r   r	   r
   r   r   r   pathlibr   rN   rR   r  r:  typingr   rJ   r  zoneinfor   r   pydanticr   r   r   app.core.configr   app.core.mock_stater   app.models.tenantr   app.models.tenant_moduler   app.models.userr   app.models.venuer   r  r  r  r  r  r  r  r   r   r   ri  HOMEMADE_USAGE_SCOPESre   r8   r;   r?   rA   rC   rW   r[   rc   rf   rk   rx   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r"  r$  r&  r.  r0  r:  r?  rN  rQ  r_  rb  re  rj  rp  rv  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r   r  r  r  r  r!  r&  r-  r	  r  r	  r5   r6   r7   <module>r	     s   " % ! > >   	  	       4 1 1 ( . $ 1   ". , * ) ) . .  $  
  5  &!'2$4!O@	),8a	a(a &a #	a
 
a" ,0(,%):(: ): &	:
 #: :&E>(&G(;
; ; &; 	;,G(I ((9 (
( (,I ,
	4 	=I =>9 >>9 >3Y 34i 4) )>y >
>I >&I &%Y %Ay AAI AGi G	r 	r;i ;-	 -DY De eJ9 JY 
XY X$I $ D) DB	 B
4) 4
5i 5:I : $  & $  " $  < $  . $   $  $ $    $  ( $   $   $  [MC [MC|Z %)! (r6   