Ë
    ?žjÖ,  ã            	      ó®   — U d Z ddlmZ ddlZddlZddlmZmZmZ ddlm	Z	m
Z
 ddlmZ ddlZdddd	d
ddddœZded<   h d£Zdd„Zdd„Zdd„Zdd„Zdd„Zy)uM   Pertamina POS Excel/CSV parser â€” reads .xlsx or .csv export into row dicts.é    )ÚannotationsN)ÚdateÚdatetimeÚtime)ÚDecimalÚInvalidOperation)ÚAnyÚRON90ÚCN48ÚRON92ÚCN51ÚCN53ÚRON98ÚRON95)Ú	PERTALITEÚ	BIO_SOLARÚBIOSOLARÚPERTAMAXÚDEXLITEÚPERTADEXzPERTAMAX TURBOzPERTAMAX GREENzdict[str, str]Ú
PRODUK_MAP>   ÚJamÚProdukÚTanggalúNo dispenserúVolume (Liter)úPenjualan (rupiah)c                óä   — | j                  «       }d|vr|dfS |j                  dd«      }|d   j                  «       }|d   j                  «       j                  d«      j                  «       }||fS )zPSplit "PULAU 1 - A2 MIX" -> ("PULAU 1", "A2"). Strips " MIX" suffix from nozzle.z - Né   r   z MIX)ÚstripÚsplitÚremovesuffix)Úno_dispenserÚrawÚpartsÚislandÚnozzles        ú</var/www/html/spbu.com/backend/app/utils/pos_excel_parser.pyÚparse_dispenserr)      sp   € à
×
Ñ
Ó
€CØ�CÑØ�DˆyÐØ�I‰I�e˜QÓ€EØ�1‰X�^‰^Ó€FØ�1‰X�^‰^Ó×*Ñ*¨6Ó2×8Ñ8Ó:€FØ�6ˆ>Ðó    c                óÔ   — | j                  «       j                  «       }t        j                  |«      }|�!|j                  «       |j                  «       k7  rt	        d| › d|› d�«      ‚y)z_Check raw produk string matches expected produk kode. Raises ValueError with 'mismatch' if not.zproduk mismatch: file berisi 'z!' tapi produk yang dipilih kode 'Ú'T)r    Úupperr   ÚgetÚ
ValueError)Ú
produk_rawÚproduk_kodeÚkeyÚmappeds       r(   Úvalidate_produk_matchr4   )   sl   € à
×
Ñ
Ó
×
"Ñ
"Ó
$€CÜ�^‰^˜CÓ €FØ€~˜Ÿ™›¨;×+<Ñ+<Ó+>Ò>ÜØ,¨Z¨Lð 9.Ø.9¨]¸!ð=ó
ð 	
ð r*   c                óN   — dj                  d„ | D «       «      j                  «       S )zMStrip dots, dashes, spaces for loose comparison. '31.17201.01' -> '311720101'Ú c              3  óB   K  — | ]  }|j                  «       sŒ|–— Œ y ­w©N)Úisalnum©Ú.0Úcs     r(   Ú	<genexpr>z%_normalize_spbu_no.<locals>.<genexpr>7   s   è ø€ Ð/™a˜ 1§9¡9¥;”1™aùs   ‚˜)Újoinr-   )Úss    r(   Ú_normalize_spbu_nor@   5   s    € à�7‰7Ñ/™aÓ/Ó/×5Ñ5Ó7Ð7r*   c           	     óL  ‡‡‡— t        j                  t        j                  | «      dd¬«      }|j                  }|j                  d¬«      }t        |d«      }|€t        d«      ‚|D �cg c]  }|�t        |«      j                  «       nd‘Œ! c}Št        t        ‰«      z
  }|r&t        ddj                  t        |«      «      › �«      ‚d:ˆfd	„Šg }d
}t        «       }	d}
|D �]E  Št        d„ ‰D «       «      rŒd;ˆˆfd„}d<ˆˆfd„} |d«      }t        |t         «      r|j#                  «       }nt        |t"        «      r|}nŒb |d«      }t        |t$        «      r|}nst        |t        «      rb	 |j                  «       j'                  d«      }t%        t)        |d
   «      t)        |d   «      t+        |«      dkD  rt)        |d   «      nd
