o
    úðÅjç2 ã                   @   sH  d dl mZ d dlmZ ddlmZmZmZmZm	Z	m
Z
mZmZmZmZmZ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 d d
lmZ d dlmZ  d dlmZ d dl!m"Z"m!Z!m#Z# d dl$Z$d dl%Z%d dl&Z&d dl'Z(d dl)Z)d dl*Z*d dl+Z+d dl*Z*d dlmZ d dl,m-Z- d dl.Z.d dl/m0Z0m1Z1m2Z2 d dl3m4Z4 d dl5m6Z6m7Z7 ddl8m9Z9 d dl:m;Z; d dl!m!Z!m<Z< d dl=Z=d dl>m?Z? d dl@mAZA d dlBZBd dlmZ d dlCZCd dl mZ d dlDZDd dl>mEZE d dlFmGZG d dl3mHZH d d
lmZ d dlmZmZ d dlImJZJ d dlKmLZL d dlMmNZN d dlmZ d dlmZ d dlCZCG dd„ deNƒZOG dd „ d e6ƒZPd!d"„ ZQd#d$„ ZRd d%lmSZS eSƒ ZG d&d'„ d'eNƒZTG d(d)„ d)eNƒZUd d*l!m#Z# d dlmZ G d+d,„ d,eNƒZVG d-d.„ d.eNƒZWG d/d0„ d0eNƒZXG d1d2„ d2eNƒZYG d3d4„ d4eNƒZZG d5d6„ d6eNƒZ[G d7d8„ d8eNƒZ\G d9d:„ d:eNƒZ]G d;d<„ d<eNƒZ^G d=d>„ d>eNƒZ_d dl!m!Z!m<Z< d dl`Z`d dl`Z`d d?l!m!Z!m<Z<m#Z#m"Za d dlKmLZL d dlMmNZN d dlImJZJ dd@lmZm	Z	mZmZ d dAl!m"Z" d dlmZ d dlDZDd dl<Zbg g d dBœaceD d¡ ZedCZf�d#dEdF„ZgdGdH„ ZhdIdJ„ ZiG dKdL„ dLeNƒZjG dMdN„ dNeNƒZkG dOdP„ dPeNƒZld dl!m!Z!m<Zm d dQlnmoZompZp d dlKmLZL d dlMmNZN d dlImJZJ ddRlmZm	Z	 g dS¢ZqdTdU„ ZrdVdW„ ZsdXdY„ ZtdZd[„ Zud dl`Z`d d?l!m!Z!m<Z<m#Z#m"Za d dlKmLZL d dlMmNZN d dlImJZJ dd@lmZm	Z	mZmZ g d\¢Zvd]d^„ Zwd_d`„ Zxdadb„ Zydcdd„ Zzdedf„ Z{dgdh„ Z|diZ}djdk„ Z~G dldm„ dmeNƒZG dndo„ doeNƒZ€G dpdq„ dqeNƒZ�d drl!m<Zm emdsdtƒZ‚�d$dudv„ZƒG dwdx„ dxeNƒZ„G dydz„ dzeNƒZ…G d{d|„ d|eNƒZ†G d}d~„ d~eNƒZ‡G dd€„ d€eNƒZˆG d�d‚„ d‚eNƒZ‰G dƒd„„ d„eNƒZŠdd…lm‹Z‹ d†d‡„ ZŒG dˆd‰„ d‰eNƒZ�G dŠd‹„ d‹eNƒZŽG dŒd�„ d�eNƒZ�G dŽd�„ d�eNƒZ�G d�d‘„ d‘eNƒZ‘	 d dlCZCd d’l!m!Z!m"Z" d dQlnmoZompZp d d“l’m“Z“ d d”l3mHZHm”Z”m4Z4 d dlmZ d dlKmLZL d dlMmNZN d dlImJZJ dd•lmZm•Z•m–Z–m—Z— d–Z˜d—d˜„ Z™d™dš„ Zšd›dœ„ Z›d�dž„ ZœdŸd „ Z��d#d¡d¢„ZžG d£d¤„ d¤eNƒZŸG d¥d¦„ d¦eNƒZ G d§d¨„ d¨eNƒZ¡G d©dª„ dªeNƒZ¢d«d¬„ Z£G d­d®„ d®eNƒZ¤G d¯d°„ d°eNƒZ¥G d±d²„ d²eNƒZ¦d dl3mHZH d dl§Z§e§ ¨e©¡Zªd³Z«d´dµ„ Z¬G d¶d·„ d·eNƒZ­G d¸d¹„ d¹eNƒZ®G dºd»„ d»eNƒZ¯G d¼d½„ d½eNƒZ°G d¾d¿„ d¿eNƒZ±G dÀdÁ„ dÁeNƒZ²G dÂdÃ„ dÃeNƒZ³G dÄdÅ„ dÅeNƒZ´G dÆdÇ„ dÇeNƒZµd dl`Z`d d?l!m!Z!m<Z<m#Z#m"Za d dlKmLZL d dlMmNZN d dlImJZJ dd@lmZm	Z	mZmZ g d\¢ZvdÈd^„ ZwdÉd`„ ZxdÊdh„ Z|dËdÌ„ Z¶G dÍdÎ„ dÎeNƒZ·G dÏdÐ„ dÐeNƒZ¸G dÑdÒ„ dÒeNƒZ¹d dl+Z+d dl&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 dÕl’m½Z½m“Z“ d dÖl3m”Z”m¾Z¾m¿Z¿m4Z4 d d×l,mÀZÀm-Z- d dlmZ d dlKmLZL d dØlÁmÂZÂmÃZÃ d dl:m;Z; d dlImJZJ d dlMmNZN d dÙlÄmÅZÅ d dlmZ ddÚlmÆZÆmÇZÇmÈZÈmÉZÉmÊZÊmËZËmZmÌZÌmÍZÍ dZÎdÛZÏdÜZÐdÝZÑdÞZÒdßZÓdàZÔdádâ„ ZÕdãdä„ ZÖddeLj×fdådæ„ZØeLjÙdfdçdè„ZÚdédê„ ZÛ�d#dëdì„ZÜdídî„ ZÝdïdð„ ZÞdñdò„ Zßdódô„ Zàdõdö„ Zád÷dø„ Zâdùdú„ ZãG dûdü„ düeNƒZäG dýdþ„ dþeNƒZådÿ�d „ ZæG �d�d„ �deNƒZç�dZè�d�d„ Zé�d�d„ Zê�d#�d�d	„Zë�d
�d„ Zì�d�d„ ZíG �d�d„ �deNƒZîG �d�d„ �deNƒZï�d�d„ ZðG �d�d„ �deNƒZñG �d�d„ �deNƒZòg �d¢Zó�d�d„ ZôG �d�d„ �deNƒZõ�d�d„ Zö�d�d „ Z÷�d!�d"„ ZødS (%  é    )Úsettings)Úrenderé   )ÚProfilesÚOtpÚProductÚSettingÚHolidayÚAnnouncementÚLeaveRequestÚPayslipÚEmployeeÚ
AttendanceÚ
DepartmentÚDesignation)ÚObjectDoesNotExist)ÚNotFound)ÚauthenticateÚlogin)ÚRefreshToken)Úmake_password)ÚUser)Útimezone)ÚdateÚdatetimeÚ	timedeltaN)ÚHttpResponse)ÚImageÚImageEnhanceÚImageFilter)ÚQ)ÚRetrieveAPIViewÚListAPIView)ÚUserSerializer)ÚIsAuthenticated)r   Útime)ÚEmailMessage)ÚBASE_DIR)Ú	send_mail)Úget_random_string)ÚSum©ÚResponse©Ústatus)ÚAPIViewc                   @   ó   e Zd Zdd„ ZdS )ÚLoginAPIViewc                 C   s&  |j }| d¡}| d¡}|r|s!ttjddœddd dœtjd�S t d	|¡rEztjj|d
�}t	||j
|d�}W n tjyD   d }Y nw t	|||d�}|d u r`ttjddœddd dœtjd�S t||ƒ t |¡}z
tjj|jd�}W n ty   d }Y nw |d ur™|jdkr™ttjddœddd dœtjd�S d}	d}
d}d }d }d}|d urî|j}|j}|j}|jr¹|jj}	|jdkräztjj|jd�}tjj|jd�}|jrÙ|jj}
|j}W n tyã   Y nw |jrî|jj}
|j}ttjddœd d|j
|j|j|j|j t!|ƒt!|j"ƒ|	|
||||dœdœƒS )NÚusernameÚpasswordÚerror©ÚcodeÚresponsezMissing credentialsz)Username/email and password are required.©r.   r4   ÚmessageÚdatar-   z[^@]+@[^@]+\.[^@]+©Úemail©r2   r3   zAuthentication failedzInvalid username or password©Úuser_idé   zInactive userú/Your account is inactive. Please contact Admin.ú../static/dummy.pngÚloopÚsuccesszLogin success)ÚnameÚidr2   r<   ÚisSuperÚrefreshÚtokenÚprofileÚcompanylogoÚcompanynameÚphoneÚroleÚ	geostatus)#r:   Úgetr,   r.   ÚHTTP_400_BAD_REQUESTÚreÚmatchr   Úobjectsr   r2   ÚDoesNotExistZHTTP_401_UNAUTHORIZEDr   r   Zfor_userr   rF   r   ÚHTTP_403_FORBIDDENrM   rN   rO   ÚprofilesÚurlr?   Úcreated_by_idrK   ÚservicenameÚHTTP_200_OKÚ
first_namer<   Úis_superuserÚstrZaccess_token)ÚselfÚrequestr:   Zusername_or_emailr3   Zuser_objÚuserrI   rJ   ÚproÚcomrL   rM   rN   rO   Zcreated_companyZcompany_creator_profile© rd   ú4C:\Users\psant\Downloads\Manju\school\myapp\views.pyÚpost”   s´   


üûÿ
üû

ÿ
üû
€ÿ
óüzLoginAPIView.postN©Ú__name__Ú
__module__Ú__qualname__rf   rd   rd   rd   re   r1   ’   ó    r1   c                   @   s&   e Zd ZefZeZdd„ Zdd„ ZdS )ÚUserAPIViewc                 C   s   | j jS ©N)r`   ra   )r_   rd   rd   re   Ú
get_objectþ   ó   zUserAPIView.get_objectc                 C   sT   |j }tjj|d d�}tjj|d�}ttjddœd|j|j	|j
j|jjdœdœƒS )	Nr<   r;   ©ra   rD   r5   T)r2   r<   rN   rJ   ©r.   r4   r:   )r:   r   rT   rP   r   r,   r.   r[   r2   r<   rN   rF   rW   rX   )r_   r`   r:   ra   rJ   rd   rd   re   rf     s   
üýzUserAPIView.postN)	rh   ri   rj   r$   Úpermission_classesr#   Zserializer_classrn   rf   rd   rd   rd   re   rl   ù   s
    rl   c              
   C   ób   d}d|› d�}zt ||tj| gdd� W d S  ty0 } ztd|› �ƒ W Y d }~d S d }~ww )Nú%Your One-Time  (OTP) for Verificationú?Dear User,

    Your One-Time  (OTP) for verification is:

    zâ

    Please enter this code to proceed. This OTP is valid for the next 1 minutes.

    If you did not request this code, please ignore this email.

    Thank you,  
    Pearlcon Technologies LLP
    https://pearlcons.com
    F©Úfail_silentlyúError sending email: ©r(   r   ÚDEFAULT_FROM_EMAILÚ	ExceptionÚprint©r<   ÚotpÚsubjectr9   Úerd   rd   re   Úsend_otp_email  s    üû€ÿr�   c              
   C   rs   )Nrt   ru   zæ

    Please enter this code to proceed. This OTP is valid for the next 1 minutes.

    If you did not request this code, please ignore this email.

    Thank you,  
    Pearlcon Technologies LLP
    https://pearlcons.com
        Frv   rx   ry   r}   rd   rd   re   Úsend_otp_emails/  s    üû€ÿr‚   )Úget_user_modelc                   @   r0   )ÚSendotpAPIViewc              
   C   s  |j }zX| di ¡ dd ¡}td|ƒ tjj|d� ¡ s)ttj	ddœddd	œƒW S t
jj|d�}td
dd�}||_t ¡ |_| ¡  tjt||fd� ¡  ttjddœd d|idœƒW S  ty€ } ztd|ƒ ttjddœdt|ƒd	œƒW  Y d }~S d }~ww )NÚparamsr<   úReceived email:r;   zEmail not foundr5   TzEmail not registered©r.   r4   r9   é   Ú
1234567890©ÚlengthÚallowed_chars©ÚtargetÚargsÚSuccessrq   zError:úInternal Server Error)r:   rP   r|   r   rT   ÚfilterÚexistsr,   r.   ÚHTTP_404_NOT_FOUNDr   Úcreater)   r~   r   ÚnowÚotp_created_atÚsaveÚ	threadingÚThreadr�   Ústartr[   r{   ÚHTTP_500_INTERNAL_SERVER_ERRORr^   )r_   r`   r:   r<   ra   r~   r€   rd   rd   re   rf   P  s<   


ý


ý

ý€þzSendotpAPIView.postNrg   rd   rd   rd   re   r„   O  ó    r„   c                   @   r0   )ÚSendotpsAPIViewc              
   C   sà   |j }zL| dd ¡}td|ƒ tjj|d� ¡ r%ttj	ddœdddœƒW S t
d	d
d�}tjj||t ¡ d�}tjt||fd� ¡  ttjddœd d|idœƒW S  tyo } zttjddœdt|ƒdœƒW  Y d }~S d }~ww )Nr<   r†   r;   zEmail already registeredr5   Tz;This email is already registered. Please use another email.r‡   rˆ   r‰   rŠ   )r<   r~   r—   r�   r�   rq   r‘   )r:   rP   r|   r   rT   r’   r“   r,   r.   ÚHTTP_409_CONFLICTr)   r   r•   r   r–   r™   rš   r‚   r›   r[   r{   rœ   r^   )r_   r`   r:   r<   r~   ra   r€   rd   rd   re   rf   v  s4   


ý

ý
ý€ÿzSendotpsAPIView.postNrg   rd   rd   rd   re   rž   u  r�   rž   ©r   c                   @   ó   e Zd Zdd„ Zdd„ ZdS )ÚVerifyotpAPIViewc                 C   sÆ   |j  d¡}|j  d¡}zAtjj|d� d¡}|jtdd� }t 	¡ |kr1t
ddd	d
œtjd�W S ||jkrBt
dddd
œtjd�W S t
ddd	d
œtjd�W S  tjyb   t
ddd	d
œtjd� Y S w )Nr<   r~   r;   r—   é<   )Úsecondsé‘  z&OTP expired. Please request a new one.T)r.   r9   r4   r-   éÈ   zOTP VerifiedFé�  zInvalid OTPé”  zOTP not found)Úquery_paramsrP   r   rT   r’   Úlatestr—   r   r   r–   r,   r.   r[   r~   rU   )r_   r`   r<   Zotp_enteredZuser_otpZexpiration_timerd   rd   re   rP   ™  sJ   ýü
ýüýüý
üÿzVerifyotpAPIView.getc                 C   s–   |d ur?z%t jj|d�}|jr|jjdd� d |_| ¡  ttjddœd ddœƒW S  t	y>   ttj
ddœd d	dœƒ Y S w ttjddœd
ddœƒS )Nr>   F)r˜   rD   r5   z"Profile image deleted successfullyr‡   ÚfailedzProfile not foundzID not providedzUser ID is required)r   rT   rP   rW   Údeleter˜   r,   r.   r[   r   r”   rQ   )r_   r`   rF   rJ   rd   rd   re   r¬   À  s0   

ý
ýÿ
ýzVerifyotpAPIView.deleteN)rh   ri   rj   rP   r¬   rd   rd   rd   re   r¢   ˜  s    'r¢   c                   @   s*   e Zd ZefZdd„ Zdd„ Zdd„ ZdS )ÚProfileupdateAPIViewc                 K   sº   |  d¡}|d ur[z<tjj |d�}tjj |jd�}|jr'|jjdkr'|jj}nd}|j|j|j	|j
|j||jdœ}ttjddœd |dœƒW S  tyZ   ttjd	dœd
ddœƒ Y S w d S )NrF   ©rF   r>   rB   )rE   r<   rM   Úf_nameZl_namerJ   rF   r�   r5   rq   ÚFailedTúCompany not foundr‡   )rP   r   rT   r   rF   rW   rX   rE   r<   rM   r\   Ú	last_namer,   r.   r[   r   r”   )r_   r`   ÚkwargsrF   ra   ÚcompanyÚprr:   rd   rd   re   rP   å  s:   

ø

ý
ýÿãzProfileupdateAPIView.getc              
   C   sÖ   |j }z"| d¡}tjj|jjd�}||_| ¡  tt	j
ddœd ddœƒW S  tyH } ztt	jddœdd	t|ƒ› �dœƒW  Y d }~S d }~w tyj } ztt	jddœdd
t|ƒ› �dœƒW  Y d }~S d }~ww )NÚ
prof_photor>   rD   r5   zProfile updated successfullyr‡   r«   TúMissing required field: úAn error occurred: )r:   rP   r   rT   ra   rF   rW   r˜   r,   r.   ÚHTTP_201_CREATEDÚKeyErrorrQ   r^   r{   rœ   )r_   r`   r:   ZphoÚtaker€   rd   rd   re   rf     s4   


ý
ý€
ý€ÿzProfileupdateAPIView.postc           
      C   sø   |j }|j  d¡}|j  d¡}|j  d¡}|j  d¡}zKtjj|d�}tjj|d�}	|d |	_|d |	_|d |	_|d |	_|	 	¡  |d |_|d |_
|d |_|d |_| 	¡  ttjd	d
œd ddœƒW S  ty{   ttjdd
œdddœƒ Y S w )Nr¶   Úphone_noÚemail_idr\   r>   r®   Zfirst_namesr²   rD   r5   úUser updated successfullyr‡   r«   TúSomething went wrong)r:   rP   r   rT   r   r2   r\   r²   r<   r˜   rM   rE   rW   r,   r.   r¹   r   rQ   )
r_   r`   rF   r:   r¶   r¼   r½   r\   ÚmmÚsdrd   rd   re   Úput3  s<   









ý
ýÿzProfileupdateAPIView.putN)rh   ri   rj   r$   rr   rP   rf   rÂ   rd   rd   rd   re   r­   á  s
    (&r­   c                   @   s,   e Zd Zdd„ Zdd„ Zdd„ Zdd„ Zd	S )
ÚRegisterAPIViewc                 K   s|  |  d¡}|d urezFtjj |d�}tjj |jd�}|jr'|jjdkr'|jj}nd}|j|j|j	|j
|j||j|j|j|j|j|jdœ}ttjddœd |dœƒW S  tyd   ttjd	dœd
ddœƒ Y S w |j  d¡}t|ƒ z¸g }|j  dd¡ ¡ }	|j  dd¡ ¡ }
|j  dd¡ ¡ }|j  dd¡ ¡ }|j  dd¡ ¡ }|dkr¯tjjddd� d¡}ntjjddd� d¡}|	rÂ|j|	d�}|rÊ|j|d�}|rÒ|j|d�}|
rÚ|j|
d�}|rözt|ƒ}|dv rë|j|d�}W n	 tyõ   Y nw |D ]"}tjj |jd�}|j|j|j	|j|j|j
|j	|jd œ}| |¡ qøttjddœd |dœƒW S  t�y=   ttj d	dœd
d!dœƒ Y S w )"NrF   r®   rp   rB   )rE   r<   rM   ÚaddressÚpincoderJ   Ú	firstnameÚlastnameÚgenderÚdojr3   rF   r�   r5   rq   r°   Tr±   r‡   ÚvalZ
vendornameÚ rÅ   r<   rM   r.   Ú2r@   r   )r.   rN   z-user_idr   ©Zname__icontains©Zemail__icontains©Zphone__icontains)Zpincode__icontains)r   r@   r-   )rE   rF   rM   rÅ   r<   rÄ   rM   r.   r¿   )!rP   r   rT   r   rF   rW   rX   rE   r<   rM   rÄ   rÅ   r\   r²   rÈ   rÉ   r3   r,   r.   r[   r   r”   ÚGETr|   r©   Ústripr’   Úorder_byÚintÚ
ValueErrorr?   ÚappendrQ   )r_   r`   r³   rF   ra   r´   rµ   r:   rÊ   r2   rÅ   r<   rM   Ústatus_paramÚ	companiesZ
status_intÚusersZcompany_datard   rd   re   rP   h  s¬   

ï

ý
ýÿ€ÿ	ó

ý
ýÿzRegisterAPIView.getc              
   C   s"  |j }zH| d¡}| d¡}| d¡}| d¡}| d¡}| d¡}t|ƒ}	t|||	d�}
|
 ¡  t||||||
d�}| ¡  ttjd	d
œd ddœƒW S  t	yn } zttj
dd
œddt|ƒ› �dœƒW  Y d }~S d }~w ty� } zttjdd
œddt|ƒ› �dœƒW  Y d }~S d }~ww )NZemployeenamer<   ZmobilenumberrÄ   rÅ   r3   )r2   r<   r3   )r3   rE   r<   rM   rÄ   ra   rD   r5   z New User Registered successfullyr‡   r«   Tr·   r¸   )r:   rP   r   r   r˜   r   r,   r.   r¹   rº   rQ   r^   r{   rœ   )r_   r`   r:   rE   r<   rM   rÄ   rÅ   r3   Úhashed_passwordÚvanÚprofile_datar€   rd   rd   re   rf   ï  sJ   





ý

ý
ý€
ý€ÿzRegisterAPIView.postc           
      C   s¤  |j  d¡}|j}|j d¡}|dkr6tjj|d�}|jdkr#d|_nd|_| ¡  ttj	ddœd d	d
œƒS |dkr]tjj|d�}|jdkrJd|_nd|_| ¡  ttj	ddœd dd
œƒS |r™tjj|d�}|d |_
|d |_|d |_|d |_|d |_|d |_|d |_| ¡  ttj	ddœd dd
œƒS z¢tjj|d�}tjj|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 |	_t)|d( ƒ|	_%|	 ¡  ttj	ddœd d+d
œƒW S  t*�yQ   ttj+d,dœd-d.d
œƒ Y S w )/NrÊ   ÚctcÚ1r>   r   r@   rD   r5   zWFH updated successfullyr‡   rÌ   zInactive updated successfullyÚgrossÚptÚuanÚpanÚaccountÚreimz#Salary details updated successfullyr®   rE   ÚpemailÚshiftÚmarriedstatusrÈ   r<   rM   rÄ   ÚcityÚstateÚcountryrÅ   Ú
departmentÚdesignationrN   Ú	authorityrÉ   rW   Úempcoder3   rÆ   rÇ   r¾   r«   Tr¿   ),rÐ   rP   r:   r   rT   Zwfhr˜   r,   r.   r¹   rÜ   rÞ   rß   rà   rá   râ   rã   r   rE   rä   rå   ræ   rÈ   r<   rM   rÄ   rç   rè   ré   rÅ   Údepartment_idÚdesignation_idrN   Úauthority_idrÉ   rW   rí   r3   r2   r\   r²   r   r   rQ   )
r_   r`   rF   rÊ   r:   rÜ   ZpoZmeowrÀ   rÁ   rd   rd   re   rÂ   1  s¤   

ý

ý







ý

























ý
ýÿzRegisterAPIView.putc                 O   s|   |  d¡}z#tjj |d�}| ¡  tjj |d�}| ¡  ttjddœd ddœƒW S  ty=   ttj	ddœd	d
dœƒ Y S w )NrF   r>   r®   rD   r5   zCompany deleted successfullyr‡   r«   TúNo data found)
rP   r   rT   r¬   r   r,   r.   r[   r   r”   )r_   r�   r³   Z
company_idrµ   r´   rd   rd   re   r¬   �  s$   


