Ë
    6ŸSiº'  ã                   óh  — d dl 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 d dlmZ d d	lmZmZ d d
lmZ d dlmZ d dlmZ  e j6                  e«      Z ed«      Z ed«       ed«       ed«      dœZdZ dZ! ede «       G d„ de«      «       Z" ede!«       G d„ de«      «       Z#y)é    N)ÚdateÚ	timedelta)ÚDecimalÚROUND_HALF_UP)Útransaction)Útimezone)ÚQ)ÚCronTaskÚregister_task)ÚCronExecution)Ú
InvestmentÚReward)Ú
Commission)ÚTransaction)ÚUserz
0.00000001z50.00z30.00z20.00)é   é   é   ÚR001ÚD001zcalculate-rewardsc                   óL   — e Zd ZdZdedefd„Zdedefd„Zde	e
z  ez  de	fd„Zy	)
ÚCalculateRewardsTaskzÊ
    Phase 1: Calculate daily rewards.
    
    This task should be run for a specific date (e.g., Yesterday).
    It identifies active investments for that date and creates PENDING Reward records.
    r   Úreturnc                 óD   — |}|}| j                  |«      }dd|› d|› d�dœS )NFzCalculated z new rewards for Ú.©ÚerrorÚmessage)Ú_calculate_pending_rewards)Úselfr   Útarget_dateÚreward_dateÚrewards_calculateds        úL/home/cursorai/projects/telegram-earn/backend/apps/investments/cron_tasks.pyÚexecutezCalculateRewardsTask.execute%   sF   € Øˆð "ˆð "×<Ñ<¸[ÓIÐð Ø$Ð%7Ð$8Ð8IÈ+ÈÐVWÐXñ
ð 	
ó    r"   c           	      óò  — t         j                  j                  t         j                  t         j                  g||¬«      j                  «       }d}t        j                  «       5  |D ]ö  }| j                  |j                  |j                  z  t        d«      z  «      }t        j                  j                  |||ddœ¬«      \  }}|j                  }|j                  t         j                  k(  rt         j                  }||j                  j!                  «       k\  rt         j"                  }|j                  |k7  r||_        |j%                  ddg¬	«       |sŒò|d
z  }Œø 	 ddd«       |S # 1 sw Y   |S xY w)z´
        Identify eligible investments and create pending Reward records.
        
        reward_date: The date for which we're calculating rewards (typically yesterday).
        )Ú
status__inÚstart_date__date__ltÚend_date__date__gter   Ú100N)ÚamountÚdistributed_at)Ú
investmentr"   ÚdefaultsÚstatusÚ
updated_at©Úupdate_fieldsr   )r   ÚobjectsÚfilterÚSTATUS_PENDINGÚSTATUS_ACTIVEÚselect_for_updater   ÚatomicÚ	_quantizer,   Údaily_reward_rater   r   Úget_or_creater0   Úend_dater   ÚSTATUS_COMPLETEDÚsave)	r    r"   Úeligible_investmentsÚcountr.   Úreward_amountÚrewardÚcreatedÚ
new_statuss	            r$   r   z/CalculateRewardsTask._calculate_pending_rewards3   s_  € ô  *×1Ñ1×8Ñ8Ü"×1Ñ1´:×3KÑ3KÐLØ!,Ø +ð  9ó  
÷ Ñ
Ó
ð	 	ð ˆä×ÑÕ!Û2�
à $§¡Ø×%Ñ%¨
×(DÑ(DÑDÄwÈuÃ~ÑUó!�ô #)§.¡.×">Ñ">Ø)Ø +à"/Ø*.ñð #?ó #‘�˜ð (×.Ñ.�
Ø×$Ñ$¬
×(AÑ(AÒAÜ!+×!9Ñ!9�Jà *×"5Ñ"5×":Ñ":Ó"<Ò<Ü!+×!<Ñ!<�Jà×$Ñ$¨
Ò2Ø(2�JÔ%Ø—O‘O°8¸\Ð2J�OÔKâØ˜Q‘J‘EñA 3÷ "ðF ˆ÷G "ðF ˆús   Á%C4E,ÅE,Å,E6Úvaluec                 óJ   — t        |«      j                  t        t        ¬«      S ©N)Úrounding©r   ÚquantizeÚDECIMAL_QUANTIZEr   ©r    rF   s     r$   r:   zCalculateRewardsTask._quantizej   ó   € Ü�u‹~×&Ñ&Ô'7Ä-Ð&ÓPÐPr&   N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   Údictr%   Úintr   r   Úfloatr:   © r&   r$   r   r      sM   „ ñð
˜Dð 
 Tó 
ð5°dð 5¸só 5ðnQ˜w¨™°Ñ4ð Q¸ô Qr&   r   zdistribute-rewardsc            
       óž   ‡ — e Zd ZdZdefˆ fd„Zdedefd„Zdede	e
e
f   fd„Zded	ede
fd
„Zdededededdf
d„Zdeez  e
z  defd„Zˆ xZS )ÚDistributeRewardsTaskzÏ
    Phase 2: Distribute rewards.
    
    DEPENDENCY: Depends on 'calculate-rewards' completing successfully for the same date.
    
    This task distributes the rewards calculated for the given date.
    r   c           	      ó  •— t         ‰| �  «       syt        j                  j	                  t
        | j                  d¬«      j                  «       }|s8t        j                  dt
        › d| j                  › d| j                  › d�«       yy)z}
        Custom check: Ensure CalculateRewardsTask (INV001) completed successfully 
        for this execution date.
        FT)Ú	task_codeÚexecution_dateÚsuccesszDependency z not completed for z. Skipping r   )ÚsuperÚ
