
    y{j[K                     <   d Z ddlmZ ddlmZ ddlmZ ddlmZ  G d dej                        Z
 G d d	ej                        Zdd
lZ G d dej                        Z G d dej                        Z G d dej                        Z G d dej                        Zy
)u  
Accounting models for the Kalingar Trust Portal.

Design decisions:
- StaffProfile: Lightweight model for non-member staff (e.g. Accountants).
  Keeps the Member table clean — only actual temple members live there.
- AccountTransaction uses SOFT DELETE (is_deleted flag). Financial records are
  NEVER hard-deleted so the audit trail is preserved and receipt numbers are
  never reused, even after soft-deletion.
- change_log (JSONField) stores an append-only list of edit history entries so
  previous values are never silently overwritten.
- No approval workflow — transactions count toward totals immediately.
  An optional approval step (is_approved, approved_by) could be added later
  without schema breakage.
    )models)User)ValidationError)timezonec                      e Zd ZdZdgZ ej                  eej                  d      Z	 ej                  dd      Z ej                  ddd	d
      Z ej                  ddddd      Z ej                  ded      Z ej                  dd      Z ej"                  d      Z ej"                  d      Z G d d      Zd Zy)StaffProfileu  
    A non-member staff user such as an Accountant.

    Separate from Member so the membership table is not polluted with
    non-member records.  Admin users (is_staff=True) do NOT need a
    StaffProfile — they are identified by the Django User flag.
    )
ACCOUNTANT
Accountantstaff_profile	on_deleterelated_name   z	Full Name
max_lengthverbose_nameT zFull Name (Tamil)r   blankdefaultr      zPhone Number)r   uniquenullr   r      r	   )r   choicesr   Activer   r   auto_now_addauto_nowc                       e Zd ZdZdZy)StaffProfile.MetazStaff ProfilezStaff ProfilesN)__name__
__module____qualname__r   verbose_name_plural     ;/var/www/kalinger_portal/trust-backend/accounting/models.pyMetar#   6   s    &.r)   r+   c                 B    | j                    d| j                          dS Nz ())nameget_role_displayselfs    r*   __str__zStaffProfile.__str__:   s#    ))Bt4467q99r)   N)r$   r%   r&   __doc__ROLE_CHOICESr   OneToOneFieldr   CASCADEuser	CharFieldr/   name_taphoneroleBooleanField	is_activeDateTimeField
created_at
updated_atr+   r3   r(   r)   r*   r   r      s     	%L  6_D 6sEDf#T2TghGF4d$]klE6|\D $##DxHI%%%48J%%%t4J/ /:r)   r   c                      e Zd ZdZddgZ ej                  dd      Z ej                  dddd	
      Z ej                  dd      Z
 ej                  ddd      Z ej                  deddd      Zg dZ ej                  dedd      Z ej                  d      Z ej"                  eej&                  ddd      Z ej*                  d      Z ej*                  d      Z G d d      Zd Zy)AccountHeadz
    A category (aka "account head") under which income/expenses are recorded.

    Examples: "Kovil Kodai 2026", "Monthly Maintenance", "General Fund".
    )KodairD   )GeneralrE   i,  zAccount Head Namer   Tr   zAccount Head Name (Tamil)r   r   r   zDescription (Tamil)r   r   r   r   Typer   r   r   r   r   ))AssetrJ   )	LiabilityrK   )EquityrL   )RevenuerM   )ExpenserN   rM   Account Typer   r   r   r   r   created_account_headsr   r   r   r   r   r    c                       e Zd ZdgZdZdZy)AccountHead.Meta-created_atzAccount HeadzAccount HeadsNr$   r%   r&   orderingr   r'   r(   r)   r*   r+   rU   i   s    !?%-r)   r+   c                     | j                   S N)r/   r1   s    r*   r3   zAccountHead.__str__n   s    yyr)   N)r$   r%   r&   r4   TYPE_CHOICESr   r9   r/   r:   	TextFielddescriptiondescription_ta	head_typeACCOUNT_TYPE_CHOICESaccount_typer=   r>   
ForeignKeyr   SET_NULL
created_byr?   r@   rA   r+   r3   r(   r)   r*   rC   rC   B   s!    	L
 6s9LMDf#T2TopG"&""r:K%V%%D"K`aN   |4I $6##3Y#L $##D1I"""d$,J &%%48J%%%t4J. .