ý
ýÿzRegisterAPIView.deleteN)rh   ri   rj   rP   rf   rÂ   r¬   rd   rd   rd   re   rÃ   g  s     B_rÃ   c                   @   r0   )ÚCompanydashboardAPIViewc                 C   sâ   z[ddl m} |jj}tjj|d� ¡ }ddl m} ddlm	} |jj}|ƒ }d}d}d}	d}
d}d}d	}d}d}d
}d}d}|||	|
|||||||||dœ}t
tjddœd |dœƒW S  typ   t
tjddœdddœƒ Y S w )Nr   )r–   ©rY   r    rˆ   é   é   é   é	   é   r@   )ÚtotalenquiryÚtotalvendorÚtotalenquirytodaydateÚactivevendorÚinactivevendorÚ	monthdataÚtotalvendorlastmonthÚcurrent_month_enquiryÚlast_month_enquiryÚcurrent_month_auctionÚlast_month_auctionÚcurrent_month_productÚlast_month_productrD   r5   rq   r«   Trñ   r‡   )Údjango.utils.timezoner–   ra   rF   r   rT   r’   Úcountr   r   r,   r.   r[   r   r”   )r_   r`   r–   Úoprù   r   Zcurrent_daterÿ   rú   rû   rü   rþ   rý   r  r   r  r  r  r  r:   rd   rd   re   rP   «  s\   ó

ý
ýÿzCompanydashboardAPIView.getN©rh   ri   rj   rP   rd   rd   rd   re   rò   ¨  s    rò   c                   @   s   e Zd Zddd„ZdS )ÚCheckcompanyAPIViewNc           	      C   sî   |j  d¡}|j  d¡}|j  d¡}|j  d¡p|j  d¡}td|||ƒ |s-|s-|s-|rhtƒ }|r9|t|d�O }|rB|t|d�O }|rK|t|d	�O }|rT|t|d
�O }tj |¡ ¡ }tt	j
ddœd |dœƒS tt	jddœdddœt	jd�S )NrM   r<   r2   Ú	gstnumberÚgstzCheck Params:)rE   )rM   r;   )r  rD   r5   )r.   r4   r“   r«   Tz!No phone, email, or GST provided.r‡   r-   )r©   rP   r|   r    r   rT   r’   r“   r,   r.   r[   rQ   )	r_   r`   rF   rM   r<   rE   r  Úqueryr“   rd   rd   re   rP   ó  s8   
ý
ýüzCheckcompanyAPIView.getrm   r	  rd   rd   rd   re   r
  ò  s    r
  c                   @   ó   e Zd ZdZefZdd„ ZdS )ÚDashboardapiAPIViewa}  
    GET /dashboardapi/?date=YYYY-MM-DD
 
    - Late count: Setting.shift_start + Setting.grace_minutes (NOT hardcoded)
    - Holiday / Weekly-off: Holiday table + Setting.weekly_off
    - Holiday/Weekly-off nu iruntha, "Absent" concept illa (working day illa),
      so absent_list empty-a return aagum, is_holiday/is_weekly_off flag-a paathu
      frontend banner kaatikkum.
    c                 C   sè  |j  d¡}z|rt |d¡ ¡ nt ¡ }W n ty#   t ¡ }Y nw tƒ }t	||ƒ}| |¡}| 
¡ |d v }t|ƒp>|}tjjdd�}	|	 ¡ }
tjj|dd�}| ¡ }|j|d d	� ¡ }g }|s�|jd
dd�}|	j|d�}|D ]}t|dd ƒr‚| |jj¡nd }| |j|j|dœ¡ qs|
dkrœt||
 d ƒnd}|dkrªt|| d ƒnd}tdddœd||
|dœd|||dœ|| d¡|d  d¡|d  d¡|d |d  d¡t| d¡pådd„ |d D ƒƒdœt|ƒ|||d œ	d!œƒS )"Nr   ú%Y-%m-%dÚ
weekly_offr   r-   ÚPresent)r   r.   Ú
late_after)Zin_time__gtÚemployee_idT©Úflat©Zid__inÚphoto)rF   rE   Úimager   éd   r¦   rD   r5   zActive Staff)ÚlabelZpresentÚtotalÚpercentz
Late EntryÚshift_startú%I:%M %pÚ	shift_endÚgrace_minutesÚweekly_off_namesc                 S   ó   h | ]}t | ’qS rd   ©ÚDAY_KEYS©Ú.0Úird   rd   re   Ú	<setcomp>m  ó    z*DashboardapiAPIView.get.<locals>.<setcomp>)r›   Úendr!  r  r  )	Zn_dataZs_dataZabsent_employeesZselected_daterå   Ú
is_holidayÚholiday_nameÚis_weekly_offÚ
is_off_day©r.   r:   )r©   rP   r   Ústrptimer   Údate_clsÚtodayrÔ   Úget_attendance_configÚget_holiday_mapÚweekdayÚboolr   rT   r’   r  r   Úvalues_listÚexcludeÚgetattrÚbuild_absolute_urir  rX   rÕ   rF   rE   rÓ   r,   ÚstrftimeÚsorted)r_   r`   Údate_strÚtarget_dateÚcfgÚholiday_mapÚhol_namer.  r/  Zall_empÚtotal_countZpresent_todayÚpresent_countÚ
late_countÚabsent_listZpresent_emp_idsZabsent_empsÚempZ	image_urlZ	p_percentZ	l_percentrd   rd   re   rP   )  sx   ÿþÿ


ýüüÿûáþzDashboardapiAPIView.getN©rh   ri   rj   Ú__doc__r$   rr   rP   rd   rd   rd   re   r    s    	r  c                   @   r0   )ÚStudentClassCountAPIViewc              
   C   sŒ   z(t jj|dd� ¡ }t jj|dd� ¡ }t||dƒ tdddœ||dœd	œƒW S  tyE } ztd
t|ƒitj	d�W  Y d }~S d }~ww )Nr   )Ú
student_idÚsession_typer@   Zjsabdbsdsmdbasmdbsandr¦   rD   r5   )Únormal_countÚspecial_countr0  r4   r-   )
r   rT   r’   r  r|   r,   r{   r^   r.   rQ   )r_   r`   rF   rM  rN  r€   rd   rd   re   rP   {  s   þ
þ"€ÿzStudentClassCountAPIView.getNr	  rd   rd   rd   re   rJ  z  r�   rJ  c                   @   s6   e Zd Zddd„Zdd„ Zdd„ Zdd	„ Zd
d„ ZdS )ÚEmployeeAPIViewNc           
      C   sÌ  |d urkzPt jj|d�}|j|j|j|j|j|jr|jjnd |jr%|jjnd|j	r-|j	jnd |j	r5|j	jnd|j
|j|jrD| |jj¡nd dœ}ttjddœd |dœƒW S  t jyj   ttjddœdd	d
œƒ Y S w zZt j ¡  d¡}g }t|dƒD ]8\}}tjj|jd�}| ||j|j|j|jr—|jjnd|j	rŸ|j	jnd|j|j|jr®| |jj¡nd dœ	¡ q{t|dƒ ttjddœd |dœƒW S  tyå }	 zttjddœdt|	ƒd
œƒW  Y d }	~	S d }	~	ww )Nr®   zN/A)rF   rE   ÚmobileÚagerÄ   rî   Zdepartment_namerï   Zdesignation_namerÉ   r.   Úprofile_picrD   r5   rq   r«   TzNo employee foundr‡   ú-created_atr   r>   )	ÚrowrF   rE   rP  rê   rë   r.   rO   rR  ZHHHHHHHHHHHHHHHHHHHH)r   rT   rP   rF   rE   rP  rQ  rÄ   rê   rë   rÉ   r.   r  r;  rX   r,   r[   rU   r”   ÚallrÒ   Ú	enumerater   Úusers_idrÕ   rO   r|   r{   rQ   r^   )
r_   r`   rF   rG  r:   Ú	employeesZemp_listÚindexrb   r€   rd   rd   re   rP   ”  st   ô

ý
ýÿ
÷


ý
ý€ÿzEmployeeAPIView.getc              
   C   sÄ   |j sdS |j j}tj |¡sdS z-t|ƒ\}}|sW dS tj||dd�}|s*W dS | |d ¡ tƒ  t	d|j
› �ƒ W d	S  tya } zt	d
|j
› d|› �ƒ dt|ƒfW  Y d}~S d}~ww )uç   
        Employee photo-à®µà®¿à®²à¯� face encoding à®Žà®Ÿà¯�à®¤à¯�à®¤à¯� save à®ªà®£à¯�à®£à¯�à®®à¯�.
        à®Žà®¨à¯�à®¤ size photo-à®µà¯�à®®à¯� handle à®ªà®£à¯�à®£à¯�à®®à¯�.
        Returns: (success: bool, message: str)
        )Fõ   Photo à®‡à®²à¯�à®²à¯ˆ)Fu2   Photo file à®•à®¿à®Ÿà¯ˆà®•à¯�à®•à®µà®¿à®²à¯�à®²à¯ˆ)Fu>   Photo-à®²à¯� à®®à¯�à®•à®®à¯� à®¤à¯†à®°à®¿à®¯à®µà®¿à®²à¯�à®²à¯ˆrô   ©Zknown_face_locationsZnum_jitters)Fu$   Face encode à®†à®•à®µà®¿à®²à¯�à®²à¯ˆr   z[Encoding] Saved for: )TzEncoding savedz[Encoding Error] ú: FN)r  ÚpathÚosr“   Úpreprocess_imageÚface_recognitionÚface_encodingsÚsave_encodingÚinvalidate_cacher|   rE   r{   r^   )r_   ÚemployeeZ
photo_pathÚ	img_arrayÚface_locationsÚ	encodingsr€   rd   rd   re   Ú_save_face_encodingB  s2   ý€þz#EmployeeAPIView._save_face_encodingc                 C   s   |j }|j d¡}zâ| d¡}| d¡}|rtjj|d�nd }|r(tjj|d�nd }| d¡p0d ¡ }| d¡}	| d¡pA| d	¡}
| d
¡}|sVttj	ddœdddœƒW S | 
dd¡› d�}t|ƒ}| 
dd¡ ¡ }|}d}tjj|d� ¡ r‹|› |› �}|d7 }tjj|d� ¡ sxt||d�}| ¡  td|||
|||d�}| ¡  tjj|| d¡| d¡|||| d¡||j|jjrÁ|jnd d�
}d}|rÝ|  |¡\}}|sÝtd|j› d|› �ƒ ttjddœd d||dœƒW S  t�y } zttj	ddœdd t|ƒ› �dœƒW  Y d }~S d }~ww )!NrR  rê   rë   r®   rE   rË   r<   rP  rM   rÄ   r«   r5   TzName is requiredr‡   ú ú@123r   )r2   r=   é   )rN   r3   rE   rM   rÄ   ra   rW   rQ  rÉ   )
rE   rP  rQ  rÄ   rê   rë   rÉ   r  rW  Ú
created_byrZ  z[Warning] Encoding failed for r\  rD   zEmployee created successfully)r.   r4   r9   Úencoding_statusZlogin_usernameúError: )r:   ÚFILESrP   r   rT   r   rÑ   r,   r.   rQ   Úreplacer   Úlowerr   r’   r“   r˜   r   r   r•   rF   ra   Úis_authenticatedrh  r|   rE   r¹   r{   r^   )r_   r`   r:   rR  Zdept_idZdesig_idZdeptZdesigrE   r<   rM   rÄ   Úraw_passwordrÙ   Zbase_usernamer2   ÚcounterrÚ   rÛ   rd  rm  Úokr€   rd   rd   re   rf   g  sŒ   





ýþù	ö

û
ý€ÿzEmployeeAPIView.postc              
   C   s�  |j }|j d¡}�z
tjj|d�}d|v r!tjj| d¡d�|_d|v r0tjj| d¡d�|_| d¡r>| d|j	¡ 
¡ n|j	}||j	k}||_	| d|j¡|_| d|j¡|_| d|j¡|_| d	|j¡|_| d
|j¡|_|rv||_| ¡  d}|j}	|	rçzNtjj|	d�}
tjj|
d�}||_	| d|j¡|_| d|j¡|_|r¨||_|rÄ| dd¡› d�}t|ƒ}||
_||
_|
 ¡  ||_| ¡  |rÌdnd}W n tjyÛ   d}Y n tjyæ   d}Y nw d}|só|j�r|j�s|  |¡\}}|�std|j	› d|› �ƒ tdddœd||dœƒW S  tj�y)   tdddœddœƒ Y S  t �yG } ztd ddœt!|ƒdœƒW  Y d }~S d }~ww )!NrR  r®   rê   rë   rE   rP  rQ  rÄ   rÉ   r.   zProfile link illarp   ri  rË   rj  zProfile & password update aachuzProfile update aachuzUser record kedaikalazProfiles record kedaikalau!   Photo à®®à®¾à®±à®µà®¿à®²à¯�à®²à¯ˆz%[Warning] Encoding update failed for r\  r¦   rD   r5   zEmployee updated successfully)r.   r9   rm  Úprofile_statusr¨   r«   úEmployee not found©r.   r9   r§   )"r:   ro  rP   r   rT   r   rê   r   rë   rE   rÑ   rP  rQ  rÄ   rÉ   r.   r  r˜   rW  r   r   rM   rW   rp  r   r3   r2   rU   Úface_encoding_datarh  r|   r,   r{   r^   )r_   r`   rF   r:   rR  rG  Únew_nameZname_changedrv  r?   Zvan_userZprofile_objrs  rÙ   rm  ru  r€   rd   rd   re   rÂ   »  s†   "
ÿ
üþþ€ÿzEmployeeAPIView.putc                 C   sV   zt jj|d�}| ¡  tdddœddœƒW S  t jy*   tdddœd	dœƒ Y S w )
Nr®   r¦   rD   r5   zEmployee deletedrx  r¨   r«   z	Not found)r   rT   rP   r¬   r,   rU   )r_   r`   rF   rG  rd   rd   re   r¬     s   
þÿzEmployeeAPIView.deleterm   )rh   ri   rj   rP   rh  rf   rÂ   r¬   rd   rd   rd   re   rO  ‘  s    
 /%T^rO  c                   @   r0   )ÚFetchdesignationAPIViewc                 C   sH   t jj|d�}g }|D ]}| |j|jdœ¡ qttjddœd |dœƒS )N)rî   )rE   rF   rD   r5   rq   )	r   rT   r’   rÕ   rE   rF   r,   r.   r[   )r_   r`   rF   r´   r:   r(  rd   rd   re   rP   D  s   
þ
ýzFetchdesignationAPIView.getNr	  rd   rd   rd   re   r{  C  r�   r{  c                   @   r0   )ÚStudentStatusAPIViewc                 C   sn   z$t jj|d�}|jdkrd|_nd|_| ¡  tdddœ|jddœƒW S  t jy6   td	d
idd� Y S w )Nr®   r   r@   r¦   rD   r5   úStatus updated successfully©r.   Ú
new_statusr9   r4   zStudent not foundr¨   r-   )r   rT   rP   r.   r˜   r,   rU   )r_   r`   rF   Ústudentrd   rd   re   ÚpatchY  s   

ýÿzStudentStatusAPIView.patchN©rh   ri   rj   r�  rd   rd   rd   re   r|  X  r�   r|  )r   r%   r   r   )r   r	   r   r   ©r   )rg  ÚidsÚlast_reloadé,  Fc                 C   sÔ   t  ¡ }t�Z | s|td  tk r	 W d   ƒ d S tjjdd�jdd�}g g }}|D ]}| ¡ }|d urA| 	|¡ | 	|j
¡ q,|td< |td< |td< tdt|ƒ› d	�ƒ W d   ƒ d S 1 scw   Y  d S )
Nr…  T)Zface_encoding_data__isnullrË   )ry  rg  r„  z[Cache] z encodings loaded.)Útime_moduler%   Ú_cache_lockÚ_encoding_cacheÚ	CACHE_TTLr   rT   r9  Úget_encodingrÕ   rF   r|   Úlen)Úforcer–   rX  rg  r„  rG  Úencrd   rd   re   Úload_encoding_cacheè  s,   þÿþ

€"ñr�  c                   C   s2   t � dtd< W d   ƒ d S 1 sw   Y  d S )Nr   r…  )rˆ  r‰  rd   rd   rd   re   rc  þ  s   
"ÿrc  c                 C   s®  t | tƒrt | ¡ d¡}n|  d¡ t t |  ¡ ¡¡ d¡}|j	\}}t
d|› d|› �ƒ t||ƒ}|dk r\d| }| t|| ƒt|| ƒftj¡}|j	\}}t
d|› d|› �ƒ t||ƒ}|dkr‰d| }| t|| ƒt|| ƒftj¡}|j	\}}t
d	|› d|› �ƒ t |¡ d
¡}t |¡ d¡}t |¡ d¡}t |¡}tj|ddd�}t
dt|ƒ› d�ƒ |r¾||fS tj|ddd�}t
dt|ƒ› d�ƒ |rÖ||fS t t |¡ d¡¡}	tj|	ddd�}t
dt|ƒ› d�ƒ |rù|	|fS |dk�rtd| | ƒnd}
| d|
ftj¡}t |¡}tj|ddd�}t
dt|ƒ› d�ƒ |�r/||fS | tj¡}t |¡}tj|ddd�}t
dt|ƒ› d�ƒ |�rS||fS |g fS )u  
    source: uploaded file object à®…à®²à¯�à®²à®¤à¯� file path string
 
    5 different attempts-à®²à¯� face detect à®ªà®£à¯�à®£à¯�à®®à¯�:
      1. Normal HOG
      2. HOG + upsample (small photos)
      3. Extra brightness (dark photos)
      4. 800px width resize (portrait photos)
      5. Horizontally flipped (selfie mirror issue)
 
    Returns: (img_array, face_locations)
    ÚRGBr   z[DEBUG] Original size: Úxr¦   r†  z[DEBUG] Upscaled to: i°  z[DEBUG] Downscaled to: g333333ó?gÍÌÌÌÌÌô?g      ø?Zhogr   )ÚmodelZnumber_of_times_to_upsamplez[DEBUG] Attempt 1 (HOG): z facesr@   z$[DEBUG] Attempt 2 (HOG upsample=2): gÍÌÌÌÌÌü?z"[DEBUG] Attempt 3 (extra bright): i   z![DEBUG] Attempt 4 (800w resize): z[DEBUG] Attempt 5 (flipped): N)Ú
isinstancer^   r   ÚopenÚconvertÚseekÚioÚBytesIOÚreadÚsizer|   ÚminÚresizerÓ   ZLANCZOSÚmaxr   Z
BrightnessZenhanceZContrastZ	SharpnessÚnpÚarrayr`  rf  rŒ  Ú	transposeZFLIP_LEFT_RIGHT)ÚsourceZpil_imgÚwÚhÚshortÚscaleÚlongre  ÚlocsZ
bright_arrZnew_hZresizedZ
resize_arrZflippedZflip_arrrd   rd   re   r_    sj   



ÿ

ÿ



r_  c                   @   r¡   )ÚAttendanceAPIViewc           	      C   sä   zQ|j  dt ¡  ¡ ¡}|j  d¡}tjj|d� d¡}|r$|j|d�}g }t	|dƒD ]\}}| 
||j|jj|jj|j|j|jdœ¡ q+ttjdd	œd |d
œƒW S  tyq } zttjdd	œdt|ƒdœƒW  Y d }~S d }~ww )Nr   rL  rƒ  rS  )rL  r   )rT  rF   Z	studentIdÚstudentNamer.   rL  r   rD   r5   rq   r«   Tr‡   )r©   rP   r   r3  Ú	isoformatr   rT   r’   rÒ   rV  rÕ   rF   r€  r©  r.   rL  r,   r[   r{   rQ   r^   )	r_   r`   Z
query_daterL  ZattendancesZattendance_listrY  Úattr€   rd   rd   re   rP   e  s<   
ù	

ý
ý€ÿzAttendanceAPIView.getc              
   C   sæ  |j  d¡}|stddidd�S z¿t|ƒ\}}|s"tddidd�W S tj||dd�}|s5tdd	idd�W S |d
 }tƒ  td }td }|sOtddidd�W S t ||¡}	t	t
 |	¡ƒ}
t|	|
 ƒ}td|d›d�ƒ |dkrxtddidd�W S ||
 }tjj|d�}t ¡  ¡ }t ¡  ¡ }|j d¡}tjj|j|d||dœd�\}}|s·||_| ¡  d|j› �}d}nd|j› �}d}td||j|t|dƒdœd œƒW S  tyò } ztd!|› �ƒ td"t|ƒid#d�W  Y d }~S d }~ww )$Nr  r9   zImage requiredr§   r-   u†   à®®à¯�à®•à®®à¯� à®šà®°à®¿à®¯à®¾à®• à®¤à¯†à®°à®¿à®¯à®µà®¿à®²à¯�à®²à¯ˆ! à®¨à¯‡à®°à®Ÿà®¿à®¯à®¾à®• camera-à®� à®ªà®¾à®°à¯�à®™à¯�à®•à®³à¯�.r   r[  u¡   à®®à¯�à®•à®®à¯� à®¤à¯†à®°à®¿à®•à®¿à®±à®¤à¯� à®†à®©à®¾à®²à¯� encode à®†à®•à®µà®¿à®²à¯�à®²à¯ˆ. à®®à¯€à®£à¯�à®Ÿà¯�à®®à¯� à®®à¯�à®¯à®±à¯�à®šà®¿à®•à¯�à®•à®µà¯�à®®à¯�.r   rg  r„  uq   à®ªà®¤à®¿à®µà¯� à®šà¯†à®¯à¯�à®¯à®ªà¯�à®ªà®Ÿà¯�à®Ÿ à®®à¯�à®•à®™à¯�à®•à®³à¯� à®Žà®¤à¯�à®µà¯�à®®à¯� à®‡à®²à¯�à®²à¯ˆ!z[DEBUG] Best match distance: z.4fz (threshold: 0.50)ç      à?ub   à®¯à®¾à®°à¯� à®Žà®©à¯�à®±à¯� à®…à®Ÿà¯ˆà®¯à®¾à®³à®®à¯� à®•à®¾à®£ à®®à¯�à®Ÿà®¿à®¯à®µà®¿à®²à¯�à®²à¯ˆ!r¥   r®   Zlocation_namer  )r.   Úin_timeÚlocation)r  r   Údefaultsu@   à®µà¯†à®³à®¿à®¯à¯‡à®±à¯�à®®à¯� à®¨à¯‡à®°à®®à¯� à®ªà®¤à®¿à®µà¯�: Úoutu!   à®µà®°à¯�à®•à¯ˆ à®ªà®¤à®¿à®µà¯�: ÚinrD   rk  )rE   ÚactionZdistance)r.   r9   r:   z[ERROR] Attendance post: r4   iô  )ro  rP   r,   r_  r`  ra  r�  r‰  Zface_distancerÓ   rž  ÚargminÚfloatr|   r   rT   r   r–   r   r%   r:   r   Úget_or_createrF   Úout_timer˜   rE   Úroundr{   r^   )r_   r`   Úuploaded_filere  rf  Ztarget_encodingsZ
target_encZknown_encodingsZ	known_idsZface_distancesZbest_idxZbest_distanceÚemp_idZ
found_userr3  Znow_timer®  r«  ÚcreatedÚmsgr²  r€   rd   rd   re   rf   „  s�   ÿþýÿþÿþÿþ

ýý
ý
 €þzAttendanceAPIView.postN©rh   ri   rj   rP   rf   rd   rd   rd   re   r¨  c  s    r¨  c                   @   ó4   e Zd ZefZd
dd„Zdd„ Zdd„ Zdd	„ ZdS )ÚDesignationAPIViewNc              	   C   s(  |d ur9zt jj|d�}|j|j|j|jdœ}ttj	ddœd |dœƒW S  t
y8   ttjddœddd	œƒ Y S w zEd
}g }t j ¡ D ].}|d7 }ztjj|jd�}|j}W n tjyb   d}Y nw | ||j|j|j|dœ¡ qCttj	ddœd |dœƒW S  t
y“   ttjddœddd	œƒ Y S w )Nr®   )rF   rE   Údescriptionrê   rD   r5   rq   r«   Trñ   r‡   r   r   rË   )rT  rF   rE   r¿  rê   )r   rT   rP   rF   rE   r¿  rî   r,   r.   r[   r   r”   rU  r   rU   rÕ   )r_   r`   rF   rë   r:   rY  Zdesignationsrê   rd   rd   re   rP   Ý  sd   ü

ý
ýÿ
ÿ
û

ý
ýÿzDesignationAPIView.getc              
   C   s˜   |j }z'tjj|d |d tjj|d d�|jd�}| ¡  tt	j
