Ë
    >UÂj’=  ã                   ód  — d Z ddlZddlZddlZddlZ ej
                  d«      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mZmZmZmZ dd	lmZmZmZmZ dd
lmZ ddlmZmZ defd„Zdefd„Zdedz  defd„Z de	dz  de	dz  fd„Z!de"eef   fd„Z#defd„Z$dd„Z%dd„Z&dd„Z'de"fd„Z(y)a¬  Periodic live-status reconciliation for Billing, customers and hotspot vouchers.

Voucher lifecycle rules (independent triggers, either one is enough):
  1. Duration/jatah habis  -> hapus dari RouterOS + database.
  2. Masa berlaku habis   -> hapus dari RouterOS + database.
Status juga disinkronkan LIVE dari RouterOS (uptime / limit-uptime / active /
disabled) supaya DataTables voucher selalu mencerminkan kondisi perangkat.
é    Nzbestweb.voucher_monitor)Úselect)ÚdatetimeÚtimezoneÚ	timedelta©ÚAsyncSessionLocal)Úrefresh_customer_online_status)ÚVoucherÚVoucherBatchÚCustomerÚMikrotikDeviceÚHotspotProfile)r   ÚDeviceCredentialr   r
   )ÚRealMikrotikAdapter)Údecrypt_secret_strictÚSecretDecryptionErrorÚreturnc            	      ót   — 	 t        dt        t        j                  dd«      «      «      S # t        $ r Y yw xY w)Né
   ÚBESTWEB_STATUS_POLL_SECONDSÚ30é   ©ÚmaxÚintÚosÚgetenvÚ
ValueError© ó    úapp/core/status_monitor.pyÚpoll_intervalr"   !   s7   € ðÜ�2”sœ2Ÿ9™9Ð%BÀDÓIÓJÓKÐKøÜò Ùðúó   ‚(+ «	7¶7c            	      ót   — 	 t        dt        t        j                  dd«      «      «      S # t        $ r Y yw xY w)Né   ÚBESTWEB_VOUCHER_POLL_SECONDSÚ10r   r   r   r    r!   Úvoucher_poll_intervalr(   (   s7   € ðÜ�1”cœ"Ÿ)™)Ð$BÀDÓIÓJÓKÐKøÜò Ùðúr#   Úvaluec                 óh  — t        | xs d«      j                  «       j                  «       }|syd|v rád}|}t        j                  d|«      }|r+t        |j                  d«      «      }|j                  d«      }|j                  d«      }	 |D �cg c]  }t        t        |«      «      ‘Œ }}t        |«      dk  r!|j                  dd«       t        |«      dk  rŒ!t        |«      dk\  r||d   z  }|d	d
 }|dz  |d   dz  z   |d   dz  z   |d   z   S t        j                  d|«      }|rPt        |j                  «       «      r7d„ |j                  «       D «       \  }	}
}}}|	dz  |
dz  z   |dz  z   |dz  z   |z   S 	 t        t        |«      «      S c c}w # t        $ r Y yw xY w# t        $ r Y yw xY w)z|Parse RouterOS uptime/limit-uptime into seconds.

    Accepts 1d02:03:04, 02:03:04, 3h04m, 2w, 5m, 45s and bare digits.
    Ú r   ú:z^(\d+)d(.*)$é   é   é   é   éýÿÿÿNi€Q i  é<   z7(?:(\d+)w)?(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?c              3   ó:   K  — | ]  }|rt        |«      nd –— Œ y­w)r   N)r   )Ú.0Úgs     r!   ú	<genexpr>z'parse_uptime_seconds.<locals>.<genexpr>P   s   è ø€ ÒD°¡1œ˜Qœ¨!Ó+ÑDùs   ‚i€:	 )ÚstrÚstripÚlowerÚreÚmatchr   ÚgroupÚsplitÚfloatr   ÚlenÚinsertÚ	fullmatchÚanyÚgroups)r)   ÚrawÚdaysÚbodyÚ	day_matchÚpartsÚpÚnumsr;   ÚwÚdÚhÚmÚss                 r!   Úparse_uptime_secondsrP   /   s¼  € ô
 ˆeŠk�rÓ
×
 Ñ
 Ó