r)   rC   Nc                      e Zd Zg dZg dZddgZ ej                  dej                  d      Z
 ej                  dd	
      Z ej                  ded      Z ej                  ded      Z ej                  dej                   dddd      Z ej                  dej                   dddd      Z ej                  dddd      Z ej                  dddd      Z ej                  dddd      Z ej                  dedd      Z ej.                  dd      Z ej.                  ddd !      Z ej                  eej                   ddd"d#      Z G d$ d%      Zd& Zd' Zy())TrustAccount)Cashrh   )Bankri   Commoditiesrk   ))Trust_DirectzTrust Direct)Memberrm   )Member_FamilyzMember Family)r   r   )Inactivero   TF)primary_keyr   editable   zAccount Namer   r   rO   )r   r   r   zAssociated Entity Typemembers.Membertrust_accountszCustodian Memberr   r   r   r   r   custodian_trust_accountszCustodian Family Memberd   r   zAccount Numberr   z	Bank NamezBranch Namer   StatusrP   zCreated Date)r   r   zDeactivated Date)r   r   r   created_trust_accounts
Created Byc                       e Zd ZdgZdZdZy)TrustAccount.Metaaccount_nameTrust AccountzTrust AccountsNrW   r(   r)   r*   r+   r|      s    "#&.r)   r+   c                 :    | j                    d| j                   dS r-   )r}   ra   r1   s    r*   r3   zTrustAccount.__str__   s#    ##$Bt'8'8&9;;r)   c                     | j                   dk(  r0| j                  r| j                  r| j                  st	        d      y y )Nri   zcBank fields (account number, bank name, branch name) are strictly mandatory for Bank account types.)ra   account_number	bank_namebranch_namer   r1   s    r*   cleanzTrustAccount.clean   sH    &&&dnnDDTDT%  'L  M  M EU 'r)   N)r$   r%   r&   r`   ENTITY_TYPE_CHOICESSTATUS_CHOICESr   	UUIDFielduuiduuid4
account_idr9   r}   ra   associated_entity_typerb   rc   memberfamily_memberr   r   r   statusr?   created_datedeactivated_dater   rd   r+   r3   r   r(   r)   r*   rf   rf   x   s   
 	 N
 "!!dDJJQVWJ#6##sPL#6##r;O^lmL-V--EXg  AVFOO$d%4FF &F%%FOO$d/>WM
 &V%%D"[klN   CtRVabI"&""crXefKV^XdlmF'6''TWL+v++TPbc"""d$-LJ
/ /
<Mr)   rf   c                       e Zd ZdZddgZg dZ ej                  eej                  d      Z
 ej                  dej                  ddd	d
      Z ej                  eej                  dddd      Z ej                  de      Z ej"                  dd      Z ej&                         Z ej                  de      Z ej                  dg dddd      Z ej                  ddd      Z ej                  dddd      Z ej                  ddd      Z ej                  dej                  dddd      Z ej6                  ddd !      Z ej6                  ddd"!      Z ej                  ddd      Z ej                  dddd#      Z ej6                  dd$      Z  ej6                  ddd%!      Z! ej                  ddd      Z" ejF                  d&dd'      Z$ ej                  e%ej                  d(      Z& ejN                  d)*      Z( ej                  d+ejR                  ddd,      Z* ejV                  e,d-      Z- ej\                  d.      Z/ ej\                  d/      Z0 G d0 d1      Z1d2 Z2d3 Z3 fd4Z4 xZ5S )5AccountTransactionu   
    A single financial transaction — either Debit or Credit.

    See module docstring for soft-delete and audit-log rationale.
    )DEBITDebit)CREDITCredit)rg   )Bank Transferr   )UPIr   )Chequer   )r   r   rj   )Journal Entryr   transactionsr   zmembers.TaxMasterTaccounting_transactionsz2Link to the tax event this transaction relates to.)r   r   r   r   	help_textr~   ru   
   r   r         )
max_digitsdecimal_placesr   ))Goldr   )Silverr   )Otherr   r   zCommodity TyperI   r   )r   r   r   zDonor Name (Tamil)r   2   rs   donation_transactionsz3Link to registered member if the donor is a member.zPurpose / RemarksrG   zPurpose / Remarks (Tamil)zPaid To (Tamil)rF   zPurpose/Description (Tamil)ztransaction_proofs/	upload_tor   r   entered_transactionsFrQ   ReceiptrS   )r   r   r   r    c                       e Zd ZddgZdZdZy)AccountTransaction.Metaz-transaction_daterV   zAccount TransactionzAccount TransactionsNrW   r(   r)   r*   r+   r     s    '7,4r)   r+   c                 n    | j                          d| j                   d| j                  j                   S )Nu    ₹u    — )get_transaction_type_displayamountaccount_headr/   r1   s    r*   r3   zAccountTransaction.__str__  s=    00234} E$$))*,	
r)   c                    i }| j                   | j                   dk  rd|d<   | j                  r4| j                  t        j                         j	                         kD  rd|d<   | j
                  sH| j                  r<	 t        j                  j                  | j                        }|j                  sd|d	<   |rt        |      y# t        j                  $ r d
