Ë
    ~WÂjêK  ã                   óT  — d Z ddlZddlZddlZddlmZmZ ddlmZmZm	Z	m
Z
mZ ddlmZmZ ddlmZ ddlmZmZmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZmZm Z m!Z! ddl"Z" edg¬«      Z#e"jH                  jK                  e"jH                  jK                  e"jH                  jK                  e"jH                  jM                  e'«      «      «      «      Z( ee"jH                  jS                  e(d«      ¬«      Z*dZ+dZ,dddddœZ-dddddddœZ.ddd d!d"œZ/d#„ Z0d$„ Z1d%„ Z2d&„ Z3d'„ Z4d(„ Z5d)„ Z6d*„ Z7d+„ Z8e#js                  d,e¬-«       ee«      fd.ed/efd0„«       Z:e#js                  d1e¬-«      dd2 ee«      fd.ed3e;d4e;d/efd5„«       Z<e#j{                  d1«       e	d6«       e	d2«       e	d6«       e	d6«       e	d7«       ee«      fd.ed3e;d4e;d8e>d9e>d:e>d/efd;„«       Z?d<„ Z@e#js                  d=e¬-«       ee«      fd>e>d.ed/efd?„«       ZAd@„ ZB e e edA«      «      gdBg¬C«      ZCeCjs                  dDe¬-«       ee«      fd.ed/efdE„«       ZDeCj{                  dF«       ee«      fdGe;d/efdH„«       ZEeCj{                  dI«       ee«      fdGe;d/efdJ„«       ZFeCj{                  dK«       ee«      fdGe;d/efdL„«       ZGy)MaÒ  
Katalog voucher publik + mekanisme pembelian online.

Batas tanggung jawab file ini: SAMPAI SEBELUM Midtrans.
Yang sudah berfungsi:
  - katalog paket (harga dari hotspot_profiles, tidak di-hardcode)
  - checkout: bikin order + RESERVASI voucher secara atomik
  - halaman status order untuk pembeli
  - admin (login) bisa melihat order, tandai lunas, serahkan, batalkan

Yang BELUM ada dan disengaja tidak ada:
  - panggilan API Midtrans, halaman pembayaran, webhook

Alasan: reservasi voucher adalah bagian paling rawan (bisa menyebabkan
voucher terjual dua kali atau hilang permanen). Itu harus bisa diuji sendiri
sebelum payment ditumpuk di atasnya. Titik integrasi Midtrans nanti ada di
`admin_mark_paid()`.

Anti-penjualan ganda: reservasi memakai UPDATE bersyarat
`WHERE id=? AND status='available'`, dan voucher baru dianggap terpegang kalau
`rowcount == 1`. Dua order yang membaca baris yang sama hanya akan berhasil
satu, jadi mustahil satu voucher dipegang dua order.
é    N)ÚdatetimeÚ	timedelta)Ú	APIRouterÚDependsÚFormÚHTTPExceptionÚRequest)ÚHTMLResponseÚRedirectResponse)ÚJinja2Templates)ÚfuncÚselectÚupdate)ÚAsyncSession)Úget_db)Údecrypt_secret)Úrequire_roles)ÚHotspotProfileÚOnlineOrderÚVoucherÚVoucherBatchÚshop)Útagszapp/templates)Ú	directoryé   é   ÚDetikÚMenitÚJamÚHari)ÚsÚmÚhÚd)zMenunggu PembayaranÚamber)ÚLunasÚemerald)ÚSelesair'   )ÚKedaluwarsaÚslate)Ú
DibatalkanÚrose)ÚpendingÚreservedÚpaidÚ	deliveredÚexpiredÚ	cancelledz2bg-amber-500/10 text-amber-400 border-amber-500/25z8bg-emerald-500/10 text-emerald-400 border-emerald-500/25z2bg-slate-500/10 text-slate-400 border-slate-500/25z/bg-rose-500/10 text-rose-400 border-rose-500/25)r%   r'   r*   r,   c                 ó  — | syt        j                  dt        | «      t         j                  «      }|st        | «      S t	        |j                  d«      «      › dt        |j                  d«      j                  «          › �S )z2'10h' -> '10 Jam'. None kalau format tak dikenali.Nz\s*(\d+)\s*([smhd])\s*é   ú é   )ÚreÚ	fullmatchÚstrÚ
IGNORECASEÚintÚgroupÚ_UNITSÚlower)Úvaluer"   s     úapp/routes/shop.pyÚhumanize_durationrA   E   se   € áØÜ
�‰Ð.´°E³
¼B¿M¹MÓJ€AÙÜ�5‹zÐÜ�!—'‘'˜!“*‹oÐ˜a¤ q§w¡w¨q£z×'7Ñ'7Ó'9Ñ :Ð;Ð<Ð<ó    c                 óp   — | syt        | «      } | dk  rydD ]  \  }}| |z  dk(  sŒ| |z  › d|› �c S  | › d�S )z:43200 -> '12 Jam'. Masa berlaku voucher bersifat opsional.Nr   ))i€Q r    )i  r   )é<   r   r5   z Detik)r;   )ÚsecondsÚsizeÚunits      r@   Úhumanize_secondsrH   O   s_   € áØÜ�'‹l€GØ�!‚|ØØEò /‰
ˆˆdØ�T‰>˜QÓØ ‘oÐ& a¨ vÐ.Ò.ð/ð ˆY�fÐÐrB   c                 óH   — dt        | xs d«      d›�j                  dd«      S )z5000 -> 'Rp5.000'.ÚRpr   ú,ú.)r;   Úreplace)r?   s    r@   ÚrupiahrN   \   s'   € à”�E’J˜Q“ Ð"Ð#×+Ñ+¨C°Ó5Ð5rB   c                  óz   — dt        j                  «       d›dt        j                  d«      j	                  «       › �S )zCKode unik. 3 byte hex acak + stempel waktu, cukup untuk volume ini.zORD-z%y%m%dú-é   )r   ÚnowÚsecretsÚ	token_hexÚupper© rB   r@   Únew_order_coderW   a   s3   € à”(—,‘,“. Ð(¨¬'×*;Ñ*;¸AÓ*>×*DÑ*DÓ*FÐ)GÐHÐHrB   c                 óÄ   — | sg S 	 t        j                  | «      }t        |t        «      r|D �cg c]  }t	        |«      ‘Œ c}S g S c c}w # t
        t        f$ r g cY S w xY w©N)ÚjsonÚloadsÚ
