
    bj                       d dl mZ d dlZd dl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 dlmZ d d	lmZmZ h d
Zh dZdddZdZdZdZd[dZd\dZd]dZd^dZd_d$Zd`d&Z dad(Z!dbd*Z"dcd,Z#ddd/Z$ded6Z%dfd8Z&dgdBZ'dhdDZ(didFZ)dGdHdjdJZ*ddKdkdLZ+dldNZ,dmdOZ-dndQZ.dodSZ/dodTZ0dUddVdpdYZ1dUddVdpdZZ2dS )q    )annotationsN)date)DecimalInvalidOperation)
ConnectionIntegrityError)Any)HTTPException)record_audit_event)nowrow_to_dict>   proposedrejected	confirmed	unmatched
superseded>   pendingneeds_reviewauto_categorizedtransfer_candidate)
raiffeisen)akbzaargauische kantonalbank)raiffeisen_bankakb_bank)transfer	uebertragu	   übertragzeigenes kontokontoausgleich	umbuchungtwinttransfer_pairing_v2uY   Als interner Transfer erkannt. Die endgültige Verbuchung erfolgt erst nach Bestätigung.valueobjectreturnDecimal | Nonec                x    | dv rd S 	 t          t          |                     S # t          t          f$ r Y d S w xY w)N)N )r   strr   
ValueError)r!   s    /home/agent/.hermes/worktrees/FinanceManager-sprint3-reconciliation-snapshot-foundation/src/jarvis_finance/services/transfer_pairing.py_decimalr*   $   sR    
ts5zz"""j)   tts   $ 99rowr	   c                   t          | d                   }||dk    r|S t          | d                   }||dk    rd S 	 t          j        t          | d         pd                    }n# t          j        $ r i }Y nw xY wt          |                    d          pd                                          }|dk    rt          |          S |d	v rt          |           S d S )
Nsigned_amount_originalr   amount_originalnotes{}transaction_typer&   income>   feeexpense)r*   jsonloadsr'   JSONDecodeErrorgetlowerabs)r+   explicitamountmetar1   s        r)   _signed_amountr>   -   s    4566HAc+,--F~1tz#c'l2d3344   488$677=2>>DDFF8##6{{---F|4s    )A* *A>=A>date | Nonec                    t          | d         p	| d         pd          d d         }	 t          j        |          S # t          $ r Y d S w xY w)N
value_datetransaction_dater&   
   )r'   r   fromisoformatr(   r+   texts     r)   _candidate_daterG   @   se    s< AC(:$;ArBB3B3GD!$'''   tts   = 
A
Aconnr   
Any | Nonec                   |                      d                                          }t          |d         pd                                                                          r)fd|D             }t          |          dk    r|d         S t                              t          |d         pd          d          fd	|D             }t          |          dk    r|d         nd S )
Nz
        SELECT budget_account_id, name, linked_account_id
        FROM budget_accounts
        WHERE is_active=1
        ORDER BY name, budget_account_id
        account_sourcer&   c           	        g | ]}t          |d          pd                                          t          |d         pd                                          t          |d         pd                                          hv ~|S )budget_account_idr&   linked_account_idnamer'   casefold).0itemrK   s     r)   
<listcomp>z _own_account.<locals>.<listcomp>S   s     	
 	
 	
D,-344==??D,-344==??DL&B''0022           r   source_type c                L    g | ]t          fd D                        S )c              3  n   K   | ]/}|t          d          pd                                          v V  0dS )rO   r&   NrP   )rR   hintrS   s     r)   	<genexpr>z*_own_account.<locals>.<listcomp>.<genexpr>c   sE      LLdts4<-2..77999LLLLLLrU   )any)rR   rS   hintss    @r)   rT   z _own_account.<locals>.<listcomp>`   sM       LLLLeLLLLL  rU   )executefetchallr'   striprQ   lenACCOUNT_SOURCE_HINTSr8   )rH   r+   accountsexacthintedrK   r^   s        @@r)   _own_accountrg   H   s   ||	  hjj  -.4"55;;==FFHHN 	
 	
 	
 	
 	
 	
 	
 u::??8O $$S]);)Ar%B%BBGGE     F
 Fq((6!99d2rU   source_candidate_idr'   target_candidate_id
str | Nonec                    |  d|pd }dt          j        |                    d                                                    d d         z   S )N|r   btpair_utf-8   hashlibsha256encode	hexdigest)rh   ri   raws      r)   _pair_idrv   h   sQ     
G
G#6#E+
G
GCw~cjj&9&9::DDFFssKKKrU   pair_idc                    dt          j        |                     d                                                    d d         z   S )Nbtrf_rn   ro   rp   )rw   s    r)   _transfer_idrz   m   s8    W^GNN7$;$;<<FFHH"MMMrU   sidec                    |  d| }dt          j        |                    d                                                    d d         z   S )Nrl   btx_rn   ro   rp   )rw   r{   ru   s      r)   _transaction_idr~   q   sK    