ddœd dd	œƒW S  tyK } ztt	jd
dœdd|› �d	œƒW  Y d }~S d }~ww )NrE   r¿  rê   r®   )rE   r¿  rê   rl  rD   r5   z Designation created successfullyr‡   r«   Tr·   )r:   r   rT   r•   r   rP   ra   r˜   r,   r.   r¹   rº   rQ   )r_   r`   r:   rê   r€   rd   rd   re   rf     s,   ü

ý
ý€ÿzDesignationAPIView.postc                 C   sŠ   |j }z,tjj|d�}|d |_|d |_tjj|d d�|_| ¡  t	t
jddœd ddœƒW S  tyD   t	t
jd	dœd
ddœƒ Y S w )Nr®   rE   r¿  rê   rD   r5   z Designation updated successfullyr‡   r«   Trñ   )r:   r   rT   rP   rE   r¿  r   rê   r˜   r,   r.   r[   r   r”   )r_   r`   rF   r:   rë   rd   rd   re   rÂ   -  s&   



ý
ýÿzDesignationAPIView.putc                 C   s|   |d ur2zt jj|d�}| ¡  ttjddœd ddœƒW S  ty1   ttjddœd ddœƒ Y S w ttj	ddœd ddœƒS )	Nr®   rD   r5   z Designation deleted successfullyr‡   r«   rñ   r¿   )
r   rT   rP   r¬   r,   r.   r[   r   r”   rQ   )r_   r`   rF   rë   rd   rd   re   r¬   D  s*   

ý
ýÿ
ýzDesignationAPIView.deleterm   ©	rh   ri   rj   r$   rr   rP   rf   rÂ   r¬   rd   rd   rd   re   r¾  Ú  s    
8r¾  c                   @   r½  )ÚDepartmentAPIViewNc                 C   óî   |d ur7zt jj|d�}|j|j|jdœ}ttjddœd |dœƒW S  t	y6   ttj
ddœddd	œƒ Y S w z*d
}g }t j ¡ D ]}|d7 }| ||j|j|jdœ¡ qAttjddœd |dœƒW S  t	yv   ttj
ddœddd	œƒ Y S w )Nr®   ©rF   rE   r¿  rD   r5   rq   r«   Trñ   r‡   r   r   )rT  rF   rE   r¿  )r   rT   rP   rF   rE   r¿  r,   r.   r[   r   r”   rU  rÕ   ©r_   r`   rF   r´   r:   rY  r×   rd   rd   re   rP   _  óT   ý

ý
ýÿ
ü

ý
ýÿzDepartmentAPIView.getc              
   C   sˆ   |j }ztjj|d |d |jd�}| ¡  ttjddœd ddœƒW S  t	yC } zttj
ddœd	d
|› �dœƒW  Y d }~S d }~ww )NrE   r¿  )rE   r¿  rl  rD   r5   zDepartment created successfullyr‡   r«   Tr·   )r:   r   rT   r•   ra   r˜   r,   r.   r¹   rº   rQ   )r_   r`   r:   r´   r€   rd   rd   re   rf   �  s*   ý

ý
ý€ÿzDepartmentAPIView.postc              
   C   s¸   |j }z"tjj|d�}|d |_|d |_| ¡  ttj	ddœd ddœƒW S  t
y:   ttjddœd	d
dœƒ Y S  ty[ } zt|ƒ ttjddœd	ddœƒW  Y d }~S d }~ww )Nr®   rE   r¿  rD   r5   úDepartment updated successfullyr‡   r«   Trñ   r¿   )r:   r   rT   rP   rE   r¿  r˜   r,   r.   r[   r   r”   r{   r|   rQ   ©r_   r`   rF   r:   r´   r€   rd   rd   re   rÂ   §  s4   



ý
ý
ý€þzDepartmentAPIView.putc                 C   óh   |d ur2zt jj|d�}| ¡  ttjddœd ddœƒW S  ty1   ttjddœd ddœƒ Y S w d S ©Nr®   rD   r5   zDepartment deleted successfullyr‡   r«   rñ   ©	r   rT   rP   r¬   r,   r.   r[   r   r”   ©r_   r`   rF   r´   rd   rd   re   r¬   Ã  ó"   

ý
ýÿözDepartmentAPIView.deleterm   rÀ  rd   rd   rd   re   rÁ  \  s    
1rÁ  )ÚDecimalÚInvalidOperation)r   r	   ©r  z%I:%M%pú%H:%M:%Sú%H:%Mc              	   C   st   | dv rdS t | tƒr| S t| ƒ ¡  ¡  dd¡}tD ]}zt ||¡ 	¡ W   S  t
y1   Y qw t
d| › d�ƒ‚)zl
    '09:30 AM', '9:30AM', '09:30', '09:30:00' -> datetime.time
    Invalid na ValueError raise pannum.
    ©NrË   NÚ.rË   úInvalid time 'ú'. Use hh:mm AM/PM format.)r“  Údt_timer^   rÑ   Úupperrp  ÚTIME_FORMATSr   r1  r%   rÔ   ©ÚvalueÚrawÚfmtrd   rd   re   Ú
parse_timeå  s   
ÿrÝ  c              	   C   s,   zt | ƒW S  ttfy   t|› d�ƒ‚w ©Nz must be a number.)rÓ   Ú	TypeErrorrÔ   ©rÚ  Úfieldrd   rd   re   Ú	parse_intø  s
   
ÿrâ  c              
   C   s2   zt t| ƒƒW S  tttfy   t|› d�ƒ‚w rÞ  )rÍ  r^   rÎ  rß  rÔ   rà  rd   rd   re   Úparse_decimalÿ  s
   ÿrã  c                 C   s"   t | tƒr| S t| ƒ ¡  ¡ dv S )N)rÝ   ÚtrueÚyesÚon)r“  r7  r^   rÑ   rq  ©rÚ  rd   rd   re   Ú
parse_bool	  s   
rè  )ÚMonÚTueÚWedÚThuÚFriÚSatÚSunc                 C   ó(   t dt| ƒƒ} | d d›d| d d›�S ©Nr   r£   Ú02dú:©r�  rÓ   ©Ztotal_minutesrd   rd   re   Úfmt_hhmm"	  ó   rö  c               	      s®   t jjdd�\} }| jptddƒ}| jptddƒ}t| jpdƒ}dd„ | jp'd	 	d
¡D ƒ‰ ‡ fdd„t
tƒD ƒ}t t ¡ |¡t|d�  ¡ }| |||||ˆ t| jpSdƒdœS )uè   
    Setting table (pk=1) -la irundhu shift timing, grace period,
    weekly off days edukkum. Monthly + Today rendu API-um idhe
    function-a use pannanum â€” settings maathi na rendu idathulayum
    automatic-a update aagum.
    r   ©Úpkr÷   é   é   r   c                 S   ó(   h | ]}|  ¡ r|  ¡ d d…  ¡ ’qS ©Nrk  ©rÑ   Útitle©r'  Úprd   rd   re   r)  5	  ó    ýÿú(get_attendance_config.<locals>.<setcomp>rË   ú,c                    ó   h | ]
\}}|ˆ v r|’qS rd   rd   ©r'  r(  Únm©Z	off_namesrd   re   r)  :	  ó    ©Úminutesrø   )Úsettingr  r   r!  r  r  r"  Úhalf_day_hoursN)r   rT   rµ  r  r%   r   rÓ   r!  r  ÚsplitrV  r%  r   Úcombiner2  r3  r   r´  r  ©r  Ú_r  r   Zgracer  r  rd   r  re   r4  '	  s(   þþør4  c                 C   s    t jj| |gd�}dd„ |D ƒS )uL   Holiday table -la irundhu {date_obj: name} â€” date range kku eduthu tharum.©Údate__rangec                 S   s   i | ]}|j |j“qS rd   ©r   rE   ©r'  r£  rd   rd   re   Ú
<dictcomp>P	  ó    z#get_holiday_map.<locals>.<dictcomp>N)r	   rT   r’   )Ú
start_dateÚend_dateÚqsrd   rd   re   r5  M	  s   r5  c                 C   s   t | dd ƒpt | ddƒpdS )NrE   r©  rË   õ   â€”)r:  ©rG  rd   rd   re   Úemp_display_nameS	  s   r  c                 C   s"   t | dd ƒpt | dd ƒpt| jƒS )NrP  r¹  )r:  r^   rF   r  rd   rd   re   Úemp_display_codeW	  s   "r  c                 C   ó   t | o| |d kƒS ©Nr  ©r7  ©r­  r@  rd   rd   re   Úis_late[	  ó   r#  i€p  c                 C   sZ   |d j sdS | r|sdS t ¡ }t ||¡t || ¡ }| ¡ }|tkr'dS t|t ƒS )zÛ
    Total worked time (out_time - in_time) 8 hours-a thaandi irundha,
    andha extra seconds mattum OT-a return pannum.
    in_time/out_time illama irundha, or out_time < in_time (data issue)
    irundha, OT = 0.
    r  r   N)Úovertime_enabledr2  r3  r   r  Útotal_secondsÚSTANDARD_WORK_SECONDSrÓ   )r­  r¶  r@  ÚdZworkedZ
worked_secrd   rd   re   Ú
ot_secondsd	  s   
r)  c                   @   s(   e Zd ZdZdd„ Zdd„ Zdd„ ZdS )	ÚSettingAPIViewu=   Single settings row (pk=1) â€” GET fetch, PUT partial update.c                 C   s   t jjdd�\}}|S )Nr   rø  )r   rT   rµ  )r_   r  r  rd   rd   re   rn   	  s   zSettingAPIView.get_objectc                 C   s”   |   ¡ }|jr|j d¡nd |jr|j d¡nd dd„ |jpd d¡D ƒ|j|jd ur0t|jƒnd|j	|j
|j|j|jdœ
}ttjdd	œd |d
œƒS )Nr  c                 S   s   g | ]}|r|‘qS rd   rd   ©r'  r(  rd   rd   re   Ú
<listcomp>ˆ	  r*  z&SettingAPIView.get.<locals>.<listcomp>rË   r  r   )
r  r   r  r!  r  r%  Úgeofence_enabledÚface_sensitivityÚcompany_nameÚ	time_zonerD   r5   rq   )rn   r  r<  r   r  r  r!  r  r´  r%  r-  r.  r/  r0  r,   r.   r[   )r_   r`   r  r:   rd   rd   re   rP   ƒ	  s"   ö
ýzSettingAPIView.getc              
   C   s´  |   ¡ }|j}dd„ }z–d|v r|| d¡ƒ|_d|v r$|| d¡ƒ|_d|v r<| d¡}t|tƒr7d |¡n|p:d|_d|v rHt	| d¡ƒ|_
d	|v rTt| d	¡ƒ|_d
|v r`t| d
¡ƒ|_d|v rlt| d¡ƒ|_d|v rv| d¡|_d|v r„| d¡p€d ¡ |_d|v r’| d¡pŽd ¡ |_|jjr™|jnd |_| ¡  W n% ttfyÆ } zttjddœdt|ƒdœtjd�W  Y d }~S d }~ww ttjddœd d|  |¡jd dœƒS )Nc              	   S   sf   | dv rd S t | ƒ ¡  ¡  dd¡}dD ]}zt ||¡ ¡ W   S  ty*   Y qw td| › d�ƒ‚)NrÒ  rÓ  rË   rÏ  rÔ  rÕ  )r^   rÑ   r×  rp  r   r1  r%   rÔ   rÙ  rd   rd   re   rÝ  ›	  s   ÿz&SettingAPIView.put.<locals>.parse_timer  r   r  r  rË   r!  r  r%  r-  r.  r/  r0  r«   r5   Tr‡   r-   rD   zSettings updated successfullyr:   r8   )rn   r:   rP   r  r   r“  ÚlistÚjoinr  rÓ   r!  r´  r  r7  r%  r-  r.  rÑ   r/  r0  ra   rr  Ú
updated_byr˜   rÔ   rß  r,   r.   rQ   r^   r[   )r_   r`   r  r:   rÝ  Zwor€   rd   rd   re   rÂ   —	  sV   

ýü€ÿ
üzSettingAPIView.putN)rh   ri   rj   rI  rn   rP   rÂ   rd   rd   rd   re   r*  |	  s
    r*  c                   @   s,   e Zd ZdZd	dd„Zdd„ Zd	dd„ZdS )
ÚHolidayAPIViewu%   Holiday CRUD â€” List / Add / Delete.Nc                 C   sª   |d ur<z	t jj|d�}W n t jy&   ttjddœdddœtjd� Y S w ttjddœd |j|j	t
|jƒd	œd
œƒS t j ¡  d¡}ttjddœd dd„ |D ƒd
œƒS )Nr®   r«   r5   TúHoliday not foundr‡   r-   rD   ©rF   rE   r   rq   r   c                 S   s"   g | ]}|j |jt|jƒd œ‘qS )r6  )rF   rE   r^   r   r  rd   rd   re   r,  è	  s   " z&HolidayAPIView.get.<locals>.<listcomp>)r	   rT   rP   rU   r,   r.   r”   r[   rF   rE   r^   r   rU  rÒ   )r_   r`   rF   r£  Úholidaysrd   rd   re   rP   Ô	  s.   
ý
üÿ
ý
ýzHolidayAPIView.getc                 C   sâ   |j  d¡pd ¡ }|j  d¡pd ¡ }|r|s'ttjddœdddœtjd	�S zt |d
¡ W n tyG   ttjddœdddœtjd	� Y S w t	j
j|||jjrT|jnd d�}ttjddœd d|j|jt|jƒdœdœtjd	�S )NrE   rË   r   r«   r5   TzName and date are required.r‡   r-   r  z"Date must be in YYYY-MM-DD format.)rE   r   rl  rD   zHoliday added successfullyr6  r8   )r:   rP   rÑ   r,   r.   rQ   r   r1  rÔ   r	   rT   r•   ra   rr  r¹   rF   rE   r^   r   )r_   r`   rE   Zdate_valÚholidayrd   rd   re   rf   ë	  sD   
ýü
ý
üÿý
üûzHolidayAPIView.postc                 C   sŒ   |d u rt tjddœdddœtjd�S z	tjj|d�}W n tjy5   t tjddœdddœtjd� Y S w | ¡  t tj	d	dœd d
dœƒS )Nr«   r5   TzHoliday id required in URL.r‡   r-   r®   r5  rD   zHoliday deleted)
r,   r.   rQ   r	   rT   rP   rU   r”   r¬   r[   )r_   r`   rF   r8  rd   rd   re   r¬   
  s2   
ýü
ý
üÿ
ýzHolidayAPIView.deleterm   )rh   ri   rj   rI  rP   rf   r¬   rd   rd   rd   re   r4  Ñ	  s
    
 r4  c                   @   r0   )ÚEmployeeMiniListAPIViewc                 C   s:   t jjdd� d¡}dd„ |D ƒ}ttjddœd |dœƒS )	Nr   r-   rE   c                 S   s"   g | ]}|j t|ƒt|ƒd œ‘qS )©rF   rE   r6   )rF   r  r  ©r'  r€   rd   rd   re   r,  *
  ó    ÿÿz/EmployeeMiniListAPIView.get.<locals>.<listcomp>rD   r5   rq   )r   rT   r’   rÒ   r,   r.   r[   )r_   r`   rX  r:   rd   rd   re   rP   (
  s   þ
ýzEmployeeMiniListAPIView.getNr	  rd   rd   rd   re   r9  '
  r�   r9  )r%   rû  é-   c              	   C   sÈ   |sdS t |tƒr=z'dd„ | ¡  d¡dd… D ƒ}t|ƒdk r+| d¡ t|ƒdk s t|Ž }W n ttfy<   Y dS w t	|dƒrF| 
¡ }|jd |jd	  |j }tjd tjd	  }td|| ƒS )
ze
    OT = out_time - 6:45 PM
    out_time illa na (None / empty) -> OT = 0 (calculate pannaadhu)
    r   c                 S   ó   g | ]}t |ƒ‘qS rd   )rÓ   ©r'  r‘  rd   rd   re   r,  ×
  r*  z%ot_seconds_static.<locals>.<listcomp>ró  Nrk  r%   é  r£   )r“  r^   rÑ   r  rŒ  rÕ   rÖ  rÔ   rß  Úhasattrr%   ÚhourÚminuteÚsecondÚOT_STARTr�  )r­  r¶  r@  ÚpartsZout_secZ	start_secrd   rd   re   Úot_seconds_staticÎ
  s"   
 
ÿÿ
rG  c                   @   ó   e Zd ZdZdd„ ZdS )ÚMonthattendanceAPIViewa†  
    Status per day: P / A / H (Holiday table) / W (Setting.weekly_off) / '--' (future)
    Late  = in_time > shift_start + grace_minutes (Setting table)
    3 Late = 0.5 day cut from Present (effective_present)
    OT    = out_time - 6:45 PM (static)
    Check-in irundhu check-out illa (past date) = A + no_checkout flag
    Optional filter: ?employee_id=<id>  (illa na ellame varum)
    c           !         s”  |j  d¡}|stdditjd�S zt |d d¡ ¡ ‰W n ty/   tdditjd� Y S w ˆj	dkrFˆj
ˆjd	 d	d	d
�td	d� }nˆj
ˆj	d	 d	d�td	d� }‡fdd„t|ˆ jd	 ƒD ƒ}tƒ ‰ tˆ|ƒ‰|j  d¡px|j  d¡}tjjd	d�}|rŒ|dkrŒ|j|d�}tjjˆ|gd�}	t ¡ }
g }|D ]è‰‡fdd„|	D ƒ}d } }}d}i }|D ]�}| d¡}d}d}| ¡ }|ˆv }|ˆ d v }|rÒd}nf|r×d}na||
krÞd}nZ||v �r2|| }|jdk�r+|jsý||
k rýd}|d	7 }d}n;d }|d	7 }t|jˆ ƒ�r|d	7 }|j�rt|j|jƒnd}|dk�r*||7 }t|d! ƒ}nd}|d	7 }nd}|d	7 }|||ˆ |¡d"œ||< q´|d# }t|d$ d%ƒ}ttd|| ƒd%ƒ}t |d& ƒ}t |d& d! ƒ} | !ˆj"t#ˆƒt$ˆƒ||||d'›d(| d'›�||||d)œ¡ q�t‡ ‡fd*d„|D ƒ|ˆ d+  d,¡ˆ d-  d,¡ˆ d. ˆ d/  d,¡t%ˆ  d0¡�pµd1d2„ ˆ d D ƒƒd3d4œd5d„ t%ˆ &¡ ƒD ƒd6œtj'd�S )7NrÊ   r4   ú!val is required. Use ?val=YYYY-MMr-   ú-01r  úInvalid val format. Use YYYY-MMé   r   ©ÚyearÚmonthÚday©Údays©rP  rQ  c                    ó   g | ]	}ˆ t |d � ‘qS ©rR  r    r&  ©Ú	first_dayrd   re   r,    ó    ÿz.MonthattendanceAPIView.get.<locals>.<listcomp>r  rK  rU  r®   r  c                    s    i | ]}|j ˆ jkr|j|“qS rd   )r  rF   r   ©r'  Úrr  rd   re   r    ó     z.MonthattendanceAPIView.get.<locals>.<dictcomp>r   ú00:00Fr  ÚHÚWú--r  ÚATÚPr£   ©r.   ÚotÚno_checkoutr-  rk  r¬  r@   r@  rò  ró  )rF   rE   r6   Útotal_presentÚeffective_presentÚtotal_absentÚtotal_otrE  Úhalf_day_cutsÚlate_deductionÚ
attendancec                    óJ   g | ]!}|  d ¡|j|  d¡|  d¡| ¡ ˆ d v |ˆv ˆ |¡dœ‘qS ©r  z%dz%ar  )r   Zday_numÚdisplayÚday_namer.  r,  r-  ©r<  rQ  r6  rP   r+  ©r@  rA  rd   re   r,  c  ó    
øùÿr  r  r   r!  r  r"  c                 S   r#  rd   r$  r&  rd   rd   re   r)  u  r*  z-MonthattendanceAPIView.get.<locals>.<setcomp>z3 Late = 0.5 day deduction)r  r   r!  r  r  Z	late_rulec                 S   ó    g | ]\}}|  d ¡|dœ‘qS ©r  r  ©r<  ©r'  r(  Únrd   rd   re   r,  x  ó    ÿÿ)rS  ÚreportÚconfigr7  )(rÐ   rP   r,   r.   rQ   r   r1  r   rÔ   rP  rp  rO  r   ÚrangerS  r4  r5  r   rT   r’   r   r2  r3  r<  r6  r¶  r#  r­  rG  rö  r·  r�  rÓ   rÕ   rF   r  r  r=  Úitemsr[   )!r_   r`   r�   r³   rÊ   Úlast_dayÚdates_in_monthÚ	emp_paramrX  Zall_attendancer3  Zfinal_reportÚemp_recordsÚtotal_pÚtotal_arE  Útotal_ot_secÚattendance_maprQ  Úday_strÚ
ot_displayre  Úwdr,  r.  Ústatus_codeÚrecÚsecrj  rk  rg  Ú
total_ot_hÚ
total_ot_mrd   )r@  rG  rX  rA  re   rP   ó
  sØ   þ
þÿ
"
ÿ