isinstanceÚlistr;   Ú
ValueErrorÚ	TypeError)ÚrawÚparsedÚxs      r@   Ú_decode_idsrc   f   s[   € ÙØˆ	ðÜ—‘˜C“ˆÜ+5°f¼dÔ+C Ö'˜1”�A•Ò'ÐKÈÐKùÒ'øÜœ	Ð"ò ØŠ	ðús(   †)A ¯AÁA ÁA ÁA ÁAÁAc                 ód   — t        j                  | D �cg c]  }t        |«      ‘Œ c}«      S c c}w rY   )rZ   Údumpsr;   )ÚidsÚis     r@   Ú_encode_idsrh   p   s#   € Ü�:‰: sÖ+ !”s˜1•vÒ+Ó,Ð,ùÒ+s   ”-c           	   ƒ   óÆ  K  — | j                  t        t        j                  t	        j
                  t        j                  «      «      j                  t        j                  dk(  t        j                  j                  |xs dg«      «      j                  t        j                  «      «      ƒ d {  –—† j                  «       }|D ��ci c]  \  }}||“Œ
 c}}S 7 Œ(c c}}w ­w)NÚ	availabler   )Úexecuter   r   Ú
profile_idr   ÚcountÚidÚwhereÚstatusÚin_Úgroup_byÚall)ÚdbÚprofile_idsÚrowsÚpidrm   s        r@   Ú
_stock_maprx   t   sª   è ø€ à�j‰jÜ”7×%Ñ%¤t§z¡z´'·*±*Ó'=Ó>ß‰UÜ—‘ +Ñ-Ü×"Ñ"×&Ñ& {Ò'9°q°cÓ:ó÷ ‰X”g×(Ñ(Ó)ó
÷ 	
÷ 
�cƒeð 	ð *.×.™:˜3 ˆC�‰JÓ.Ð.ð	
úó /ùs$   ‚B.C!Â0CÂ1C!ÃCÃC!ÃC!c              ƒ   óè  K  — | j                  t        t        «      j                  t        j                  dk(  «      j                  t        j                  t        j                  «      «      ƒ d {  –—† j                  «       j                  «       }t        | |D �cg c]  }|j                  ‘Œ c}«      ƒ d {  –—† }g }|D ]£  }t        |j                  «      }|j                  |j                  d«      }|j                  |j                  |xs |j                  |j                  |j                  t!        |j                  «      |t#        |j$                  «      ||dkD  dœ	«       Œ¥ |S 7 Œøc c}w 7 Œ·­w)NTr   )	rn   ÚlabelÚnameÚpriceÚ
price_textÚdurationÚvalidityÚstockÚin_stock)rk   r   r   ro   Ú	is_activeÚorder_byr|   rn   Úscalarsrs   rx   rA   Úsession_timeoutÚgetÚappendr{   rN   rH   Úvoucher_expiry_seconds)rt   rv   Úpr€   Úpackagesr~   rj   s          r@   Ú_load_packagesr‹   ‚   s$  è ø€ à�j‰jÜ”>Ó"ß‰U”>×+Ñ+¨tÑ3Ó4ß‰X”n×*Ñ*¬N×,=Ñ,=Ó>ó
÷ 	
÷
 �gƒi—‘“ð 	ô ˜R°Ö!5¨1 !§$£$Ò!5Ó6×6€EØ€HØò ˆÜ$ Q×%6Ñ%6Ó7ˆØ—I‘I˜aŸd™d AÓ&ˆ	Ø�‰Ø—$‘$ØÒ' §¡Ø—F‘FØ—W‘WÜ  §¡›/Ø Ü(¨×)AÑ)AÓBØØ! A™ñ

