
    Lrdjq!                        d dl mZ d dlZd dlmc mZ d dlm	Z	 d dl
Z
d dlmZ d dlmZmZmZmZ d dlmZmZmZ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 Z"d Z#d Z$d Z%d Z&d Z'd Z(dS )    )annotationsN)Decimal)HTTPException)AccountValueConfirmRequestAccountValuePreviewRequestCashSnapshotConfirmRequestCashSnapshotPreviewRequest)CANONICAL_CASH_ACCOUNTSconfirm_cash_snapshotensure_canonical_cash_accountsget_cash_summarypreview_cash_snapshot)confirm_account_valuepreview_account_valuepreview_position)get_overview)connect_memory)apply_migrationsc                 @    t                      } t          |            | S )N)r   r   )conns    /home/agent/.hermes/worktrees/FinanceManager-sprint7-postfinance-portfolio-source/tests/unit/test_cash_truewealth_management.pydbr      s     DTK    c                 &   t                      } 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
         }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             }t          |          }d t          D             }	||	k    }
|
st          j        d|
fd||	f          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        |          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}||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             }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                                           }t          |          }d}	||	k    }
|
st          j        d|
fd||	f          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        |          t          j        |	          dz  }dd|iz  }t          t          j        |                    d x}x}
}	d S )#Ntest)
created_bycreated   ==z%(py1)s == %(py4)spy1py4assert %(py6)spy6
duplicatesz
        SELECT a.account_name, a.account_type, a.currency, a.balance_mode, p.name AS platform
        FROM accounts a JOIN platforms p ON p.platform_id=a.platform_id
        WHERE a.account_type='cash'
        ORDER BY a.account_name
        c                     i | ]}|d          |S account_name .0rows     r   
<dictcomp>zZtest_canonical_cash_accounts_are_present_with_expected_modes_and_audit.<locals>.<dictcomp>0   s    888Cs>"C888r   c                    h | ]
}|d          S r)   r+   r-   items     r   	<setcomp>zYtest_canonical_cash_accounts_are_present_with_expected_modes_and_audit.<locals>.<setcomp>1   s    UUUTD0UUUr   )z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)ssetby_name)py0r#   py3r&   zassert %(py8)spy8
Raiffeisenbalance_modecsv_calculatedzAKB HaushaltskontoPostFinance Cash-KontomanualplatformzPostFinance Cashc              3  .   K   | ]}|d          dk    V  dS )currencyCHFNr+   r,   s     r   	<genexpr>zYtest_canonical_cash_accounts_are_present_with_expected_modes_and_audit.<locals>.<genexpr>6   s+      88Cs:%'888888r   z,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}all)r6   py2r$   zESELECT action FROM audit_log WHERE action='cash_account_seed_confirm'lenaudits)r   r   
@pytest_ar_call_reprcompare	_safereprAssertionError_format_explanationexecutefetchallr4   r
   @py_builtinslocals_should_repr_global_namerC   rE   )r   result@py_assert0@py_assert3@py_assert2@py_format5@py_format7rowsr5   @py_assert5@py_assert4@py_format9@py_assert1rF   s                 r   Ftest_canonical_cash_accounts_are_present_with_expected_modes_and_auditr\   !   s   44D+DVDDDF)!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!,%2%2%%%%%%%%%%2%%%%%%%%%2%%%%%%%%%%%%%%%%%%%%%%%%%%%<<	  hjj 	 984888Gw<<UUU=TUUUU<UUUUUUUUUUU<UUUUUUUUUUUUU3UUUUU3UUUUUUUUUUUUUwUUUUUwUUUUUU<UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU< 0D4DD04DDDDDDDDDDD04DDDDDD0DDDD4D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DD'(8L<LL8<LLLLLLLLLLL8<LLLLLL8LLLL<LLLLLLLLLLLLLLLLLLLLLLLLLLLL+,^<HH<HHHHHHHHHH<HHHHH<HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH+,Z8N<NN8<NNNNNNNNNNN8<NNNNNN8NNNN<NNNNNNNNNNNNNNNNNNNNNNNNNNNN88488883888888888888888838888838888888888888888888888888888888\\abbkkmmFv;;!;!;!33vv;!r   c                    t                      } t          |            |                     d           |                     d           |                                  t          |           }|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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    }|st	          j        d|fd||f          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}}d S )NzINSERT INTO platforms(platform_id, name, platform_type, default_currency, created_at) VALUES ('plat_pf_duplicate', 'PostFinance Duplicate', 'bank', 'CHF', '2026-01-01')zINSERT INTO accounts(account_id, platform_id, account_name, account_type, currency, balance_mode, created_at) VALUES ('acct_dup_pf_cash', 'plat_pf_duplicate', 'PostFinance Cash-Konto', 'cash', 'CHF', 'manual', '2026-01-01')r   r   r   r!   r"   r%   r&   r'   r<   zISELECT COUNT(*) FROM accounts WHERE account_name='PostFinance Cash-Konto'   )z%(py0)s == %(py3)scount)r6   r7   zassert %(py5)spy5)r   r   rL   commitrG   rH   rI   rJ   rK   fetchonerN   rO   rP   )r   rQ   rR   rS   rT   rU   rV   r_   r[   @py_format4@py_format6s              r   Ntest_duplicate_cash_accounts_return_consolidation_preview_without_silent_mergere   ;   s   44D"4(((LL  |  }  }  }LL 	j   	KKMMM+D11F)!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!,=$<#==#===========#==========#============================LLdeennppqrsE5A:5A55Ar   c                    t                      } t          |            |                     d                                          d         }t	          | t          |dddd                    }|j        }d}||k    }|st          j        d|fd	||f          d
t          j
                    v st          j        |          rt          j        |          nd
t          j        |          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}x}}|j        }d}||k    }|st          j        d|fd||f          d
t          j
                    v st          j        |          rt          j        |          nd