t

CGN3::g#6#677AACCCRCHHHrU   boolc                    | d         pd d| d         pd                                  t          fdt          D                       S )Ndescriptionr&    merchantc              3      K   | ]}|v V  	d S )NrX   )rR   r[   rF   s     r)   r\   z _text_support.<locals>.<genexpr>x   s'      <<tt|<<<<<<rU   )rQ   r]   TRANSFER_TEXT_HINTSrE   s    @r)   _text_supportr   v   sY    - &B@@Z)>B@@IIKKD<<<<(;<<<<<<rU   	list[Any]c                    d                     d t          D                       }|                     d| dt          t	          t                                                                        S )N,c              3     K   | ]}d V  dS )?NrX   )rR   _s     r)   r\   z!_eligible_rows.<locals>.<genexpr>|   s"      AAACAAAAAArU   zN
        SELECT * FROM budget_transaction_candidates
        WHERE status IN (z)
          AND confirmed_transaction_id IS NULL
          AND amount_original IS NOT NULL
          AND currency_original IS NOT NULL
        ORDER BY transaction_candidate_id
        )joinOPEN_CANDIDATE_STATUSESr_   tuplesortedr`   )rH   placeholderss     r)   _eligible_rowsr   {   sq    88AA)@AAAAAL<<	&	 	 	 	f,--..
 
 hjj
rU   	source_id	target_idc                p    t          |                     d||f                                                    S )Nz
            SELECT 1 FROM budget_transfer_pairs
            WHERE source_candidate_id=? AND target_candidate_id=? AND status='rejected'
            )r   r_   fetchone)rH   r   r   s      r)   _is_rejected_relationr      s?     	"	
 	
 (**  rU   outflowdict[str, Any]inflowslist[dict[str, Any]]date_window_daysintc               p   g }|D ]}|d         |d         k    r|d         |d         k    r(t          |d                   t          |d                   k    rUt          |d         |d         z
  j                  |k    r}t          | |d         |d                   r|                    |           |S )N
account_idcurrencyr<   r   candidate_id)r:   daysr   append)rH   r   r   r   matchesinflows         r)   _match_candidatesr      s     G  < F<$888:&"444wx !!S)9%:%:::&.0677:JJJ w~'>~@VWW 	vNrU   pairc           	         | d         | d         | d         |                      d          | d         | d         dt          dS )	Ntransfer_pair_idstatussource_account_nametarget_account_namer<   r   0)r   r   r   r   r<   r   budget_effect_chfmatcher_version)r8   MATCHER_VERSION)r   s    r)   _redacted_pair_auditr      sO     !34x.#$9:#xx(=>>x.$ *	 	 	rU   sourcetargetdict[str, Any] | Noner   quality_statusreason_codes	list[str]alternative_counttuple[str, bool]c                  t          |d         |r|d         nd           }|                     d|f                                          }	|	r|	d         dv r|dfS t                      }
t          ||t          |o|d         |d         k              t          |o|d         |d         k              t          |o)t          |d                   pt          |d                             dd	}||d         |r|d         nd |d
         |r|d
         nd t          |d         d          |rt          |d         d          nd |d         |d         d         |r|d         d         nd |d         d         |r|d         d         nd ||t          j	        |d          t          j	        |d          |
|
f}|                     d|           ||	d u fS )Nr   zASELECT status FROM budget_transfer_pairs WHERE transfer_pair_id=?r   >   r   r   FrW   source_file_labelr+   )r   r   r   different_sourcesdifferent_import_runstext_supportmerchant_text_decisiver   r<   fr   rB   rA   T)	sort_keysa~  
        INSERT INTO budget_transfer_pairs(
            transfer_pair_id, source_candidate_id, target_candidate_id,
            source_account_id, target_account_id, source_signed_amount,
            target_signed_amount, currency, source_booking_date, target_booking_date,
            source_value_date, target_value_date, status, quality_status,
            evidence_json, reason_codes_json, budget_effect_chf, created_at, created_by,
            updated_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '0', ?, 'system', ?)
        ON CONFLICT(transfer_pair_id) DO UPDATE SET
            target_candidate_id=excluded.target_candidate_id,
            target_account_id=excluded.target_account_id,
            target_signed_amount=excluded.target_signed_amount,
            target_booking_date=excluded.target_booking_date,
            target_value_date=excluded.target_value_date,
            status=excluded.status,
            quality_status=excluded.quality_status,
            evidence_json=excluded.evidence_json,
            reason_codes_json=excluded.reason_codes_json,
            updated_at=excluded.updated_at
        )
rv   r_   r   r   r   r   r   formatr5   dumps)rH   r   r   r   r   r   r   r   rw   existing	timestampevidencevaluess                r)   _upsert_pairr      s-    vn-/Yvn/E/EUYZZG||K	
  hjj   HX&*CCC~I*,.!&"[VM-Bf]F[-[\\!%Qv12f=P6QQ"
 "
 Vfve})E)E)eW]^cWdIeIegg"'
 
H 	~"(2~d| &0|Dvh%%)/9vh%%%Tzu()-3=u())ul#'-7ul##4
8t,,,
<4000%F( 	LL	* 	-  0 H$$$rU   Nonec                   t                      }||df||dffD ]\  }}}|d         }	 t          j        t          |d         pd                    }	n# t          j        $ r i }	Y nw xY wt          |	t                    si }	d|	d<   |d|d	d
|	d<   |                     dt          |d         t          j	        |	dd          ||d         f           d S )Nr   r   r+   r/   r0   r   r1   r   neutral)rw   pair_status	directionbudget_impacta  
            UPDATE budget_transaction_candidates
            SET status='transfer_candidate', classification='transfer_candidate',
                requires_review=1, review_reason='generic_internal_transfer_match',
                proposed_category_id=NULL, proposed_category_name=NULL,
                confidence='0.95', rule_id=?, rule_name='Generischer interner Transfer',
                linked_candidate_id=?, notes=?, updated_at=?
            WHERE transaction_candidate_id=?
            r   FTensure_asciir   )
r   r5   r6   r'   r7   
isinstancedictr_   r   r   )
rH   r   r   rw   r   	candidatecounterpartr   r+   metadatas
             r)   _set_candidate_pair_metadatar     s/    I	#	". #
 #
)	;	 	z#c'l&:d";";<<HH# 	 	 	HHH	(D)) 	H'1#$%"&	 
  
 	  N+
8%4HHH.)	
 	
 	
 	
%#
 #
s   )AA&%A&current_pair_idc               (   |                      d||f                                          }t                      }|D ]I}|                      d|||d         f           t          | ddd|d         d|d         id|d	d
           Jt	          |          S )Nz
        SELECT transfer_pair_id, status FROM budget_transfer_pairs
        WHERE source_candidate_id=?
          AND transfer_pair_id<>?
          AND status IN ('proposed','unmatched')
        z
            UPDATE budget_transfer_pairs
            SET status='superseded', superseded_by_pair_id=?, updated_at=?
            WHERE transfer_pair_id=?
            r   transfer_matchertransfer_pair_supersededbudget_transfer_pairr   r   )r   superseded_by_pair_idsystem)r   actionentity_type	entity_id
old_values
new_values
created_by)r_   r`   r   r   rb   )rH   rh   r   rowsr   r+   s         r)   _supersede_other_open_pairsr   4  s     <<	 
o.  hjj 	 I 
 

 i-?)@A	
 	
 	
 	%-.,- #h-0".YY		
 		
 		
 		
 		
 t99rU      r   dict[str, int]c                   dcxk    rdk    sn t          dd          g }t                     D ]}t          |          }t          |          }t	           |          }|||7|                    |t          |d                   |t          |d                   |t          |d                   t          |d	                   t          |d
                   t          |d                   d	           d |D             }d |D              fd|D             }i }	|                                D ]/}