õ 
	ðð €Oð/	
úò "6Ð6ús1   ‚A.E2Á0E)Á1*E2ÂE+
Â.
E2Â8E0Â9B1E2Å+E2c              ƒ   óø  K  — t        j                  «       }| j                  t        t        «      j                  t        j                  j                  d«      t        j                  j                  d«      t        j                  |k  «      «      ƒ d{  –—† j                  «       j                  «       }|D ]•  }t        |j                  «      }|ru| j                  t        t        «      j                  t        j                   j                  |«      t        j                  dk(  «      j#                  dd¬«      «      ƒ d{  –—†  d|_        Œ— |r| j%                  «       ƒ d{  –—†  t'        |«      S 7 Œß7 Œ47 Œ­w)ao  
    Order yang lewat batas bayar dilepas; vouchernya dikembalikan ke stok.

    WAJIB mengembalikan status ke 'available'. Kalau tidak, setiap orang yang
    checkout lalu berubah pikiran akan mengikis stok permanen sampai habis.
    Hanya voucher yang masih 'reserved' yang disentuh, supaya kalau sudah
    terlanjur sold atau used tidak ada data yang ditimpa.
    ©r-   r.   Nr.   rj   ©rp   Úcommentr1   )r   Úutcnowrk   r   r   ro   rp   rq   Ú
expires_atÚisnotr„   rs   rc   Úreserved_voucher_idsr   r   rn   ÚvaluesÚcommitÚlen)rt   rR   ÚstaleÚorderÚvoucher_idss        r@   Ú_release_expiring_ordersrš   ž   s,  è ø€ ô �/‰/Ó
€Cà�j‰jÜ”;Ó×%Ñ%Ü×"Ñ"×&Ñ&Ð'>Ó?Ü×&Ñ&×,Ñ,¨TÓ2Ü×&Ñ&¨Ñ,óó
÷ 	
÷ �gƒi—‘“ð 
ð ò !ˆÜ! %×"<Ñ"<Ó=ˆÙØ—*‘*Ü”w“ß‘”w—z‘z—~‘~ kÓ2´G·N±NÀjÑ4PÓQß‘˜{°D�Ó9ó÷ ð ð
 !ˆ�ð!ñ Ø�i‰i‹k×ÐÜˆu‹:Ðð)	
øðøð 	ús7   ‚BE:ÂE4ÂB,E:ÅE6Å"E:Å$E8Å%E:Å6E:Å8E:ú/shop)Úresponse_classÚrequestrt   c           
   ƒ   óÈ   K  — t        |«      ƒ d{  –—†  t        |«      ƒ d{  –—† }t        j                  | d|t	        |«      t        d„ |D «       «      dœ¬«      S 7 ŒL7 Œ;­w)z.Katalog paket. Publik, read-only, tanpa login.Nz	shop.htmlc              3   ó,   K  — | ]  }|d    sŒ	d–— Œ y­w)r�   r4   NrV   )Ú.0r‰   s     r@   ú	<genexpr>zshop_catalog.<locals>.<genexpr>Í   s   è ø€ Ò!G¨¸¸:»¤!Ñ!Gùs   ‚
�)rŠ   Útotal_productsÚin_stock_count©r�   r{   Úcontext)rš   r‹   Ú	templatesÚTemplateResponser–   Úsum)r�   rt   rŠ   s      r@   Úshop_catalogr©   Â   sg   è ø€ ô # 2Ó
&×&Ð&Ü# BÓ'×'€HÜ×%Ñ%ØØà Ü! (›mÜ!Ñ!G¨XÔ!GÓGñ
ð &ó ð ð 'øØ'ús   ‚A"‘A’A"¤A ¥:A"Á A"z/shop/checkoutr4   rl   Úquantityc              ƒ   ó>  K  — t        |«      ƒ d{  –—†  t        |«      ƒ d{  –—† }|D �ci c]  }|d   |“Œ
 }}||vrt        dd¬«      S t        j	                  | d||t        dt        t        |xs d«      t        «      «      ddddd	œd