t          j        |          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}x}}t!          | t#          |dddd|j        d                    }|j        }d}||k    }|st          j        d|fd||f          dt          j
                    v st          j        |          rt          j        |          ndt          j        |          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}x}}|j        }|j        }d}	 ||	          }
|
sddt          j
                    v st          j        |          rt          j        |          ndt          j        |          t          j        |          t          j        |	          t          j        |
          dz  }t          t          j        |                    d x}x}x}	}
t-          |           }t/          d |j        D                       }|j        }d}||k    }|st          j        d|fd||f          dt          j
                    v st          j        |          rt          j        |          ndt          j        |          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}x}}|j        }d}||k    }|st          j        d|fd||f          dt          j
                    v st          j        |          rt          j        |          ndt          j        |          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}x}}|j        }d}||k    }|st          j        d|fd||f          dt          j
                    v st          j        |          rt          j        |          ndt          j        |          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}x}}|j        }d}||k    }|st          j        d|fd||f          dt          j
                    v st          j        |          rt          j        |          ndt          j        |          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}x}}|j        }d}||k    }|st          j        d|fd||f          dt          j
                    v st          j        |          rt          j        |          ndt          j        |          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}x}}|j        }d}||k    }|st          j        d|fd ||f          dt          j
                    v st          j        |          rt          j        |          ndt          j        |          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}x}}d S )!NzKSELECT account_id FROM accounts WHERE account_name='PostFinance Cash-Konto'r   manual_balance