|
D ]*}|	                    |d         d          dz   |	|d         <   +0t          |          ddddd}|D ]}||d                  }
t          |
          dk    r-|	                    |
d         d                   dk    r|
d         nd }|rdddddd dddg}t           ||dd|d          \  }}t           |||           |dxx         dz  cc<   |d xx         t           |d         |!          z  cc<   |rWt           d"d#d$|d|d%         |d%         t          t          |d&                   d'          |d(         d)t           d*d+d,-            t#          |
          }g d.}|r&|                    d/           |d0xx         dz  cc<   n%|                    d1           |d2xx         dz  cc<   t           |d d3|rd4nd3|t          |
                    \  }}|d xx         t           |d         |!          z  cc<   Ӊ                                  |S )5Nr        z(date_window_days must be between 0 and 7status_codedetailtransaction_candidate_idcurrency_originalrM   rO   rW   r   )	r+   r   r<   r   r   r   account_namerW   r   c                *    g | ]}|d          dk     |S r<   r   rX   rR   rS   s     r)   rT   z*propose_transfer_pairs.<locals>.<listcomp>q  s&    BBBtH~/A/A/A/A/ArU   c                *    g | ]}|d          dk    |S r   rX   r   s     r)   rT   z*propose_transfer_pairs.<locals>.<listcomp>r  s&    AAAd8nq.@.@t.@.@.@rU   c           	     D    i | ]}|d          t          |          S )r   r   )r   )rR   rS   rH   r   r   s     r)   