€
ü
õ
ö ú
þêæzMonthattendanceAPIView.getN©rh   ri   rj   rI  rP   rd   rd   rd   re   rI  é
  ó    	rI  c                   @   rH  )ÚTodayAttendanceAPIViewuZ  
    Settings + Holiday table wise, particular date-oda present/absent.
 
    âœ… Past date la check-in irundhu check-out illa na -> ABSENT list la varum
       (no_checkout = True, in_time kaattum). Admin out time update pannalaam
       illa delete pannalaam.
    âœ… Innaiku (today) check-out illa na innum velai la irukkaanga -> PRESENT.
    c                 C   s|  |j  d¡}z|rt |d¡ ¡ nt ¡ }W n ty(   tddit	j
d� Y S w t ¡ }tƒ }t||ƒ}| |¡}| ¡ |d v }|rFdn|rJdnd}	|j  d	¡}
tjjd
d�}|
re|
dkre|j|
d�}tjj||d� d¡}g }g }|D ]r}|j}t|j|ƒ}|j|jt|ƒt|ƒt|dd ƒr•|jjnd |jrŸ|j d¡nd|jr©|j d¡nd|r®dnd||jddddœ}|jsÉ||k rÉd|d< | |¡ qv|jrÓt |j|jƒnd}t!|d ƒ|d< |d |d< | |¡ qvdd„ |D ƒ}dd„ |j"|d �D ƒ}t#|| d!d"„ d#�}t| $¡ |	t%|ƒ||t&|ƒt&|ƒt&|ƒt#|d$d"„ d#�||d%  d¡|d&  d¡|d' |d(  d¡d)œd*œt	j'd�S )+Nr   r  r4   úInvalid date. Use YYYY-MM-DDr-   r  r8  Úworkingr  r   rU  r®   )r   Zemployee__inrd  r  r  r`  ÚLatezOn TimeFr]  r   )Úattendance_idrF   rE   r6   r  r­  r¶  Zentry_statusÚlater®  re  Úot_timeÚ
ot_minutesTre  r£   r–  r—  c                 S   s   g | ]}|j ‘qS rd   ©r  rZ  rd   rd   re   r,    ó    z.TodayAttendanceAPIView.get.<locals>.<listcomp>c              
   S   s<   g | ]}d |j t|ƒt|ƒt|dd ƒr|jjnd ddœ‘qS )Nr  F)r”  rF   rE   r6   r  re  )rF   r  r  r:  r  rX   r;  rd   rd   re   r,    s    	ùúÿr  c                 S   s   | d  | d fS )Nre  rE   rd   ©r‘  rd   rd   re   Ú<lambda>,  r™  z,TodayAttendanceAPIView.get.<locals>.<lambda>©Úkeyc                 S   s   | d S )NrE   rd   rš  rd   rd   re   r›  8  s    r  r   r!  r  ©r  r   r!  r  )r   Úday_typer,  r-  r.  rD  Zabsent_countZno_checkout_countÚpresent_listrF  r{  )(r©   rP   r   r1  r   r2  r3  rÔ   r,   r.   rQ   r4  r5  r6  r   rT   r’   r   Úselect_relatedrd  r#  r­  rF   r  r  r:  r  rX   r<  r¶  r®  rÕ   rG  rö  r9  r=  rª  r7  rŒ  r[   )r_   r`   r>  r?  r3  r@  rA  rB  r.  rŸ  r€  rX  Úrecordsr   Zno_checkout_listrŠ  rG  r•  Úitemr‹  Zrecorded_idsZplain_absentrF  rd   rd   re   rP   Ü  s¢   ÿþ
þÿ

ÿþ
ó

	÷þüõïzTodayAttendanceAPIView.getNrŽ  rd   rd   rd   re   r�  Ò  r�  r�  c                   @   r0   )ÚUpdateAttendanceOutTimeAPIViewc           	   	   C   s`  t |j d¡pdƒ ¡ }|stdditjd�S d }dD ]}zt ||¡ 	¡ }W  q2 t
y1   Y qw |d u r?tdditjd�S ztj d¡j|d	�}W n tjy^   tdd
itjd� Y S w |j}|d urot|dƒro| 	¡ }|d ur‡||kr‡tdd| d¡› d�itjd�S ||_| ¡  t|j|jƒ}td|j|j|j ¡ |j d¡t|d ƒdœtjd�S )Nr¶  rË   r4   z)out_time is required. Use HH:MM (24-hour)r-   )rÑ  rÐ  z%Invalid out_time. Use HH:MM (24-hour)rd  r®   úAttendance record not foundr%   z Out time must be after in time (r  ú)zOut time updatedr£   )r9   r”  r  r   r¶  r–  )r^   r:   rP   rÑ   r,   r.   rQ   r   r1  r%   rÔ   r   rT   r¡  rU   r”   r­  rA  r<  r¶  r˜   rG  rF   r  r   rª  rö  r[   )	r_   r`   r”  Zout_strÚparsedrÜ  rŠ  Zin_tr‹  rd   rd   re   r�  J  s`   þÿþ
þÿþ

úùz$UpdateAttendanceOutTimeAPIView.patchNr‚  rd   rd   rd   re   r¤  H  rk   r¤  c                   @   r0   )ÚDeleteAttendanceAPIViewc                 C   sT   z	t jj|d�}W n t jy   tdditjd� Y S w | ¡  tdditjd�S )Nr®   r4   r¥  r-   r9   zAttendance deleted)	r   rT   rP   rU   r,   r.   r”   r¬   r[   )r_   r`   r”  rŠ  rd   rd   re   r¬   †  s   
þÿþzDeleteAttendanceAPIView.deleteN)rh   ri   rj   r¬   rd   rd   rd   re   r¨  „  rk   r¨  c                   @   rH  )ÚEmployeeDashboardAPIViewz²
    GET /employee-dashboard/?employee_id=<id>
 
    employee_id kudukkalana, login panna user-oda user_data-la
    irundhu employee_id anuppanum (frontend already anuppum).
    c                 C   sp  |j  d¡}|stdditjd�S z	tjj|d�}W n tjy-   tdditjd� Y S w t	ƒ }t
 ¡ }|jdd�}|jd	krQ|j|jd ddd
�tdd� }n|j|jd dd�tdd� }t||ƒ}tjj|d� d¡ ¡ }	tjj|j|d� ¡ }
||v }| ¡ |d v }|r�d}n|r’d}n|
r¤|
jdkr¤t|
j|ƒr¡dnd}nd}tjj|j||gd�}dd„ |D ƒ}d } }}|}|t||ƒk�r| ¡ |d v }||v }|s÷|s÷| |¡}|ró|jdkró|d7 }t|j|ƒrò|d7 }n|d7 }|tdd�7 }|t||ƒksÈ|d }t|d dƒ}ttd|| ƒdƒ}ttjddœd |jt|ƒt|ƒt |dd ƒ�r4|j!j"nd d œ| #¡ ||
�rH|
j�rH|
j $d!¡nd"|
�rV|
j%�rV|
j% $d!¡nd"d#œ|d$  $d!¡|d%  $d!¡|d& |d'  $d!¡t&d(d)„ |d D ƒƒt&| d*¡�p‡d+d,„ |d D ƒƒd-œd.d/„ t&| '¡ ƒD ƒ|	�r |	j( #¡ |	j)d0œnd d1œ| $d2¡||||||d3œd4œd5œtjd�S )6Nr  r4   úemployee_id is requiredr-   r®   rw  r   )rQ  rM  rN  rR  rT  )Ú	date__gter   ©r  r   r  r	   z
Weekly Offr  r“  úNot Checked In©r  r  c                 S   ó   i | ]}|j |“qS rd   rƒ  rZ  rd   rd   re   r  Ò  r*  z0EmployeeDashboardAPIView.get.<locals>.<dictcomp>r   rk  r¬  r@   rD   r5   r  )rF   rE   r6   r  r  r`  )r   r.   r­  r¶  r  r   r!  r  c                 s   s   � | ]}t | V  qd S rm   r$  r&  rd   rd   re   Ú	<genexpr>ý  ó   € z/EmployeeDashboardAPIView.get.<locals>.<genexpr>r"  c                 S   r#  rd   r$  r&  rd   rd   re   r)    r*  z/EmployeeDashboardAPIView.get.<locals>.<setcomp>)r›   r+  r!  r  r  Zweekly_off_daysc                 S   s   g | ]\}}|  ¡ |d œ‘qS )r  )rª  )r'  Úd2rx  rd   rd   re   r,    s    ÿÿz0EmployeeDashboardAPIView.get.<locals>.<listcomp>r  )Ú
this_monthÚnextú%B %Y)Úmonth_labelrf  rg  rh  rE  rj  rk  )rd  r3  rå   r7  Zmonth_summaryrq   )*r©   rP   r,   r.   rQ   r   rT   rU   r”   r4  r2  r3  rp  rP  rO  r   r5  r	   r’   rÒ   Úfirstr   rF   r6  r#  r­  r›  r·  r�  r[   r  r  r:  r  rX   rª  r<  r¶  r=  r}  r   rE   )r_   r`   r¹  rG  r@  r3  Úmonth_startÚ	month_endZmonth_holidaysÚupcomingZ	today_recZis_holiday_todayZis_weekly_off_todayZtoday_statusZmonth_recordsZrec_by_dater‚  rƒ  rE  r(  Zis_offZis_holrŠ  rj  rk  rg  rd   rd   re   rP   £  s¾   þ
þÿ
"

ÿ
€õ
üüÿ÷
þÿùùÚý3ÍzEmployeeDashboardAPIView.getNrŽ  rd   rd   rd   re   r©  ›  ó    r©  c                   @   rH  )ÚEmployeeAttendanceAPIViewa  
    GET /employee-attendance/?employee_id=<id>&date=YYYY-MM-DD

    date illama kudutha, today edukkum.
    Status: Present / Late / H (Holiday table) / W (Setting.weekly_off) / Absent / Not Checked In
    OT = out_time - 6:45 PM (static), out_time irundha mattum
    c                 C   s0  |j  d¡}|stdditjd�S z	tjj|d�}W n tjy-   tdditjd� Y S w |j  d¡}z|r?t	 
|d¡ ¡ nt ¡ }W n tyV   tdd	itjd� Y S w tƒ }t||ƒ}| |¡}| ¡ |d
 v }	tjj|j|d� ¡ }
d}d}|r€d}n>|	r…d}n9|
r¥|
jdkr¥t|
j|ƒ}|
jrœt|
j|
jƒnd}|r¢dnd}n|
rªd}n|t ¡ kr³d}n|t ¡ k r¼d}nd}ttjddœd |jt|ƒt|ƒdœ| ¡ |||
rß|
jrß|
j d¡nd|
rë|
jrë|
j d¡ndt|d ƒ|dœt |ƒ|	|d  d¡|d  d¡|d |d  d¡dœd œd!œtjd�S )"Nr  r4   rª  r-   r®   rw  r   r  r‘  r  r¬  Fr   r^  r_  r  r“  ÚAbsentr`  r­  rD   r5   r:  r  r£   )r.   r•  r­  r¶  r–  r-  r  r   r!  r  rž  )rd  r   Úrecordr,  r.  r{  rq   )!r©   rP   r,   r.   rQ   r   rT   rU   r”   r   r1  r   r2  r3  rÔ   r4  r5  r6  r   r’   rF   r·  r#  r­  r¶  rG  r[   r  r  rª  r<  rö  r7  )r_   r`   r¹  rG  r>  r?  r@  rA  rB  r.  rŠ  r•  r‹  Zrecord_statusrd   rd   re   rP   -  s”   þ
þÿÿþ
þÿ


ý
úüïýåzEmployeeAttendanceAPIView.getNrŽ  rd   rd   rd   re   r¼  $  ó    r¼  c                   @   rH  )Ú EmployeeMonthlyAttendanceAPIViewzø
    GET /employee-monthly-attendance/?employee_id=<id>&val=YYYY-MM
 
    Same day-status rules as MonthattendanceAPIView, but scoped to
    ONE employee and returns a single "row" object instead of a list.
    OT = out_time - 6:45 PM (static)
    c                    s¬  |j  d¡}|j d¡}|stdditjd�S |s"tdditjd�S z	tjj|d�}W n tjy>   tdditj	d� Y S w zt
 |d	 d
¡ ¡ ‰W n ty]   tdditjd� Y S w ˆjdkrtˆjˆjd ddd�tdd� }nˆjˆjd dd�tdd� }‡fdd„t|ˆ jd ƒD ƒ}tƒ ‰ tˆ|ƒ‰dd„ tjj|jˆ|gd�D ƒ}t ¡ }d }	 }
}d}i }|D ]’}| d
¡}d}d}| ¡ }|ˆv }|ˆ d v }|rÙd}nh|rÞd}nc||kråd}n\||v �r;|| }|jdk�r4|j�s||k �rd}|
d7 }
d}n;d }|	d7 }	t|jˆ ƒ�r|d7 }|j�r"t |j|jƒnd}|dk�r3||7 }t!|d! ƒ}nd}|
d7 }
nd}|
d7 }
|||ˆ |¡d"œ||< q»|d# }t"|d$ d%ƒ}t"t#d|	| ƒd%ƒ}t$|d& ƒ}t$|d& d! ƒ}ttj%d'd(œd |jt&|ƒt'|ƒd)œ‡ ‡fd*d„|D ƒ|	||
|d+›d,|d+›�||||d-œˆ d.  d/¡ˆ d0  d/¡ˆ d1 ˆ d2  d/¡t(ˆ  d3¡�pÀd4d5„ ˆ d D ƒƒd6œd7d„ t(ˆ )¡ ƒD ƒd8œd9œtj%d�S ):Nr  rÊ   r4   rª  r-   rJ  r®   rw  rK  r  rL  rM  r   rN  rR  rT  c                    rU  rV  r    r&  rW  rd   re   r,  K  rY  z8EmployeeMonthlyAttendanceAPIView.get.<locals>.<listcomp>c                 S   r¯  rd   rƒ  rZ  rd   rd   re   r  R  s    ÿÿz8EmployeeMonthlyAttendanceAPIView.get.<locals>.<dictcomp>r®  r   r]  Fr  r^  r_  r`  r  ra  Trb  r£   rc  rk  r¬  r@   r@  rD   r5   r:  c                    rm  rn  rq  r+  rr  rd   re   r,  ™  rs  rò  ró  )rf  rg  rh  ri  rE  rj  rk  rl  r  r  r   r!  r  r"  c                 S   r#  rd   r$  r&  rd   rd   re   r)  µ  r*  z7EmployeeMonthlyAttendanceAPIView.get.<locals>.<setcomp>)r  r   r!  r  r  c                 S   rt  ru  rv  rw  rd   rd   re   r,  ¸  ry  )rd  rS  rT  r{  Zholidays_this_monthrq   )*r©   rP   rÐ   r,   r.   rQ   r   rT   rU   r”   r   r1  r   rÔ   rP  rp  rO  r   r|  rS  r4  r5  r   r’   rF   r2  r3  r<  r6  r¶  r#  r­  rG  rö  r·  r�  rÓ   r[   r  r  r=  r}  )r_   r`   r¹  rÊ   rG  r~  r  r�  r3  r‚  rƒ  rE  r„  r…  rQ  r†  r‡  re  rˆ  r,  r.  r‰  rŠ  r‹  rj  rk  rg  rŒ  r�  rd   )r@  rX  rA  re   rP   '  sð   þþ
þÿ
þÿ
"
ÿ
þ


€
ü
ý
öøÿû	
þÛý-Óz$EmployeeMonthlyAttendanceAPIView.getNrŽ  rd   rd   rd   re   rÀ    r¿  rÀ  )ÚLeaveRequestsc                 C   sn   | j | jt| jƒt| jƒ| j| j ¡ | j ¡ | j	| j
| j| j| jr'| j d¡nd | jr3| j d¡dœS d dœS )Nz%Y-%m-%d %I:%M %p)rF   r  Úemployee_nameZemployee_codeÚ
leave_typer  r  Ú
days_countr¿  r.   Ú
admin_noteÚ
applied_atÚreviewed_at)rF   r  r  rd  r  rÃ  r  rª  r  rÄ  r¿  r.   rÅ  rÆ  r<  rÇ  )Úlvrd   rd   re   Ú
leave_jsonÆ  s    óórÉ  c                   @   s    e Zd ZdZdd„ Zdd„ ZdS )ÚEmployeeLeaveAPIViewz°
    GET  /employee-leave/?employee_id=<id>              -> list own leave requests
    POST /employee-leave/  {employee_id, leave_type, start_date, end_date, description}
    c                 C   sX   |j  d¡}|stdditjd�S tjj|d� d¡}ttj	ddœd d	d
„ |D ƒdœƒS )Nr  r4   rª  r-   r˜  rd  rD   r5   c                 S   r>  rd   ©rÉ  ©r'  rÈ  rd   rd   re   r,  è  r*  z,EmployeeLeaveAPIView.get.<locals>.<listcomp>rq   )
r©   rP   r,   r.   rQ   rÁ  rT   r’   r¡  r[   )r_   r`   r¹  Úleavesrd   rd   re   rP   ß  s   
ýzEmployeeLeaveAPIView.getc              	   C   s6  |j }| d¡}|stdditjd�S z	tjj|d�}W n tjy/   tdditjd� Y S w | dd¡}| d	¡}| d
¡}| d¡pFd 	¡ }zt
 |d¡ ¡ }	t
 |d¡ ¡ }
W n ttfyo   tdditjd� Y S w |
|	k r}tdditjd�S tjj|||	|
|dd�}ttjddœd dt|ƒdœtjd�S )Nr  r4   rª  r-   r®   rw  rÃ  ÚCasualr  r  r¿  rË   r  z1start_date/end_date required in YYYY-MM-DD formatú$End date cannot be before start dateÚPending)rd  rÃ  r  r  r¿  r.   rD   r5   zLeave request submittedr8   )r:   rP   r,   r.   rQ   r   rT   rU   r”   rÑ   r   r1  r   rß  rÔ   rÁ  r•   r¹   rÉ  )r_   r`   r:   r¹  rG  rÃ  Z	start_strZend_strr¿  r  r  rÈ  rd   rd   re   rf   ë  sP   
ÿ


þÿú
üûzEmployeeLeaveAPIView.postN)rh   ri   rj   rI  rP   rf   rd   rd   rd   re   rÊ  Ù  s    rÊ  c                   @   s0   e Zd ZdZdd„ Zdd„ Zdd„ Zdd	„ Zd
S )ÚEmployeeLeaveDetailAPIViewzÈ
    GET    /employee-leave/<id>/
    PUT    /employee-leave/<id>/   -- edit, only allowed while status="Pending"
    DELETE /employee-leave/<id>/   -- cancel, only allowed while status="Pending"
    c                 C   s.   zt j d¡j|d�W S  t jy   Y d S w )Nrd  r®   )rÁ  rT   r¡  rP   rU   ©r_   rF   rd   rd   re   rn     s
   ÿz%EmployeeLeaveDetailAPIView.get_objectc                 C   s<   |   |¡}|stdditjd�S ttjddœd t|ƒdœƒS )Nr4   úLeave request not foundr-   rD   r5   rq   )rn   r,   r.   r”   r[   rÉ  ©r_   r`   rF   rÈ  rd   rd   re   rP   %  s   

ýzEmployeeLeaveDetailAPIView.getc                 C   s$  |   |¡}|stdditjd�S |jdkr#tdd|j› d�itjd�S |j}z d|v r6t |d d¡ ¡ |_	d	|v rEt |d	 d¡ ¡ |_
W n tyX   tdd
itjd� Y S w |j
|j	k rhtdditjd�S d|v rq|d |_d|v r| d¡p{d ¡ |_| ¡  ttjddœd dt|ƒdœƒS )Nr4   rÓ  r-   rÐ  u&   Cannot edit â€” this leave is already rÓ  r  r  r  úDates must be YYYY-MM-DDrÏ  rÃ  r¿  rË   rD   r5   zLeave request updatedr8   )rn   r,   r.   r”   rQ   r:   r   r1  r   r  r  rÔ   rÃ  rP   rÑ   r¿  r˜   r[   rÉ  )r_   r`   rF   rÈ  r:   rd   rd   re   rÂ   /  s>   

þ€ÿ

üzEmployeeLeaveDetailAPIView.putc                 C   sf   |   |¡}|stdditjd�S |jdkr#tdd|j› d�itjd�S | ¡  ttjddœd d	d
œƒS )Nr4   rÓ  r-   rÐ  u(   Cannot cancel â€” this leave is already rÓ  rD   r5   zLeave request cancelledr‡   )rn   r,   r.   r”   rQ   r¬   r[   rÔ  rd   rd   re   r¬   R  s   

þ
ýz!EmployeeLeaveDetailAPIView.deleteN)rh   ri   rj   rI  rn   rP   rÂ   r¬   rd   rd   rd   re   rÑ    s    
#rÑ  c                   @   rH  )ÚAdminLeaveListAPIViewz³
    GET /admin-leave/?employee_id=&status=&val=YYYY-MM
 
    employee_id="all" or omitted -> every employee
    status omitted -> every status
    val omitted -> every month
    c              	   C   sò   t j d¡ ¡ }|j d¡}|r|dkr|j|d�}|j d¡}|r,|dkr,|j|d�}|j d¡}|rQztt| 	d¡ƒ\}}|j||d	�}W n	 t
yP   Y nw ttjd
dœd dd„ |D ƒ|jdd� ¡ |jdd� ¡ |jdd� ¡ dœdœƒS )Nrd  r  rU  r˜  r.   r-   rÊ   ú-)Zstart_date__yearZstart_date__monthrD   r5   c                 S   r>  rd   rË  rÌ  rd   rd   re   r,  ƒ  r*  z-AdminLeaveListAPIView.get.<locals>.<listcomp>rÐ  ÚApprovedÚRejected)ÚpendingZapprovedÚrejected©r.   r4   r:   Úsummary)rÁ  rT   r¡  rU  r©   rP   r’   ÚmaprÓ   r  rÔ   r,   r.   r[   r  )r_   r`   r  r¹  Ústatus_filterrÊ   rO  rP  rd   rd   re   rP   m  s0   ÿ
ýüzAdminLeaveListAPIView.getNrŽ  rd   rd   rd   re   rÖ  d  r¿  rÖ  c                   @   rH  )ÚAdminLeaveActionAPIViewzX
    PUT /admin-leave/<id>/action/   {status: "Approved" | "Rejected", admin_note?}
    c                 C   sÐ   zt j d¡j|d�}W n t jy   tdditjd� Y S w |j d¡}|dvr3tdditj	d�S ||_|j d	¡p=d
 
¡ pAd |_|jjrJ|jnd |_t ¡ |_| ¡  ttjddœd d|› �t|ƒdœƒS )Nrd  r®   r4   rÓ  r-   r.   )rØ  rÙ  rÐ  z-status must be Approved, Rejected, or PendingrÅ  rË   rD   r5   zLeave marked as r8   )rÁ  rT   r¡  rP   rU   r,   r.   r”   r:   rQ   rÑ   rÅ  ra   rr  Úreviewed_byr   r–   rÇ  r˜   r[   rÉ  )r_   r`   rF   rÈ  r  rd   rd   re   rÂ   ’  s,   ÿþ

üzAdminLeaveActionAPIView.putN)rh   ri   rj   rI  rÂ   rd   rd   rd   re   rà  �  s    rà  c                   @   rH  )ÚTodayUpcomingLeaveAPIViewuÃ   
    GET /leave-today-upcoming/?employee_id=
 
    Used by the admin "View Employee" screen â€” shows who's on
    Approved leave today, and Approved leaves starting in the
    next 30 days.
    c                 C   sž   |j  d¡}t ¡ }tjjdd� d¡}|r |dkr |j|d�}|j||d�}|j||tdd	� d