2026-05-20z1234.56Abgleich)
account_idsnapshot_typebalance_date
amount_chfnoter   z2%(py2)s
{%(py2)s = %(py0)s.amount_chf
} == %(py5)spreviewr6   rD   r`   assert %(py7)spy7
not_needed)z1%(py2)s
{%(py2)s = %(py0)s.fx_status
} == %(py5)sTrj   rk   rl   rm   rn   
preview_idconfirm	confirmedz.%(py2)s
{%(py2)s = %(py0)s.status
} == %(py5)saudit_jassert %(py8)s
{%(py8)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.audit_id
}.startswith
}(%(py6)s)
}r6   rD   r$   r&   r8   c              3  0   K   | ]}|j         d k    |V  dS )r<   Naccount_labelr1   s     r   rB   zitest_manual_cash_snapshot_preview_confirm_audit_and_dashboard_value_for_manual_account.<locals>.<genexpr>Z   s0      ddT5GKc5c5ct5c5c5c5cddr   r=   z4%(py2)s
{%(py2)s = %(py0)s.balance_mode
} == %(py5)sr.   z:%(py2)s
{%(py2)s = %(py0)s.manual_balance_chf
} == %(py5)sz6%(py2)s
{%(py2)s = %(py0)s.used_value_chf
} == %(py5)s0.00z>%(py2)s
{%(py2)s = %(py0)s.calculated_balance_chf
} == %(py5)sz6%(py2)s
{%(py2)s = %(py0)s.difference_chf
} == %(py5)s)zB%(py2)s
{%(py2)s = %(py0)s.last_manual_reconciliation
} == %(py5)s)r   r   rL   rb   r   r	   rm   rG   rH   rN   rO   rP   rI   rJ   rK   	fx_statusr   r   rv   statusaudit_id
startswithr   next	positionsr:   manual_balance_chfused_value_chfcalculated_balance_chfdifference_chflast_manual_reconciliation)r   rj   rp   r[   rY   rS   rd   @py_format8rx   rX   @py_assert7rZ   summaryr.   s                 r   Vtest_manual_cash_snapshot_preview_confirm_audit_and_dashboard_value_for_manual_accountr   L   s	   44D"4(((klluuwwxyzJ#D*DPZjz  JV  cl  s}  +~  +~  +~    G************************7*****7*************************************,,,,,,,,,,,,,,,,,,,,,,,,7,,,,,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,%d,FR\l|  LX  en  u  LS  L^  hl  -m  -m  -m  n  nI*{*{**********{************9*****9**********{***************************2(22((22222222222222292222292222222222(22222222222222222222222222222222t$$G
dd 1ddd
d
dC'x'x''''''''''x''''''''''''3'''''3''''''''''x'''''''''''''''''''''''''''!.Y.!Y..........!Y............3.....3......!....Y...........................************************3*****3*************************************%//%//////////%////////////3/////3//////%///////////////////////////////************************3*****3*************************************)9\9)\9999999999)\9999999999993999993999999)9999\99999999999999999999999999999r   c                 <   t                      } t          |            |                     d                                          d         }t	          | t          |dddddd	                     |                     d
|f           t	          | t          |dddddd	                     t          d t          |           j        D                       }|j	        }d}||k    }|st          j        d|fd||f          dt          j                    v st          j        |          rt          j        |          ndt          j        |          t          j        |          dz  }dd|iz  }t!          t          j        |                    d x}x}}|j        }d}||k    }|st          j        d|fd||f          dt          j                    v st          j        |          rt          j        |          ndt          j        |          t          j        |          dz  }dd|iz  }t!          t          j        |                    d x}x}}|j        }d}||k    }|st          j        d|fd||f          dt          j                    v st          j        |          rt          j        |          ndt          j        |          t          j        |          dz  }dd|iz  }t!          t          j        |                    d x}x}}|j        }d}||k    }|st          j        d|fd||f          dt          j                    v st          j        |          rt          j        |          ndt          j        |          t          j        |          dz  }dd|iz  }t!          t          j        |                    d x}x}}|j        }d}||k    }|st          j        d|fd||f          dt          j                    v st          j        |          rt          j        |          ndt          j        |          t          j        |          dz  }dd|iz  }t!          t          j        |                    d x}x}}|j        }d}||k    }|st          j        d|fd||f          dt          j                    v st          j        |          rt          j        |          ndt          j        |          t          j        |          dz  }dd|iz  }t!          t          j        |                    d x}x}}|j        }d}||k    }|st          j        d|fd||f          dt          j                    v st          j        |          rt          j        |          ndt          j        |          t          j        |          dz  }dd|iz  }t!          t          j        |                    d x}x}}d S ) Nz?SELECT account_id FROM accounts WHERE account_name='Raiffeisen'r   csv_anchor_balance
2026-05-01z1000.00StartpTru   a  INSERT INTO transactions(transaction_id, transaction_type, account_id, trade_date, gross_amount_original, net_amount_original, currency_original, fx_rate_to_chf, fx_status, gross_amount_chf, net_amount_chf, source_type, is_confirmed, quality_status, created_at) VALUES ('tx_csv_1', 'csv_import', ?, '2026-05-05', '250.00', '250.00', 'CHF', '1', 'ok', '250.00', '250.00', 'csv_import', 1, 'ok', '2026-05-05')rg   rh   z1300.00	Kontrollec              3  0   K   | ]}|j         d k    |V  dS )r9   Nr~   r1   s     r   rB   zetest_csv_calculated_account_uses_calculated_value_and_manual_as_control_difference.<locals>.<genexpr>n   s0      ggDDVZfDfDftDfDfDfDfggr   r;   r   r   r.   rq   rr   rs   )z>%(py2)s
{%(py2)s = %(py0)s.csv_anchor_balance_chf
} == %(py5)sz1250.00r   r   r   z50.00r   zAbgleich offenry   )r   r   rL   rb   r   r   r   r   r   r:   rG   rH   rN   rO   rP   rI   rJ   rK   csv_anchor_balance_chfr   r   r   r   r   )r   rj   r.   r[   rY   rS   rd   r   s           r   Rtest_csv_calculated_account_uses_calculated_value_and_manual_as_control_differencer   c   sa   44D"4(((_``iikklmnJ$ :j`t  DP  ]f  mt  AD  NR  !S  !S  !S  T  T  TLL 	b	   $ :j`p  @L  Yb  it  AD  NR  !S  !S  !S  T  T  T
gg 0 6 6 @ggg
g
gC///////////////////////////3/////3//////////////////////////////////////%22%2222222222%2222222222223222223222222%2222222222222222222222222222222%22%2222222222%2222222222223222223222222%2222222222222222222222222222222!.Y.!Y..........!Y............3.....3......!....Y...........................************************3*****3*************************************((((((((((((((((((((((((3(((((3(((((((((((((((((((((((((((((((((((((:))):))))))))))):)))))))))))))3)))))3)))))):))))))))))))))))))))))))))))))))))r   c                 <
   t                      } d}d}|                     d|f           |                     d||f           |                     d           |                                  t          | |t	          ddd	                    }|j        }d}||k    }|st          j        d
|fd||f          dt          j	                    v st          j
        |          rt          j        |          ndt          j        |          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}x}}t          | |t          ddd|j        d                    }	|	j        }|j        }d}
 ||
          }|sddt          j	                    v st          j
        |	          rt          j        |	          ndt          j        |          t          j        |          t          j        |
          t          j        |          dz  }t          t          j        |                    d x}x}x}
}t'          |           }|j        }d}||k    }|st          j        d
|fd||f          dt          j	                    v st          j
        |          rt          j        |          ndt          j        |          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}x}}|j        }d}||k    }|st          j        d
|fd||f          dt          j	                    v st          j
        |          rt          j        |          ndt          j        |          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}x}}t-          j        t0                    5 }t3          |  t5          dd|dddddd d d d ddd                                  d d d            n# 1 swxY w Y   d!}|j        }|j        }||v }|st          j        d"|fd#||f          t          j        |          d$t          j	                    v st          j
        |          rt          j        |          nd$t          j        |          t          j        |          d%z  }d&d'|iz  }t          t          j        |                    d x}x}x}}d S )(Nplat_truewealthacct_truewealthzINSERT INTO platforms(platform_id, name, platform_type, default_currency, created_at) VALUES (?, 'TrueWealth', 'roboadvisor', 'CHF', '2026-01-01')zINSERT INTO accounts(account_id, platform_id, account_name, account_type, currency, created_at) VALUES (?, ?, 'TrueWealth Gesamtwert', 'managed_portfolio', 'CHF', '2026-01-01')zINSERT INTO instruments(instrument_id, asset_class, name, currency, created_at) VALUES ('inst_vti', 'etf', 'Do not allow', 'USD', '2026-01-01')rh   z55555.55Monat)valuation_datetotal_value_chfrn   r   ro   rp   rq   rr   rs   T)r   r   rn   rv   rw   rz   r{   rx   r|   z<%(py2)s
{%(py2)s = %(py0)s.truewealth_value_chf
} == %(py5)soverview)z7%(py2)s
{%(py2)s = %(py0)s.total_value_chf
} == %(py5)sReqr+   1USDetfinst_vtibuy)rj   quantityr@   
trade_dateasset_classinstrument_idcandidate_idnametickerisincost_basis_originaltransaction_typeEinzelpositionen)in)zI%(py1)s in %(py7)s
{%(py7)s = %(py5)s
{%(py5)s = %(py3)s.value
}.detail
}exc)r#   r7   r`   rs   zassert %(py9)spy9)r   rL   ra   r   r   rm   rG   rH   rN   rO   rP   rI   rJ   rK   r   r   rv   r   r   r   truewealth_value_chfr   pytestraisesr   r   typevaluedetail)r   platform_idrj   rp   r[   rY   rS   rd   r   rx   rX   r   rZ   r   r   rR   @py_assert6rT   @py_format10s                      r   7test_truewealth_manual_value_history_and_position_blockr   y   s   44D#K"JLL  f  it  hv  w  w  wLL  D  GQ  S^  F_  `  `  `LL  c  d  d  dKKMMM#D*6P`l  I  PW  7X  7X  7X  Y  YG++++++++++++++++++++++++7+++++7+++++++++++++++++++++++++++++++++++++%dJ8Rbn  AK  RY  fm  fx  BF  9G  9G  9G  H  HI2(22((22222222222222292222292222222222(22222222222222222222222222222222D!!H(6J6(J6666666666(J6666666666668666668666666(6666J666666666666666666666666666#1z1#z1111111111#z1111111111118111118111111#1111z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	}	%	% z   wtE2jVYgl  }I  Z_  r|  NR  \`  lp  z~  WZ  pu  0v  0v   w   w   y   y  	z  	z  	zz z z z z z z z z z z z z z z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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1s   55P66P:=P:c                    t                      } t          |            |                     d                                          d         }|                     d                                          d         }t	          | t          |dddddd	
                     t	          | t          |dddddd	
                     t          |           }|j        }d}||k    }|st          j	        d|fd||f          dt          j                    v st          j        |          rt          j        |          ndt          j        |          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}x}}|j        }d}||k    }|st          j	        d|fd||f          dt          j                    v st          j        |          rt          j        |          ndt          j        |          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}x}}|j        }	t%          |	          }d}