should_runr   r4   r5   ÚCALCULATE_TASK_CODEr[   ÚexistsÚloggerÚwarningrZ   )r    Údependency_completedÚ	__class__s     €r$   r^   z DistributeRewardsTask.should_runx   s’   ø€ ô ‰wÑ!Ô#Øô  -×4Ñ4×;Ñ;Ü)Ø×.Ñ.Øð  <ó  
÷ ‰&‹(ð	 	ñ $Ü�N‰NØÔ1Ð2Ð2EÀd×FYÑFYÐEZð [Ø ŸN™NÐ+¨1ð.ôð àr&   r   c                 óv   — |}t        j                  «       }| j                  ||«      \  }}dd|› d|› d|› d�dœS )NFzDistributed z rewards and z commissions for r   r   )r   ÚnowÚ_distribute_rewards)r    r   r!   rf   Úrewards_distributedÚcommissions_createds         r$   r%   zDistributeRewardsTask.execute‘   s]   € ØˆÜ�l‰l‹nˆð 48×3KÑ3KÈKÐY\Ó3]Ñ0ÐÐ0ð Ø%Ð&9Ð%:¸-ÐH[ÐG\Ð\mÐnyÐmzÐz{Ð|ñ
ð 	
r&   r!   c           
      óì  — t         j                  j                  d|¬«      j                  dd«      j	                  «       }d}d}t        j                  «       5  |D �]{  }| j                  |j                  j                  |j                  t        j                  |j                  j                  |j                  dœ¬«       t        j                  j	                  «       j                  |j                  j                  ¬«      }| j!                  |j"                  |j                  z   «      |_        |j                  dkD  r9| j!                  |j"                  |j                  z  t%        d	«      z  «      |_        |j)                  g d
¢¬«       ||_        |j)                  dg¬«       |dz  }| j-                  ||j                  j                  |«      }||z  }�Œ~ 	 d d d «       ||fS # 1 sw Y   ||fS xY w)NT)Údistributed_at__isnullr"   r.   Úinvestment__userr   )Úinvestment_idÚ	reward_id©Úuserr,   Útx_typeÚmetadata©Úpkr+   )Úroi_total_amountÚroi_total_percentr1   r2   r-   r   )r   r4   r5   Úselect_relatedr8   r   r9   Ú_credit_userr.   rp   r,   r   ÚTYPE_REWARDÚidr   Úgetr:   ru   r   rv   r?   r-   Ú_create_commissions)	r    r!   rf   Úpending_rewardsÚreward_countÚcommission_countrC   r.   Úc_counts	            r$   rg   z)DistributeRewardsTask._distribute_rewardsž   sÊ  € ô !Ÿ.™.×/Ñ/Ø#'Ø#ð 0ó 
÷ ‰.˜Ð'9Ó
:×;LÑ;LÓ;Nð 	ð
 ˆØÐä×ÑÕ!Ü)�à×!Ñ!Ø×*Ñ*×/Ñ/Ø!Ÿ=™=Ü'×3Ñ3Ø/5×/@Ñ/@×/CÑ/CÐRX×R[ÑR[Ñ\ð	 "ô ô (×/Ñ/×AÑAÓC×GÑGÈ6×K\ÑK\×K_ÑK_ÐGÓ`�
Ø.2¯n©n¸Z×=XÑ=XÐ[a×[hÑ[hÑ=hÓ.i�
Ô+Ø×$Ñ$ qÒ(Ø37·>±>Ø#×4Ñ4°z×7HÑ7HÑHÌGÐTYËNÑZó4�JÔ0ð —‘Ò.e�Ôfð ),�Ô%Ø—‘Ð+;Ð*<�Ô=Ø Ñ!�ð ×2Ñ2°6¸6×;LÑ;L×;QÑ;QÐSVÓW�Ø  GÑ+Ò ñ3 *÷ "ð8 Ð-Ð-Ð-÷9 "ð8 Ð-Ð-Ð-ús   ÁFG'Ç'G3rC   Údownline_userc                 óT  — d}g }|j                   }|r<t        |«      dk  r.|j                  |«       |j                   }|rt        |«      dk  rŒ.t        |«      D ]Ë  \  }}|dz   }	t        j                  |	«      }
|
s |S | j                  |j                  |
z  t        d«      z  «      }t        j                  j                  ||	|||
|t        j                  |dœ¬«      \  }}|sŒŒ|dz  }| j                  ||t        j                  |j                   |j                   |	dœ¬«       ŒÍ |S )	Nr   r   r   r+   )r�   Úcommission_rater,   r0   r-   )rC   ÚlevelÚupline_userr/   )rn   Údownline_user_idr„   ro   )Úreferred_byÚlenÚappendÚ	enumerateÚCOMMISSION_RATESr{   r:   r,   r   r   r4   r<   ÚSTATUS_DISTRIBUTEDrx   r   ÚTYPE_COMMISSIONrz   )r    rC   r�   rf   r   ÚuplinesÚcurrent_userÚir…   r„   Úrater,   Ú
commissionrD   s                 r$   r|   z)DistributeRewardsTask._create_commissionsÈ   sD  € ØÐð !ˆØ$×0Ñ0ˆÙœs 7›|¨aÒ/Ø�N‰N˜<Ô(Ø'×3Ñ3ˆLñ œs 7›|¨aÓ/ô (¨Ö0‰NˆAˆ{Ø˜‘EˆEÜ#×'Ñ'¨Ó.ˆDÙØð<  Ðð9 —^‘^ F§M¡M°DÑ$8¼7À5»>Ñ$IÓJˆFä",×"4Ñ"4×"BÑ"BØØØ'à%2Ø'+Ø$Ü(×;Ñ;Ø&)ñð	 #Có #ÑˆJ˜ò Ø  AÑ%Ð Ø×!Ñ!Ø$Ø!Ü'×7Ñ7à%+§Y¡YØ,9×,<Ñ,<Ø!&ñð	 "õ 	ð/ 1ðD  Ðr&   rp   r,   rq   rr   Nc           	      ó`  — t         j                  j                  «       j                  |j                  ¬«      }|j
                  }| j                  ||z   «      |_        |j                  dg¬«       t        j                  j                  |||t        j                  ||j
                  |¬«       y)z;Credit a user's balance and log the transaction atomically.rs   Úcredit_balancer2   )rp   Útransaction_typer,   r0   Úbalance_beforeÚbalance_afterÚdataN)r   r4   r8   r{   rt   r”   r:   r?   r   Úcreater>   )r    rp   r,   rq   rr   Úuser_lockedr–   s          r$   rx   z"DistributeRewardsTask._credit_userö   s˜   € ô —l‘l×4Ñ4Ó6×:Ñ:¸d¿g¹gÐ:ÓFˆØ$×3Ñ3ˆØ%)§^¡^°NÀVÑ4KÓ%LˆÔ"Ø×ÑÐ(8Ð'9ÐÔ:ä×Ñ×"Ñ"ØØ$ØÜ×/Ñ/Ø)Ø%×4Ñ4Øð 	#õ 	
r&   rF   c                 óJ   — t        |«      j                  t        t        ¬«      S rH   rJ   rM   s     r$   r:   zDistributeRewardsTask._quantize	  rN   r&   )rO   rP   rQ   rR   Úboolr^   r   rS   r%   ÚtuplerT   rg   r   r   r|   r   Ústrrx   rU   r:   Ú__classcell__)rd   s   @r$   rX   rX   n   sª   ø„ ñð˜Dõ ð2
˜Dð 
 Tó 
ð(.¨tð (.¸UÀ3ÈÀ8¹_ó (.ðT, ¨&ð , Àð , Èsó , ð\
Øð
Ø")ð
Ø47ð
ØCGð
à	ó
ð&Q˜w¨™°Ñ4ð Q¸÷ Qr&   rX   )$ÚloggingÚdatetimer   r   Údecimalr   r   Ú	django.dbr   Údjango.utilsr   Údjango.db.modelsr	   Údjango_cronjob_utilsr
   r   Údjango_cronjob_utils.modelsr   Úapps.investments.modelsr   r   Úapps.referrals.modelsr   Úapps.transactions.modelsr   Úapps.users.modelsr   Ú	getLoggerrO   ra   rL   r‹   r_   ÚDISTRIBUTE_TASK_CODEr   rX   rV   r&   r$   Ú<module>r®      sÇ   ðÛ ß $ß *å !Ý !Ý ç 8Ý 5ß 6Ý ,Ý 0Ý "à	ˆ×	Ñ	˜8Ó	$€á˜<Ó(Ð áˆwÓÙˆwÓÙˆwÓñÐ ð Ð ØÐ áÐ"Ð$7Ó8ôNQ˜8ó NQó 9ðNQñb Ð#Ð%9Ó:ô[Q˜Hó [Qó ;ñ[Qr&   