|d	<   Y (w xY w)zModel-level validation.Nr   z!Amount must be greater than zero.r   z)Transaction date cannot be in the future.transaction_datepkz=Cannot create a transaction under a deactivated Account Head.r   zAccount Head does not exist.)r   r   r   nowdater   account_head_idrC   objectsgetr>   DoesNotExistr   )r2   errorsheads      r*   r   zAccountTransaction.clean%  s    ;;"t{{a'7BF8  T%:%:X\\^=P=P=R%R)TF%&ww4//H"**..$2F2F.G~~( >* !&))  ++ H)G~&Hs   <;C C C c                    | j                          | j                  d u }d }d }|sC	 t        j                  j	                  | j                        }|j
                  }|j                  }t        | $  |i | |r-|| j
                  k7  s|| j                  k7  rddl
m}  |||       | j
                  r$ddl
m}  || j
                  | j                         y y # t        j                  $ r Y w xY w)Nr      )recalculate_member_financials)
full_cleanr   r   r   r   r   r   r   supersaveutilsr   )	r2   argskwargsis_neworig_member	orig_dateorigr   	__class__s	           r*   r   zAccountTransaction.save:  s    D	)11555A"kk 11	 	d%f%K4;;6)tG\G\:\<)+yA;;<)$++t7L7LM  &22 s   AC C.-C.)6r$   r%   r&   r4   TRANSACTION_TYPE_CHOICESPAYMENT_MODE_CHOICESr   rb   rC   PROTECTr   rc   	tax_eventrf   trust_accountr9   transaction_typeDecimalFieldr   	DateFieldr   payment_modecommodity_type
donor_namedonor_name_tadonor_contactr   r\   purpose
purpose_tapaid_to
paid_to_tapurpose_descriptionpurpose_description_tabill_reference	FileFieldproof_documentr   
entered_byr=   
is_deletedr7   receipt_number	JSONFieldlist
change_logr?   r@   rA   r+   r3   r   r   __classcell__r   s   @r*   r   r      s    	 
 %6$$v~~#L "!!v,EFI
 &F%%N$M
 (v''7 !V  BqAF'v'')#6##3L &V%%[B-=N "!!SbIJ$F$$4ZnoM$F$$$KMVFOO,CGF
 fB(G "!!B0J f#T2FG!!!SbWhiJ*&**rB-V--D"Spq%V%%D"MN &V%%'t$N
 #""+J %$$U3J&V&&V^^#N "!!$d;J%%%48J%%%t4J5 5

**N Nr)   r   c                       e Zd ZdZ ej
                  ddd      ZddgZ ej
                  ded	d
      Z ej                  dej                  dddd      Z ej
                  dddd      Z ej
                  dddd      Z ej                  ddd      Z ej                   ej$                  d      Z ej                  eej*                  dd      Z ej
                  dej0                  dd      Z ej4                  d       Z ej8                  d!dd"      Z G d# d$      Zd% Z fd&Z e!d)d'       Z"e#d*d(       Z$ xZ%S )+r   aa  
    Receipt generated for a member's payment, broken down across multiple AccountTransactions.

    receipt_number is sequential, FISCAL-year scoped, and NEVER reused.
    Format: <YYYY-YY>/<NNNNN>   e.g. 2026-27/00001

    Fiscal year runs April-March:
      1-Apr-2026 to 31-Mar-2027  ->  "2026-27"
      1-Apr-2025 to 31-Mar-2026  ->  "2025-26"
       TF)r   r   rq   )Taxu   கொடை வரி (Tax))Seethanau&   சீதன வரி (Seethana Vari)r   r   zReceipt TyperP   rs   receiptszMember / Family Headru   r   r   z