t%          |
          }||k    }|s`t          j	        d|fd||f          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        |	          t          j        |          dt          j                    v st          j        t$                    rt          j        t$                    ndt          j        |
          t          j        |          dz  }dd|iz  }t          t          j        |                    d x}	x}x}x}
}d S )NzJSELECT account_id FROM accounts WHERE account_name='AKB gemeinsames Konto'r   zGSELECT account_id FROM accounts WHERE account_name='AKB Haushaltskonto'rg   rh   z2000.00 r   Tru   r   r   z3000.00z5000.00r   )z6%(py2)s
{%(py2)s = %(py0)s.cash_value_chf
} == %(py5)sr   rq   rr   rs   r   r   )>=)zu%(py5)s
{%(py5)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.total_value_chf
})
} >= %(py11)s
{%(py11)s = %(py7)s(%(py9)s)
}r   )r6   r#   r7   r`   rs   r   py11zassert %(py13)spy13)r   r   rL   rb   r   r   r   cash_value_chfrG   rH   rN   rO   rP   rI   rJ   rK   r   r   r   )r   	manual_idcsv_idr   r[   rY   rS   rd   r   rT   @py_assert8@py_assert10r   @py_format12@py_format14s                  r   Itest_portfolio_uses_cash_balance_modes_without_double_counting_truewealthr      sz   44D"4(((ijjssuuvwxI\\cddmmoopqrF$ :i_o  K  Xa  hj  wz  DH  !I  !I  !I  J  J  J$ :f\p  @L  Yb  ik  x{  EI  !J  !J  !J  K  K  KD!!H"/i/"i//////////"i////////////8/////8//////"////i///////////////////////////(2F2(F2222222222(F2222222222228222228222222(2222F222222222222222222222222222+B7+,,B	B	0B0BB,0BBBBBBBBBBB,0BBBBBBBBBBBBB7BBBBB7BBBBBBBBBBBBB8BBBBB8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B	BBBB0B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BBr   ))
__future__r   builtinsrN   _pytest.assertion.rewrite	assertionrewriterG   decimalr   r   fastapir   $jarvis_finance.api.schemas.positionsr   r   r   r	   $jarvis_finance.services.cash_servicer
   r   r   r   r   ,jarvis_finance.services.manual_entry_servicer   r   r   )jarvis_finance.services.portfolio_servicer   jarvis_finance.storage.databaser   !jarvis_finance.storage.migrationsr   r   r\   re   r   r   r   r   r+   r   r   <module>r      s   " " " " " "                      ! ! ! ! ! !                         x w w w w w w w w w B B B B B B : : : : : : > > > > > >    4  ": : :.* * *,2 2 2,C C C C Cr   