
    Yaj!                        d dl mZ d dlZd dlmc mZ d dlm	Z	 d dl
mZmZmZ d dlmZ d dlmZmZ d dlmZmZ d dlmZ d d	lmZ d
 Zd ZddZddZddZddZddZ dS )    )annotationsN)confirm_create_budget_account)confirm_create_categorylist_category_tree_groupedpreview_category_merges)get_budget_status_by_category)confirm_budget_transactionlist_budget_transactions)get_category_analysisget_monthly_comparison)connect_memory)apply_migrationsc                 @    t                      } t          |            | S )N)r   r   )conns    Z/home/agent/.hermes/worktrees/FinanceManager-test-baseline/tests/unit/test_budget_ux_v2.pydbr      s     DTK    c           	         t          | dddd          d         }t          | dddd	d
d          d         }t          | dddddd          d         }t          | ddddd
d          d         }||||fS )NHaushaltcheckingCHF)nameaccount_typecurrency	entity_idEssen & Haushaltexpense#22c55ebasket
   )r   category_typecoloricon
sort_orderzGesundheit / Medizinz#ef4444heart   Lohn Marcelincomez#38bdf8wallet)r   r   )r   accountfoodhealthsalarys        r   	seed_baser.      s   +D:Waot2u2uvv  xC  DG"42DW`kt  G  WY  *Z  *Z  [  [  \g  hD$T4J]fqz  EL  \^  ,_  ,_  `  `  al  mF$TMT\gp  {C  SU  ,V  ,V  W  W  Xc  dFD&&((r   returnNonec                    t                      } t          |           \  }}}}|                     d          }t          | |dddddd|dgd		          d
         }t          | |ddddd|d           t          | |ddddd|d           |                     d           t	          | ddddd          }d |D             }|g}	||	k    }
|
slt          j        d|
fd||	f          t          j        |          t          j        |	          dz  }dd|iz  }t          t          j	        |                    d x}x}
}	|d         d         }d}	||	k    }
|
slt          j        d|
fd||	f          t          j        |          t          j        |	          dz  }dd|iz  }t          t          j	        |                    d x}x}
}	|d         d         }d }	||	k    }
|
slt          j        d|
fd||	f          t          j        |          t          j        |	          dz  }dd|iz  }t          t          j	        |                    d x}x}
}	|d         d!         }d"}	||	k    }
|
slt          j        d|
fd||	f          t          j        |          t          j        |	          dz  }dd|iz  }t          t          j	        |                    d x}x}
}	|d         d#         }dg}	||	k    }
|
slt          j        d|
fd||	f          t          j        |          t          j        |	          dz  }dd|iz  }t          t          j	        |                    d x}x}
}	|d         d$         }d}	||	k    }
|
slt          j        d|
fd||	f          t          j        |          t          j        |	          dz  }dd|iz  }t          t          j	        |                    d x}x}
}	d S )%NzINSERT INTO budget_tags(tag_id, name, color, is_active, created_at, updated_at) VALUES ('tag_food', 'Haushalt', '#22c55e', 1, '2026-01-01', '2026-01-01')r   z
2026-05-03zRohbeschreibung 123Migrosz42.00r   r   )	
account_idtransaction_typetransaction_datedescriptionpayeeamount_originalcurrency_originalcategory_id	tag_namesr   r(   z
2026-05-04Lohn100.00r3   r4   r5   r6   r8   r9   r:   z
2025-05-03Alt999.00zEUPDATE budget_transactions SET status='draft' WHERE description='Alt'2026	confirmed)yearr4   statustagmerchantc                    g | ]
}|d          S )budget_transaction_id ).0rs     r   
<listcomp>zjtest_effective_expenses_returns_confirmed_expenses_only_with_display_metadata_and_tags.<locals>.<listcomp>"   s    5551A%&555r   ==z%(py1)s == %(py4)spy1py4assert %(py6)spy6r   merchant_display_namecategory_iconr   category_colorr   r;   transaction_date_group)
r   r.   executer	   r
   