œ¬«      S 7 Œ‚7 Œqc c}w ­w)zEFormulir checkout. Paket yang tidak aktif tidak bisa dibuka langsung.Nrn   r›   é/  ©ÚurlÚstatus_codeúshop_checkout.htmlr4   Ú ©r{   ÚphoneÚemail©rŠ   Úselected_idrª   ÚerrorÚoldr¤   )	rš   r‹   r   r¦   r§   ÚmaxÚminr;   ÚMAX_PER_ORDER)r�   rl   rª   rt   rŠ   r‰   Úby_ids          r@   Úshop_checkout_formr½   Ô   s¯   è ø€ ô # 2Ó
&×&Ð&Ü# BÓ'×'€HØ!)Ö*˜AˆQˆt‰W�a‰ZÐ*€EÐ*Ø˜ÑÜ G¸Ô=Ð=Ü×%Ñ%ØØ!à Ø%Ü˜Aœs¤3 x¢}°1Ó#5´}ÓEÓFØØ¨°bÑ9ñ
ð &ó 
ð 
ð 'øØ'úÚ*ùs0   ‚B‘B’B¤B¥B­BºABÂBÂB.r±   Úcustomer_nameÚcustomer_phoneÚcustomer_emailc              ƒ   ó(	  K  — 	 t        |xs d«      }t        dt	        |t
        «      «      }|xs dj                  «       }|xs dj                  «       }|xs dj                  «       xs d}	t        |«      dk  rt        | |||d|||	«      ƒ d{  –—† S t        t        j                  dd|«      «      dk  rt        | |||d|||	«      ƒ d{  –—† S |	r0t        j                  d	|	«      st        | |||d
|||	«      ƒ d{  –—† S t        |«      ƒ d{  –—†  |j                  t        |«      ƒ d{  –—† }
|
r|
j                  st!        dd¬«      S |j#                  t%        t&        j(                  t&        j*                  «      j-                  t&        j.                  |
j(                  k(  t&        j0                  dk(  «      j3                  t&        j(                  «      j5                  |dz  «      «      ƒ d{  –—† j7                  «       }g }d}|D ]²  \  }}t        |«      |k\  r nŸ|j#                  t9        t&        «      j-                  t&        j(                  |k(  t&        j0                  dk(  «      j;                  dd¬«      «      ƒ d{  –—† }|j<                  dk(  sŒ�|j?                  |«       ||xs |
j*                  z  }Œ´ t        |«      |k  r¹|ru|j#                  t9        t&        «      j-                  t&        j(                  jA                  |«      t&        j0                  dk(  «      j;                  dd¬«      «      ƒ d{  –—†  |jC                  «       ƒ d{  –—†  t        | |||dt        |«      › d|› d�|||	«      ƒ d{  –—† S tE        tG        «       |||	|
j(                  |
jH                  tK        |
jL                  «      ||dtO        |«      tQ        jR                  «       tU        tV        ¬«      z   ¬«      }|jY                  |«       |jC                  «       ƒ d{  –—†  |j[                  |«      ƒ d{  –—†  t!        d|j\                  › �d¬«      S # t        t        f$ r d}Y �ŒYw xY w7 �Œà7 �Œ¦7 �Œw7 �Œg7 �ŒL7 �ŒŠ7 �Œú7 �ŒB7 �Œ-7 �Œ7 Œm7 ŒV­w)z6Buat order dan reservasi voucher dalam satu transaksi.r4   r±   Nr6   zNama minimal 2 karakter.z\Dé   zNomor HP/WA tidak valid.z[^@\s]+@[^@\s]+\.[^@\s]+zFormat email tidak valid.r›   r¬   r­   rj   rQ   r   r.   zdipesan onlinerŽ   zStok tersisa z voucher, tidak cukup untuk rL   )Úminutes)Ú
order_coder¾   r¿   rÀ   rl   Úprofile_nameÚduration_labelrª   Útotal_pricerp   r“   r‘   z/shop/order/)/r;   r_   r^   r¹   rº   r»   Ústripr–   Ú_checkout_errorr7   Úsubr8   rš   r†   r   r‚   r   rk   r   r   rn   r|   ro   rl   rp   rƒ   Úlimitrs   r   r”   Úrowcountr‡   rq   r•   r   rW   r{   rA   r…   rh   r   r�   r   ÚRESERVE_MINUTESÚaddÚrefreshrÄ   )r�   rl   rª   r¾   r¿   rÀ   rt   r{   r³   r´   ÚprofileÚ
candidatesÚreserved_idsÚtotalÚ
voucher_idÚvoucher_priceÚresultr˜   s                     r@   Úshop_checkoutr×   î   s  è ø€ ðÜ�x’} 1Ó%ˆô �1”c˜(¤MÓ2Ó3€HàÒ˜R×&Ñ&Ó(€DØÒ!˜r×(Ñ(Ó*€EØÒ!˜r×(Ñ(Ó*Ò2¨d€Eä
ˆ4ƒy�1‚}Ü$ W¨b°*¸hØ%?ÀÀuÈeóU÷ Uð 	Uä
Œ2�6‰6�%˜˜UÓ#Ó$ qÒ(Ü$ W¨b°*¸hØ%?ÀÀuÈeóU÷ Uð 	Uá”R—\‘\Ð"=¸uÔEÜ$ W¨b°*¸hØ%@À$ÈÈuóV÷ Vð 	Vô # 2Ó
&×&Ð&à—F‘Fœ>¨:Ó6×6€GÙ˜'×+Ò+Ü G¸Ô=Ð=ð
 �j‰jÜ”7—:‘:œwŸ}™}Ó-ß‰U”7×%Ñ%¨¯©Ñ3´W·^±^À{Ñ5RÓSß‰X”g—j‘jÓ!ß‰U�8˜a‘<Ó ó	
÷ 	
÷ 
�cƒeð ð €LØ€EØ%/ò 4Ñ!ˆ
�MÜˆ|Ó Ò(ÙØ—z‘zÜ”7‹Oß‰U”7—:‘: Ñ+¬W¯^©^¸{Ñ-JÓKß‰V˜:Ð/?ˆVÓ@ó
÷ 
ˆð �?‰?˜aÓØ×Ñ 
Ô+Ø�]Ò3 g§m¡mÑ3‰Eð4ô ˆ<Ó˜8Ò#áØ—*‘*Ü”w“ß‘”w—z‘z—~‘~ lÓ3´W·^±^ÀzÑ5QÓRß‘˜{°D�Ó9ó÷ ð ð
 �i‰i‹k×ÐÜ$Ø�R˜ XØœC Ó-Ð.Ð.JÈ8È*ÐTUÐVØ�%˜ó
÷ 
ð 	
ô Ü!Ó#ØØØØ—:‘:Ø—\‘\Ü(¨×)@Ñ)@ÓAØØØÜ(¨Ó6Ü—?‘?Ó$¤y¼Ô'IÑIô€Eð ‡F�Fˆ5„MØ
�)‰)‹+×ÐØ
�*‰*�UÓ
×Ðô  ,¨u×/?Ñ/?Ð.@Ð AÈsÔSÐSøôm ”zÐ"ò Ø‹ðúðUùðUùðVùð 'ùà6ùð	
ùð
ùðùð
 	ùð
ùð* øØús  ‚R„Q “A=RÂQ0Â<RÃQ3Ã1RÃ?Q6Ä RÄQ9ÄRÄ0Q<Ä1CRÇ5Q?Ç6BRÊRÊ	RÊB&RÍRÍRÍRÍ)RÎRÎBRÐ RÐ!RÐ9RÐ:RÑQ-Ñ)RÑ,Q-Ñ-RÑ3RÑ6RÑ9RÑ<RÑ?RÒRÒRÒRÒRÒRÒRc              ƒ   ó‚   K  — t        |«      ƒ d {  –—† }t        j                  | d|||||||xs ddœdœd¬«      S 7 Œ+­w)Nr°   r±   r²   rµ   i¦  ©r�   r{   r¥   r¯   )r‹   r¦   r§   )	r�   rt   rl   rª   Úmessager{   r³   r´   rŠ   s	            r@   rÉ   rÉ   T  sZ   è ø€ Ü# BÓ'×'€HÜ×%Ñ%ØØ!à Ø%Ø ØØ ¨5¸5º;ÀBÑGñ
ð ð &ó ð ð (ús   ‚?‘=’,?z/shop/order/{order_code}rÄ   c              ƒ   ó`  K  — t        |«      ƒ d{  –—†  |j                  t        t        «      j	                  t        j
                  | k(  «      «      ƒ d{  –—† j                  «       }|st        j                  |dd| dœd¬«      S g }|j                  dv rt        ||«      ƒ d{  –—† }t        j                  |j                  |j                  df«      \  }}d}|j                  d	k(  re|j                  rY|j                  j                  d¬
«      }|t        j                   «       z
  j#                  «       }	t%        dt'        |	dz  «      «      }t        j                  |dd|||t(        j                  |t(        d   «      |t+        |j,                  «      |t.        dœ	¬«      S 7 �Œ”7 �ŒQ7 �Œ­w)z©
    Halaman status untuk pembeli.

    Kode voucher HANYA ditampilkan setelah order lunas. Sebelum itu sengaja
    disembunyikan supaya tidak bisa dibaca dari URL.
    Nzshop_order.htmlF)ÚfoundrÄ   é”  rÙ   ©r/   r0   r*   r.   )Útzinfor   rD   T)	rÜ   r˜   Ústatus_labelÚstatus_toneÚ
tone_classÚvouchersr}   Úminutes_leftÚreserve_minutesr¤   )rš   rk   r   r   ro   rÄ   Úscalar_one_or_noner¦   r§   rp   Ú_decrypt_vouchersÚSTATUS_LABELr†   r‘   rM   r   r�   Útotal_secondsr¹   r;   Ú
TONE_CLASSrN   rÇ   rÍ   )
rÄ   r�   rt   r˜   rã   rz   Útonerä   Úexpires_naiveÚdeltas
             r@   Úshop_order_statusrî   f  s�  è ø€ ô # 2Ó
&×&Ð&ð �j‰jœ¤Ó,×2Ñ2´;×3IÑ3IÈZÑ3WÓXÓY×YßÑÓð 
ñ Ü×)Ñ)ØØ"Ø#°:Ñ>Øð	 *ó 
ð 	
ð €HØ‡|�|Ð,Ñ,Ü*¨2¨uÓ5×5ˆä×"Ñ" 5§<¡<°%·,±,ÀÐ1HÓI�K€Eˆ4Ø€LØ‡|�|�zÒ! e×&6Ò&6Ø×(Ñ(×0Ñ0¸Ð0Ó=ˆØ¤§¡Ó!2Ñ2×AÑAÓCˆÜ˜1œc %¨2¡+Ó.Ó/ˆä×%Ñ%ØØàØØ!ØÜ$Ÿ.™.¨¬z¸'Ñ/BÓCØ Ü  ×!2Ñ!2Ó3Ø(Ü.ñ

ð &ó ð ð3 'ùð 	Zùð 6ûs6   ‚F.‘F%’AF.ÁF(ÁAF.Â'F+Â(C>F.Æ(F.Æ+F.c              ƒ   óÔ  K  — t        |j                  «      }|sg S | j                  t        t        j
                  «      j                  t        j
                  j                  |«      «      «      ƒ d{  –—† j                  «       j                  «       }i }|�r| j                  t        t        j                  «      j                  t        j
                  j                  |«      «      «      ƒ d{  –—† j                  «       j                  «       }|rŽ| j                  t        t        j
                  t        j                  «      j                  t        j
                  j                  |«      «      «      ƒ d{  –—† j                  «       }|D ��ci c]  \  }}||“Œ
 }}}| j                  t        t        j                  t        j                  t        j                  t        j                   t        j"                  t        j$                  t        j                  «      j                  t        j
                  j                  |«      «      «      ƒ d{  –—† j                  «       }	g }
|	D ]X  \  }}}}}}}d}|r	 t'        |«      }|xs |}|�||k(  s|dv s|j+                  |«      dk(  r|}|
j-                  |||||dœ«       ŒZ |
S 7 �ŒS7 �ŒÙ7 �ŒSc c}}w 7 Œ‚# t(        $ r d}Y Œ`w xY w­w)a  
    Bentuk daftar voucher siap tampil untuk pembeli.

    Password disimpan terenkripsi hanya untuk mode 'separate'; mode 'same'
    berarti password sama dengan username. Kegagalan decrypt satu voucher
    tidak boleh menggagalkan seluruh halaman, jadi ditangani per baris.
    N)Úlegacy_sameÚsamerñ   )ÚcodeÚusernameÚpasswordrp   Úuptime)rc   r“   rk   r   r   rn   ro   rq   r„   rs   Úbatch_idr   Ú	user_moderò   ró   Úpassword_ciphertextÚcredential_moderp   Úlimit_uptimer   Ú	Exceptionr†   r‡   )rt   r˜   r™   rv   Úbatch_modesÚ	batch_idsrÜ   ÚbidÚmodeÚrecordsÚpreparedrò   ró   ÚcipherÚvstatusrõ   rö   rô   Úusername_or_codes                      r@   rç   rç   ˜  sv  è ø€ ô ˜e×8Ñ8Ó9€KÙØˆ	ð �j‰jÜ”7—:‘:Ó×$Ñ$¤W§Z¡Z§^¡^°KÓ%@ÓAó