"×
(Ñ
(Ó
*€CÙØð ˆc�zØˆØˆÜ—H‘H˜_¨cÓ2ˆ	ÙÜ�y—‘ qÓ)Ó*ˆDØ—?‘? 1Ó%ˆDØ—
‘
˜3“ˆð	Ø+0Ö1 a”Cœ˜a›•MÐ1ˆDÐ1ô �$‹i˜!ŠmØ�K‰K˜˜1Ôô �$‹i˜!‹mäˆt‹9˜Š>Ø�D˜‘G‰OˆDØ˜˜�9ˆDØ�e‰|˜d 1™g¨™nÑ,¨t°A©w¸©|Ñ;¸dÀ1¹gÑEÐEä�L‰LØBÀCó€Eñ ”�U—\‘\“^Ô$ÙD°U·\±\³^ÔD‰ˆˆ1ˆa��AØ�6‰z˜A ™IÑ%¨¨D©Ñ0°1°r±6Ñ9¸AÑ=Ð=ðÜ”5˜“:‹Ðùò% 2øÜò 	Ùð	ûô$ ò Ùðús6   ÂF ÂFÂ+F Å=F% ÆF Æ	F"Æ!F"Æ%	F1Æ0F1c                 óž   — | €y| j                   € | j                  t        j                  ¬«      S | j	                  t        j                  «      S )zGSQLite drops tzinfo, so treat naive timestamps as UTC before comparing.N)Útzinfo)rR   Úreplacer   ÚutcÚ
astimezone)r)   s    r!   Úas_utcrV   Y   s>   € à€}ØØ‡|�|ÐØ�}‰}¤H§L¡Lˆ}Ó1Ð1Ø×ÑœHŸL™LÓ)Ð)r    c              ƒ   ó8  K  — | j                  t        t        «      j                  t        j                  dk(  «      «      ƒ d{  –—† j                  «       j                  «       }i }|D ]¨  }|j                  sŒ| j                  t        |j                  «      ƒ d{  –—† }|r|j                  sŒGt        |j                  d|j                  › �¬«      }t        |j                  |j                  ||j                   xs d¬«      ||j"                  <   Œª |S 7 ŒÑ7 Œ{­w)a  Build one adapter per active MikroTik device that has valid credentials.

    Raises SecretDecryptionError when a stored secret cannot be decrypted with
    the active encryption key, so the caller can surface it instead of silently
    pushing ciphertext to the device.
    TNzKredensial )Úlabelé"  )ÚhostÚusernameÚpasswordÚapi_port)Úexecuter   r   ÚwhereÚ	is_activeÚscalarsÚallÚcredential_idÚgetr   r   Úencrypted_secretÚnamer   rZ   r[   r]   Úid)ÚdbÚdevicesÚadaptersÚmkÚcredr\   s         r!   Ú_device_adaptersrm   b   sõ   è ø€ ð —Z‘ZÜŒ~Ó×$Ñ$¤^×%=Ñ%=ÀÑ%EÓFó÷ ç�wƒy—‘“ð ð 02€HØò 
ˆØ×ÒØØ—V‘VÔ,¨b×.>Ñ.>Ó?×?ˆÙ˜ŸšØÜ(Ø×!Ñ!¨;°t·y±y°kÐ)Bô
ˆô .Ø—‘Ø—]‘]ØØ—[‘[Ò( Dô	
ˆ�—‘Šð
ð €Oð'øð @ús&   ‚ADÁDÁADÂDÂA:DÄDc           
      óÎ  — | dv ryt        | «      j                  «       }|j                  «       rt        |«      S ddddddd	œ}|j	                  «       j                  d
d«      }|j                  «       D ]=  \  }}|j                  |«      sŒ	 t        t        |dt        |«        «      |z  «      c S  	 t        t        |«      «      S # t        $ r Y  yw xY w# t        $ r Y yw xY w)zDParse RouterOS byte counters like '123456' or '1.2MiB' -> int bytes.)Nr+   r    r   i   i   i   @iè  i@B i Êš;)ÚkibÚmibÚgibÚkbÚmbÚgbú r+   N)r7   r8   Úisdigitr   r9   rS   ÚitemsÚendswithr>   r?   r   )r)   ÚtextÚunitsÚloweredÚsuffixÚmults         r!   Ú_routeros_bytesr~      sè   € à�ÑØÜˆu‹:×ÑÓ€DØ‡|�|„~Ü�4‹yÐØ °9Ø˜y°	ñ;€Eà�j‰j‹l×"Ñ" 3¨Ó+€GØŸ™›ò ‰ˆ�Ø×Ñ˜FÕ#ðÜœ5 ¨¬C°«K¨<Ð!8Ó9¸DÑ@ÓAÒAððÜ”5˜“>Ó"Ð"øô ò Úðûô ò Ùðús$   Â#CÂ4C Ã	CÃCÃ	C$Ã#C$c               ƒ   óÒ  K  — t        «       4 ƒd{  –—† } t        j                  t        j                  «      }| j                  t        t        «      j                  t        j                  dk7  «      «      ƒ d{  –—† j                  «       j                  «       }|s	 ddd«      ƒd{  –—†  y	 t        | «      ƒ d{  –—† }i }i }d}|j#                  «       D ]ƒ  \  }}		 t%        j&                  |	j(                  «      ƒ d{  –—† }
|
D �ci c]  }|d   |“Œ
 c}||<   d||<   | j+                  t,        |«      ƒ d{  –—† }|€Œk|j.                  |k7  sŒ{||_        d}Œ… |D �]  }|j2                  xs |j4                  xs dj7                  «       }|j8                  }| j+                  t:        |j<                  «      ƒ d{  –—† }|r!|j+                  |i «      j+                  |«      nd}|j+                  |d«      }t?        |j@                  «      }|jB                  rtE        |xr ||k  «      }nd}d}|rÊtG        |j+                  d	«      «      }tG        |j+                  d