@pytest_ar_call_reprcompare	_safereprAssertionError_format_explanation)r   r*   r+   r,   r-   rE   r   rows@py_assert0@py_assert3@py_assert2@py_format5@py_format7s                r   Vtest_effective_expenses_returns_confirmed_expenses_only_with_display_metadata_and_tagsre      s   44D4.$
,,  s  t  tC(g[d  {G  Xm  x@  U\  sx  IM  ]g  \h  0i  0i  j  j  kv  wGtGQYo{  MS  hp  GL  ]c  &d  &d  e  e  etGQZp|  NS  hp  GL  ]c  &d  &d  e  e  eLLXYYY#Dv	Zeku  AI  J  J  JD55555B'B5BBBBBBBBBB5BBBBB5BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB7*+7x7+x7777777777+x77777+7777x7777777777777777777777777777?#/x/#x//////////#x/////#////x///////////////////////////7#$1	1$	1111111111$	11111$1111	1111111111111111111111111117;/J</<//////////</////////<///////////////////////////7+,<<,<<<<<<<<<<,<<<<<,<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<r   c                 	   t                      } t          |           \  }}}t          | |dddddd           t          | |dddd	d|d           t          | |d
dddd|d           |                     d           t	          | dd          }|d         }d}||k    }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }	t          t          j	        |	                    d x}x}}|d         }d}||k    }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }	t          t          j	        |	                    d x}x}}t          fd|d         D                       }
|
d         }d}||k    }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }	t          t          j	        |	                    d x}x}}|
d         }d}||k    }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }	t          t          j	        |	                    d x}x}}|
d         }d}||k    }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }	t          t          j	        |	                    d x}x}}d}|
d          }||v }|slt          j        d!|fd"||f          t          j        |          t          j        |          dz  }dd|iz  }	t          t          j	        |	                    d x}x}}d#}t          |          }||v}|st          j        d$|fd%||f          t          j        |          d&t          j                    v st          j        t                    rt          j        t                    nd&d't          j                    v st          j        |          rt          j        |          nd't          j        |          d(z  }	d)d*|	iz  }t          t          j	        |                    d x}x}}d S )+Nr   
2026-01-10r2   z80.00r   r>   z
2026-01-11Apothekez20.00r(   z
2026-01-12r<   z1000.00a&  INSERT INTO budget_transaction_candidates(transaction_candidate_id, source_type, source_file_label, transaction_date, description, amount_original, currency_original, status, created_at, updated_at) VALUES ('cand1','csv','x','2026-01-12','candidate only','999.00','CHF','pending','2026','2026')rA   )rC   tx_type	total_chfr=   rM   rO   rP   rS   rT   modec              3  4   K   | ]}|d          k    |V  dS r:   NrI   rJ   rK   r+   s     r   	<genexpr>zitest_category_analysis_uses_confirmed_transactions_only_and_calculates_share_and_links.<locals>.<genexpr>3   s2      PP!q7G47O7OA7O7O7O7OPPr   
categories
amount_chfshare_percenttransaction_count   z /planning/budget/expenses/actualtransactions_url)in)z%(py1)s in %(py4)s999not inz4%(py1)s not in %(py6)s
{%(py6)s = %(py3)s(%(py4)s)
}strresultrQ   py3rR   rT   assert %(py8)spy8)r   r.   r	   rY   r   rZ   r[   r\   r]   r^   nextr{   @py_builtinslocals_should_repr_global_name)r   r*   r,   r-   r|   r`   ra   rb   rc   rd   food_row@py_assert5@py_format9r+   s                @r   Vtest_category_analysis_uses_confirmed_transactions_only_and_calculates_share_and_linksr   *   sf   44D4.$tGQZp|  NV  kr  IN  _c  &d  &d  e  e  etGQZp|  NX  mt  KP  ag  &h  &h  i  i  itGQYo{  MS  hq  HM  ^d  &e  &e  f  f  fLL  z  {  {  {"4fiHHHF+*(*(**********(*********(***************************&>&Y&>Y&&&&&&&&&&>Y&&&&&>&&&&Y&&&&&&&&&&&&&&&&&&&&&&&&&&&PPPPvl3PPPPPHL!,W,!W,,,,,,,,,,!W,,,,,!,,,,W,,,,,,,,,,,,,,,,,,,,,,,,,,,O$//$//////////$/////$///////////////////////////////'(-A-(A----------(A-----(----A----------------------------M:L1MM-1MMMMMMMMMMM-1MMMMMM-MMMM1MMMMMMMMMMMMMMMMMMMMMMMMMMMM#F#5##########5#####5#############################F#####F###################################r   c                 
   t                      } t          |           \  }}}}t          | |ddddd|d           t          | |ddd	d	d
d|d           t          | |ddddd|d           t          | d          }|d         }d}||k    }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }	dd|	iz  }
t          t	          j        |
                    d x}x}}|d         }t          |          }d}||k    }|st	          j        d|fd||f          dt          j                    v st	          j        t                    rt	          j        t                    ndt	          j        |          t	          j        |          t	          j        |          dz  }dd|iz  }t          t	          j        |                    d x}x}x}}t          d |d         D                       }|d         }d}||k    }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }	dd|	iz  }
t          t	          j        |
                    d x}x}}|d          }d
}||k    }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }	dd|	iz  }
t          t	          j        |
                    d x}x}}|d!         }d"}||k    }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }	dd|	iz  }
t          t	          j        |
                    d x}x}}|d#         d$         d%         }d&}||k    }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }	dd|	iz  }
t          t	          j        |
                    d x}x}}|d'         d$         d(         }d	}||k    }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }	dd|	iz  }
t          t	          j        |
                    d x}x}}|d)         }|j        }d*} ||          }|ssd+t	          j        |          t	          j        |          t	          j        |          t	          j        |          d,z  }t          t	          j        |                    d x}x}x}}d S )-Nr(   z
2026-02-25r<   z300.00r   r>   r   z
2026-02-10r2   z120.00)r3   r4   r5   r6   r7   r8   r9   r:   z
2025-02-10r?   r@   rA   rC   rC   rM   rO   rP   rS   rT   months   )z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py7)slen)py0py2rR   py7zassert %(py9)spy9c              3  2   K   | ]}|d          dk    |V  dS )monthz2026-02NrI   )rJ   ms     r   ro   zatest_monthly_comparison_returns_2026_income_expense_net_and_drilldown_metadata.<locals>.<genexpr>C   s0      FFQajI.E.Eq.E.E.E.EFFr   
income_chfexpense_chfnet_cashflow_chfz180.00top_categoriesr   categoryr   top_merchantsrF   ru   zmonth=2026-02zJassert %(py7)s
{%(py7)s = %(py3)s
{%(py3)s = %(py1)s.endswith
}(%(py5)s)
})rQ   r~   py5r   )r   r.   r	   r   rZ   r[   r\   r]   r^   r   r   r   r   r   endswith)r   r*   r+   _healthr-   r|   r`   ra   rb   rc   rd   @py_assert1@py_assert6r   @py_format8@py_format10feb@py_assert4s                     r   Ntest_monthly_comparison_returns_2026_income_expense_net_and_drilldown_metadatar   ;   s   44D)D///$tGQYo{  MS  hp  GL  ]c  &d  &d  e  e  etGQZp|  NV  ai  ~F  ]b  sw  &x  &x  y  y  ytGQZp|  NS  hp  GL  ]a  &b  &b  c  c  c#Dv666F&>#V#>V##########>V#####>####V###########################h&3  &B& B&&&&&&&&&& B&&&&&&&&&&&&3&&&&&3&&&&&&&&&& &&&&B&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
FF&*FFF
F
FC|((((((((((((((((((((((((((((((((((((((((((((((((}))))))))))))))))))))))))))))))))))))))))))))))))!".h."h.........."h....."....h........................... #J/E3EE/3EEEEEEEEEEE/3EEEEEE/EEEE3EEEEEEEEEEEEEEEEEEEEEEEEEEEE":.:(:.(::::::::::.(:::::.::::(:::::::::::::::::::::::::::!"<"+<O<++O<<<<<<<<"<<<<+<<<<O<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<r   c                 R   t                      } t          |           \  }}}|                     df           t          | |dddddd           |                     df           t	          fd	t          | d
          D                       }|d         }d}||k    }|slt          j        d|fd||f          t          j        |          t          j        |          dz  }dd|iz  }	t          t          j
        |	                    d x}x}}d}t          |          }