«      }nŒð	 t/        t         |d«      xs d«      «      }t/        t         |d«      xs d«      «      }t         |d«      xs d«      j                  «       }|	j3                  |j5                  «       «       t         |d«      xs d«      j                  «       }t7        |«      \  }} |d«      }|r|
€|}
|d
k  xs |d
k  }|r|dz  }|j9                  i d|“d|“d |d«      “d |d«      “d |d «      “d!|“d" |d#«      “d$|“d%|“d&|“d'|“d( |d)«      “d* |d+«      “d, |d-«      “d. |d/«      “d0 |d1«      “d2 |d3«      “||d4œ¥«       �ŒH |st        d5«      ‚t+        |	«      dkD  r't        d6dj                  t        |	«      «      › d7�«      ‚||
d8œ|d
   d9<   |S c c}w # t        t,        f$ r Y �Œ¯w xY w# t0        $ r Y �Œ¿w xY w)=aú  Parse Pertamina .xlsx bytes -> list of row dicts with keys matching pos_transaksi_pertamina fields.

    Validation applied per-row:
    - Skip rows with volume = 0 (counted in skipped_invalid)
    - Skip rows with nilai = 0 but volume > 0, or nilai > 0 but volume = 0 (counted in skipped_invalid)

    Validation applied file-wide (raises ValueError):
    - All rows must have the same produk_raw (no mixed products)
    - Returns no_spbu_from_file so caller can validate against SPBU nomor_pertamina
    T)Ú	read_onlyÚ	data_only)Úvalues_onlyNzFile Excel kosongr6   úKolom tidak ditemukan: ú, c                ó&   •— ‰j                  | «      S r8   ©Úindex©ÚnameÚheaderss    €r(   Úcol_idxz#parse_xlsx_to_rows.<locals>.col_idxS   ó   ø€ Ø�}‰}˜TÓ"Ð"r*   r   c              3  ó$   K  — | ]  }|d u –— Œ
 y ­wr8   © )r;   Úvs     r(   r=   z%parse_xlsx_to_rows.<locals>.<genexpr>\   s   è ø€ Ð*¡'˜Qˆq�DŒy¡'ùs   ‚c                ó>   •—  ‰| «      }|t        ‰«      k  r‰|   S d S r8   )Úlen)rK   ÚidxrM   Úraw_rows     €€r(   r.   zparse_xlsx_to_rows.<locals>.get_   s&   ø€ Ù˜$“-ˆCØ#&¬¨W«Ò#5�7˜3‘<Ð?¸4Ð?r*   c                óž   •— | ‰v r‰j                  | «      nd}|dk  s|t        ‰«      k\  ry ‰|   }|�t        |«      j                  «       S d S )Néÿÿÿÿr   )rI   rS   Ústrr    )rK   rT   rQ   rL   rU   s      €€r(   Úget_optz#parse_xlsx_to_rows.<locals>.get_optc   sP   ø€ Ø)-°©�'—-‘- Ô%¸bˆCØ�QŠw˜#¤ W£Ò-ØØ˜‘ˆAØ%& ]”3�q“6—<‘<“>Ð<¸Ð<r*   r   r   Ú:r   é   r   Ú0r   r   r   úNo spbuÚtanggalÚjamÚcode_morÚCode_morÚprovinceÚProvinceÚcityÚCityÚno_spbu_pertaminaÚ	no_nozzleú	No nozzler#   r0   ÚvolumeÚnilaiÚoperator_nameÚOperatorÚno_hpúNo HPÚdelivery_typeúDelivery TypeÚ
plat_nomorú
Plat NomorÚnikÚNIKÚinstansiÚInstansi©Ú
island_rawÚ
nozzle_rawz<Tidak ada data valid dalam file Excel (semua baris dilewati)ú.File mengandung lebih dari satu jenis produk: ú. Upload satu produk per file.©Úskipped_invalidÚno_spbu_from_fileÚ_meta©rK   rX   ÚreturnÚint)rK   rX   r�   r	   )rK   rX   r�   ú
str | None)ÚopenpyxlÚload_workbookÚioÚBytesIOÚactiveÚ	iter_rowsÚnextr/   rX   r    ÚREQUIRED_COLUMNSÚsetr>   ÚsortedÚallÚ
isinstancer   r   r   r!   r‚   rS   Ú
IndexErrorr   r   Úaddr-   r)   Úappend)Ú
file_bytesÚwbÚwsÚ	rows_iterÚ
header_rowÚhÚmissingÚresultsr}   Úproduk_raw_setr~   r.   rY   Útanggal_valr^   Újam_valr_   r%   ri   rj   r0   r#   rx   ry   Úspbu_noÚis_suspiciousrM   rL   rU   s                             @@@r(   Úparse_xlsx_to_rowsr    :   s1  ú€ ô 
×	Ñ	¤§
¡
¨:Ó 6À$ÐRVÔ	W€BØ	�‰€BØ—‘¨�Ó.€Iä�i Ó&€JØÐÜÐ,Ó-Ð-á@JÓKÁ
¸1  Œs�1‹v�|‰|Œ~°BÑ6À
ÑK€Gä¤ W£Ñ-€GÙÜÐ2°4·9±9¼VÀG»_Ó3MÐ2NÐOÓPÐPõ#ð %'€GØ€OÜ"›u€NØ$(ÐäˆÜÑ*¡'Ó*Ô*Øö	@ö	=ñ ˜)“nˆÜ�k¤8Ô,Ø!×&Ñ&Ó(‰GÜ˜¤TÔ*Ø!‰Gàá�e“*ˆÜ�gœtÔ$Ø‰CÜ˜¤Ô%ðØŸ™›×-Ñ-¨cÓ2�Üœ3˜u Q™x›=¬#¨e°A©h«-Ì#ÈeË*ÐWXÊ.¼¸UÀ1¹X¼Ð^_Ó`‘ð ð	ÜœS¡Ð%5Ó!6Ò!=¸#Ó>Ó?ˆFÜœC¡Ð$8Ó 9Ò @¸SÓAÓBˆEô ™˜X›Ò,¨"Ó-×3Ñ3Ó5ˆ
Ø×Ñ˜:×+Ñ+Ó-Ô.ä™3˜~Ó.Ò4°"Ó5×;Ñ;Ó=ˆÜ!0°Ó!>Ñˆ
�Jñ ˜)Ó$ˆÙÐ(Ð0Ø 'Ðð  !™Ò1 u°¡zˆÙØ˜qÑ ˆOà�‰ð 
Ø�wð
à�3ð
ð ™ 
Ó+ð
ð ™ 
Ó+ð	
ð
 ‘G˜F“Oð
ð   ð
ð ™ Ó-ð
ð ˜Lð
ð ˜*ð
ð �fð
ð �Uð
ð ™W ZÓ0ð
ð ‘W˜WÓ%ð
ð ™W _Ó5ð
ð ™' ,Ó/ð
ð  ‘7˜5“>ð!
ð" ™ 
Ó+ð#
ð$ %Ø$ò'
ö 	ðs ñ^ ÜÐWÓXÐXô ˆ>Ó˜QÒÜØ<¸T¿Y¹YÄvÈnÓG]Ó=^Ð<_ð `+ð +ó
ð 	
ð +Ø.ñ€GˆA�JˆwÑð
 €Nùò[ LøôX ¤
Ð+ò Úðûô  ò 	Úð	ús+   Á*$M;Å1A N Ç<NÎ NÎNÎ	N#Î"N#c           	     óx  ‡!‡"‡#— 	 | j                  d«      }|j                  «       j                  «       }|st	        d«      ‚|d   j                  d«      |d   j                  d«      k\  rdnd}t        j                  t        j                  |j                  «       «      |¬«      }t        |«      }t        |«      dk  rt	        d	«      ‚|d   D �cg c]  }|j                  «       ‘Œ c}Š"‰"D �cg c]  }|j                  «       ‘Œ }}t        t        ‰"«      z
  }|r&t	        d
dj                  t!        |«      «      › �«      ‚dAˆ"fd„Š!dAˆ"fd„Š#dBˆ!fd„}	dBˆ#fd„}
dCd„}dDd„}g }d}t        «       }d}t#        |dd d¬«      D �]¬  \  }}t%        d„ |D «       «      rŒ |	|d«      }|sŒ& ||«      }|€Œ1 |	|d«      }|sŒ= ||«      }|€ŒH	  |	|d«      xs dj'                  dd«      } |	|d«      xs dj'                  dd«      }t)        |«      }t)        |«      } |	|d«      xs dj                  «       }|j-                  |j/                  «       «        |	|d«      xs dj                  «       }t1        |«      \  }} |
|d«      }|r|€|}|dk  xs |dk  } | r|dz  }|j3                  i d |“d!|“d" |
|d#«      “d$ |
|d%«      “d& |
|d'«      “d(|“d) |
|d*«      “d+|“d,|“d-|“d.|“d/ |
|d0«      “d1 |
|d2«      “d3 |
|d4«      “d5 |
|d6«      “d7 |
|d8«      “d9 |
|d:«      “||d;œ¥«       �Œ¯ |st	        d<«      ‚t        |«      dkD  r't	        d=dj                  t!        |«      «      › d>�«      ‚||d?œ|d   d@<   |S # t        $ r | j                  d«      }Y �Œˆw xY wc c}w c c}w # t*        $ r Y �Œ6w xY w)EaÄ  Parse Pertamina POS .csv bytes -> same row dict format as parse_xlsx_to_rows.

    Expects same columns as the xlsx export (semicolon or comma separated):
        Tanggal, Jam, No dispenser, Produk, Volume (Liter), Penjualan (rupiah)
    Optional: No spbu, Code_mor, Province, City, No nozzle, Operator,
              No HP, Delivery Type, Plat Nomor, NIK, Instansi

    Tanggal: dd/mm/yyyy or yyyy-mm-dd (auto-detected)
    Jam: HH:MM or HH:MM:SS
    z	utf-8-sigzlatin-1zFile CSV kosongr   Ú;Ú,)Ú	delimiterr[   z,File CSV harus memiliki minimal 1 baris datarE   rF   c                ó&   •— ‰j                  | «      S r8   rH   rJ   s    €r(   rM   z"parse_csv_to_rows.<locals>.col_idxß   rN   r*   c                ó2   •— | ‰v r‰j                  | «      S dS )NrW   rH   rJ   s    €r(   Úopt_idxz"parse_csv_to_rows.<locals>.opt_idxâ   s   ø€ Ø&*¨g¡oˆw�}‰}˜TÓ"Ð=¸2Ð=r*   c                óf   •—  ‰|«      }|t        | «      k  r| |   j                  «       nd}|r|S d S )Nr6   ©rS   r    )ÚrowrK   rT   rQ   rM   s       €r(   Úget_valz"parse_csv_to_rows.<locals>.get_valå   s5   ø€ Ù�d‹mˆØ #¤c¨#£h¢ˆC�‰H�N‰NÔ°BˆÙˆqÐ˜4Ðr*   c                ón   •—  ‰|«      }|dk  s|t        | «      k\  ry | |   j                  «       }|r|S d S )Nr   r©   )rª   rK   rT   rQ   r§   s       €r(   rY   z"parse_csv_to_rows.<locals>.get_optê   s=   ø€ Ù�d‹mˆØ�Š7�cœS ›X’oØØ�‰H�N‰NÓˆÙˆqÐ˜4Ðr*   c                óœ   — | j                  «       } dD ](  }	 t        j                  | |«      j                  «       c S  y # t        $ r Y Œ7w xY w)N)z%d/%m/%Yz%Y-%m-%dz%d-%m-%Yz%d/%m/%y)r    r   Ústrptimer   r/   )r?   Úfmts     r(   Úparse_tanggalz(parse_csv_to_rows.<locals>.parse_tanggalñ   sR   € Ø�G‰G‹IˆÛCˆCðÜ×(Ñ(¨¨CÓ0×5Ñ5Ó7Ò7ð Dð
 øô ò Ùðús   —#?¿	AÁ
Ac                óú   — | j                  «       } | j                  d«      }	 t        t        |d   «      t        |d   «      t	        |«      dkD  rt        |d   «      «      S d«      S # t
        t        f$ r Y y w xY w)NrZ   r   r   r[   )r    r!   r   r‚   rS   r/   r�   )r?   r%   s     r(   Ú	parse_jamz$parse_csv_to_rows.<locals>.parse_jamú   sq   € Ø�G‰G‹IˆØ—‘˜“ˆð	Üœ˜E !™H›¤s¨5°©8£}ÄsÈ5ÃzÐTUÂ~´c¸%À¹(³mÓ]Ð]Ð[\Ó]Ð]øÜœJÐ'ò 	Ùð	ús   £>A( Á"A( Á(A:Á9A:Nr   )Ústartc              3  ó>   K  — | ]  }|j                  «        –— Œ y ­wr8   )r    r:   s     r(   r=   z$parse_csv_to_rows.<locals>.<genexpr>  s   è ø€ Ð*¡c �1—7‘7“9Œ}¡cùs   ‚r   r   r   r\   Ú.r   r   r6   r   r]   r^   r_   r`   ra   rb   rc   rd   re   rf   rg   rh   r#   r0   ri   rj   rk   rl   rm   rn   ro   rp   rq   rr   rs   rt   ru   rv   rw   z:Tidak ada data valid dalam file CSV (semua baris dilewati)rz   r{   r|   r   r€   )rª   z	list[str]rK   rX   r�   rƒ   )r?   rX   r�   zdate | None)r?   rX   r�   ztime | None)ÚdecodeÚUnicodeDecodeErrorr    Ú