<dictcomp>z*propose_transfer_pairs.<locals>.<dictcomp>s  sN         	^/-	
 
 
  rU   r   rV   )scanned_countproposed_countunmatched_countambiguous_countsuperseded_countknown_own_accountsdifferent_accountsopposite_signsexact_amountsame_currencydate_within__daysmerchant_text_not_decisivemanual_confirm_requiredr   strong)r   r   r   r   r   r   r   r   r   )rh   r   r   transfer_pair_proposedr   r   r<   r   r   r   )r   r   r   r<   r   r   r   Fr   )r   r   r   r   r   r   r   )known_own_source_accountcounterbooking_not_selectedr  ambiguous_counterbookingsr   counterbooking_missingr   r   	ambiguous)r
   r   r>   rG   rg   r   r'   r   r8   rb   r   r   r   r   r   r:   r   r   commit)rH   r   
normalizedr+   r<   booking_dateaccountoutflowsforward_matchesreverse_countsr   r   countsr   unique_targetr   rw   createdr  _createdr   s   ``                  @r)   propose_transfer_pairsr  Z  s    %%%%A%%%%4^____')Jd## 
 
$$&s++tS))>\1W_ #C(B$C D D $7 899$!'*=">?? #GFO 4 4"3}#566%(-@)A%B%B
 
	
 	
 	
 	
 CBBBBHAA