� 	d¡}t
tjddœd dd„ |D ƒdd„ |D ƒdœdœƒS )Nr  rØ  r-   rd  rU  r˜  )Ústart_date__lteZend_date__gterú  rR  )Zstart_date__gtrã  r  rD   r5   c                 S   r>  rd   rË  rÌ  rd   rd   re   r,  È  r*  z1TodayUpcomingLeaveAPIView.get.<locals>.<listcomp>c                 S   r>  rd   rË  rÌ  rd   rd   re   r,  É  r*  )r3  rº  rq   )r©   rP   r2  r3  rÁ  rT   r’   r¡  r   rÒ   r,   r.   r[   )r_   r`   r¹  r3  r  Ztoday_leavesZupcoming_leavesrd   rd   re   rP   ·  s$   ÿþ
þýzTodayUpcomingLeaveAPIView.getNrŽ  rd   rd   rd   re   râ  ®  r¿  râ  )r   r   )Útransaction)r*   ÚCountr    )r   Ú	FeeClientÚFeeÚ
FeePaymentTc                 C   s>   t  ¡ }t  | ¡rt  | ¡ ¡ t  |¡ ¡ kS |  ¡ | ¡ kS rm   )r   r–   Úis_awareÚ	localtimer   )Údtr–   rd   rd   re   Ú	_is_todayø  s   
rì  c                 C   ó,   |rt | jƒt |ƒkrdS tot| jƒ  S )zGPayment-ah collect pannina employee mattum (same day-na) edit pannalam.FN)r^   Úcollected_by_idÚEMPLOYEE_EDIT_SAME_DAY_ONLYrì  Ú
created_at)Úpaymentr  rd   rd   re   Ú_can_edit_paymentÿ  s   rò  c                 C   rí  )zoFee record-ah create pannina employee mattum (same day-na) service /
    due date / fee amount ellam maathalam.FN)r^   rY   rï  rì  rð  )Úfeer  rd   rd   re   Ú_can_edit_fee  s   rô  c                 C   s   | j | j| j| j| j| jdœS )N©rF   Úclient_coderE   rP  Ú
gst_numberr.   rõ  )Úcrd   rd   re   Úclient_json  s   úrù  c              
   C   sP   | j | jt| jƒ| j d¡| j| j| j| j	r| j	j nd | j	r"| j	j
nddœdœS )Nr  r  )rF   rE   )rF   Úfee_idÚamountÚpayment_dateÚpayment_modeÚreference_noÚnoteÚcollected_by)rF   rú  r^   rû  rü  r<  rý  rþ  rÿ  r   rE   )r  rd   rd   re   Úpayment_json  s   
þør  c                 C   sˆ   | j t| jƒ| jdkr| jn| jpdt| jƒ| j| j 	d¡| j
r&| j
 	d¡nd t| jƒt| jƒ| jdœ
}|rBdd„ | j ¡ D ƒ|d< |S )NÚOtherr  )
rF   ÚclientÚserviceÚ
fee_amountÚbilling_frequencyÚdue_dateÚnext_due_dateÚpaid_amountÚbalanceÚpayment_statusc                 S   r>  rd   )r  r   rd   rd   re   r,  :  r*  zfee_json.<locals>.<listcomp>Úpayments)rF   rù  r  r  Úservice_otherr^   r  r  r  r<  r  r	  r
  r  r  rU  )ÚfÚwith_paymentsr:   rd   rd   re   Úfee_json,  s   
ör  c                   @   r¡   )ÚFeeClientListAPIViewc                 C   sj   |j  dd¡ ¡ }tjjdd�}|r$| t|d�t|d�B t|d�B ¡}ttj	dd	œd d
d„ |D ƒdœƒS )NÚsearchrË   r   r-   rÍ   ©Zclient_code__icontains)Zmobile__icontainsrD   r5   c                 S   r>  rd   )rù  ©r'  rø  rd   rd   re   r,  Q  r*  z,FeeClientListAPIView.get.<locals>.<listcomp>rq   )
r©   rP   rÑ   ræ  rT   r’   r    r,   r.   r[   )r_   r`   r  Úclientsrd   rd   re   rP   C  s   ÿþÿ
ýzFeeClientListAPIView.getc                 C   sR  |j }| d¡p	d ¡ }| d¡pd ¡ }|s tdditjd�S t|ƒdk r/tdditjd�S |r@t d	|¡s@tdd
itjd�S t	j
j|dd� ¡ }|rattjddœd d|j› d�dt|ƒdœƒS |r„t	j
j|dd� ¡ }|r„ttjddœd d|j› d�dt|ƒdœƒS t	j
j||p‹d | d¡| d¡d�}ttjddœd ddt|ƒdœtjd�S )NrE   rË   rP  r4   zClient name is requiredr-   r@   zClient name is too shortz\d{10}z'Mobile number must be exactly 10 digitsr   )Úname__iexactr.   rD   r5   zA client named "z" already exists.T)r.   r4   r9   Zalready_existsr:   )rP  r.   z2A client with this mobile number already exists ("z").rÄ   r÷  )rE   rP  rÄ   r÷  zClient addedF)r:   rP   rÑ   r,   r.   rQ   rŒ  rR   Ú	fullmatchræ  rT   r’   r·  r[   rE   rù  r•   r¹   )r_   r`   r:   rE   rP  Zexisting_by_nameÚexistingrø  rd   rd   re   rf   T  s\   þ
û
ûü
ûúzFeeClientListAPIView.postNr¼  rd   rd   rd   re   r  B  s    r  c                   @   r0   )ÚFeeClientDetailAPIViewc                 C   sÄ   z	t jj|d�}W n t jy   tdditjd� Y S w tjj|d�j	t
dƒd�d p-d	}tjj|d
� dd¡ ¡ }ttjddœd i t|ƒ¥t|ƒ|rR|j d¡nd |rZt|jƒnd dœ¥dœƒS )Nr®   r4   úClient not foundr-   ©Ú	client_idr
  ©Útr  r   ©Úfee__client_idú-payment_daterS  rD   r5   ú%d-%m-%Y)Úpending_balanceÚlast_payment_dateÚlast_payment_amountrq   )ræ  rT   rP   rU   r,   r.   r”   rç  r’   Ú	aggregater*   rè  rÒ   r·  r[   rù  r^   rü  r<  rû  )r_   r`   rF   r  r#  Úlast_paymentrd   rd   re   rP   “  s*   ÿ"ý
ÿüýzFeeClientDetailAPIView.getNr	  rd   rd   rd   re   r  ’  r�   r  c                   @   r0   )ÚFeeDetailAPIViewc                 C   sf   zt j d¡ d¡j|d�}W n t jy"   tdditjd� Y S w ttj	ddœd t
|d	d
�dœƒS )Nr  r  r®   r4   úFee record not foundr-   rD   r5   T©r  rq   )rç  rT   r¡  Úprefetch_relatedrP   rU   r,   r.   r”   r[   r  )r_   r`   rF   ró  rd   rd   re   rP   ²  s   ÿ

ýzFeeDetailAPIView.getNr	  rd   rd   rd   re   r(  ±  r�   r(  c                   @   r0   )ÚAdminFeesListAPIViewc           
      C   s¢  t j d¡ d¡}|j d¡}|j d¡}|j d¡}|j d¡}|j d¡}|rAz|jt |d¡ 	¡ d	�}W n	 t
y@   Y nw |r[z|jt |d¡ 	¡ d
�}W n	 t
yZ   Y nw |rg|dkrg|j|d�}|rs|dkrs|j|d�}|r�|dkr�|j|d� ¡ }| d¡}|jtdƒd�d p‘d}|jtdƒd�d p�d}	ttjddœd dd„ |D ƒt|ƒt|	ƒt||	 ƒ|jdd� ¡ |jdd� ¡ |jdd� ¡ dœdœƒS )Nr  r  ÚfromÚtor  r.   r  r  )Zdue_date__gte)Zdue_date__lterU  )r  )r  )Zpayments__collected_by_idú	-due_dater  r  r  r   r	  rD   r5   c                 S   r>  rd   ©r  ©r'  r  rd   rd   re   r,  å  r*  z,AdminFeesListAPIView.get.<locals>.<listcomp>rÐ  ÚPartialÚPaid)Útotal_billedÚtotal_collectedÚtotal_pendingÚpending_countZpartial_countZ
paid_countrÜ  )rç  rT   r¡  r+  r©   rP   r’   r   r1  r   rÔ   ÚdistinctrÒ   r&  r*   r,   r.   r[   r^   r  )
r_   r`   r  Úfrom_strÚto_strr  rß  Zservice_filterr4  r5  rd   rd   re   rP   Ã  sN   ÿÿ


úüzAdminFeesListAPIView.getNr	  rd   rd   rd   re   r,  Â  r�   r,  c                 C   sp   |j  d¡}|j  d¡}|j  d¡}|r|dkr| j|d�} |r*|dkr*| j|d�} |r6|dkr6| j|d�} | S )	u‡   employee_id / service / payment_mode â€” AdminFeesSummary & EmployeeCollectionReport
    rendayum idhu 3 filters-ah share pannikuranga.r  r  rý  rU  ©rî  ©Úfee__service©rý  N)r©   rP   r’   )Z
payment_qsr`   r  r  rý  rd   rd   re   Ú_apply_common_filtersñ  s   r?  c                   @   r0   )ÚAdminFeesSummaryAPIViewc                 C   s¢  t  ¡ }tj ddd¡}t||ƒ}t|j|d�jt	dƒd�d p!dƒt|j|j
|jd	�jt	dƒd�d p5dƒt|j|j
d
�jt	dƒd�d pGdƒt|j|d�jt	dƒd�d pXdƒdœ}|}|j d¡}|j d¡}|r„z|jt |d¡  ¡ d�}W n	 tyƒ   Y nw |ržz|jt |d¡  ¡ d�}W n	 ty�   Y nw | d¡jt	dƒd� d¡}dd„ |D ƒ}	|jt	dƒd�d p¾d}
ttjddœd |t|
ƒ|	dœdœƒS )Nró  Úfee__clientr   ©rü  rû  r  r  r   ©Úpayment_date__yearZpayment_date__month)rD  ©Zpayment_date__lte)r3  r³  Ú	this_yearZ
till_todayr-  r.  r  ©Zpayment_date__gter=  )r  ú-totalc                 S   s"   g | ]}|d  t |d ƒdœ‘qS )r=  r  )r  r  ©r^   ©r'  rT  rd   rd   re   r,  8  r<  z/AdminFeesSummaryAPIView.get.<locals>.<listcomp>rD   r5   )ÚcountsÚall_services_totalÚ
by_servicerq   )r   r3  rè  rT   r¡  r?  r^   r’   r&  r*   rO  rP  r©   rP   r   r1  rÔ   ÚvaluesÚannotaterÒ   r,   r.   r[   )r_   r`   r3  Úbase_qsrK  Zrange_qsr9  r:  Zby_service_rowsrM  rL  rd   rd   re   rP     sn   
ÿÿþþý ÿÿôÿÿÿýþ
ýýzAdminFeesSummaryAPIView.getNr	  rd   rd   rd   re   r@    r�   r@  c                   @   rH  )ÚEmployeeFeesAPIViewuÑ  
    GET /employee-fees/?employee_id=&from=&to=

    âœ… Date-wise filter â€” "from"/"to" kudukkaama vittal, DEFAULT-a "TODAY" oru
    naaà®³à¯� data mattume varum (munnadi "whole month" default-a irundhuchu,
    ippo employee login pà®£à¯�à®£à¯�à®®à¯�à®ªà¯‹à®¤à¯‡ "today's collection" first-a à®¤à¯†à®°à®¿à®¯à¯�à®®à¯�).
    "from"/"to" kudutha, andha range-oda data varum. Old "month=YYYY-MM"
    param-um still support pà®£à¯�à®£à¯�rom (backward-compatible).
    c              	      s  |j  d¡‰ ˆ stdditjd�S |j  d¡}|j  d¡}|j  d¡}tjjˆ d� d	d
¡}|s2|r¤|rNzt	 
|d¡ ¡ }|j|d�}W n	 tyM   Y nw |rjzt	 
|d¡ ¡ }|j|d�}W n	 tyi   Y nw |r‡|r‡||kr‡|t ¡  d¡kr}dnt	 
|d¡ d¡}nZ|r¡|r¡t	 
|d¡ d¡› dt	 
|d¡ d¡› �}n@d}n=|rÕztt| d¡ƒ\}	}
|j|	|
d�}t|	|
dƒ d¡}W n tyÔ   tdditjd� Y S w t ¡ }|j|d�}d}| d¡}|jtdƒd�d pñd}ttjddœd |t|ƒ| ¡ ‡ fd d!„|D ƒd"œd#œƒS )$Nr  r4   rª  r-   r-  r.  rP  r;  ró  rA  r  rG  rE  ÚTodayz%d %b %Yz%d %bu    â€“ zSelected ranger×  rC  r   rµ  zmonth must be YYYY-MMrB  r!  rû  r  r  r   rD   r5   c              
      sN   g | ]#}i t |ƒ¥|jj|jjj|jjj|jjt|ˆ ƒp!t|jˆ ƒd œ¥‘qS ))r  rö  Úclient_namer  Zcan_edit)	r  ró  r  r  rö  rE   r  rò  rô  r   ©r¹  rd   re   r,  –  s    	øÿúÿz+EmployeeFeesAPIView.get.<locals>.<listcomp>)r¶  r5  Úpayment_countr  rq   )r©   rP   r,   r.   rQ   rè  rT   r’   r¡  r   r1  r   rÔ   r3  r<  rÞ  rÓ   r  rÒ   r&  r*   r[   r^   r  )r_   r`   r9  r:  Z	month_strr  Z	from_dateZto_dateZrange_labelrO  rP  r3  r5  rd   rT  re   rP   W  st   ÿÿÿþÿÿÿ


	÷üýzEmployeeFeesAPIView.getNrŽ  rd   rd   rd   re   rQ  L  s    
rQ  c                   @   rH  )ÚEmployeeCollectionReportAPIViewu³  
    GET /employee-collection-report/?employee_id=&from=&to=&service=&payment_mode=&client_id=

    â”€â”€ employee_id="all" (illa kudukkaama vittal) â”€â”€
      "mode": "all" â€” ella employees-oda totals-um oru list-a varum (idha vachu
      "All Employees" filter-la, andha date range-oda ella employees collection-um
      oru table-a paakalam).

    â”€â”€ employee_id=<id> â”€â”€
      "mode": "employee" â€” andha à®’à®°à¯� employee-oda full report (Total Clients,
      Total Collections, payment history table) â€” munnadi maari thaan.

      client_id kà¯‚da kudutha, adhoda mà¯‡là¯‡ oru "client_summary" block-um varum:
      andha client-ku last payment eppo, evlo, à®‡à®ªà¯�à®ªà¯‹à®¤à¯� pending balance evlo nu.
      (client_summary â€” andha client-oda ella employees payments-um vachu
      calculate aagum, ellai ippove filter panna employee mattum illa â€”
      "last paid when/how much" nu client-oda real status idhà¯�.)
    c              
   C   s¾  |j  d¡}|j  d¡}|j  d¡}|j  d¡}|j  d¡}|j  d¡}|r*|dkrÄtj dd	d
¡}|rLz|jt |d¡ ¡ d�}W n	 t	yK   Y nw |rfz|jt |d¡ ¡ d�}W n	 t	ye   Y nw |rr|dkrr|j|d�}|r~|dkr~|j|d�}|rŠ|dkrŠ|j|d�}| 
dd¡jtdƒtdƒtddd�d� d¡}	|jtdƒd�d p¬d}
ttjddœd dt|
ƒdd „ |	D ƒd!œd"œƒS z	tjj|d#�}W n tjyà   td$d%itjd&� Y S w tjj|d'� dd	¡}|�rz|jt |d¡ ¡ d�}W n
 t	�y   Y nw |�r$z|jt |d¡ ¡ d�}W n
 t	�y#   Y nw |�r2|dk�r2|j|d�}|�r@|dk�r@|j|d�}|�rN|dk�rN|j|d�}| d(¡}|jtdƒd�d �p_d}
| 
d¡ ¡  ¡ }d }|�rÄ|dk�rÄzEtjj|d#�}tjj|d)�jtd*ƒd�d �pŒd}tjj|d� d+d,¡ ¡ }|j|jt|ƒ|�rª|j d-¡nd |�r³t|j ƒnd d.œ}W n tj�yÃ   Y nw ttjddœd d/|j|t|
ƒ|d0d „ |D ƒd1œd"œƒS )2Nr  r-  r.  r  rý  r  rU  ró  rA  r   r  rG  rE  r<  r>  r  rî  Úcollected_by__namerû  rF   r   T)r8  )r  rU  r  rH  r  r  r   rD   r5   c                 S   s8   g | ]}|d  |d pdt |d ƒ|d |d dœ‘qS )rî  rW  r  r  rU  r  )r  rÂ  Útotal_collectionsrU  Útotal_clientsrI  rJ  rd   rd   re   r,  ð  s    ú

ûÿz7EmployeeCollectionReportAPIView.get.<locals>.<listcomp>)ÚmoderX  rX  rq   r®   r4   rw  r-   r;  rü  r  r
  r!  rS  r"  )rö  rE   r#  r$  r%  rd  c                 S   s6   g | ]}|j  d ¡|jjj|jjt|jƒ|jdœ‘qS )r"  )r   r  r  rû  rý  )	rü  r<  ró  r  rE   r  r^   rû  rý  r   rd   rd   re   r,  ;  s    
úûÿ)rZ  rÂ  rY  rX  Úclient_summaryr  )!r©   rP   rè  rT   r¡  r’   r   r1  r   rÔ   rN  rO  r*   rå  rÒ   r&  r,   r.   r[   r^   r   rU   r”   r8  r  ræ  rç  r·  rö  rE   rü  r<  rû  )r_   r`   r¹  r9  r:  r  rý  r  r  ÚrowsrX  rd  rY  r[  r  r#  r'  rd   rd   re   rP   Â  sÐ   ÿÿ

üù	
øýýÿÿÿ
"ÿý
ûÿ
øúýz#EmployeeCollectionReportAPIView.getNrŽ  rd   rd   rd   re   rV  ®  s    rV  ZPtsImh5xd6D9V8yaSQoifOPlrj41c2TELqFwp7BkHCKeNAXgJYWWpKkENHqSL35VRrnJ6XlF8xBIgZfC4c                 C   sð  zÜt | jjpdƒ ¡ }|st d¡ W dS | dd¡ dd¡ dd¡}| d¡r.|dd	… }t|ƒd
ks8| 	¡ sCt d|› �¡ W dS t
jj| d�jtdƒd�d pSd}| j| }| jdkrd| jrd| jn| j}d| jj› d|jd›d|› d|d›d|j› d|j d¡› d�}d}tdddœ}	d|dd|d d!œ}
tj||	|
d"d#�}t d$|j¡ |jd%krÍ| ¡ }| d&¡d'u rÂt d(|› �¡ W d'S t d)|› �¡ W dS t d*|j› d+|j› �¡ W dS  ty÷ } zt d)|› �¡ W Y d	}~dS d	}~ww ),z9Payment save aana apparam client-ku Fast2SMS SMS anuppum.rË   zClient mobile number missingFz+91ri  r×  Ú0r   Né
   zInvalid client mobile number: )ró  rû  r  r  r   r  zHi zW,

Manju Associates
Payment Received Successfully

We have received your payment of Rs.z,.0fz for z.
Balance: Rs.z
Collected by: z
Date: r"  z;

Thank you for choosing Manju Associates.
Have a nice day!z#https://www.fast2sms.com/dev/bulkV2zapplication/json)ÚauthorizationÚacceptzcontent-typeÚqÚenglishrÝ   )Úrouter9   ÚlanguageZflashÚnumbersZsms_detailsé   )ÚheadersÚjsonÚtimeoutzFast2SMS Response: %sr¦   ÚreturnTz!Payment SMS sent successfully to zFast2SMS SMS failed: zFast2SMS HTTP Error r\  ) r^   r  rP  rÑ   Úloggerr4   rp  Ú
startswithrŒ  Úisdigitrè  rT   r’   r&  r*   r  r  r  rE   rû  rü  r<  ÚFAST2SMS_API_KEYÚrequestsrf   ÚinfoÚtextr‰  rh  rP   r{   Ú	exception)ró  rñ  rd  rM   Ú
total_paidr
  r  ÚbodyrX   rg  r:   r7   Úresultr€   rd   rd   re   Úsend_payment_smsX  s¬   

ÿýüû
ÿÿý
üüûú
ùÿýúüþ
ÿÿ
ÿÿÿ€ürv  c                   @   r0   )ÚFeePaymentCreateAPIViewc              
   C   sò  |j }| d¡}|stdditjd�S z	tjj|d�}W n tjy/   tdditjd� Y S w | d¡}|s@tdditjd�S zt	t
|ƒƒ}|d	krMt‚W n ttfyb   tdd
itjd� Y S w | d¡}|r‡z	tjj|d�}W n¦ tjy†   tdditjd� Y S w | d¡}| d¡}	|r•|	sžtdditjd�S z	tjj|d�}
W n tjyº   tdditjd� Y S w z
t |	d¡ ¡ }W n ty×   tdditjd� Y S w | d|¡}zt	t
|ƒƒ}|d	krët‚W n ttf�y   tdditjd� Y S w tjj|
| dd¡| d¡|| dd¡||d�}| d¡}z|�r+t |d¡ ¡ nt ¡ }W n t�yC   tdditjd� Y S w tjj||||| dd¡| d¡| d ¡d!�}| ¡  t|||ƒ ttjd"d#œd d$t|d%d&�d'œtjd�S )(Nr  r4   rª  r-   r®   rw  rû  zamount is requiredr   z&amount must be a valid positive numberrú  r)  r  r  z7client_id and due_date are required to create a new feer  r  zdue_date must be YYYY-MM-DDr  z*fee_amount must be a valid positive numberr  z
GST Filingr  r  ÚMonthly)r  r  r  r  r  r  rl  rü  zpayment_date must be YYYY-MM-DDrý  ÚCashrþ  rÿ  )ró  r   rû  rü  rý  rþ  rÿ  rD   r5   zPayment recordedTr*  r8   )r:   rP   r,   r.   rQ   r   rT   rU   r”   rÍ  r^   rÎ  rß  rç  ræ  r   r1  r   rÔ   r•   r3  rè  Úrefresh_from_dbrv  r¹   r  )r_   r`   r:   r  rd  rû  rú  ró  r  Zdue_date_strZ
fee_clientr  Zfee_amount_rawr  Zpayment_date_strrü  rñ  rd   rd   re   rf     sº   
ÿ
ÿÿ
ÿ

þÿÿÿ
þÿ

ù

ÿþÿ
ù	

üûzFeePaymentCreateAPIView.postNrg   rd   rd   rd   re   rw    r�   rw  c                   @   r0   )ÚClientHistoryAPIViewc              
      s\  |j  d¡}|stdditjd�S z	tjj|d�}W n tjtfy/   tdditj	d� Y S w t
jj|d� d¡ d	¡}|jtd
ƒtdƒtdƒd�}tjj|d� dd¡ dd¡}| ¡ }dd„ ‰ ttjddœd t|ƒt|d prdƒt|d pydƒt|d p€dƒdœ|r™|j d¡t|jƒ|jr•|jjnddœnd dd„ |D ƒ‡ fd d„|D ƒd!œd"œƒS )#Nr  r4   zclient_id is requiredr-   r®   r  r  r  r/  r  r	  r
  )ró  ÚpaidrÚ  r  ró  r   r!  rS  c                 S   s   | j dkr| j S | jpdS )Nr  )r  r  )r  rd   rd   re   Úsvc�  ó   z%ClientHistoryAPIView.get.<locals>.svcrD   r5   r   r|  rÚ  )Z	total_feers  r6  r"  r  )r   rû  r   c                 S   r>  rd   r0  r1  rd   rd   re   r,  ¡  r*  z,ClientHistoryAPIView.get.<locals>.<listcomp>c              
      sH   g | ] }|j |j|j d ¡t|jƒ|jˆ |jƒ|jr|jj	nddœ‘qS )r"  r  )rF   rú  r   rû  rý  r  r   )
rF   rú  rü  r<  r^   rû  rý  ró  r   rE   r   ©r}  rd   re   r,  ¢  s    
ø
ùÿ)r  Útotalsr'  Úfeesr  rq   )r©   rP   r,   r.   rQ   ræ  rT   rU   rÔ   r”   rç  r’   r¡  rÒ   r&  r*   rè  r·  r[   rù  r^   rü  r<  rû  r   rE   )r_   r`   r  r  r�  r€  r  Úlastrd   r  re   rP   }  sJ   ÿý
ý	ü
ý

öóýzClientHistoryAPIView.getNr	  rd   rd   rd   re   r{  |  r�   r{  c                   @   s4   e Zd Zdd„ Zdd„ Zdd„ Zdd„ Zd	d
„ ZdS )ÚFeePaymentDetailAPIViewc                 C   s2   zt j ddd¡j|d�W S  t jy   Y d S w )Nró  rA  r   r®   )rè  rT   r¡  rP   rU   rÒ  rd   rd   re   rn   ¼  s
   ÿz"FeePaymentDetailAPIView.get_objectc                 C   sx   |j }t|ƒ|jt|jƒ|j|jt|jƒ|j	|j
 d¡|jr$|j d¡nd t|jƒt|jƒ|jdœt||ƒt||ƒdœS )Nr  )rF   r  r  r  r  r  r  r  r	  r
  r  )rñ  ró  Zcan_edit_paymentZcan_edit_fee)ró  r  rF   rù  r  r  r  r^   r  r  r  r<  r  r	  r
  r  rò  rô  )r_   r  r  ró  rd   rd   re   Ú_detailÂ  s"   