splitlinesr/   ÚcountÚcsvÚreaderr†   ÚStringIOÚlistrS   Úlowerr‹   rŒ   r>   r�   Ú	enumeraterŽ   Úreplacer   r   r‘   r-   r)   r’   )$r“   r$   ÚlinesÚsepr»   Úrowsr˜   Úheaders_lowerr™   r«   rY   r°   r²   rš   r}   r›   r~   Úirª   Útanggal_strr^   Újam_strr_   Úvol_strÚnil_strri   rj   r0   r#   rx   ry   rž   rŸ   rM   rL   r§   s$                                    @@@r(   Úparse_csv_to_rowsrÊ   ½   s€  ú€ ð+Ø×Ñ Ó,ˆð �I‰I‹K×"Ñ"Ó$€EÙÜÐ*Ó+Ð+à�q‘—‘ Ó$¨¨a©¯©°sÓ(;Ò;‰#À€CÜ�Z‰ZœŸ™ C§I¡I£KÓ0¸CÔ@€FÜ�‹<€Dä
ˆ4ƒy�1‚}ÜÐGÓHÐHà"& q¢'Ó*¡'˜Qˆq�w‰w�y 'Ñ*€GÙ(/Ó0© 1�Q—W‘W•Y¨€MÐ0ä¤ W£Ñ-€GÙÜÐ2°4·9±9¼VÀG»_Ó3MÐ2NÐOÓPÐPõ#õ>õ õ
 óóð %'€GØ€OÜ"›u€NØ$(Ðä˜D  ˜H¨A×.Ð.‰ˆˆ3ÜÑ*¡cÓ*Ô*Øá˜c 9Ó-ˆÙØÙ Ó,ˆØˆ?Øá˜#˜uÓ%ˆÙØÙ˜Ó ˆØˆ;Øð	Ù˜sÐ$4Ó5Ò<¸×EÑEÀcÈ3ÓOˆGÙ˜sÐ$8Ó9Ò@¸S×IÑIÈ#ÈsÓSˆGÜ˜WÓ%ˆFÜ˜GÓ$ˆEñ ˜c 8Ó,Ò2°×9Ñ9Ó;ˆ