«      «      xs tG        |jH                  «      }||jJ                  xs dk  rd|_%        ||jJ                  xs dz
  }|dkD  r*tM        |jN                  xs d«      |z   |_'        ||_%        d}|dkD  rtM        |jN                  xs d«      |k\  rd}|xr |du xr |j                  dk(  }tQ        |xr |j+                  d«      «      xs tQ        |xr |j+                  d«      «      }|rä|j+                  d«      stG        |j+                  d	«      «      dkD  s|r´|j                  dv r¦d|_	        |jB                  xs ||_!        |r+|jR                  rtU        tM        |jR                  «      ¬«      n.|r+|jV                  rtU        tM        |jV                  «      ¬«      nd}|r|jB                  |z   |_         d}t        jY                  d|«       |s|s|s�Œä|r|j+                  |«      nd}	|	r+|r)	 t%        j&                  |	jZ                  |«      ƒ d{  –—†  ddl.m/} |rdn|rdnd}| ja                   ||jb                  |j4                  |j2                  |jd                  |jf                  |j                  |jh                  |jB                  tM        |jN                  xs d«      |||j<                  |jj                  ¬«      «       t        jY                  d||«       | jm                  |«      ƒ d{  –—†  d}�Œ	 |r| jo                  «       ƒ d{  –—†  ddd«      ƒd{  –—†  y7 �Œ…7 �Œ7 �Œó7 �Œá# t        $ r}t        j                  d|«       ‚ d}~wt         $ r Y ddd«      ƒd{  –—†7   yw xY w7 �Œãc c}w 7 �Œ±# t         $ r$ i ||<   d||<   t        j1                  d|«       Y �ŒCw xY w7 �Œ`7 �Œ¡# t         $ r t        j1                  d|«       Y �ŒÁw xY w7 Œê7 ŒÍ7 Œ¿# 1 ƒd{  –—†7  sw Y   yxY w­w)z9Sync voucher status/deletion against live RouterOS state.NÚreservedzvoucher reconcile stopped: %sFrf   Tz5voucher reconcile: RouterOS read failed for device %sr+   ÚuptimeÚlimit_uptimer   ÚusedÚbytes_inÚ	bytes_outÚactive)ÚsoldÚ	available)Úseconds)rE   z/voucher %s marked used (live RouterOS activity)z"voucher %s: RouterOS delete failed)ÚVoucherArchiveÚexpiredÚduration_exhaustedÚgone_from_device)Ú