õðzFeePaymentDetailAPIView._detailc                 C   sL   |   |¡}|stdditjd�S |j d¡}ttjddœd |  ||¡dœƒS )Nr4   úPayment not foundr-   r  rD   r5   rq   )rn   r,   r.   r”   r©   rP   r[   r„  ©r_   r`   rF   r  r  rd   rd   re   rP   ×  s   


ýzFeePaymentDetailAPIView.getc              	      sš  |   |¡}|stdditjd�S |j‰ ˆ  d¡}|s#tdditjd�S |j}t||ƒ}t	||ƒ}t
r4dnd}|sG|sGtdd|› d	�itjd�S d
}	d}
|set‡ fdd„|	D ƒƒretdd|› d	�itjd�S |st‡ fdd„|
D ƒƒrtdd|› d	�itjd�S dd„ }dd„ }dd„ }|j}|j}|j}tt|jƒƒ}z¡|rÓdˆ v r¨|ˆ d ƒ}ˆ  d¡r³|ˆ d ƒ}ˆ  d¡rÓdd„ tj d¡jD ƒ}ˆ d |vrÏ|dƒW S ˆ d }|�r7dˆ v rà|ˆ d ƒ}ˆ  d¡rüdd„ tj d¡jD ƒ}ˆ d |vrü|d ƒW S ˆ  d!¡�rd"d„ tj d!¡jD ƒ}ˆ d! |v�r|d#ƒW S ˆ  d$¡�r&|ˆ d$ ƒnd }ˆ  d%¡�r4|ˆ d% ƒnd }nd  }}W n ttf�yL   |d&ƒ Y S  t�yY   |d'ƒ Y S w |jj|jd(�jtdƒd)�d* �pntd+ƒ}|| |k�rƒ|d,|| › d-|› d.�ƒS t  !¡ �§ |�rìt"|�o“||j#kƒ}t"ˆ  d!¡�o¢ˆ d! |j$kƒ}||_ˆ  d¡�r²ˆ d |_%d/ˆ v �rÅˆ  d/¡�p¾d &¡ �pÃd |_'ˆ  d!¡�rÐˆ d! |_$|�rÖ||_#|�rÝ||_(n|�sã|�rè| )¡ |_(| *¡  |�r#||_||_||_d0ˆ v �rˆ  d0¡�pd &¡ �p	d |_+d1ˆ v �rˆ  d1¡�pd &¡ �pd |_,| *¡  n| -¡  W d   ƒ n	1 �s2w   Y  |   |¡}ttj.d2d3œd d4|  /||¡d5œƒS )6Nr4   r…  r-   r  rª  z (same day only)rË   z5Only the employee who entered this record can edit itrÓ  )rû  rü  rý  rþ  rÿ  )r  r  r  r  r  r  c                 3   ó   � | ]}|ˆ v V  qd S rm   rd   ©r'  Úk©r:   rd   re   r°  ø  r±  z.FeePaymentDetailAPIView.put.<locals>.<genexpr>zYou can't edit this paymentc                 3   r‡  rm   rd   rˆ  rŠ  rd   re   r°  ú  r±  zOYou can't edit the fee details (service / due date / fee amount) of this recordc                 S   s   t d| itjd�S )Nr4   r-   )r,   r.   rQ   )r»  rd   rd   re   Úbad   ó   z(FeePaymentDetailAPIView.put.<locals>.badc                 S   s   t  | d¡ ¡ S )Nr  )r   r1  r   rç  rd   rd   re   Ú
parse_date  ó   z/FeePaymentDetailAPIView.put.<locals>.parse_datec                 S   s   t t| ƒƒ}|dkrt‚|S )Nr   )rÍ  r^   rÎ  )rÚ  Úvrd   rd   re   Úparse_money  s   z0FeePaymentDetailAPIView.put.<locals>.parse_moneyrû  rü  rý  c                 S   ó   h | ]}|d  ’qS ©r   rd   r  rd   rd   re   r)    r*  z.FeePaymentDetailAPIView.put.<locals>.<setcomp>zInvalid payment moder  r  c                 S   r‘  r’  rd   r  rd   rd   re   r)     r*  zInvalid servicer  c                 S   r‘  r’  rd   r  rd   rd   re   r)  $  r*  zInvalid billing frequencyr  r  z&Amount must be a valid positive numberrÕ  r®   r  r  r]  u   Total collected (â‚¹u"   ) would exceed the fee amount (â‚¹ú).r  rþ  rÿ  rD   r5   zRecord updatedr8   )0rn   r,   r.   r”   r:   rP   rQ   ró  rò  rô  rï  rV   Úanyrû  rü  rý  rÍ  r^   r  rè  Ú_metaÚ	get_fieldÚchoicesrç  rÎ  rß  rÔ   r  r9  rF   r&  r*   rä  Úatomicr7  r  r  r  rÑ   r  r  Úcompute_next_due_dater˜   rþ  rÿ  Úrecalcr[   r„  )r_   r`   rF   r  r  ró  Zcan_payZcan_feeZruleZpay_keysZfee_keysr‹  r�  r�  Z
new_amountZnew_pay_dateZnew_modeZnew_fee_amountÚvalidZnew_dueZnew_next_dueÚothersZdue_changedZfreq_changedrd   rŠ  re   rÂ   â  sÖ   



þþ





€ÿ*ÿ
ÿ






€â
 

üzFeePaymentDetailAPIView.putc                 C   st   |   |¡}|stdditjd�S |j d¡p|j d¡}t||ƒs*tdditjd�S | 	¡  ttj
ddœd dd	œƒS )
Nr4   r…  r-   r  z9Only the employee who entered this payment can cancel it.rD   r5   zPayment removedr‡   )rn   r,   r.   r”   r©   rP   r:   rò  rV   r¬   r[   r†  rd   rd   re   r¬   ^  s   

þ
ýzFeePaymentDetailAPIView.deleteN)rh   ri   rj   rn   r„  rP   rÂ   r¬   rd   rd   rd   re   rƒ  »  s    |rƒ  c                   @   rH  )ÚEmployeeStatusAPIViewuÃ   
    Active / Inactive toggle mattum. geostatus-à®� à®‡à®¤à¯�à®² à®¤à¯Šà®Ÿà®¾à®¤à¯� â€”
    à®…à®¤à¯� à®¤à®©à®¿ concept, à®¤à®©à®¿ button, à®¤à®©à®¿ API (à®•à¯€à®´ EmployeeGeoStatusAPIView).
    c                 C   st   z	t jj|d�}W n t jy   tdddœddœdd� Y S w |jdkr'd	nd|_| ¡  td
ddœ|jddœƒS )Nr>   r¨   r«   r5   rw  ©r.   r4   r-   r   r@   r¦   rD   r}  r~  )r   rT   rP   rU   r,   r.   r˜   ©r_   r`   rF   rd  rd   rd   re   r�  t  s"   þ
ýÿýzEmployeeStatusAPIView.patchN©rh   ri   rj   rI  r�  rd   rd   rd   re   r�  n  s    r�  c                   @   rH  )ÚEmployeeGeoStatusAPIViewu“  
    âœ… à®ªà¯�à®¤à¯� API â€” "Anywhere Attendance" / WFH toggle.
    geostatus = 1 -> office geofence-à®� bypass à®ªà®£à¯�à®£à®¿ à®Žà®™à¯�à®•à®¿à®°à¯�à®¨à¯�à®¤à¯�à®®à¯� attendance mark à®ªà®£à¯�à®£à®²à®¾à®®à¯�
    geostatus = 2 (à®…à®²à¯�à®²à®¤à¯� default) -> office radius-à®•à¯�à®•à¯�à®³à¯� à®¤à®¾à®©à¯� attendance mark à®ªà®£à¯�à®£ à®®à¯�à®Ÿà®¿à®¯à¯�à®®à¯�
 
    PATCH /employee-geostatus/<id>/
    c                 C   s’   z	t jj|d�}W n t jy   tdddœddœdd� Y S w t|dƒ |jd	kr,d
nd	|_| ¡  tdddœ|j|jd	krDddœƒS ddœƒS )Nr>   r¨   r«   r5   rw  rž  r-   ZRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRr   r@   r¦   rD   zAnywhere attendance enabledu9   Anywhere attendance disabled â€” office location required)r.   Znew_geostatusr9   )r   rT   rP   rU   r,   r|   rO   r˜   rŸ  rd   rd   re   r�  �  s*   þ
ýÿ
ÿüúzEmployeeGeoStatusAPIView.patchNr   rd   rd   rd   re   r¡  ˆ  r»  r¡  c                   @   r½  )Ú
AnnAPIViewNc                 C   rÂ  )Nr®   )rF   r   r¿  rD   r5   rq   r«   Trñ   r‡   r   r   )rT  rF   r   r¿  )r
   rT   rP   rF   r   r¿  r,   r.   r[   r   r”   rU  rÕ   rÄ  rd   rd   re   rP   .  rÅ  zAnnAPIView.getc              
   C   s˜   |j }z'|d }|d }tjj|d |d |jd�}| ¡  ttjddœd ddœƒW S  t	yK } zttj
ddœd	d
|› �dœƒW  Y d }~S d }~ww )Nr   r¿  )r   r¿  rl  rD   r5   z!Announcement created successfullyr‡   r«   Tr·   )r:   r
   rT   r•   ra   r˜   r,   r.   r¹   rº   rQ   )r_   r`   r:   r   r¿  r´   r€   rd   rd   re   rf   _  s.   ý

ý
ý€ÿzAnnAPIView.postc              
   C   sÈ   |j }z*|d }|d }tjj|d�}|d |_|d |_| ¡  ttj	ddœd ddœƒW S  t
yB   ttjddœd	d
dœƒ Y S  tyc } zt|ƒ ttjddœd	ddœƒW  Y d }~S d }~ww )Nr   r¿  r®   rD   r5   z!Announcement updated successfullyr‡   r«   Trñ   r¿   )r:   r
   rT   rP   r   r¿  r˜   r,   r.   r[   r   r”   r{   r|   rQ   )r_   r`   rF   r:   r   r¿  r´   r€   rd   rd   re   rÂ   y  s8   



ý
ý
ý€þzAnnAPIView.putc                 C   rÈ  rÉ  )	r
   rT   rP   r¬   r,   r.   r[   r   r”   rË  rd   rd   re   r¬   �  rÌ  zAnnAPIView.deleterm   rÀ  rd   rd   rd   re   r¢  +  s    
1$r¢  c                   @   r½  )ÚLeaveAPIViewNc           	      C   s  |d ur=z#t jj|d�}|j|j|j|j|j|jdœ}t	tj
ddœd |dœƒW S  ty<   t	tjddœddd	œƒ Y S w z<d
}g }t jj|jd�D ]"}|d7 }|j}| d¡}| ||j|j|j|j||j|jdœ¡ qJt	tj
ddœd |dœƒW S  tyŽ   t	tjddœddd	œƒ Y S w )Nr®   )rF   rE   r¿  Ú	totaldaysÚ
leavedatesr.   rD   r5   rq   r«   Trñ   r‡   r   ró   r   ú%Y-%m-%d %H:%M:%S)rT  rF   ÚtypeÚreasonÚtdr   r.   Úwhy)r   rT   rP   rF   r§  r¨  Ú
total_daysÚleave_datesr.   r,   r[   r   r”   r’   ra   rð  r<  rÕ   rª  )	r_   r`   rF   r´   r:   rY  r×   Útfr>  rd   rd   re   rP   µ  sf   ú	

ý
ýÿ

ø

ý
ýÿzLeaveAPIView.getc              
   C   s  |j }zc|d }|d }t|d |d |d |d |jd�}| ¡  tjj|jd�}tjj|jd�}|j}d}	d}
d	|j	› d
|› d|› d|	› d�	}|j}t
|
|tj|gd�}| ¡  ttjddœd ddœƒW S  ty‡ } zttjddœdd|› �dœƒW  Y d }~S d }~ww )NrE   Útotaldayr¿  Ú	leavedate)r§  r¨  r«  r¬  rl  r>   z+https://pearlpackagingindia.com/manageleaveúLeave requestz
            Hi am z,,

            This is my leave request for z.

            Total days: z[

            

            You can log in using the following link and check:
            z)

            

            
            ©r   rt  Ú
from_emailr.  rD   r5   z"Leave request created successfullyr‡   r«   Tr·   )r:   r   ra   r˜   r   rT   rP   rð   r<   rE   r&   r   ÚEMAIL_HOST_USERÚsendr,   r.   r¹   rº   rQ   )r_   r`   r:   ZcasualÚmanZleave_requestÚjkZmeesiÚroÚ	login_urlr   rt  Úwer<   r€   rd   rd   re   rf   ò  s^   ûÿýû
öü	

ý
ý€ÿzLeaveAPIView.postc              
   C   sÌ   |j }z,tjj|d�}|d |_|d |_|d |_|d |_| ¡  t	t
jddœd dd	œƒW S  tyD   t	t
jd
dœddd	œƒ Y S  tye } zt|ƒ t	t
jd
dœddd	œƒW  Y d }~S d }~ww )Nr®   rE   r¿  r¯  r®  rD   r5   rÆ  r‡   r«   Trñ   r¿   )r:   r   rT   rP   r§  r¨  r¬  r«  r˜   r,   r.   r[   r   r”   r{   r|   rQ   rÇ  rd   rd   re   rÂ   6  s8   





ý
ý
ý€þzLeaveAPIView.putc                 C   rÈ  rÉ  rÊ  rË  rd   rd   re   r¬   U  rÌ  zLeaveAPIView.deleterm   rÀ  rd   rd   rd   re   r£  °  s    
=Dr£  c                   @   s$   e Zd ZefZddd„Zdd„ ZdS )ÚManageleaveAPIViewNc                 C   s€  |d ur?z%t jj|d�}|j|j|jt|jƒ|j|j	dœ}t
tjddœd |dœƒW S  ty>   t
tjddœddd	œƒ Y S w zkd
}g }tjj|jd�}|D ]O}t jj|jd�D ]D}|d7 }|j}	|	 d¡}
z|j}tjj|d�}|jd |j }W n ttjfyˆ   d}Y nw | ||j|j|j|j|
|j|dœ¡ qXqNt
tjddœd |dœƒW S  ty¿   t
tjddœddd	œƒ Y S w )Nr®   )rF   rE   r¿  r¤  r¥  rª  rD   r5   rq   r«   Trñ   r‡   r   )rð   )rl  r   r¦  ri  ÚUnknown)rT  rF   r§  r¨  r©  r   r.   r2   )r   rT   rP   rF   r§  r¨  rÓ   r«  r¬  rª  r,   r.   r[   r   r”   r   r’   ra   r?   rð  r<  rY   r   r\   r²   rÔ   rU   rÕ   )r_   r`   rF   r´   r:   rY  r×   ZerpÚmr­  r>  r?   rÊ   r2   rd   rd   re   rP   k  sz   ú	

ý
ýÿ
ÿ
øó

ý
ýÿzManageleaveAPIView.getc              
   C   sÖ  |j }�z+z|d d urt|d ƒnd}W n ttfy"   d}Y nw | d¡}| dd¡}| dd¡}|dkr;d}n	|dkrBd	}nd
}z_tjj|d�}	||	_| dd¡|	_| dd¡|	_	||	_
||	_| dd¡|	_|	 ¡  |dkr¢ztjj|	jd�}
|
jd u r�d|
_t|
jƒ|
_|
 ¡  W n tjy¡   td|	j› d�ƒ Y nw W n tjyÂ   td|› d�ƒ ttjddœdddœƒ Y W S w z>tjj|jd�}tjj|	jd�}
|j}d}d}d|› d|› d|› d|› d�	}|
j}t|d ƒ t||tj|gd!�}| ¡  W n! tj�y"   td|	j› d�ƒ ttjddœdd"dœƒ Y W S w ttjd#dœd d$dœƒW S  t�yE   ttjddœdddœƒ Y S  t�yj } ztd%|› �ƒ ttjddœdd&dœƒW  Y d }~S d }~ww )'Nr®  ç        r.   rE   rË   rÌ   rØ  Ú3rÙ  rÐ  r®   r¿  r¯  rª  r>   r   zProfile for user ID z does not exist.zLeaveRequest with ID r«   r5   Trñ   r‡   z,https://pearlpackagingindia.com/leaverequestr°  z$
                Your leave request z is z.
                Total days: zU

                You can log in using the following link and check:
                z
                zSending email to recipientr±  z(Profile not found for email notificationrD   zRequest updated successfullyzError occurred: r¿   ) r:   r´  rÔ   rß  rP   r   rT   r§  r¨  r¬  r«  r.   rª  r˜   r   rY   Ztotal_leaverÓ   rU   r|   r,   r”   ra   r<   r&   r   r³  r´  r[   r   r{   rQ   )r_   r`   rF   r:   ZrtoÚstaÚmlÚnamesr¹  r´   rµ   r¶  r·  r¸  r   rt  Zrecipient_emailr<   r€   rd   rd   re   rÂ   ·  sÂ    ÿ

€ý€
ýý
ÿÿþû
ü
ýþ

ý
ý
ý€þzManageleaveAPIView.putrm   )rh   ri   rj   r$   rr   rP   rÂ   rd   rd   rd   re   rº  h  s    
Lrº  c                   @   r0   )Ú	FpAPIViewc           	   
   C   s¨   |j }z/| di ¡}| d¡}| d¡}t|ƒ}tjj|d�}||_| ¡  ttj	ddœd ddœƒW S  t
yS } zttjd	dœd
d|› �dœƒW  Y d }~S d }~ww )Nr…   r<   r3   r;   rD   r5   zPassword reset successfullyr‡   r«   Tr·   )r:   rP   r   r   rT   r3   r˜   r,   r.   r[   rº   rQ   )	r_   r`   r:   r…   r<   r3   rÙ   rµ  r€   rd   rd   re   rf   u  s,   



ý
ý€ÿzFpAPIView.postNrg   rd   rd   rd   re   rÂ  q  s    rÂ  c                 C   rð  rñ  rô  rõ  rd   rd   re   rö  ¤  r÷  c                     s    t jjdd�\} }| jptddƒ}| jptddƒ}t| jpdƒ}dd„ | jp'd	 	d
¡D ƒ‰ ‡ fdd„t
tƒD ƒ}t t ¡ |¡t|d�  ¡ }| |||||dœS )Nr   rø  r÷   rú  rû  r   c                 S   rü  rý  rþ  r   rd   rd   re   r)  ¯  r  r  rË   r  c                    r  rd   rd   r  r  rd   re   r)  ´  r	  r
  )r  r  r   r!  r  r  )r   rT   rµ  r  r%   r   rÓ   r!  r  r  rV  r%  r   r  r2  r3  r   r  rd   r  re   r4  ©  s$   þþúc                 C   r  r   r!  r"  rd   rd   re   r#  Ä  r$  c                 C   sH   | r| |d kr
dS t  ¡ }t || ¡t ||d ¡ }t| ¡ d ƒS )Nr   r   r£   )r2  r3  r   r  rÓ   r&  )r¶  r@  r(  Údiffrd   rd   re   r—  È  s
   r—  c                   @   ó   e Zd Zddd„Zdd„ ZdS )ÚSalaryAPIViewNc                 C   s²  |d ur7zt jj|d�}|j|j|jdœ}ttjddœd |dœƒW S  t	y6   ttj
ddœddd	œƒ Y S w zùt ¡ }t|j d
|j¡ƒ}t|j d|j¡ƒ}t ||¡d }ddlm}	 |	||dƒ}
|	|||ƒ}tdddƒ}d}d}g }tj ¡ D ]¨}|d7 }tjj|j|
|d�}|jdd� ¡ }d}|jdd�D ]}|jr©|j|kr©|d7 }q›|d d }td|| ƒ}| i d|“d|j“d|j“d|j“dt|jƒ“dt|j ƒ“dt|j!ƒ“dt|j"ƒ“dt|j#ƒ“dt|j$ƒ“d |“d!|“d"|“d#|“d$|“d%t|j%ƒ“d&t|j&ƒ“t|j'ƒt|j(ƒt|j)ƒt|j*ƒ||d'œ¥¡ t+|ƒ q{ttjddœd |dœƒW S  t,�yX } zt+d(|› �ƒ ttj-ddœdt.|ƒd	œƒW  Y d }~S d }~ww ))Nr®   rÃ  rD   r5   rq   r«   Trñ   r‡   rP  rO  r   r   rƒ  r÷   rk  )r  r«  Z	date__lter  r-   r¬  r½  rT  rF   rE   rá   rÜ   ÚgrossSalaryÚbasicÚhraÚ
conveyanceÚotherAllowanceZ	totalDaysZworkingDaysZpresentDaysZ	lateCountZlateDeductionÚfixedPTÚpfLimit)Úadvancerd  ÚreimbursementÚ	incentiverP  rO  rn  )/r   rT   rP   rF   rE   r¿  r,   r.   r[   r   r”   r   r–   rÓ   r©   rP  rO  ÚcalendarÚ
monthranger   r%   r   rU  r   r’   r  r­  r�  rÕ   rá   r´  rÜ   rÆ  rÇ  rÈ  rÉ  rÊ  rË  rÌ  rÍ  rd  rÎ  rÏ  r|   r{   rQ   r^   )r_   r`   rF   r´   r:   r–   rP  rO  r«  r   r¸  r¹  Z	LATE_TIMEZLATES_PER_DAY_CUTrY  r×   Zattendance_qsZpresent_daysrE  r«  Zlate_deduction_daysZworking_daysr€   rd   rd   re   rP   Ò  sÎ   ý

ý
ýÿý€ÿþýü
û
ú
ù
ø
	÷

öõôóòñ
ð
ï
é


ý
ý€þzSalaryAPIView.getc              
   C   s†  |j }z…tjj|d�}| d|j¡|_| d|j¡|_| d|j¡|_| d|j¡|_| d|j¡|_| d|j	¡|_	| d|j
¡|_
| d	|j¡|_| d
|j¡|_| d|j¡|_| d|j¡|_| d|j¡|_| d|j¡|_| ¡  ttjddœd d|j› d�dœƒW S  tjyž   ttjddœdddœƒ Y S  tyÂ } ztd|› �ƒ ttjddœdddœƒW  Y d }~S d }~ww )Nr®   rá   rÜ   rÆ  rÇ  rÈ  rÉ  rÊ  rË  rÌ  rÍ  rd  rÎ  rÏ  rD   r5   z	Employee z updated successfullyr‡   r«   Trw  rn  z"Something went wrong during update)r:   r   rT   rP   rá   rÜ   rÆ  rÇ  rÈ  rÉ  rÊ  rË  rÌ  rÍ  rd  rÎ  rÏ  r˜   r,   r.   r[   rE   rU   r”   r{   r|   rQ   )r_   r`   rF   r:   rd  r€   rd   rd   re   rÂ   C  sJ   

ý
ý
ý€þzSalaryAPIView.putrm   )rh   ri   rj   rP   rÂ   rd   rd   rd   re   rÅ  Ð  s    
qrÅ  c                   @   rÄ  )ÚPayslipAPIViewNc              
   C   s   |d ur·zœt jj|d�}i d|j“d|j“d|j“d|j“d|j“d|j“d|j	“d	|j
“d
|j“d|j“d|j“d|j“d|j“d|j“d|j“d|j“d|j“i d|j“d|j“d|j“d|j“d|j“d|j“d|j“d|j“d|j“d|j“d|j“d|j“d|j “d |j!“d!|j"“d"|j#“¥}t$t%j&d#d$œd |d%œƒW S  t j'y¶   t$t%j(d&d$œd'd(d)œƒ Y S w z0d*}g }t j )¡ D ]}|d+7 }| *||j|j|j"|j#d,œ¡ t+|ƒ qÁt$t%j&d#d$œd |d%œƒW S  t,�y } zt+d-|› �ƒ t$t%j-d&d$œd't.|ƒd)œƒW  Y d }~S d }~ww ).Nr®   rF   ÚempnameÚempidÚemppanrÜ   ÚgrosssalaryÚperdaysalaryÚbasicsalaryrÈ  rÉ  Úotherallowancer¤  ÚworkingdaysÚbaiscnewÚhranewÚconveyancenewÚotherallowancenewÚgrosspayÚvariablepfgrossÚpflessÚesilessÚlessptÚpfÚesiÚbankrà   rÍ  Únetpayrd  rÎ  rÏ  ÚtopayrP  rD   r5   rq   r«   TzPayslip not foundr‡   r   r   )rT  rF   rE   rè  rP  rn  )/r   rT   rP   rF   rÓ  rÔ  rÕ  rÜ   rÖ  r×  rØ  rÈ  rÉ  rÙ  r¤  rÚ  rÛ  rÜ  rÝ  rÞ  rß  rà  rá  râ  rã  rä  rå  ræ  rà   rÍ  rç  rd  rÎ  rÏ  rè  rP  r,   r.   r[   rU   r”   rU  rÕ   r|   r{   rQ   r^   )r_   r`   rF   r´   r:   rY  r×   r€   rd   rd   re   rP   |  sÚ   ÿþýüûúùø	÷
öõôóòñðïîíìëêéèçæåäãâá à!ß#

ý
ýÿû


ý
ý€þzPayslipAPIView.getc                 C   sB  �zw|j  dg ¡}|sttjddœdddœƒW S g }g }ddd	d
dddddddddœ}|D �]-}| d¡}| d¡}| t|ƒt|ƒ¡}	z	tjj|d�}
W n
 tj	yW   Y q-w t