||
v}|st          j        d|fd||
f          t          j        |          dt          j                    v st          j        t                    rt          j        t                    nddt          j                    v st          j        |          rt          j        |          ndt          j        |
          dz  }	dd|	iz  }t          t          j
        |                    d x}x}}
d S )Na!  INSERT INTO budget_plan_items(plan_item_id, category_id, name, plan_month, monthly_amount_chf, annual_amount_chf, cadence, is_fixed_cost, source_type, is_active, created_at, updated_at) VALUES ('p_food', ?, 'Food plan', '2026-01', '100.00', NULL, 'monthly', 0, 'manual', 1, '2026', '2026')r   rg   	Confirmedz50.00r   r>   a>  INSERT INTO budget_transaction_candidates(transaction_candidate_id, source_type, source_file_label, transaction_date, description, amount_original, currency_original, proposed_category_id, status, created_at, updated_at) VALUES ('cand1','csv','x','2026-01-12','candidate only','999.00','CHF',?,'pending','2026','2026')c              3  4   K   | ]}|d          k    |V  dS rm   rI   rn   s     r   ro   zYtest_budget_vs_actual_still_uses_confirmed_transactions_not_candidates.<locals>.<genexpr>Q   s4      ggQaP]N^bfNfNfqNfNfNfNfggr   rA   r   actual_year_to_daterM   rO   rP   rS   rT   rw   rx   rz   r{   rowr}   r   r   )r   r.   rY   r	   r   r   rZ   r[   r\   r]   r^   r{   r   r   r   )r   r*   r   _salaryr   r`   ra   rb   rc   rd   r   r   r+   s               @r   Ftest_budget_vs_actual_still_uses_confirmed_transactions_not_candidatesr   L   s   44D9T??0$LL  u  x|  w~      tGQZp|  NY  nu  LQ  bf  &g  &g  h  h  hLL  R  UY  T[  \  \  \
gggg76JJJggg
g
gC$%00%0000000000%00000%0000000000000000000000000000000 C 5          5     5                             C     C                                   r   c                    t                      } t          |           \  }}}}|                     d           |                     d           t          |           }d |D             }ddg}||k    }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }	d	d
|	iz  }
t          t	          j        |
                    d x}x}}d |d         d         D             }t          |          }|sddt          j                    v st	          j        t                    rt	          j        t                    ndt	          j        |          t	          j        |          dz  }	t          t	          j        |	                    d x}}d |d         d         D             }t          |          }|sddt          j                    v st	          j        t                    rt	          j        t                    ndt	          j        |          t	          j        |          dz  }	t          t	          j        |	                    d x}}t          |           }|d         }|s:ddt	          j        |          iz  }t          t	          j        |                    d }|                     d                                          d         }d}||k    }|slt	          j        d|fd||f          t	          j        |          t	          j        |          dz  }	d	d
|	iz  }
t          t	          j        |
                    d x}x}}d S )NzINSERT INTO budget_categories(category_id, parent_category_id, name, category_type, color, icon, is_active, sort_order, created_at, updated_at) VALUES ('dup_vers_a', NULL, 'Versicherungen', 'expense', NULL, NULL, 1, 900, '2026', '2026')zINSERT INTO budget_categories(category_id, parent_category_id, name, category_type, color, icon, is_active, sort_order, created_at, updated_at) VALUES ('dup_vers_b', NULL, 'versicherungen', 'expense', NULL, NULL, 1, 901, '2026', '2026')c                    g | ]
}|d          S )r   rI   )rJ   bs     r   rL   zgtest_category_buckets_and_merge_preview_detect_duplicate_like_names_without_merging.<locals>.<listcomp>[   s    $$$!AfI$$$r   	EinnahmenAusgabenrM   rO   rP   rS   rT   c              3  .   K   | ]}|d          dk    V  dS )r   r'   NrI   rJ   cs     r   ro   zftest_category_buckets_and_merge_preview_detect_duplicate_like_names_without_merging.<locals>.<genexpr>\   s+      GGaqyM)GGGGGGr   r   childrenz,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}any)r   r   rR   c              3  .   K   | ]}|d          dk    V  dS )r   VersicherungenNrI   r   s     r   ro   zftest_category_buckets_and_merge_preview_detect_duplicate_like_names_without_merging.<locals>.<genexpr>]   s,      JJqy,,JJJJJJr   rt   suggestionszassert %(py1)srQ   zeSELECT COUNT(*) AS c FROM budget_categories WHERE lower(name)=lower('versicherungen') AND is_active=1r      )>=)z%(py1)s >= %(py4)s)r   r.   rY   r   rZ   r[   r\   r]   r^   r   r   r   r   r   fetchone)r   _account_foodr   r   treer`   ra   rb   rc   rd   r   preview@py_format2s                 r   Stest_category_buckets_and_merge_preview_detect_duplicate_like_names_without_mergingr   V   s   44DYt__25'7LL  @  A  A  ALL  @  A  A  A%d++D$$t$$$Aj(AA$(AAAAAAAAAAA$(AAAAAA$AAAA(AAAAAAAAAAAAAAAAAAAAAAAAAAAAGG47:3FGGGG3GGGGGGGGGGGGGGGG3GGGGG3GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGJJd1gj6IJJJJ3JJJJJJJJJJJJJJJJ3JJJJJ3JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ%d++G=!!!!!!!!!!!!!!!!!!!!!!!!!<<  A  A  J  J  L  L  MP  Q  V  UV  V  Q  UV  V  V  V  V  V  V  V  V  V  V  Q  UV  V  V  V  V  V  Q  V  V  V  V  UV  V  V  V  V  V  V  V  V  V  V  V  V  V  V  V  V  V  V  V  V  V  V  V  V  V  V  V  V  Vr   )r/   r0   )!
__future__r   builtinsr   _pytest.assertion.rewrite	assertionrewriterZ   'jarvis_finance.services.budget_accountsr   )jarvis_finance.services.budget_categoriesr   r   r   'jarvis_finance.services.budget_overviewr   +jarvis_finance.services.budget_transactionsr	   r
   (jarvis_finance.services.budget_analyticsr   r   jarvis_finance.storage.databaser   !jarvis_finance.storage.migrationsr   r   r.   re   r   r   r   r   rI   r   r   <module>r      sx   " " " " " "               Q Q Q Q Q Q C  C  C  C  C  C  C  C  C  C Q Q Q Q Q Q l l l l l l l l b b b b b b b b : : : : : : > > > > > >  ) ) )= = = = $ $ $ $"= = = ="! ! ! !
V 
V 
V 
V 
V 
Vr   