voucher_idÚcoder[   ÚprofileÚpriceÚstatusÚsold_atÚused_atÚused_secondsÚ
deleted_atÚreasonÚbatch_idÚagent_idz*voucher %s removed (%s) from DB and device)8r   r   Únowr   rT   r^   r   r
   r_   r’   ra   rb   rm   r   Ú_logÚerrorÚ	Exceptionrw   ÚasyncioÚ	to_threadÚlist_hotspot_usersrd   r   Úlast_sync_atÚwarningr[   r�   r8   Ú	device_idr   r˜   rV   Ú
expires_atr”   ÚboolrP   r‚   Úlast_poll_uptimer   r•   r~   Úduration_secondsr   Úduration_daysÚinfoÚdelete_hotspot_userÚapp.models.modelsrŠ   Úaddrg   r�   r‘   r“   r™   ÚdeleteÚcommit)rh   rš   Úvouchersrj   ÚexcÚremote_by_deviceÚdevice_syncedÚchangedr£   ÚadapterÚremote_rowsÚrowÚdeviceÚvr[   ÚbatchÚremoteÚ	is_syncedr¤   r‹   rŒ   Úsession_secondsÚlimit_secondsÚdeltar�   Úhas_trafficÚvalidityrŠ   r—   s                                r!   Úreconcile_vouchersrÁ   •   së  è ø€ ä Ó"÷ Yð Y bÜ�l‰lœ8Ÿ<™<Ó(ˆð —*‘*œV¤G›_×2Ñ2´7·>±>ÀZÑ3OÓPÓQ×Qß
‰'‹)—C‘C“Eð 	ñ Ø÷Y÷ Yð Yð	Ü-¨bÓ1×1ˆHð 8:ÐØ)+ˆØˆØ"*§.¡.Ó"2ò 	#ÑˆI�wð#Ü$+×$5Ñ$5°g×6PÑ6PÓ$Q×Q�ØKVÖ.WÀC¨s°6©{¸CÑ/?Ò.WÐ  Ñ+Ø+/�˜iÑ(ð  "Ÿv™v¤n°iÓ@×@�ØÑ%¨&×*=Ñ*=ÀÓ*DØ*-�FÔ'Ø"‘Gð#	#ð& ó h	ˆAØŸ
™
Ò2 a§f¡fÒ2°×9Ñ9Ó;ˆHØŸ™ˆIØŸ&™&¤¨q¯z©zÓ:×:ˆEÙJSÐ%×)Ñ)¨)°RÓ8×<Ñ<¸XÔFÐY]ˆFØ%×)Ñ)¨)°UÓ;ˆIô
   §¡Ó-ˆJà�yŠyÜ˜zÒ?¨j¸CÑ.?Ó@‘à�ð "'ÐÙÜ"6°v·z±zÀ(Ó7KÓ"L�Ü 4°V·Z±ZÀÓ5OÓ PÒ xÔThÐij×iwÑiwÓTx�à" a×&8Ñ&8Ò&=¸AÒ>ð *+�AÔ&à'¨1×+=Ñ+=Ò+BÀÑC�Ø˜1’9Ü%(¨¯©Ò)<¸1Ó%=ÀÑ%E�A”NØ)8�AÔ&Ø"�Gà  1Ò$¬¨Q¯^©^Ò-@¸qÓ)AÀ]Ò)RØ)-Ð&ð  )ÒR¨V°t¨^ÒRÀÇÁÈFÑ@RÐô *¨&Ò*K°V·Z±ZÀ
Ó5KÓLò ÔP_ØÒ2˜6Ÿ:™: kÓ2óQˆKñ Ø—:‘:˜hÔ'Ô+?ÀÇ
Á
È8Ó@TÓ+UÐXYÒ+YÑ]hØ—x‘xÐ#8Ñ8Ø#)˜œØ$%§I¡IÒ$4°˜œ	ñ # u×'=Ò'=ô $-Ü$'¨×(>Ñ(>Ó$?õ$ñ # u×':Ò':ô DMÜ!$ U×%8Ñ%8Ó!9õDà@Dð	 !ñ
 $Ø+,¯9©9°xÑ+?˜AœLØ"&˜ÜŸ	™	ØMÈxôñ Ñ1Ñ5EÙá1:�h—l‘l 9Ô-ÀˆGÙ™8ðQÜ!×+Ñ+¨G×,GÑ,GÈÓR×RÐRõ
 9á$‘	Ù.@Ñ*ÐFXð ð �F‰FÙØ Ÿt™t¨!¯&©&¸1¿:¹:ÈqÏyÉyØŸ'™'¨!¯(©(¸A¿I¹IÈqÏyÉyÜ!$ Q§^¡^Ò%8°qÓ!9Ø"¨6ØŸZ™Z°!·*±*ôôô �I‰IÐBÀHÈfÔUØ—)‘)˜A“,×ÐØŠGðQh	ñZ Ø—)‘)“+×Ð÷sY÷ Yñ Yùð RùðYùð 2úÜ$ò 	Ü�J‰JÐ6¸Ô<ØûÜò 	Ø÷%Y÷ Yñ Yð"	úð RûÚ.Wð Aúô ò ð /1Ð  Ñ+Ø+0�˜iÑ(Ü—‘ØKÈY÷ðúð" ;ùð` SúÜ ò QÜ—L‘LÐ!EÀx×PðQúð& øð øðsYø÷ Y÷ Yñ YüsŒ  ‚Y'‘V’Y'•A%YÁ:VÁ;#YÂY'Â)VÂ*Y'Â0V"Â>VÂ?V"ÃYÃ "W0ÄW%ÄW0ÄW(ÄW0Ä#YÄ<W-Ä=YÅYÅA,YÇ X ÇJYÑ#X&ÒX#ÒX&ÒCYÕ!YÕ"YÖ YÖYÖY'ÖYÖY'ÖYÖY'ÖV"Ö"	W"Ö+W×W"×Y×Y'×W×Y'×!W"×"Y×%W0×(W0×-Y×0)XØYØXØYØ#X&Ø&Y	ÙYÙY	Ù	YÙYÙY'ÙY$ÙYÙY$Ù Y'c               ƒ   ó&  K  — 	 	 t        «       4 ƒd{  –—† } | j                  t        t        «      j	                  t        j
                  dk(  t        j                  j                  d«      t        j                  j                  d«      t        j                  j                  d«      «      «      ƒ d{  –—† j                  «       j                  «       }|D ]  }	 t        || «      ƒ d{  –—†  Œ ddd«      ƒd{  –—†  t        j"                  t%        «       «      ƒ d{  –—†  �Œ#7 �Œ7 Œv7 ŒB# t        $ r | j                  «       ƒ d{  –—†7   Y Œ|w xY w7 ŒZ# 1 ƒd{  –—†7  sw Y   ŒjxY w# t        j                   $ r ‚ t        $ r Y Œ‹w xY w7 Œn­w)zMKeep billing status aligned with live OLT/MikroTik state and expire vouchers.NF)r   r^   r   r   r_   Úis_archivedÚpppoe_usernameÚis_notÚmikrotik_device_idÚolt_device_idra   rb   r	   r�   Úrollbackrž   ÚCancelledErrorÚsleepr"   )rh   Ú	customersÚcustomers      r!   Úmonitor_looprÍ   3  sZ  è ø€ à
ð	Ü(Ó*÷ ,ð ,¨bØ#%§:¡:Üœ8Ó$×*Ñ*Ü ×,Ñ,°Ñ5Ü ×/Ñ/×6Ñ6°tÓ<Ü ×3Ñ3×:Ñ:¸4Ó@Ü ×.Ñ.×5Ñ5°dÓ;ó	ó$÷ ÷ ‘7“9ŸS™S›Uð ð !*ò ,�Hð,Ü<¸XÀrÓJ×JÑJð,÷,÷ ,ô$ �m‰mœM›OÓ,×,Ð,ñ) ð,ùðøð KùÜ$ò ,Ø Ÿk™k›m×+Ó+ð,úð,ø÷ ,÷ ,ñ ,ûô ×%Ñ%ò 	ØÜò 	Ùð	úà,úsÎ   ‚F…E- “D'”E- —BEÂ3D*Â4%EÃD.Ã)D,Ã*D.Ã.EÃ0E- Ã;EÃ<E- Ä  FÄ FÄ!FÄ'E- Ä*EÄ,D.Ä.EÅ
E
ÅEÅEÅEÅEÅE- ÅE*ÅE!ÅE*Å&E- Å-FÆ	FÆFÆFc               ƒ   óô   K  — 	 	 t        «       ƒ d{  –—†  t        j                  t        «       «      ƒ d{  –—†  Œ97 Œ*# t        j                  $ r ‚ t        $ r t        j                  d«       Y Œ[w xY w7 Œ>­w)zDFast lane voucher reconciliation so UI reflects RouterOS state live.Nzvoucher reconcile cycle failed)rÁ   rž   rÉ   r�   r›   Ú	exceptionrÊ   r(   r   r    r!   Úvoucher_monitor_looprÐ   L  sj   è ø€ à
ð	=Ü$Ó&×&Ð&ô
 �m‰mÔ1Ó3Ó4×4Ð4ð à&ùÜ×%Ñ%ò 	ØÜò 	=Ü�N‰NÐ;Ö<ð	=úà4ús;   ‚A8…? ’=“? — A8·A6¸A8½? ¿1A3Á0A8Á2A3Á3A8c                  óÖ   ‡‡— ddl ŠddlmŠ ˆˆfd„} 	 ‰j                   | «       «      }|rt        d„ |D «       «      |dœS d|dœS # t        $ r}dt        |«      g dœcY d}~S d}~ww xY w)	zFReport whether live voucher reconciliation can actually reach devices.r   Nr   c            	   “   ó   •K  —  ‰	«       4 ƒd {  –—† } | j                  t        t        «      j                  t        j                  dk(  «      «      ƒ d {  –—† j                  «       j                  «       }g }|D ]º  }|j                  |j                  |j                  xs dd dd dœ}	 t        | «      ƒ d {  –—† }|j                  |j                  «      }|€d|d<   nE‰
j                  |j                  «      ƒ d {  –—† }|j                  |d<   d|d<   t!        |«      |d	<   |j'                  |«       Œ¼ |cd d d «      ƒd {  –—†  S 7 �Œ;7 Œ÷7 Œ—7 ŒS# t"        $ r}t%        |«      |d<   Y d }~ŒKd }~ww xY w7 Œ2# 1 ƒd {  –—†7  sw Y   y xY w­w)
NTrY   F)r·   rZ   ÚportÚ
credentialÚokrœ   z)Kredensial tidak aktif / belum ditugaskanrœ   rÔ   rÕ   Úusers)r^   r   r   r_   r`   ra   rb   rf   rZ   r]   rm   rd   rg   rŸ   r    r[   r?   r�   r7   Úappend)rh   ri   Úreportrk   Úitemrj   r´   Úrowsr°   Ú_SessionÚ_asyncios            €€r!   Úprobez'voucher_reconcile_health.<locals>.probe]  ss  øè ø€ Ù“:÷ 	ð 	 ØŸZ™ZÜ”~Ó&×,Ñ,¬^×-EÑ-EÈÑ-MÓNó÷ ç‰w‹yŸ™›ð ð ˆFØò $�à Ÿg™gØŸG™GØŸK™KÒ/¨4Ø"&ØØ!ñ�ð-Ü%5°bÓ%9×9�HØ&Ÿl™l¨2¯5©5Ó1�GØ�Ø(S˜˜Wšà%-×%7Ñ%7¸×8RÑ8RÓ%S×S˜Ø-4×-=Ñ-=˜˜\Ñ*Ø%)˜˜T™
Ü(+¨D«	˜˜W™ð —‘˜dÕ#ð+$ð, ÷7	÷ 	ò 	ùðøð  :øð
  Tùô !ò -Ü$'¨£H�D˜–Mûð-úð1	ø÷ 	÷ 	ñ 	üs§   ƒF�E
�F“AE9ÁEÁAE9Â)EÂ7EÂ8AEÃ=EÃ>&EÄ$E9Ä8FÅE7ÅFÅE9ÅEÅEÅ	E4ÅE/Å*E9Å/E4Å4E9Å7FÅ9FÅ?FÆ FÆFF)rÕ   rœ   ri   c              3   ó&   K  — | ]	  }|d    –— Œ y­w)rÕ   Nr   )r4   rL   s     r!   r6   z+voucher_reconcile_health.<locals>.<genexpr>€  s   è ø€ Ò+˜a�!�D•'Ñ+ùs   ‚)rÕ   ri   )rž   Úapp.core.databaser   Úrunr�   r7   rb   )rÝ   ri   r°   rÛ   rÜ   s      @@r!   Úvoucher_reconcile_healthrá   X  su   ù€ ãÝ?õð<?Ø—,‘,™u›wÓ'ˆñ 07ŒcÑ+ 7Ô+Ó+Øñð Ø<AØñð øô ò ?Ø¤c¨#£h¸2Ñ>Õ>ûð?ús   ”A Á	A(ÁA#ÁA(Á#A()r   N))Ú__doc__rž   Úloggingr   r:   Ú	getLoggerr›   Ú
sqlalchemyr   r   r   r   rß   r   Úapp.core.onboardingr	   r«   r
   r   r   r   r   r   Úapp.core.mikrotikr   Úapp.core.secretsr   r   r   r"   r(   r7   rP   rV   Údictrm   r~   rÁ   rÍ   rÐ   rá   r   r    r!   ú<module>rê      sÝ   ðñó Û Û 	Û 	à€w×ÑÐ2Ó3€å ß 2Ñ 2å /Ý >ß ]Õ ]÷ó õ 2ß Ið�só ð˜só ð'  d¡
ð '¨só 'ðT*�(˜T‘/ð * h°¡oó *ð $ sÐ,?Ð'?Ñ"@ó ð:˜có ó,[ó|-ó2	5ð* $ô *r    