Ø×Ñ˜:×+Ñ+Ó-Ô.á  ^Ó4Ò:¸×AÑAÓCˆÜ!0°Ó!>Ñˆ
�Já˜#˜yÓ)ˆÙÐ(Ð0Ø 'Ðà !™Ò1 u°¡zˆÙØ˜qÑ ˆOà�‰ð 
Ø�wð
à�3ð
ð ™  ZÓ0ð
ð ™  ZÓ0ð	
ð
 ‘G˜C Ó(ð
ð   ð
ð ™  kÓ2ð
ð ˜Lð
ð ˜*ð
ð �fð
ð �Uð
ð ™W S¨*Ó5ð
ð ‘W˜S 'Ó*ð
ð ™W S¨/Ó:ð
ð ™' # |Ó4ð
ð  ‘7˜3 Ó&ð!
ð" ™  ZÓ0ð#
ð$ %Ø$ò'
ö 	ðQ /ñ| ÜÐUÓVÐVä
ˆ>Ó˜QÒÜØ<¸T¿Y¹YÄvÈnÓG]Ó=^Ð<_ð `+ð +ó
ð 	
ð +Ø.ñ€GˆA�JˆwÑð
 €NøôS ò +Ø×Ñ 	Ó*‹ð+üò +ùÚ0øôJ  ò 	Úð	ús0   …N ÃN"Ã/N'ÇAN,ÎNÎNÎ,	N9Î8N9)r#   rX   r�   ztuple[str | None, str | None])r0   rX   r1   rX   r�   Úbool)r?   rX   r�   rX   )r“   Úbytesr�   zlist[dict[str, Any]])Ú__doc__Ú
__future__r   rº   r†   r   r   r   Údecimalr   r   Útypingr	   r„   r   Ú__annotations__r‹   r)   r4   r@   r    rÊ   rP   r*   r(   Ú<module>rÒ      sl   ðÚ Så "ã 
Û 	ß )Ñ )ß -Ý ã ð ØØØØØØØñ	€
ˆNó 	òÐ óó	ó8ó
@ôFVr*   