Donor Namer   zFather Namer   r   zTotal Amount)r   r   r   zReceipt Dater   created_receiptsrz   )r   r   r   rh   zPayment Moder   z	receipts/r   c                       e Zd ZdgZdZdZy)Receipt.Metaz-generated_atr   ReceiptsNrW   r(   r)   r*   r+   r     s    #$ (r)   r+   c                     | j                   S rZ   )r   r1   s    r*   r3   zReceipt.__str__  s    """r)   c                 |    | j                   s | j                  | j                        | _         t        |   |i | y rZ   )r   generate_receipt_numberreceipt_typer   r   )r2   r   r   r   s      r*   r   zReceipt.save  s6    """&">">t?P?P"QDd%f%r)   c                     | t        j                         } | j                  dk\  r| j                  n| j                  dz
  }t	        |dz         dd }| d| S )z
        Return the fiscal-year label for *dt* (defaults to now).
        April-March cycle:  1-Apr-2026 to 31-Mar-2027  ->  "2026-27"
        N   r   -)r   r   monthyearstr)dtstart	end_shorts      r*   _fiscal_year_stringzReceipt._fiscal_year_string  sV     :B88q=bggk	N23'	)%%r)   c                 
   ddl m} | j                         }|dk(  r'|j                  d      }|d   dd  d|d    }| d}n| d	}|j	                         5  | j
                  j                  d
      j                  |      j                  d      j                         }|r,	 t        |j                  j                  d	      d         dz   }nd}| |dcddd       S # t        t        f$ r d}Y #w xY w# 1 sw Y   yxY w)a  
        Atomically generate the next sequential receipt number for the
        current fiscal year (April-March).

        Uses SELECT ... FOR UPDATE on the last receipt row so that two
        concurrent transaction saves cannot read the same sequence value
        and produce a duplicate.  The unique=True DB constraint remains as
        a last-resort safety net.

        Format: <YYYY-YY>/<NNNNN>
        Seethana Format: <YY-YY>-S/<NNNNN>
        r   )transactionr   r   r   Nr   z-S//F)nowait)receipt_number__startswithz-receipt_number05d)	django.dbr  r  splitatomicr   select_for_updatefilterorder_byfirstintr   
ValueError
IndexError)	clsr   db_transactionfiscal_yearpartsyy_yyprefixlastseqs	            r*   r   zReceipt.generate_receipt_number  s!    	<--/:%%%c*EQx}oQuQxj1Ewc]F#}A&F""$ 	( ""%"06:+,  d1177<R@AAEC Xc#Y'#	( 	( #J/ C	( 	(s1   AC9%*C"	C9"C63C95C66C99DrZ   )r   )&r$   r%   r&   r4   r   r9   r   RECEIPT_TYPE_CHOICESr   rb   rc   r   r   father_namer   total_amountr   r   r   receipt_dater   r   rd   r   r   r   r?   generated_atr   pdf_filer+   r3   r   staticmethodr  classmethodr   r   r   s   @r*   r   r   X  s   	 &V%%dUN 	0> $6##3U#L VFOO$d+F
 "!!SbWcdJ"&""crXefK&6&&a#L $6###L #""'!J
 $6##"77#	L (6''T:LvTH) )
#&
 
& 
& )( )(r)   r   c                   4   e Zd ZdZg dZ ej                  de      Z ej                  d      Z ej                  d      Z	 ej                  dd      Z ej                  eej                  dd	      Z ej                   d
      Z G d d      Zd Zy)AuditLogzq
    Centralized log for all critical operations (Create, Update, Delete)
    particularly for transactions.
    ))CREATECreate)UPDATEUpdate)DELETEDeleter   r   rw   )r   T)r   r   )r   r   r   r   c                       e Zd ZdgZdZdZy)AuditLog.Metaz
-timestampz	Audit Logz
Audit LogsNrW   r(   r)   r*   r+   r-    s     >"*r)   r+   c                 l    | j                    d| j                   d| j                   d| j                   S )N z #z at )action
model_name	record_id	timestampr1   s    r*   r3   zAuditLog.__str__  s2    ++a04>>2B$t~~FVWWr)   N)r$   r%   r&   r4   ACTION_CHOICESr   r9   r0  r1  r2  r   changesrb   r   rc   performed_byr?   r3  r+   r3   r(   r)   r*   r%  r%    s    N
 V^DF!!!S1J   C0IfT5G$6$$TV__4W[\L$$$$7I+ +
Xr)   r%  )r4   r
  r   django.contrib.auth.modelsr   django.core.exceptionsr   django.utilsr   Modelr   rC   r   rf   r   r   r%  r(   r)   r*   <module>r;     s      + 2 ! :6<<  :N-&,, -h 5M6<< 5Mx]N ]NHz(fll z(@Xv|| Xr)   