AAAG        O &(N"))++ g g 	g 	gF5C5G5G~H^`a5b5bef5fN6.122	g Z F  R
 R
!&"89 7||q  ^%7%7
>8R%S%SWX%X%X AJJ 	
  /	$$ 6/666,)	L  ,$!')"#!1	  	  	 GW )v}gNNN#$$$)$$$%&&&*E$*>$: '+ + + &&&
  "-3 6%",/5n/E/<^/L"(VH-=)>)>"D"D$*:$6-0+:    $'!   $ MM	
 
 

  	+ ;<<<$%%%*%%%% 8999$%%%*%%%(*3D;;%!'ll-	
 	
 	
 	!"""&A &~ 6#'
 '
 '
 	
""""
 	KKMMMMrU   )r   c                  |r|t           vrt          dd          |rdnd}|r|fnd}|                     d| d|                                          }g }|D ]}t	          |          }	 t          j        t          |                    d	          pd
                    }n# t
          j	        $ r i }Y nw xY w	 t          j        t          |                    d          pd                    }	n# t
          j	        $ r g }	Y nw xY wt          t          t          t          |d                                       d          }
|                    d          }|d         dk    r|                    dd           d}n|d         dk    rd}nd}|                    t          |                    d                    |                    d          |
|	t          |                    d          pd          |t           |rd|d          d| d|d          d|
 d 	nd!|d          d|d          d|
 d"d#           |                    |           |S )$Nr   zinvalid transfer pair statusr   zWHERE pair.status=?r&   rX   a  
        SELECT pair.*, source_account.name AS source_account_name,
               target_account.name AS target_account_name
        FROM budget_transfer_pairs pair
        JOIN budget_accounts source_account
          ON source_account.budget_account_id=pair.source_account_id
        LEFT JOIN budget_accounts target_account
          ON target_account.budget_account_id=pair.target_account_id
        z
        ORDER BY
          CASE pair.status WHEN 'proposed' THEN 0 WHEN 'unmatched' THEN 1 ELSE 2 END,
          pair.created_at DESC,
          pair.transfer_pair_id
        evidence_jsonr0   reason_codes_jsonz[]source_signed_amountr   r   r   r  r   r   z= gleich plausible Gegenbuchungen; keine automatische Auswahl.r   r   uI   Gegenbuchung fehlt; der Vorgang bleibt ungeklärt und wird nicht gebucht.uQ   Gegenbuchung mit gleichem Betrag, gleicher Währung und passendem Datum gefunden.target_signed_amountzInterner Transfer r   u    → u    · r   r   u    · Budgeteffekt CHF 0u   Möglicher interner Transfer u    · ungeklärt)r  r  r<   r   r   explanationconfirmation_noticesummary)PAIR_STATUSESr
   r_   r`   r   r5   r6   r'   popr7   r   r:   r   r8   updater   CONFIRMATION_NOTICEr   )rH   r   whereparamsr   resultr+   rS   r   r   r<   target_namer  s                r)   list_transfer_pairsr)    s    
  T&--4RSSSS%+3!!E+19viirF<<	 
	 	 	 	   hjj! 	" F # #3	z#dhh&?&?&G4"H"HIIHH# 	 	 	HHH		:c$((3F*G*G*O4&P&PQQLL# 	 	 	LLL	GC-C(D$E$EFFGGMMhh455 ![00%\\*=qAA  A  A  AKK(^{**eKKmK(+DHH5K,L,L(M(M(,1G(H(H  ,%(6I)J)J)Oa%P%P*': #D I.C)D I I; I IJ'I I*0I I I I D>S9T  D  DZ^_iZj  D  Dms  D  D  D 	
 	
 	
" 	dMs$   -6B$$B87B8<6C33DDtuple[Any, Any, Any]c                   |                      d|f                                          }|st          dd          |d         st          dd          |                      d|d	         f                                          }|                      d|d         f                                          }|r|st          dd
          |||fS )N<SELECT * FROM budget_transfer_pairs WHERE transfer_pair_id=?  transfer pair not foundr   ri     z,transfer pair has no selected counterbookingzLSELECT * FROM budget_transaction_candidates WHERE transaction_candidate_id=?rh   z transfer pair candidates missing)r_   r   r
   )rH   rw   r   r   r   s        r)   _load_pair_and_candidatesr0  !  s   <<F	
  hjj 	  O4MNNNN%& d4bcccc\\V	#	$&  hjj  \\V	#	$&  hjj   X X4VWWWWrU   c           	     >   |d         t           vs|d         t           vrt          dd          t          |          }t          |          }|||dk    s|dk    rt          dd          t          |          t          |          k    rt          dd          |d         |d         k    rt          dd	          t	          | |          }t	          | |          }|r|r|d
         |d
         k    rt          dd          |d
         |d         k    s|d
         |d         k    rt          dd          |                     d|d         |d         |d         |d         |d         f                                          }|rt          dd          d S )Nr   r/  z+transfer pair candidates are no longer openr   r   ztransfer pair signs changedztransfer pair amounts changedr   ztransfer pair currency changedrM   z*transfer pair account relationship changedsource_account_idtarget_account_idz%transfer pair account mapping changedz
        SELECT transfer_pair_id FROM budget_transfer_pairs
        WHERE status='confirmed' AND transfer_pair_id<>?
          AND (source_candidate_id IN (?, ?) OR target_candidate_id IN (?, ?))
        LIMIT 1
        r   rh   ri   z6candidate already belongs to a confirmed transfer pair)r   r
   r>   r:   rg   r_   r   )	rH   r   r   r   source_amounttarget_amountsource_accounttarget_account	competings	            r)   _validate_confirmabler9  7  s   h666&:JRi:i:i4abbbb"6**M"6**M 5!9K9K}`aOaOa4QRRRR