jj|
|	d� ¡ }|�rm| d|j¡|_| d|j¡|_| d|j¡|_| d|j¡|_| d|j¡|_| d|j¡|_| d|j¡|_| d|j¡|_| d |j¡|_| d!|j¡|_| d"|j¡|_| d#|j¡|_| d$|j¡|_| d%|j¡|_| d&|j¡|_| d'|j¡|_| d(|j¡|_| d)|j¡|_| d*|j¡|_| d+|j ¡|_ | d,|j!¡|_!| d-|j"¡|_"| d.|j#¡|_#| d/|j$¡|_$| d0|j%¡|_%| d1|j&¡|_&| d2|j'¡|_'| d3|j(¡|_(| d4|j)¡|_)| d5|j*¡|_*| d6|j+¡|_+|	|_,d7|_| -¡  | .|¡ q-t
jj/d@i d|
“d| dd8¡“d| dd8¡“d| dd8¡“d| dd9¡“d| dd9¡“d| dd9¡“d| dd9¡“d| dd9¡“d | d d9¡“d!| d!d9¡“d"| d"d9¡“d#| d#d9¡“d$| d$d9¡“d%| d%d9¡“d&| d&d9¡“d'| d'd9¡“d(| d(d9¡“d)| d)d9¡“d*| d*d9¡“d+| d+d9¡“d,| d,d9¡“d-| d-d9¡“d.| d.d9¡“d/| d/d8¡“d0| d0d8¡“d1| d1d9¡“d2| d2d9¡“d3| d3d9¡“d4| d4d9¡“d5| d5d9¡“d6| d6d9¡“d|	“d:d7“Ž | .|¡ q-ttj0d;dœd t1|ƒ› d<t1|ƒ› d=�t1|ƒt1|ƒd>œƒW S  t2�y  } zt3d?|› �ƒ ttjddœdt|ƒdœƒW  Y d }~S d }~ww )ANZpayslipsr«   r5   Tu,   Payslip data Ã Â®â€¡Ã Â®Â²Ã Â¯Â�Ã Â®Â²Ã Â¯Ë†r‡   ÚJanuaryÚFebruaryÚMarchÚAprilÚMayÚJuneÚJulyÚAugustÚ	SeptemberÚOctoberÚNovemberÚDecember)r   r@   rk  rø   rô   rˆ   rõ   rö   r÷   r^  é   rM  rP  rd  r®   )rd  rP  rÓ  rÔ  rÕ  rÜ   rÖ  r×  rØ  rÈ  rÉ  rÙ  r¤  rÚ  rÛ  rÜ  rÝ  rÞ  rß  rà  rá  râ  rã  rä  rå  ræ  rà   rÍ  rç  rd  rÎ  rÏ  rè  r   rË   r]  r.   rD   z
 created, z updated)r.   r4   r9   rº  ÚupdatedzPayslip Error: rd   )4r:   rP   r,   r.   rQ   rÓ   r^   r   rT   rU   r   r’   r·  rÓ  rÔ  rÕ  rÜ   rÖ  r×  rØ  rÈ  rÉ  rÙ  r¤  rÚ  rÛ  rÜ  rÝ  rÞ  rß  rà  rá  râ  rã  rä  rå  ræ  rà   rÍ  rç  rd  rÎ  rÏ  rè  rP  r˜   rÕ   r•   r[   rŒ  r{   r|   )r_   r`   Zpayslips_dataZcreated_listZupdated_listZMONTH_NAMESr  Z	month_valr¹  Ú
month_nameZemployee_instancer  r€   rd   rd   re   rf   Ô  s0  

ýü


ÿþýÿþýüûúùø	÷
öõôóòñðïîíìëêéèçæåäãâá à!ß#Ý%

û
ý€þzPayslipAPIView.postrm   r¼  rd   rd   rd   re   rÒ  y  s    
XrÒ  c                   @   r0   )Ú ProductactivestatusupdateAPIViewc              
   C   s”   z(t jj|d�}|jdkrd|_| ¡  nd|_| ¡  ttjddœd ddœƒW S  tyI } zttjddœd	d
|› �dœƒW  Y d }~S d }~ww )Nr®   r   r@   rD   r5   zQuote accepted successfullyr‡   r«   Tr·   )	r   rT   rP   r.   r˜   r,   r¹   rº   rQ   )r_   r`   rF   Zvenr€   rd   rd   re   rÂ   ^  s(   



ý
ý€ÿz$ProductactivestatusupdateAPIView.putN)rh   ri   rj   rÂ   rd   rd   rd   re   rø  \  rk   rø  )ÚValidationError)Ú	Paginator)ÚIntegrityErrorrä  )rå  ÚPrefetchÚProtectedErrorr    )ÚFileResponser   )Ú
FormParserÚMultiPartParser)Ú
TokenError)	ÚDOC_TYPE_CHOICESÚSTATUS_ACTIVEÚSTATUS_INACTIVEÚClientÚClientDocumentÚClientServicer   ÚRenewalHistoryÚServicerk  r^  é2   iˆ  rô   rú  c                   C   ó   t jrt ¡ S t ¡ S )z=Today's date. Works whether settings.USE_TZ is True or False.N)r   ÚUSE_TZr   Ú	localdater   r3  rd   rd   rd   re   Ú
today_date¥  ó   r  c                   C   r  )zFCurrent date and time. Works whether settings.USE_TZ is True or False.N)r   r  r   rê  r   r–   rd   rd   rd   re   Ú	now_localª  r  r  c                 K   s4   |ddœd | dœ}|r||d< |  |¡ t||d�S )NrD   r5   rq   r9   r-   )Úupdater,   )r:   r9   r6   Úextrart  rd   rd   re   rD   ¯  s
   
rD   c                 C   s*   |ddœd| dœ}|r||d< t ||d�S )Nr«   r5   Tr‡   Úerrorsr-   r+   )r9   r6   r  rt  rd   rd   re   r«   ·  s   r«   c                 C   s(   | j rtS tjj| jd�jddd� ¡ S )Nr>   rN   Tr  )r]   Ú
ADMIN_ROLEr   rT   r’   rF   r8  r·  rp   rd   rd   re   Úget_role¾  s   r  c                 C   s0   |rt fnt tf}t| jƒ|vrtdtjƒS dS )zKReturns an error Response if this user may not use the API, otherwise None.z&You do not have permission to do this.N)r  ÚEMPLOYEE_ROLEr  ra   r«   r.   rV   )r`   Ú
admin_onlyÚallowedrd   rd   re   Ú
check_roleÄ  s   r  c              	   C   s¸   zt t| j dd¡ƒdƒ}W n ttfy   d}Y nw zt| j dt¡ƒ}W n ttfy4   t}Y nw tt |dƒtƒ}t	||ƒ}| 
|¡}|j|j||j| ¡ | ¡ dœ}|j|fS )z>?page=2&page_size=10  ->  (rows of that page, pagination info)Úpager   Ú	page_size)r  r  r  Ztotal_pagesÚhas_nextÚhas_previousN)r�  rÓ   r©   rP   rß  rÔ   Ú	PAGE_SIZEr›  ÚMAX_PAGE_SIZErú  Úget_pager  ÚnumberÚ	num_pagesr  r  Úobject_list)r`   Úquerysetr  rš  Ú	paginatorÚpage_objrp  rd   rd   re   ÚpaginateÌ  s*   ÿÿ

ú
r'  c                 C   s.   t | dƒrdd„ | j ¡ D ƒS dd | j¡iS )z4Django ValidationError -> {"field": "first message"}Úmessage_dictc                 S   s6   i | ]\}}||r|d  dkrdn|r|d  nd“qS )r   zThis field cannot be blank.zThis field is required.zInvalid valuerd   )r'  r‰  r�  rd   rd   re   r  è  s    *ÿzerror_dict.<locals>.<dictcomp>Ú__all__z; N)rA  r(  r}  r2  Úmessages)Úexcrd   rd   re   Ú
error_dictå  s
   
ÿr,  c                 C   s   t t|  ¡ ƒƒS rm   )r´  ÚiterrN  ©r  rd   rd   re   Úfirst_errorí  rŽ  r/  c                 C   s   | dv S )N)Tr   rÝ   rä  ÚTruerå  ÚYesrd   rç  rd   rd   re   Úto_boolñ  ro   r2  c                 C   s   | r|   d¡S dS )Nr"  rË   rv  ©r(  rd   rd   re   Úfmt_dateõ  rŒ  r4  c                 C   s   | rd| dd … › �S dS )Nz
XXXX XXXX éüÿÿÿrË   rd   ©r�  rd   rd   re   Úmask_aadhaarù  r~  r7  c                 C   s.   | rdt t| ƒd dƒ › | dd … › �S dS )NÚXrø   r   r5  rË   )r�  rŒ  r6  rd   rd   re   Úmask_accountý  s   .r9  c                   @   r  )Ú	MeAPIViewzGGET /api/v1/me/  -> who owns this token + role (checked on the server).c              
   C   s€   |j }tjj|jd� ¡ }|d ur|jdkrtdtjƒS t	|j|j
|j|j|j|r,|jnd |r2|jnd |r;|jdœƒS ddœƒS )Nr>   r@   rA   )rF   rE   r2   r<   rG   rM   rN   rO   )ra   r   rT   r’   rF   r·  r.   r«   rV   rD   r2   r\   r<   r]   rM   rN   rO   )r_   r`   ra   rJ   rd   rd   re   rP     s    øøzMeAPIView.getNrH  rd   rd   rd   re   r:    ó    r:  c                   @   r  )ÚLogoutAPIViewzJPOST /api/v1/logout/  {"refresh": "..."}  -> blacklists the refresh token.c                 C   s8   zt |j d¡ƒ ¡  W n	 ty   Y nw ttjd�S )NrH   r-   )r   r:   rP   Z	blacklistr  r,   r.   ZHTTP_205_RESET_CONTENT)r_   r`   rd   rd   re   rf     s   ÿzLogoutAPIView.postN©rh   ri   rj   rI  r$   rr   rf   rd   rd   rd   re   r<    r;  r<  c                 C   s$   | j | j| jt| dd ƒt| jƒdœS )NÚclients_count)rF   rE   r.   r>  rð  )rF   rE   r.   r:  r4  rð  )Úsrd   rd   re   Úservice_to_dict$  s   
ûr@  c                   @   ó@   e Zd ZdZefZddd„Zdd„ Zdd„ Zd	d
„ Z	dd„ Z
dS )ÚServiceAPIViewaÇ  
    GET    /api/v1/services/              list, 10 per page   (?page=&search=&status=)
    GET    /api/v1/services/?val=options  all services for dropdowns
    GET    /api/v1/services/?val=summary  counts for dashboard
    GET    /api/v1/services/<id>/         one service
    POST   /api/v1/services/              create   (admin)
    PUT    /api/v1/services/<id>/         update   (admin)
    DELETE /api/v1/services/<id>/         delete   (admin)
    Nc                 C   sb  t |ƒ}|r|S |d ur2ztjjtdƒd�j|d�}W n tjy+   tdtj	ƒ Y S w t
t|ƒƒS |j dd¡}|dkrQtj d¡ d	dd
¡}t
dd„ |D ƒƒS |dkrmtj ¡ }tjjtd� ¡ }t
|||| dœƒS tjjtdƒd� d¡}	|j dd¡ ¡ }
|j d
d¡ ¡ }|
r“|	j|
d�}	|dv rŸ|	jt|ƒd�}	t||	ƒ\}}t
dd„ |D ƒ|d�S )NÚclient_services©r>  r®   úService not foundrÊ   rË   ÚoptionsrE   rF   r.   c                 S   s(   g | ]}|d  |d |d t kdœ‘qS )rF   rE   r.   )rÚ  r  Úinactive)r  rZ  rd   rd   re   r,  I  s   ( z&ServiceAPIView.get.<locals>.<listcomp>rÝ  r-   )r  ÚactiverG  r  rÍ   ©rÝ   rÌ   c                 S   r>  rd   )r@  ©r'  r?  rd   rd   re   r,  X  r*  ©Ú
pagination)r  r	  rT   rO  rå  rP   rU   r«   r.   r”   rD   r@  r©   rÒ   rN  r  r’   r  rÑ   rÓ   r'  )r_   r`   rF   Údeniedr?  rÊ   r\  r  rH  Úservicesr  rÖ   rp  rd   rd   re   rP   :  s6   ÿ
zServiceAPIView.getc              
   C   s<  |j }d|v r|d |_d|v r|d |_z|jpd ¡ |_| ¡  W n tyA } zt|ƒ}tt|ƒ|d�W  Y d }~S d }~ww t	j
j|jd�j|jd� ¡ r[d}t|d|id�S |ra|j|_|j|_z| ¡  W n tyz   tdtjƒ Y S w t	j
jtdƒd	�j|jd�}tt|ƒ|r‘d
nd|r™tjd�S tjd�S )NrE   r.   rË   r.  )r  rø  z(A service with this name already exists.rC  rD  zService created successfullyzService updated successfully©r9   r6   )r:   rE   r.   rÑ   Úclean_fieldsrù  r,  r«   r/  r	  rT   r’   r9  rù  r“   ra   rl  r3  r˜   rû  rŸ   rO  rå  rP   rD   r@  r¹   r[   )r_   r`   r  Úcreatingr:   r€   r  r»  rd   rd   re   Ú_saveZ  s@   

€þÿ
ýýzServiceAPIView._savec                 C   s&   t |dd�}|r
|S | j|tƒ dd�S )NT©r  ©rQ  )r  rR  r	  ©r_   r`   rM  rd   rd   re   rf   {  s   zServiceAPIView.postc                 C   sX   t |dd�}|r
|S z	tjj|d�}W n tjy#   tdtjƒ Y S w | j||dd�S )NTrS  r®   rE  FrT  )	r  r	  rT   rP   rU   r«   r.   r”   rR  )r_   r`   rF   rM  r  rd   rd   re   rÂ   �  s   ÿzServiceAPIView.putc                 C   sr   t |dd�}|r
|S ztjj|d� ¡  W n tjy%   tdtjƒ Y S  t	y3   tdtj
ƒ Y S w tdd�S )NTrS  r®   rE  zOThis service is used by clients and can't be deleted. Mark it Inactive instead.zService deleted successfully©r9   )r  r	  rT   rP   r¬   rU   r«   r.   r”   rý  rŸ   rD   ©r_   r`   rF   rM  rd   rd   re   r¬   ‹  s   ÿ
zServiceAPIView.deleterm   ©rh   ri   rj   rI  r$   rr   rP   rR  rf   rÂ   r¬   rd   rd   rd   re   rB  .  s    	
 !
rB  )rE   Úbusiness_namerÄ   rç   rÅ   rM   r<   rá   ÚaadhaarÚgstinÚ	bank_nameÚaccount_holderÚaccount_numberÚifscr.   Únotesc              	   C   s,   | j | j|  ¡ | j| j| j| jt| jƒdœS )N)rF   Údoc_typeZdoc_type_labelÚoriginal_nameÚcontent_typerš  ÚremarksZuploaded_at)	rF   ra  Úget_doc_type_displayrb  rc  rš  rd  r4  rð  r3  rd   rd   re   Údocument_to_dict   s   ørf  c                   C   s   t j tdtj d¡d�d¡S )zEClients with services + documents loaded in 2 extra queries (no N+1).rC  r  )r$  Ú	documentsN)r  rT   r+  rü  r  r¡  rd   rd   rd   re   Úclient_queryset­  s   þrh  c              
   C   s  t | j ¡ dd„ d�}i d| j“d| j“d| j“d| j“d| j“d	| j“d
| j	“d| j
“d| j“d| j“d| j“d|r>| jnt| jƒ“d| j“d| j“d|rQ| jnt| jƒ“d| j“ddd„ |D ƒ“| jt| jƒt| j ¡ ƒ|rzdd„ | j ¡ D ƒng | j| jt| jƒt| jƒdœ¥S )zXList + exports use masked Aadhaar / account number. full=True is only for the edit form.c                 S   ó   | j j ¡ S rm   ©r  rE   rq  )Úcsrd   rd   re   r›  ·  ó    z client_to_dict.<locals>.<lambda>rœ  rF   rö  rE   rY  rÄ   rç   rÅ   rM   r<   rá   r[  rZ  r\  r]  r^  r_  rN  c                 S   s6   g | ]}|j |jj|jr|j ¡ nd t|jƒdœ‘qS )N)rF   rE   Úrenewal_dateÚrenewal_label)Ú
service_idr  rE   rm  rª  r4  ©r'  rk  rd   rd   re   r,  É  s    ûüÿz"client_to_dict.<locals>.<listcomp>c                 S   r>  rd   )rf  r+  rd   rd   re   r,  Õ  r*  )Úfile_deliveredÚfile_delivered_onZdocuments_countrg  r.   r`  rð  Ú
updated_atN)r=  rC  rU  rF   rö  rE   rY  rÄ   rç   rÅ   rM   r<   rá   r[  rZ  r7  r\  r]  r^  r9  r_  rq  r4  rr  rŒ  rg  r.   r`  rð  rs  )rø  ÚfullÚlinksrd   rd   re   Úclient_to_dictµ  s^   ÿþýüûúùø	÷
öõôóòñðùïßrv  c                 C   sø   t ƒ  d¡}| j}| dd¡ ¡ }| dd¡ ¡ }| dd¡ ¡ }| dd¡ ¡ }|dv r5|jt|ƒd�}|d	v rA|j|d
kd�}| ¡ rM|jt|ƒd�}|rz| t|d�t|d�B t|d�B t|d�B t|d�B t|d�B t|d�B t|d�B ¡}|S )Nú-idr  rË   r.   r  rq  rI  r-   )r]  rÝ   rÝ   )rq  )Zclient_services__service_idrÍ   )Zbusiness_name__icontainsr  rÏ   rÎ   )Zpan__icontains)Zgstin__icontains)Zcity__icontains)	rh  rÒ   r©   rP   rÑ   r’   rÓ   rm  r    )r`   r  r  r  rÖ   r  Ú	deliveredrd   rd   re   Úfiltered_clientsÝ  s6   ÿÿÿþþÿry  c              
   C   s4  | dv rg S t | tƒstddiƒ‚g tƒ }}| D ]~}t |tƒs&tddiƒ‚z	t| d¡ƒ}W n ttfy>   tddiƒ‚w ||v rItddiƒ‚| 	|¡ d}| d¡}|dvr�zt
 t|ƒdd	… d
¡ ¡ }W n tyv   tddiƒ‚w tdddƒ|  kr‰tdddƒks�n tddiƒ‚| ||f¡ q|S )zX[{"service_id": 3, "renewal_date": "2026-10-10"}, ...]  ->  [(3, date(2026,10,10)), ...]rÒ  rN  zServices must be a list.zInvalid service selected.ro  z#The same service is selected twice.Nrm  r^  r  z"Renewal date must be a valid date.éÐ  r   é4  rM  é   zRenewal date is out of range.)r“  r1  rù  ÚsetÚdictrÓ   rP   rß  rÔ   Úaddr   r1  r^   r   rÕ   )rÛ  r\  ÚseenrT  ro  ÚrenewalZraw_daterd   rd   re   Úparse_service_rowsô  s8   

ÿ

 ÿ&r‚  c                   @   rA  )ÚClientAPIViewaz  
    GET    /api/v1/clients/              list, 10 per page  (?page=&search=&status=1|2&service=&file_delivered=0|1)
    GET    /api/v1/clients/?val=summary  dashboard numbers + latest clients
    GET    /api/v1/clients/?val=excel    download Excel  (same filters)
    GET    /api/v1/clients/?val=pdf      download PDF    (same filters)
    GET    /api/v1/clients/<id>/         full details
    POST   /api/v1/clients/              create   (admin + employee)
    PUT    /api/v1/clients/<id>/         update   (admin + employee)  - also used to toggle file_delivered
    DELETE /api/v1/clients/<id>/         delete   (admin only)
    Nc                 C   s”  t |ƒ}|r|S |d ur.z	tƒ j|d�}W n tjy%   tdtjƒ Y S w tt	|dd�ƒS |j
 dd¡}|dkrltj ¡ }tjjtd� ¡ }tjjtd	d
� ¡ }tƒ  d¡d d… }	t|||| |dd„ |	D ƒdœƒS |dv r¶tt|ƒd t… ƒ}
z|dkrˆt|
ƒ}d}d}nt|
ƒ}d\}}W n ty    tdtjƒ Y S w t||d�}dtƒ d›d|› d�|d< |S t|t|ƒƒ\}}tdd„ |D ƒ|d�S )Nr®   r  T©rt  rÊ   rË   rÝ  r-   F)r.   rq  rw  rô   c                 S   r>  rd   ©rv  r  rd   rd   re   r,  5  r*  z%ClientAPIView.get.<locals>.<listcomp>)r  rH  rG  Zfile_pendingrª   )ÚexcelÚpdfr†  zAapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheetZxlsx)úapplication/pdfr‡  z6Install on the server:  pip install openpyxl reportlab)rc  zattachment; filename="clients-r  rÓ  ú"zContent-Dispositionc                 S   r>  rd   r…  r  rd   rd   re   r,  I  r*  rK  )r  rh  rP   r  rU   r«   r.   r”   rD   rv  r©   rT   r  r’   r  rÒ   r1  ry  ÚEXPORT_LIMITÚbuild_clients_excelÚbuild_clients_pdfÚImportErrorZHTTP_501_NOT_IMPLEMENTEDr   r  r'  )r_   r`   rF   rM  rø  rÊ   r  rH  rÚ  rª   r  Úcontentrc  Úextr7   r\  rp  rd   rd   re   rP      sL   ÿ
þ€ÿzClientAPIView.getc              
      s²  |j }tD ]}||v rt|||| d ur|| ndƒ qd|v r7t|d p&dƒ ¡  ¡ }|r2||_n|r7d |_d|v rRt|d ƒ}|rJ|jsJt	ƒ |_
|sOd |_
||_z| ¡  d|v rbt| d¡ƒnd }W n tyƒ }	 zt|	ƒ}
tt|
ƒ|
d�W  Y d }	~	S d }	~	ww i }
|js�d|
d< |js”d|
d	< |jrµtjj|jd
�j|jd� ¡ }|rµd|j› d|j› d�|
d< dD ].\}}t||ƒ}|råtjjd(i ||i¤Žj|jd� ¡ }|rå|› d|j› d|j› d�|
|< q·i ‰|d u�r-dd„ tjjdd„ |D ƒd�D ƒ‰tˆƒt|ƒk�rd|
d< n!|�rtƒ n	t|jjddd�ƒ‰ t ‡ ‡fdd„|D ƒƒ�r-d|
d< |
�r8tt|
ƒ|
d�S zot! "¡ �` |�rE|j#|_$|j#|_%| &¡  |d u�r–dd„ |j '¡ D ƒ}|D ]+\}}| (|d ¡}|d u �rxt)jj*|||d � �q^|j+|k�rˆ||_+|j&d!gd"� �q^| ,¡ D ]}| -¡  �qŽW d   ƒ n	1 �s¡w   Y  W n t.�y·   td#t/j0ƒ Y S w t1ƒ j|jd�}t2t3|dd$�|�rËd%nd&|�rÔt/j4d'�S t/j5d'�S ))NrË   rö  rq  rN  r.  zClient name is required.rE   zMobile number is required.rM   )rö  rø  z
Client ID z is already used by rÓ  ))rá   ÚPAN)rZ  ÚAadhaarz already belongs to client z (r“  c                 S   r¯  rd   r®   rJ  rd   rd   re   r  }  r*  z'ClientAPIView._save.<locals>.<dictcomp>c                 S   ó   g | ]\}}|‘qS rd   rd   ©r'  Úsidr  rd   rd   re   r,  }  r*  z'ClientAPIView._save.<locals>.<listcomp>r  z+One or more selected services do not exist.ro  Tr  c                 3   s*   � | ]\}}ˆ| j tko|ˆ vV  qd S rm   )r.   r  r“  ©ÚalreadyÚfoundrd   re   r°  ‚  s   €( z&ClientAPIView._save.<locals>.<genexpr>z*Inactive services can't be newly assigned.c                 S   r¯  rd   ©ro  rp  rd   rd   re   r  �  r*  )r  ro  rm  rm  ©Úupdate_fieldszMAnother client already uses one of these details (Client ID / PAN / Aadhaar).r„  zClient created successfullyzClient updated successfullyrO  rd   )6r:   ÚCLIENT_TEXT_FIELDSÚsetattrr^   rÑ   r×  rö  r2  rq  r  rr  Úvalidate_fieldsr‚  rP   rù  r,  r«   r/  rE   rM   r  rT   r’   r9  rù  r·  r:  r	  rŒ  r}  rC  r8  r”  rä  r˜  ra   rl  r3  r˜   rU  Úpopr  r•   rm  rN  r¬   rû  r.   rŸ   rh  rD   rv  r¹   r[   )r_   r`   r  rQ  r:   rá  Útypedrx  Zservice_rowsr€   r  Úclashr  rÚ  Úotherr  ro  r�  ÚlinkÚleftoverrd   r•  re   rR  L  s¨    €
€þ
$€
"
 


€€ð€ÿ

ýýzClientAPIView._savec                 C   s"   t |ƒ}|r|S | j|tƒ dd�S )NTrT  )r  rR  r  rU  rd   rd   re   rf   ¤  s   zClientAPIView.postc                 C   sT   t |ƒ}|r|S z	tjj|d�}W n tjy!   tdtjƒ Y S w | j||dd�S )Nr®   r  FrT  )	r  r  rT   rP   rU   r«   r.   r”   rR  )r_   r`   rF   rM  r  rd   rd   re   rÂ   ª  s   ÿzClientAPIView.putc                 C   sV   t |dd�}|r
|S ztjj|d� ¡  W n tjy%   tdtjƒ Y S w t	dd�S )NTrS  r®   r  zClient deleted successfullyrV  )
r  r  rT   rP   r¬   rU   r«   r.   r”   rD   rW  rd   rd   re   r¬   ´  s   ÿ
zClientAPIView.deleterm   rX  rd   rd   rd   re   rƒ    s    