÷ 	
÷ �gƒi—‘“ð	 	ð €KÚà—*‘*Ü”w×'Ñ'Ó(×.Ñ.¬w¯z©z¯~©~¸kÓ/JÓKó÷ ÷ ‰'‹)—C‘C“Eð	 	ñ
 à—j‘jÜœ<Ÿ?™?¬L×,BÑ,BÓCß‘Uœ<Ÿ?™?×.Ñ.¨yÓ9Ó:ó÷ ÷ ‰c‹eð ð 7<×<©¨¨d˜3 ™9Ð<ˆKÑ<ð �j‰jÜÜ—‘œg×.Ñ.´×0KÑ0KÜ×'Ñ'¬¯©¼×9MÑ9MÜ× Ñ ó÷ ‰e”G—J‘J—N‘N ;Ó/Ó0ó
÷ 	
÷ 
�cƒeð ð €HØCJò Ñ?ˆˆh˜  g¨v°xØˆÙð Ü)¨&Ó1�ð $Ò+ tÐàÐØÐ+Ò+ØÐ.Ñ.Ø�‰˜xÓ(¨FÒ2à'ˆHØ�‰ØØ(Ø ØØñ
õ 	ðð, €Oðm	
ùðùðûó
 =ð	
ùô ò  Ø’ð üs|   ‚A1K(Á3KÁ4A<K(Ã0K	Ã1BK(Å9KÅ:K(ÆKÆB4K(ÉKÉ%K(É9KÊAK(Ë	K(ËK(ËK(ËK%Ë"K(Ë$K%Ë%K(Úadmin_csz
shop-admin)Údependenciesr   ú/shop/ordersc           	   ƒ   ó¼  ‡K  — t        |«      ƒ d{  –—†  |j                  t        t        «      j	                  t        j
                  j                  «       «      j                  d«      «      ƒ d{  –—† j                  «       j                  «       }i }dD ]  Št        ˆfd„|D «       «      |‰<   Œ t        j                  | d|t        t        |dœ¬«      S 7 ŒÅ7 Œg­w)z6Daftar order online untuk admin. Butuh login admin_cs.Néd   )r.   r/   r0   r1   r2   c              3   óB   •K  — | ]  }|j                   ‰k(  sŒd –— Œ y­w)r4   N)rp   )r    Úorp   s     €r@   r¡   zadmin_orders.<locals>.<genexpr>ì  s   øè ø€ ÒE 1°!·(±(¸fÓ2DœQÑEùs   ƒ˜zshop_orders.html)Úordersrà   râ   Úcountsr¤   )rš   rk   r   r   rƒ   Ú
created_atÚdescrË   r„   rs   r¨   r¦   r§   rè   rê   )r�   rt   r  r  rp   s       @r@   Úadmin_ordersr  ã  sÇ   øè ø€ ô # 2Ó
&×&Ð&à�j‰jœ¤Ó,×5Ñ5´k×6LÑ6L×6QÑ6QÓ6SÓT×ZÑZÐ[^Ó_Ó`×`ß�gƒi—‘“ð ð €FØKò FˆÜÓE¨ÔEÓEˆˆvŠðFä×%Ñ%ØØàÜ(Ü$Øñ	
ð &ó 	ð 	ð 'øà`ús#   ƒC’C“ACÁ2CÁ3A&CÃCz/shop/orders/{order_id}/payÚorder_idc              ƒ   ó  K  — |j                  t        | «      ƒ d{  –—† }|st        dd¬«      ‚|j                  dvr8d|_        t	        j
                  «       |_        |j                  «       ƒ d{  –—†  t        dd¬	«      S 7 Œf7 Œ­w)
zå
    Tandai lunas manual. Untuk pembayaran di luar Midtrans (transfer, tuner)
    dan jalur pemulihan kalau webhook gagal.

    Integrasi Midtrans nanti memanggil logika yang sama lewat webhook, bukan
    lewat endpoint ini.
    NrÝ   úOrder tidak ditemukan©r¯   ÚdetailrÞ   r/   r  r¬   r­   )	r†   r   r   rp   r   r�   Úpaid_atr•   r   )r  rt   r˜   s      r@   Úadmin_mark_paidr  ù  su   è ø€ ð —&‘&œ hÓ/×/€EÙÜ¨Ð4KÔLÐLØ‡|�|Ð0Ñ0ØˆŒÜ Ÿ™Ó)ˆŒØ�i‰i‹k×ÐÜ ¸CÔ@Ð@ð 0øð 	ús"   ‚BœB�ABÁ1BÁ2BÂBz/shop/orders/{order_id}/deliverc              ƒ   óV  K  — |j                  t        | «      ƒ d{  –—† }|st        dd¬«      ‚|j                  dk(  rÖt	        |j
                  «      }|r‡|j                  t        t        «      j                  t        j                  j                  |«      t        j                  dk(  «      j                  dt        j                  «       ¬«      «      ƒ d{  –—†  d	|_        t        j                  «       |_        |j!                  «       ƒ d{  –—†  t#        d
d¬«      S 7 �Œ7 ŒL7 Œ­w)zGTandai voucher sudah diserahkan ke pembeli; status voucher jadi 'sold'.NrÝ   r  r  r/   r.   Úsold)rp   Úsold_atr0   r  r¬   r­   )r†   r   r   rp   rc   r“   rk   r   r   ro   rn   rq   r”   r   r�   Údelivered_atr•   r   ©r  rt   r˜   r™   s       r@   Úadmin_deliverr    sä   è ø€ ð —&‘&œ hÓ/×/€EÙÜ¨Ð4KÔLÐLØ‡|�|�vÒÜ! %×"<Ñ"<Ó=ˆÙØ—*‘*Ü”w“ß‘”w—z‘z—~‘~ kÓ2´G·N±NÀjÑ4PÓQß‘˜v¬x¯©Ó/@�ÓAó÷ ð ð
 #ˆŒÜ%Ÿ_™_Ó.ˆÔØ�i‰i‹k×ÐÜ ¸CÔ@Ð@ð 0ùðøð 	ús4   ‚D)œD"�B;D)ÃD%Ã7D)ÄD'ÄD)Ä%D)Ä'D)z/shop/orders/{order_id}/cancelc              ƒ   óü  K  — |j                  t        | «      ƒ d{  –—† }|st        dd¬«      ‚|j                  dv r«t	        |j
                  «      }|ru|j                  t        t        «      j                  t        j                  j                  |«      t        j                  dk(  «      j                  dd¬«      «      ƒ d{  –—†  d	|_        |j                  «       ƒ d{  –—†  t        d
d¬«      S 7 ŒÙ7 Œ27 Œ­w)z0Batalkan order; voucher dilepas kembali ke stok.NrÝ   r  r  r�   r.   rj   rŽ   r2   r  r¬   r­   )r†   r   r   rp   rc   r“   rk   r   r   ro   rn   rq   r”   r•   r   r  s       r@   Úadmin_cancelr     sÏ   è ø€ ð —&‘&œ hÓ/×/€EÙÜ¨Ð4KÔLÐLØ‡|�|Ð.Ñ.Ü! %×"<Ñ"<Ó=ˆÙØ—*‘*Ü”w“ß‘”w—z‘z—~‘~ kÓ2´G·N±NÀjÑ4PÓQß‘˜{°D�Ó9ó÷ ð ð
 #ˆŒØ�i‰i‹k×ÐÜ ¸CÔ@Ð@ð 0øðøð 	ús4   ‚C<œC6�B(C<ÃC8ÃC<Ã$C:Ã%C<Ã8C<Ã:C<)HÚ__doc__rZ   r7   rS   r   r   Úfastapir   r   r   r   r	   Úfastapi.responsesr
   r   Úfastapi.templatingr   Ú
sqlalchemyr   r   r   Úsqlalchemy.ext.asyncior   Úapp.core.databaser   Úapp.core.device_credentialsr   Úapp.core.securityr   Úapp.models.modelsr   r   r   r   ÚosÚrouterÚpathÚdirnameÚabspathÚ__file__ÚBASE_DIRÚjoinr¦   rÍ   r»   r=   rè   rê   rA   rH   rN   rW   rc   rh   rx   r‹   rš   r†   r©   r;   r½   Úpostr9   r×   rÉ   rî   rç   Úadmin_routerr  r  r  r  rV   rB   r@   ú<module>r4     s’  ðñó. Û 	Û ß (ç DÕ Dß <Ý .ß +Ñ +Ý /å $Ý 6Ý +ß PÓ Pã 	á	˜˜Ô	!€Ø�7‰7�?‰?˜2Ÿ7™7Ÿ?™?¨2¯7©7¯?©?¸2¿7¹7¿?¹?È8Ó;TÓ+UÓVÓW€Ù b§g¡g§l¡l°8¸_Ó&MÔN€	ð €Ø€à˜W¨5°vÑ	>€ð 0Ø0Ø Ø'Ø'Ø'ñ€ð BØIØAØ=ñ	€
ò=ò
ò6ò
Iò
ò-ò/òò8ðH ‡�ˆG L€Ó1Ù<CÀF»Oñ  ð ¨\ò ó 2ðð" ‡�Ð¨\€Ó:ð ØÙ˜v“ñ	Øðàðð ðð 	ò	ó ;ðð2 ‡�ÐÓñ ˜3“iÙ˜“GÙ˜c›Ù˜s›)Ù˜r›(Ù˜v“ñbTØðbTàðbTð ðbTð ð	bTð
 ðbTð ðbTð 	òbTó ðbTòJð$ ‡�Ð&°|€ÓDÙRYÐZ`ÓRañ .¨ð .°gð .À<ò .ó Eð.òbCñP ¡w©}¸ZÓ/HÓ'IÐ&JÐR^ÐQ_Ô`€ð ×Ñ�.°ÐÓ>Ù<CÀF»Oñ  ð ¨\ò ó ?ðð* ×ÑÐ0Ó1Ù<CÀF»Oñ A Cð A¨\ò Aó 2ðAð$ ×ÑÐ4Ó5Ù:AÀ&»/ñ A #ð A¨<ò Aó 6ðAð& ×ÑÐ3Ó4Ù9@À»ñ A ð A¨,ò Aó 5ñArB   