=S////4STTTT!"f-@&AAA4TUUUU!$//N!$//N b b>BU3VZhi|Z}3}3}4`aaaa)*d3F.GGG>ZmKnrv  xK  sL  LL  LL4[\\\\	 #$&'&'&'&'	
  hjj   n4lmmmmn nrU   rO   c                t    | j         }|r|                     d|            n|                     d           |S )Nz
SAVEPOINT zBEGIN IMMEDIATE)in_transactionr_   rH   rO   nesteds      r)   _start_atomicr>  [  sH     F (($(())))&'''MrU   r=  c                f    |r|                      d|            d S |                                  d S )NRELEASE SAVEPOINT )r_   r  r<  s      r)   _finish_atomicrA  d  s<     0$0011111rU   c                    |r2|                      d|            |                      d|            d S |                                  d S )NzROLLBACK TO SAVEPOINT r@  )r_   rollbackr<  s      r)   _rollback_atomicrD  k  sW     4d445550$0011111rU   user)decision_bynoterF  rG  c                  |                      df                                          }|st          dd          |d         dk    rd|d         d |d         d	d
dS |d         dk    rt          dd          d}t          | |          }	 t	          |           \  }}}	|d         dk    r&t          | ||           d|d         d |d         d	d
dS |d         dk    rt          dd          t          | |||	           t                    }
t          d          }t          d          }t                      }d}|||d         |d         f||	|d         |d         ffD ]l\  }}}}|                      d|||d         |d         |||d         |d         dk    rdnd |d         dk    r|nd |d         dk    rdnd|d         ||f           m|                      d|
|||d         |d         t          t          t          t          |d                                       d           |d         |d         dk    rdnd |f	           |d!         ||d"         f|d"         ||d!         ffD ]d\  }}}|                      d#||||||f           |                      d$                                          d%         d&k    rt          dd'          e|                      d(|
||||f           |                      d$                                          d%         d&k    rt          dd)          t          fd*t          |           D                       }t!          | d+d,d-ddit#          |          ||.	  	        }t          | ||           d|
|d/d0d1dS # t          t$          f$ r t'          | ||            t(          $ r t'          | ||            w xY w)2NzkSELECT status, confirmed_transfer_id, budget_effect_chf FROM budget_transfer_pairs WHERE transfer_pair_id=?r-  r.  r   r   r   confirmed_transfer_idr   Tu#   Transferpaar war bereits bestätigt)r   r   audit_idr   
idempotentmessager   r/  z-only proposed transfer pairs can be confirmedtransfer_pair_confirmr   r   zInterner Transferr2  r  r3  r  a(  
                INSERT INTO budget_transactions(
                    budget_transaction_id, account_id, transaction_type, transaction_date,
                    booking_date, description, amount_original, currency_original,
                    fx_rate_to_chf, amount_chf, fx_status, category_id, status, source_type,
                    source_candidate_id, notes, created_at, updated_at
                ) VALUES (?, ?, 'transfer', ?, ?, ?, ?, ?, ?, ?, ?, NULL, 'confirmed',
                          'import_candidate', ?, NULL, ?, ?)
                rB   r   CHF1
not_neededmissingr   a]  
            INSERT INTO budget_transfers(
                transfer_id, from_transaction_id, to_transaction_id,
                from_account_id, to_account_id, amount_original, currency_original,
                fx_rate_to_chf, notes, created_at, transfer_type
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, 'internal_transfer')
            r   rh   ri   a  
                UPDATE budget_transaction_candidates
                SET status='confirmed', classification='transfer', linked_candidate_id=?,
                    confirmed_transaction_id=?, confirmed_at=?, confirmed_by=?, updated_at=?
                WHERE transaction_candidate_id=? AND status IN ('pending','auto_categorized','needs_review','transfer_candidate')
                SELECT changes()r   rV   z1transfer pair candidate was consumed concurrentlya  
            UPDATE budget_transfer_pairs
            SET status='confirmed', confirmed_transfer_id=?, budget_effect_chf='0',
                decided_at=?, decided_by=?, decision_note=?, updated_at=?
            WHERE transfer_pair_id=? AND status='proposed'
            &transfer pair was decided concurrentlyc              3  4   K   | ]}|d          k    |V  dS )r   NrX   )rR   rS   rw   s     r)   r\   z(confirm_transfer_pair.<locals>.<genexpr>  s4      kk$tL^G_cjGjGjGjGjGjGjkkrU   vue_dashboardtransfer_pair_confirmedr   r   r   r   r   r   r   user_text_noter   r   Fu   Transferpaar bestätigt)r_   r   r
   r>  r0  rA  r9  rz   r~   r   r   r:   r   r'   nextr)  r   r   r   rD  	Exception)rH   rw   rF  rG  r   	savepointr=  r   r   r   transfer_idsource_tx_idtarget_tx_idr   r   tx_idr   r   r<   r   counterpart_id	pair_viewrJ  s    `                     r)   confirm_transfer_pairrb  s  s7    ||u	
  hjj   O4MNNNN[((!!"9:!)*=!><
 
 	
 Z''4cdddd'I4++Fz8wGGff>[((4F333%!"9: %)*=%>"@   >Z''C8ghhhhdD&&999"7++&w99&w99EE	)64(;#<dCY>Z[64(;#<dCY>Z[5
 	 	0E9j& LL 0101$
+u44CC$":.%77FFT$($4$=$=LL989   4 	 ()()s73t,B'C#D#DEEFFLLZ J'500d
	
 	
 	
* '(,=R8ST'(,=R8ST4
 	q 	q/L% LL  	;	<X   ||.//88::1=BB#<opppp C )[$	7K	
 	
 	
 <<*++4466q9Q>>C8`aaaakkkk*=d*C*Ckkkkk	%",. *-+I66"

 

 

 	tY///!$ !$0
 
 	
 >*   y&111   y&111s   AN KN ?Oc                  d}t          | |          }	 |                     d|f                                          }|st          dd          |d         dk    rt	          | ||           d|d dd	d
S |d         dk    rt          dd          |d         dvrt          dd          t                      }|                     d|||||f           |                     d                                          d         dk    rt          dd          |d         |d         fD ]}|s|                     d|f                                          }	|	s1	 t          j        t          |	d         pd                    }
n# t          j	        $ r i }
Y nw xY wt          |
t                    si }
t          |	d                   }||dk    rdnd|
d<   |ddd|
d <   |                     d!t          j        |
d"d#          ||f           t          | d$d%d&|d|d         idd'd(||)	  	        }t	          | ||           d||d"d*d
S # t          t          f$ r t!          | ||            t"          $ r t!          | ||            w xY w)+Ntransfer_pair_rejectr,  r-  r.  r   r   r   Tz"Transferpaar war bereits abgelehnt)r   r   rJ  rK  rL  r   r/  z+confirmed transfer pairs cannot be rejected>   r   r   z2transfer pair cannot be rejected in current statusz
            UPDATE budget_transfer_pairs
            SET status='rejected', decided_at=?, decided_by=?, decision_note=?, updated_at=?
            WHERE transfer_pair_id=? AND status IN ('proposed','unmatched')
            rR  r   rV   rS  rh   ri   zhSELECT notes, signed_amount_original FROM budget_transaction_candidates WHERE transaction_candidate_id=?r/   r0   r-   r2   r4   r1   r   )rw   r   r   r   a  
                UPDATE budget_transaction_candidates
                SET status='needs_review', classification='bank_review', requires_review=1,
                    review_reason='transfer_pair_rejected', linked_candidate_id=NULL,
                    rule_id=NULL, rule_name=NULL, confidence='0.50', notes=?, updated_at=?
                WHERE transaction_candidate_id=? AND status='transfer_candidate'
                Fr   rU  transfer_pair_rejectedr   r   )r   r   rW  z0Transferpaar abgelehnt; Kandidaten bleiben offen)r>  r_   r   r
   rA  r   r5   r6   r'   r7   r   r   r*   r   r   r   rD  rZ  )rH   rw   rF  rG  r[  r=  r   r   r   r   r   signedrJ  s                r)   reject_transfer_pairrg    s    'I4++Fg||JJ
 
 (** 	  	SC8QRRRR>Z''4F333$$ "?   >[((D    >!:::K    EE	
 T9g>	
 	
 	
 <<*++4466q9Q>>?   
 &'&'
 &	 &	L   z  hjj   :c)G*<*D&E&EFF'   h-- i(@ABBF7=7IfWXjj88^gH'(")!*$ $HZ 
 LL JxetLLL     &"+. $x.1",3GG"

 

 

 	tY///   I
 
 	
 >*   y&111   y&111s8   A I
 5C$I
 )FI
 FI
 FB1I
 
?J	)r!   r"   r#   r$   )r+   r	   r#   r$   )r+   r	   r#   r?   )rH   r   r+   r	   r#   rI   )rh   r'   ri   rj   r#   r'   )rw   r'   r#   r'   )rw   r'   r{   r'   r#   r'   )r+   r	   r#   r   )rH   r   r#   r   )rH   r   r   r'   r   r'   r#   r   )
rH   r   r   r   r   r   r   r   r#   r   )r   r   r#   r   )rH   r   r   r   r   r   r   r'   r   r'   r   r   r   r   r   r   r#   r   )
rH   r   r   r   r   r   rw   r'   r#   r   )rH   r   rh   r'   r   r'   r#   r   )rH   r   r   r   r#   r   )rH   r   r   rj   r#   r   )rH   r   rw   r'   r#   r*  )
rH   r   r   r	   r   r	   r   r	   r#   r   )rH   r   rO   r'   r#   r   )rH   r   rO   r'   r=  r   r#   r   )
rH   r   rw   r'   rF  r'   rG  rj   r#   r   )3
__future__r   rq   r5   datetimer   decimalr   r   sqlite3r   r   typingr	   fastapir
   jarvis_finance.audit.logr   %jarvis_finance.services.budget_commonr   r   r!  r   rc   r   r   r$  r*   r>   rG   rg   rv   rz   r~   r   r   r   r   r   r   r   r   r  r)  r0  r9  r>  rA  rD  rb  rg  rX   rU   r)   <module>rp     sX   " " " " " "         - - - - - - - - . . . . . . . .       ! ! ! ! ! ! 7 7 7 7 7 7 B B B B B B B BPPP___ &3    (_ 
      &   3 3 3 3@L L L L
N N N NI I I I
= = = =
   	 	 	 	   .
 
 
 
J% J% J% J%Z*
 *
 *
 *
Z# # # #L IJ B B B B B BP ? ? ? ? ? ?D       ,!n !n !n !nH          U U U U U Ux p p p p p p p prU   