,X
rƒ  c                   @   r  )ÚRenewalAPIViewzò
    GET /api/v1/renewals/?when=tomorrow|today|week|overdue&search=&page=
    Default = tomorrow: clients whose renewal date is one day away (shown every day).
    Only ACTIVE clients are listed.  `counts` gives the number for every tab.
    c                    s4  t |ƒ}|r|S tƒ ‰tˆtdd� d�tˆd�tˆˆtdd� d�tˆd�dœ}|j dd	¡}||vr6d	}tj d
d¡j	dt
d�‰ ˆ  	|| ¡ dd¡}|j dd¡ ¡ }|rt| 	t|d�t|d�B t|d�B t|d�B t|d�B ¡}t||ƒ\}}‡fdd„|D ƒ}	‡ fdd„| ¡ D ƒ}
t|	||
|ˆ ¡ d�S )Nr   rR  )rm  rõ   )Zrenewal_date__gtZrenewal_date__lte)Zrenewal_date__lt)Útomorrowr3  ÚweekZoverdueÚwhenr¥  r  r  F)Zrenewal_date__isnullZclient__statusrm  Zclient__namer  rË   )Zclient__name__icontains)Zclient__client_code__icontains)Zclient__phone__icontains)Z client__business_name__icontains)Zservice__name__icontainsc                    sR   g | ]%}|j |j|jj|jj|jj|jj|jj|j 	¡ t
|jƒ|jˆ  jd œ
‘qS ))
rF   r  rö  rS  rY  rM   Úservice_namerm  rn  Z	days_left)rF   r  r  rö  rE   rY  rM   r  rm  rª  r4  rS  rp  )r3  rd   re   r,  ã  s    ö

öz&RenewalAPIView.get.<locals>.<listcomp>c                    s    i | ]\}}|ˆ   |¡ ¡ “qS rd   )r’   r  )r'  rE   ra  )Úbaserd   re   r  ï  r\  z&RenewalAPIView.get.<locals>.<dictcomp>)rL  rK  r§  r3  )r  r  r    r   r©   rP   r  rT   r¡  r’   r  rÒ   rÑ   r'  r}  rD   rª  )r_   r`   rM  Útabsr§  r\  r  Z	page_rowsrp  r:   rK  rd   )r©  r3  re   rP   È  s>   üÿÿþÿ
õzRenewalAPIView.getNrH  rd   rd   rd   re   r¤  À  s    r¤  c              	   C   s8   zt  t| ƒdd… d¡ ¡ W S  ttfy   Y dS w )z9'2026-10-10' -> date, or None when it is not a real date.Nr^  r  )r   r1  r^   r   rß  rÔ   rç  rd   rd   re   Ú	read_dateó  s
   ÿr«  c                   @   r  )ÚRenewalRenewAPIViewaé  
    POST /api/v1/renewals/<client_service id>/renew/     ("Mark renewed" button)
         { "new_date": "2027-10-10",            next renewal date (required)
           "renewed_on": "2026-10-09",          day it was renewed (default today, not in the future)
           "old_date": "2026-10-10",            the date the screen showed (stops double click / two staff)
           "remarks": "paid by UPI" }
    Saves a RenewalHistory row and moves the service to the new renewal date.
    c              
   C   sj  t |ƒ}|r|S |j}tƒ }i }|}| d¡dvr>t| d¡ƒ}|d u r)d|d< n||kr2d|d< n|tdddƒk r>d|d< t| d¡ƒ}|d u rNd	|d< n"tdddƒ|  kr`td
ddƒksfn d|d< n
|rp||krpd|d< t| d¡pwdƒ ¡ }	t|	ƒdkr…d|d< |r�t	t
|ƒ|d�S t ¡ �… ztj ¡  dd¡j|d�}
W n tjyº   t	dtjƒ Y W  d   ƒ S w |
jjtkrÌt	dƒW  d   ƒ S t| d¡pÓdƒd d… }|
jrá|
j ¡ nd}|rö||kröt	dtjƒW  d   ƒ S tjj|
j|
j|
j|||	|jd� ||
_|
jdgd� W d   ƒ n	1 �sw   Y  t|
j|
j |
jj!| ¡ t"|ƒdœd d!�S )"NÚ
renewed_onrÒ  z"Renewed date must be a valid date.z$Renewed date can't be in the future.rz  r   zRenewed date is out of range.Únew_datezNext renewal date is required.r{  rM  r|  z"Next renewal date is out of range.z1Next renewal date must be after the renewed date.rd  rË   éÿ   z#Remarks can be 255 letters at most.r.  r  r  r®   zRenewal not foundzThis client is inactive.Úold_dater^  z:This renewal was already updated. Please refresh the list.)r  r  r°  r®  r­  rd  rl  rm  r™  )rF   r  r¨  rm  rn  zRenewal saved successfullyrV  )#r  r:   r  rP   r«  r   r^   rÑ   rŒ  r«   r/  rä  r˜  r  rT   Úselect_for_updater¡  rU   r.   r”   r  r  rm  rª  rŸ   r  r•   r  ra   r˜   rD   rF   r  rE   r4  )r_   r`   rF   rM  r:   r3  r  r­  r®  rd  r¢  ZshownÚcurrentrd   rd   re   rf     sn   


&

üú
õþîþýzRenewalRenewAPIView.postNr=  rd   rd   rd   re   r¬  û  s    r¬  c                   @   r  )ÚClientRenewalHistoryAPIViewz[GET /api/v1/clients/<client_id>/renewal-history/?page=&service=   newest first, 10 per pagec           	      C   s–   t |ƒ}|r|S tjj|d� ¡ stdtjƒS tjj|d� 	dd¡}|j
 dd¡ ¡ }| ¡ r7|jt|ƒd�}t||ƒ\}}dd	„ |D ƒ}t||d
�S )Nr®   r  r  r  rl  rË   r˜  c                 S   sT   g | ]&}|j |j|jjt|jƒt|jƒt|jƒ|j|j	r$|j	j
p#|j	jnd dœ‘qS )rË   )rF   ro  r¨  r°  r®  r­  rd  Zdone_by)rF   ro  r  rE   r4  r°  r®  r­  rd  rl  r\   r2   r  rd   rd   re   r,  R  s    	ø€ 
øz3ClientRenewalHistoryAPIView.get.<locals>.<listcomp>rK  )r  r  rT   r’   r“   r«   r.   r”   r  r¡  r©   rP   rÑ   rm  rÓ   r'  rD   )	r_   r`   r  rM  Úhistoryr  r\  rp  r:   rd   rd   re   rP   E  s   	÷
zClientRenewalHistoryAPIView.getNrH  rd   rd   rd   re   r³  A  r;  r³  ))s   %PDF-r‡  rˆ  )s   ÿØÿÚjpgz
image/jpeg)s   ‰PNG

Úpngz	image/pngc                 C   s>   |   d¡}|  d¡ tD ]\}}}| |¡r||f  S qdS )zPLooks at the real file bytes (not the file name) -> (ext, content_type) or None.rö   r   N)r™  r–  ÚUPLOAD_SIGNATURESrl  )r  ÚheadÚ	signaturer�  rc  rd   rd   re   Úsniff_uploadg  s   


ÿrº  c                   @   s6   e Zd ZdZefZeefZdd„ Z	dd„ Z
dd„ ZdS )	ÚClientDocumentAPIViewa‡  
    POST   /api/v1/clients/<client_id>/documents/   multipart: file, doc_type, remarks
    GET    /api/v1/client-documents/<id>/           the file (login needed; ?download=1 to save)
    DELETE /api/v1/client-documents/<id>/           removes the record AND the file from the media folder
    Saved in MEDIA_ROOT/client_documents/<client id>/  - never served as a public /media/ link.
    c              
   C   s˜  t |ƒ}|r|S z	tjj|d�}W n tjy!   tdtjƒ Y S w |j d¡}|j	 d¡p/d 
¡ }|j	 d¡p9d 
¡ d d… }|sJtd	dd	id
�S |ttƒvrXtdddid
�S |jdkretdddid
�S |jtd d kr|dt› d�}t|d|id
�S t|ƒ}	|	sŒd}t|d|id
�S |j ¡ tkr›tdt› d�ƒS |	\}
}tj |j¡d d… }d|
› �|_t||||||j||jd�}| ¡  tt|ƒdtjd�S )Nr®   r  Úfilera  r¡  rd  rË   r¯  zPlease choose a file.r.  zInvalid document type.r   zThe file is empty.i   z#File is too large. Maximum size is z MB.z'Only PDF, JPG or PNG files are allowed.zA client can have at most z documents.zupload.)r  ra  r¼  rb  rc  rš  rd  rl  zDocument uploaded successfullyrO  )r  r  rT   rP   rU   r«   r.   r”   ro  r:   rÑ   r~  r  rš  ÚMAX_UPLOAD_MBrº  rg  r  ÚMAX_DOCS_PER_CLIENTr^  r]  ÚbasenamerE   r  ra   r˜   rD   rf  r¹   )r_   r`   r  rM  r  Úuploadra  rd  r»  Úkindr�  rc  rb  Údocrd   rd   re   rf   {  sF   ÿ
þzClientDocumentAPIView.postc              
   C   sš   t |ƒ}|r|S ztjj|d�}|j d¡}W n tjttfy*   t	dt
jƒ Y S w t||jp1d|j d¡dk|jp@d|j› �d�}d	|d
< d|d< |S )Nr®   ÚrbúFile not foundzapplication/octet-streamZdownloadrÝ   z	document-)rc  Úas_attachmentÚfilenameZnosniffzX-Content-Type-Optionszprivate, no-storezCache-Control)r  r  rT   rP   r¼  r”  rU   ÚFileNotFoundErrorrÔ   r«   r.   r”   rþ  rc  r©   rb  rF   )r_   r`   rF   rM  rÂ  Úhandler7   rd   rd   re   rP   £  s"   ÿ
ýzClientDocumentAPIView.getc                 C   sR   t |ƒ}|r|S ztjj|d� ¡  W n tjy#   tdtjƒ Y S w t	dd�S )Nr®   rÄ  zDocument deleted successfullyrV  )
r  r  rT   rP   r¬   rU   r«   r.   r”   rD   rW  rd   rd   re   r¬   µ  s   ÿ
zClientDocumentAPIView.deleteN)rh   ri   rj   rI  r$   rr   r   rÿ  Zparser_classesrf   rP   r¬   rd   rd   rd   re   r»  q  s    (r»  c                 C   sX   g }t | j ¡ dd„ d�D ]}| |jr!|jj› dt|jƒ› d�n|jj¡ qd |¡S )Nc                 S   ri  rm   rj  rš  rd   rd   re   r›  Ã  rl  zservices_text.<locals>.<lambda>rœ  z
 (renewal r¦  ú, )	r=  rC  rU  rÕ   rm  r  rE   r4  r2  )rø  rF  rk  rd   rd   re   Úservices_textÁ  s   0
rÊ  c                 C   sô  ddl m} ddlm}m}m} ddlm} |ƒ }|j}d|_	g d¢}| 
dd„ |D ƒ¡ t|d	ƒD ]*\}	\}
}||j||	ƒ _|jd	|	d
�}|ddd�|_|ddd�|_|ddd�|_q2d|jd	 _| D ]R}| 
|j|j|j|j|j|j|j|j|jt|jƒ|jt |ƒ|j!|j"t#|j$ƒ|j%|j&t'kr•dnd|j(r›dndt)|j*ƒ|j+d ,t-dd„ |j. /¡ D ƒƒ¡t)|j0ƒg¡ qe|j1dd�D ]}|D ]	}|ddd�|_qÂq¾dD ]}t2d|j3d	 ƒD ]
}d|j||d
�_4qÙqÏd |_5|j6|j7_8t9 :¡ }| ;|¡ | <¡ S )!Nr   )ÚWorkbook)Ú	AlignmentÚFontÚPatternFill)Úget_column_letterZClients))ú	Client IDé   )zClient Nameé   )zShop / Business Nameé   )zShop Addressé&   )ZCityé   )ZPincoder^  )ZMobileé   )ÚEmailrÓ  )r�  rÑ  )r‘  é   )ZGSTINrû  )zServices (renewal dates)é(   )z	Bank Nameé   )zAccount Holderé   )zAccount No.rû  )ZIFSCrÑ  )ÚStatusr^  )zFile DeliveredrÖ  )zDelivered OnrÑ  )ZNotesé"   )Z	DocumentsrÓ  )zAdded OnrM  c                 S   r’  rd   rd   )r'  r£  r  rd   rd   re   r,  Ö  r*  z'build_clients_excel.<locals>.<listcomp>r   )rT  ÚcolumnTZFFFFFF)ÚboldÚcolorZsolidZ6B0F14)ZfgColorÚcenter)ZverticalÚ	wrap_texté   ÚActiveÚInactiveZ	DeliveredrÐ  rÉ  c                 S   s   h | ]}|  ¡ ’qS rd   )re  r+  rd   rd   re   r)  æ  r*  z&build_clients_excel.<locals>.<setcomp>r@   )Zmin_rowÚtop)rˆ   rõ   r^  rf  ú@ZC2)=ZopenpyxlrË  Zopenpyxl.stylesrÌ  rÍ  rÎ  Zopenpyxl.utilsrÏ  rH  rÿ  rÕ   rV  Zcolumn_dimensionsÚwidthÚcellZfontÚfillÚ	alignmentZrow_dimensionsÚheightrö  rE   rY  rÄ   rç   rÅ   rM   r<   rá   r7  rZ  r[  rÊ  r\  r]  r9  r^  r_  r.   r  rq  r4  rr  r`  r2  r=  rg  rU  rð  Ú	iter_rowsr|  Zmax_rowÚnumber_formatZfreeze_panesÚ
dimensionsZauto_filterÚrefr—  r˜  r˜   Úgetvalue)r  rË  rÌ  rÍ  rÎ  rÏ  ÚwbÚwsrg  r(  r  rè  ré  rø  rT  Úcolr[  Úbufferrd   rd   re   r‹  È  sJ    $úÿÿ

r‹  c                    s  ddl m‰ ddlm‰ ddlm‰ m‰ ddlm}m	} ddl
m‰ ddlm‰m}m}m}m} |dd	d
dd�}|d|dd�}|d|ˆjd�‰|ƒ }	|f‡‡fdd„	‰‡fdd„}
‡‡fdd„dD ƒg}| D ]Š}dd„ t|j ¡ dd„ d�D ƒ}| ˆ|j|ƒˆdˆ|jƒ› dˆ|jƒ› �|ƒ|
|j|jƒ|
|jožd|j› �|jo¨d t|jƒ› �|jo°d!|j› �ƒ|
|Ž |
|j d" !d#d$„ |j"|j#fD ƒ¡ƒ|
|j$|j%|j&oÔd%t'|j&ƒ› �|j(oÜd&|j(› �ƒ|
|j)t*kråd'nd(|j+rëd)nd*ƒg¡ qf‡ ‡‡‡fd+d,„}t, -¡ }||ˆˆ ƒd-ˆ d-ˆ d-ˆ d.ˆ d/d0d1�}ˆˆ ƒd d2ˆ  ‰g d3¢}||‡fd4d„|D ƒd5d6�}| .|d7d8d9ˆ /d:¡fd;d<d=ˆjˆ /d>¡gfd?d8d=d@ˆ /dA¡fdBdCdDgƒ¡ ˆdE|	dF ƒˆdGt0ƒ dH›dIt1| ƒ› dJ�|	dK ƒ|d5dLˆ ƒ|g}|j2|||dM� | 3¡ S )NNr   )Úescape)Úcolors)ÚA4Ú	landscape)ÚParagraphStyleÚgetSampleStyleSheet)rÀ   )Ú	ParagraphÚSimpleDocTemplateÚSpacerÚTableÚ
TableStyleré  Ú	Helveticarõ   r÷   )ÚfontNameZfontSizeZleadingrß  zHelvetica-Bold)Úparentr  r¸  )r  Z	textColorc                    s   ˆ ˆt | pdƒƒ dd¡|ƒS )NrË   Ú
z<br/>)r^   rp  )rq  Ústyle)rü  rö  rd   re   rb     s   zbuild_clients_pdf.<locals>.Pc                     s   ˆ d  dd„ | D ƒ¡ƒS )Nr  c                 s   ó   � | ]}|r|V  qd S rm   rd   )r'  Úlrd   rd   re   r°     r±  z3build_clients_pdf.<locals>.stack.<locals>.<genexpr>)r2  )Úlines)rb  rd   re   Ústack   s   z build_clients_pdf.<locals>.stackc                    s   g | ]}ˆ |ˆƒ‘qS rd   rd   r  )rb  r¸  rd   re   r,  
   r  z%build_clients_pdf.<locals>.<listcomp>)rÐ  zClient / BusinessZContactzPAN / Aadhaar / GSTINZServicesÚAddressZBankrÜ  c                 S   s0   g | ]}|j j› |jrd t|jƒ› �nd ‘qS )z - rË   )r  rE   rm  r4  rp  rd   rd   re   r,     s    "ÿÿc                 S   ri  rm   rj  rš  rd   rd   re   r›     rl  z#build_clients_pdf.<locals>.<lambda>rœ  z<b>z	</b><br/>zPAN: z	Aadhaar: zGST: ri  c                 s   r  rm   rd   r?  rd   rd   re   r°     r±  z$build_clients_pdf.<locals>.<genexpr>zA/c: zIFSC: rä  rå  zFile: DeliveredzFile: Pendingc                    sl   |   ¡  |  dd¡ |  ˆj¡ |  dˆ dˆ d¡ |  ˆˆ ƒd dˆ  dˆ d|j› �¡ |  ¡  d S )Nr  rõ   rM  rö   zManju Associates - Confidentialr   zPage )Z	saveStateZsetFontZsetFillColorZgreyZ
drawStringZdrawRightStringr  ZrestoreState)ZcanvasrÂ  )rø  r÷  rù  rÀ   rd   re   Úfooter   s   *z!build_clients_pdf.<locals>.footerrM  rÖ  zClient ListzManju Associates)ZpagesizeZ
leftMarginZrightMarginZ	topMarginZbottomMarginrÿ  Zauthorrã  )çÃõ(\�Âµ?ç333333Ã?çìQ¸…ëÁ?r  ç      À?r  r  r  c                    s   g | ]}ˆ | ‘qS rd   rd   rZ  )rè  rd   re   r,  (   r*  r   )Z	colWidthsZ
repeatRowsZ
BACKGROUND©r   r   )éÿÿÿÿr   z#6B0F14ZROWBACKGROUNDS)r   r   ©r  r  z#FBF1DEZGRIDg      Ð?z#D9C9A3)ZVALIGNr  r  ZTOP)Z
TOPPADDINGr  r  rk  )ZBOTTOMPADDINGr  r  rk  zManju Associates - Client ListZTitlezGenerated on z%d-%m-%Y %H:%Mz | z
 client(s)ZNormalrˆ   )ZonFirstPageZonLaterPages)4Zxml.sax.saxutilsrö  Zreportlab.libr÷  Zreportlab.lib.pagesizesrø  rù  Zreportlab.lib.stylesrú  rû  Zreportlab.lib.unitsrÀ   Zreportlab.platypusrü  rý  rþ  rÿ  r   Úwhiter=  rC  rU  rÕ   rö  rE   rY  rM   r<   rá   rZ  r7  r[  rÄ   r2  rç   rÅ   r\  r]  r^  r9  r_  r.   r  rq  r—  r˜  ÚsetStyleZHexColorr  rŒ  Úbuildrñ  )r  rú  rû  rý  rþ  rÿ  r   ré  rß  Ústylesr	  r:   rø  Zservice_linesr  rõ  rÂ  ZratiosÚtableZstoryrd   )	rø  rb  rü  r÷  rö  r¸  rù  rÀ   rè  re   rŒ  ö  sb   þ
 8"0 ø"ÿû"ürŒ  )Frm   )ùÚdjango.confr   Zdjango.shortcutsr   Úmodelsr   r   r   r   r	   r
   r   r   r   r   r   r   Údjango.core.exceptionsr   Zrest_framework.exceptionsr   Údjango.contrib.authr   r   Zrest_framework_simplejwt.tokensr   Údjango.contrib.auth.hashersr   Údjango.contrib.auth.modelsr   Údjango.utilsr   Zdjtzr   r   r   Zcv2r`  r^  Únumpyrž  Úsysro  r—  Údjango.httpr   Úbase64ÚPILr   r   r   Údjango.db.modelsr    Zrest_framework.genericsr!   r"   Úserializersr#   Zrest_framework.permissionsr$   r%   rh  Údjango.core.mailr&   Zschool.settingsr'   ZpdfkitrR   r™   r(   Údjango.utils.cryptor)   r*   Zrest_framework.responser,   Úrest_frameworkr.   Zrest_framework.viewsr/   r1   rl   r�   r‚   rƒ   r„   rž   r¢   r­   rÃ   rò   r
  r  rJ  rO  r{  r|  rÐ  r2  r‡  r‰  ÚLockrˆ  rŠ  r�  rc  r_  r¨  r¾  rÁ  rÖ  ÚdecimalrÍ  rÎ  rØ  rÝ  râ  rã  rè  r%  rö  r4  r5  r  r  r#  r'  r)  r*  r4  r9  rE  rG  rI  r�  r¤  r¨  r©  r¼  rÀ  rÁ  rÉ  rÊ  rÑ  rÖ  rà  râ  Ú	django.dbrä  rå  ræ  rç  rè  rï  rì  rò  rô  rù  r  r  r  r  r(  r,  r?  r@  rQ  rV  ÚloggingÚ	getLoggerrh   rk  rn  rv  rw  r{  rƒ  r�  r¡  r¢  r£  rº  rÂ  r—  rÅ  rÒ  rø  rù  Údjango.core.paginatorrú  rû  rü  rý  rþ  Zrest_framework.parsersrÿ  r   Z#rest_framework_simplejwt.exceptionsr  r  r  r  r  r  r  r  r	  r  r  r  r  rŠ  r½  r¾  r  r  r[   rD   rQ   r«   r  r  r'  r,  r/  r2  r4  r7  r9  r:  r<  r@  rB  r›  rf  rh  rv  ry  r‚  rƒ  r¤  r«  r¬  r³  r·  rº  r»  rÊ  r‹  rŒ  rd   rd   rd   re   Ú<module>   s0   8Zg& I   CJ+]   5bý	\w y
&UV 
 jv< 
 { &?L)!P/@b 
 =h? 4       *  9  & * d),

k( .3F

P.