
    ]iO                        d Z ddlmZ ddlmZ ddlmZmZ ddl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mZ dd
lmZ ddlmZmZ ddlmZmZmZmZmZm Z  ddl!m"Z" dedededefdZ#dede$e   de%de$e&   fdZ'dedefdZ(defdZ)dedefdZ*dede%de+fdZ,dede%de$e   fdZ-	 	 	 	 	 d;dede%de%dz  dedz  de%d e%de.e$e   e%f   fd!Z/dede%d"e%defd#Z0	 d<dede%de%d$ede$e   d%e1d&e+defd'Z2dede%d"e%de$e   def
d(Z3dede%d"e%d)e%def
d*Z4dede%d"e%d)e%def
d+Z5dede%d"e%d)e%d,e6d-e6dz  defd.Z7dede%d"e%d)e%d/e6defd0Z8dede%d1e%dedef
d2Z9dede%de+fd3Z:dede%d"e%d4e%d5e6d6e;d7e6defd8Z<dede%d"e%d4e%d9e%defd:Z=y)=uS   Stock adjustment service — business logic for sounding / stock adjustment module.    )date)Decimal)and_select)IntegrityErrorSQLAlchemyError)AsyncSession)selectinload)StockAdjustmentStatusLaporan)AksiEnum	ModulEnum)Tangki)stock_repositoryrole_repository)StockAdjustmentItemInputStockAdjustmentItemResponseStockAdjustmentResponseStockAdjustmentDetailResponseStockInitResponseStockItemFotoResponse)interpolate_volumedbtangki	height_mmreturnc                    K   |j                   }	 t        ||      S # t        $ r!}t        d|j                   d|       |d}~ww xY ww)u   
    Load kalibrasi rows for a tank and interpolate height_mm → volume.
    Raises ValueError with tank name included if out of range or table empty.
    zTangki 'z': N)	kalibrasir   
ValueErrornama)r   r   r   kalibrasi_rowses        </var/www/html/spbu.com/backend/app/services/stock_service.py_interpolate_for_tankr$      sU      %%N@!)^<< @8FKK=A378a?@s$   A	 A		AAAA	item_inputsspbu_idc           
      p  K   g }|D ]  }| j                  t        t              j                  t        j                  |j
                  k(  t        j                  j                  d            j                  t        t        j                        t        t        j                                     d{   }|j                         }|t        d|j
                   d      |j                  |k7  rt        d|j
                   d      |j                  |j                  }nt!        | ||j"                         d{   }d}|j$                  t!        | ||j$                         d{   }||n|}	|j'                  |j
                  |j$                  ||j"                  ||	d        |S 7 7 t7 Iw)a`  
    Validate each item, load tangki, interpolate dipstick readings, compute volume_final.

    Rules:
    - tangki must belong to spbu_id
    - digital dipstick is always required and always interpolated
    - manual dipstick is optional (None = not measured); if provided, also interpolated
    - volume_final = manual if available, else digital
    N
Tangki id= tidak ditemukan tidak termasuk SPBU ini)	tangki_iddipstick_manual_mmvolume_manual_literdipstick_digital_mmvolume_digital_litervolume_final_liter)executer   r   whereidr+   
deleted_atis_optionsr
   r   produkscalar_one_or_noner   r&   volume_digital_liter_overrider$   r.   r,   append)
r   r%   r&   rowsinpresultr   volume_digitalvolume_manualvolume_finals
             r#   _process_itemsrA   +   s     Dzz6NU699-v/@/@/D/DT/JKWV--.V]]+
 
 **,>z#--8HIJJ>>W$z#--8PQRR ,,8 >>N#8VSE\E\#]]N !!-"7FCDZDZ"[[M )6(A}~"%"8"8#0#&#:#:$2".
 	= L KI
$ ^
 \s8   B0F62F03B
F6=F2>,F6*F4+AF62F64F6adjc                    | j                   xs g }t        d |D        t        d            }t        di d| j                  d| j
                  d| j                  d| j                  r| j                  j                  ndd| j                  d	| j                  d
| j                  r| j                  j                  ndd| j                  d| j                  r| j                  j                  ndd| j                  d| j                   d| j"                  d| j$                  r| j$                  j                  ndd| j&                  d| j(                  r| j(                  j                  ndd| j*                  dt-        |      d|S )z;Build a summary StockAdjustmentResponse from an ORM object.c              3   N   K   | ]  }|j                   |j                     y wN)r0   ).0items     r#   	<genexpr>z"_build_response.<locals>.<genexpr>g   s"     ZUTd6M6M6Y	 	 Us   %%0r3   r&   shift_id
shift_nama tanggalstatussubmitted_by_nameNsubmitted_atreviewed_by_namereviewed_atcatatan_reviewunlock_reasonrecalled_by_namerecalled_atunlocked_by_nameunlocked_at
item_counttotal_volume_final )itemssumr   r   r3   r&   rJ   shiftr    rM   rN   submitted_bynamerP   reviewed_byrR   rS   rT   recalled_byrV   unlocked_byrX   len)rB   r\   total_volumes      r#   _build_responserf   c   s~   IIOEZUZL # 66  &)YY399>>B	
  zz 473C3C#**// %% 25--d OO )) '' 25--d OO 25--d  OO!" u:#$ (%     c                    | j                   }|r|j                  nd}| j                  xs g D cg c]9  }t        |j                  |j
                  |j                  |j                        ; }}t        | j                  | j                  |r|j                  nd|r|j                  nd|r|j                  n
t        d      | j                  | j                  | j                  | j                   | j"                  |      S c c}w )zQBuild an enriched StockAdjustmentItemResponse from a StockAdjustmentItem ORM row.N)r3   stock_adjustment_item_idtipeurlrL   rI   )r3   r+   tangki_namaproduk_namakapasitas_literr,   r-   r.   r/   r0   fotos)r   r7   ro   r   r3   ri   rj   rk   r   r+   r    rn   r   r,   r-   r.   r/   r0   )rG   r   r7   fro   s        r#   _build_item_responserq      s    [[F$V]]$F **"" #A 	tt%&%?%?		
 # 
  '77..#)FKKr#)FKKr28..gcl22 44 44!6622 s   >C>c                     t        |       }| j                  xs g D cg c]  }t        |       }}t        di |j	                         d|iS c c}w )zCBuild a full StockAdjustmentDetailResponse including all item rows.r\   r[   )rf   r\   rq   r   
model_dump)rB   summaryrG   item_responsess       r#   _build_detail_responserv      s\    c"G>Aiio2oOoT*40oNO( 



  Ps   Ac                   K   |j                   ryt        fd|j                  xs g D        d      }|yt        j                  | |j
                  t        j                  t        j                         d{   S 7 w)z?Return True if user has stock:approve permission for this SPBU.Tc              3   B   K   | ]  }|j                   k(  s|  y wrE   )r&   )rF   ar&   s     r#   rH   z$_user_can_approve.<locals>.<genexpr>   s     S#9Qaii7>Rq#9s   NF)
is_superadminnextassignmentsr   has_permissionrole_idr   stockr   approve)r   userr&   
assignments     ` r#   _user_can_approver      ss     S4#3#3#9r#9SUYZJ //
J	1A1A   s   A5A?8A=9A?c                    K   t        j                  | |       d{   }|D cg c])  }t        |d   |d   |d   |d   |d   |d   |d   	      + c}S 7 9c c}w w)
zRReturn pre-fill data (last known volume/dipstick) for all active tanks in an SPBU.Nr+   rl   rm   rn   last_volume_finallast_dipstick_digital_mmis_first_time)r+   rl   rm   rn   r   r   r   )r   get_stock_initr   )r   r&   r;   rs       r#   r   r      s      "00W==D  A 	n-(-(/0 34%&'A%BO,	
   >s    AAA.AAANrJ   rN   skiplimitc           	         K   t        j                  | ||||||       d{   \  }}|D 	cg c]  }	t        |	       c}	|fS 7 "c c}	w w)z?Return a paginated list of StockAdjustmentResponse for an SPBU.N)r   get_allrf   )
r   r&   rM   rJ   rN   r   r   adj_listtotalry   s
             r#   list_adjustmentsr      sV      -44R'8U[]achiiOHe(011OA1588 j1s   AAAAAAadj_idc                 |   K   t        j                  | ||       d{   }|t        d      t        |      S 7 w)z6Return a fully enriched StockAdjustmentDetailResponse.N Stock adjustment tidak ditemukan)r   	get_by_idr   rv   )r   r&   r   rB   s       r#   
get_detailr      sA      !**2vw?
?C
{;<<!#&& @s   <:<rM   submitter_userforce_draftc           
        K   | j                  t        t              j                  t	        t        j
                  |k(  t        j                  |k(  t        j                  |k(                     d{   }|j                         t        d| d| d      t        | ||       d{   }	 t        j                  | ||||       d{   }	t        | ||       d{   xr | }
|
rsddlm}m} |j                   |	_        |j%                  |j&                        |	_        t        j*                  | |	t,        j.                  |j                   d       d{   }	| j1                          d{    t9        |	      S 7 7 7 7 7 07 # t2        $ r, | j5                          d{  7   t        d| d| d      t6        $ r | j5                          d{  7    w xY ww)	u   
    Create a new StockAdjustment.

    - Operators → saved as DRAFT
    - Admins (stock:approve permission) → immediately APPROVED in one step

    Raises ValueError if a record for the same spbu+shift+tanggal already exists,
    or on any tank/interpolation validation failure.
    Nz Stock adjustment untuk shift_id=z pada tanggal z
 sudah adar   datetimetimezonez#Auto-approved oleh admin saat inputuser_idcatatan)r1   r   r   r2   r   r&   rJ   rM   r8   r   rA   r   creater   r   r   r3   submitted_by_idnowutcrP   update_statusr   APPROVEDcommitr   rollbackr   rv   )r   r&   rJ   rM   r%   r   r   dup	item_rowsrB   can_approver   r   s                r#   create_adjustmentr      s    $ 

%%''72((H4''72	
 C +.xjwizZ
 	
 %Rg>>I$++B7IVV.r>7KK`U`Q`3"0"3"3C'||HLL9C(66C//&))= C iik "#&&S ? WK 	 
kkm.xjwizZ
 	
  kkms   A/G(1F26G((F)G(.F 
FF F A9F F
F 1F2F 6G(G(F F 
F F G%*F-+2G%G G%%G(c                 L  K   t        j                  | ||       d{   }|t        d      |j                  |k7  rt        d      |j                  t
        j                  k7  rt        d      t        | ||       d{   }	 t        j                  | ||       d{   }| j                          d{    t        |      S 7 7 I7 +7 # t        $ r% | j                          d{  7   t        d      t        $ r | j                          d{  7    w xY ww)z.Replace tank rows on a DRAFT stock adjustment.Nr   z(Stock adjustment tidak termasuk SPBU iniz8Hanya stock adjustment berstatus DRAFT yang dapat dieditz/Gagal menyimpan perubahan item stock adjustment)r   r   r   r&   rN   r   DRAFTrA   update_itemsr   r   r   r   rv   )r   r&   r   r%   rB   r   s         r#   update_adjustmentr     s
     !**2vw?
?C
{;<<
{{gCDD
zz](((STT$Rg>>I$11"c9EEiik "#&&) @ ? F LkkmJKK kkmsv   D$C	A#D$CD$C !C"C 9C:C >D$D$C C D!-C0.+D!DD!!D$r   c           	      &  K   t        j                  | ||       d{   }|t        d      |j                  t        j
                  k7  rt        d      | j                  t        t              j                  t        j                  |k(  t        j                  j                  d      t        j                  j                  d                   d{   }t        |j                         j!                               }|D ch c]  }|j"                   }}|j$                  xs g D 	ch c]  }	|	j&                   }
}	||
z
  }|rE|D cg c]  }|j"                  |v s|j(                   }}t        ddj+                  |             |j$                  xs g D ]3  }	|	j,                  |	j.                  t        d|	j&                   d       	 t        j0                  | |t        j2                  |	       d{   }| j5                          d{    t=        |      S 7 7 Tc c}w c c}	w c c}w 7 <7 &# t6        $ r% | j9                          d{  7   t        d
      t:        $ r | j9                          d{  7    w xY ww)z
    Transition a DRAFT adjustment to SUBMITTED.
    Validates that all active tanks in the SPBU have items AND all digital readings are filled.
    Nr   z;Hanya stock adjustment berstatus DRAFT yang dapat di-submitTzYSemua tangki aktif harus memiliki data sounding sebelum submit. Tangki yang belum diisi: z, r(   z.: pembacaan digital wajib diisi sebelum submit)r   zGagal submit stock adjustment)r   r   r   rN   r   r   r1   r   r   r2   r&   	is_activer5   r4   listscalarsallr3   r\   r+   r    joinr.   r/   r   	SUBMITTEDr   r   r   r   rv   )r   r&   r   r   rB   tanks_resultactive_tankstactive_tank_idsrG   item_tank_idsmissingmissing_namess                r#   submit_adjustmentr   =  sR     !**2vw?
?C
{;<<
zz](((VWW v	NNg%  &!!$'

 L ,,.2245L%12\qtt\O214bBT^^MB-G)5IAI((,		-(@'AC
 	
 b##+t/H/H/PT^^,,Z[  !
$22],,g
 
 iik "#&&[ @ 3B J
 	 :kkm899 kkms   JH%B6JH(/JH+J+H0>JH5H5-A
J8J*H> =H:>H> H<H> J(J+J:H> <H> >JI+JJ	JJc                   K   t        j                  | ||       d{   }|t        d      |j                  t        j
                  k7  rt        d      	 t        j                  | |t        j                  |d       d{   }| j                          d{    t        |      S 7 7 )7 # t        $ r% | j                          d{  7   t        d      t        $ r | j                          d{  7    w xY ww)z<Pull a SUBMITTED adjustment back to DRAFT (operator recall).Nr   zERecall hanya bisa dilakukan pada stock adjustment berstatus SUBMITTEDT)r   recallzGagal recall stock adjustment)r   r   r   rN   r   r   r   r   r   r   r   r   rv   )r   r&   r   r   rB   s        r#   recall_adjustmentr   t  s      !**2vw?
?C
{;<<
zz],,,`aa
$22](('$
 
 iik "#&&% @
 	 :kkm899 kkmsc   DB+9D+B1 B-B1 B/B1  D-B1 /B1 1DC+D9C<:DDactionr   c                 :  K   t        j                  | ||       d{   }|t        d      |j                  t        j
                  k7  rt        d      |dk(  rt        j                  nt        j                  }	 t        j                  | ||||       d{   }| j                          d{    t        |      S 7 7 )7 # t        $ r% | j                          d{  7   t        d      t        $ r | j                          d{  7    w xY ww)z)Approve or reject a SUBMITTED adjustment.Nr   z?Hanya stock adjustment berstatus SUBMITTED yang dapat di-reviewr   r   z-Gagal menyimpan hasil review stock adjustment)r   r   r   rN   r   r   r   REJECTEDr   r   r   r   r   rv   )r   r&   r   r   r   r   rB   
new_statuss           r#   review_adjustmentr     s     !**2vw?
?C
{;<<
zz],,,Z[[+1Y+>''MDZDZJ
$22Z'
 
 iik "#&&) @
 	 JkkmHII kkmsd   DCAD=C CC 2C3C 7DC C D$C'%+DDDDalasanc                   K   t        j                  | ||       d{   }|t        d      |j                  t        j
                  t        j                  fvrt        d      	 ddlm}m} t        j                  | |t        j                  |       d{   }||_        |j                  |j                        |_        | j                          d{    | j!                  |       d{    t)        |      S 7 7 i7 ,7 # t"        $ r% | j%                          d{  7   t        d      t&        $ r | j%                          d{  7    w xY ww)zLReopen an APPROVED or LOCKED adjustment back to DRAFT, recording the reason.Nr   zJHanya stock adjustment berstatus APPROVED atau LOCKED yang dapat di-unlockr   r   )rT   zGagal unlock stock adjustment)r   r   r   rN   r   r   LOCKEDr   r   r   r   unlocked_by_idr   r   rX   r   refreshr   r   r   rv   )r   r&   r   r   r   rB   r   r   s           r#   unlock_adjustmentr     s)     !**2vw?
?C
{;<<
zz-00-2F2FGGeff/$22]((
 
 %",,x||4iikjjo "#&&- @

 	 :kkm899 kkmsv   EDAE'2D	 D>D	 DD	 1D2D	 6ED	 D	 D	 	E%D(&+EEEEr+   c                   K   | j                  t        t              j                  t        j                  |k(  t        j
                  j                  d            j                  t        t        j                                     d{   }|j                         }|t        d| d      |j                  |k7  rt        d| d      t        | ||       d{   S 7 X7 w)zJLookup volume for a given dipstick height from a tank's calibration table.Nr(   r)   r*   )r1   r   r   r2   r3   r4   r5   r6   r
   r   r8   r   r&   r$   )r   r&   r+   r   r=   r   s         r#   calculate_volume_for_tankr     s      ::v	vyyI%v'8'8'<'<T'B	C	f../	0 F
 &&(F~:i[0@ABB~~ :i[0HIJJ&r69=== >s%   BC%C!	AC%C#C%#C%c                 8   K   t        | ||       d{   S 7 w)uT   Public wrapper — returns True if user can approve stock adjustments for this SPBU.N)r   )r   r   r&   s      r#   check_user_can_approver     s     "2tW5555s   item_idrj   
file_bytesfilenamec                 <  K   ddl m}m} t        j                  | ||       d{   }	|	t        d      |	j                  t        j                  k7  rt        d      t        j                  | ||       d{   }
|
t        d      |dvrt        d      dd	l m
}  || |       d{   } ||d
|	j                        } ||||       d{   }	 t        j                  | |||       d{    | j                          d{    t        j                  | ||       d{   }	t#        |	      S 7 7 7 7 n7 O7 9# t        $ r% | j                          d{  7   t        d      t         $ r | j                          d{  7    w xY w7 ow)z~
    Save a photo for a StockAdjustmentItem (tipe: 'manual' | 'digital').
    Returns the refreshed full detail response.
    r   )save_uploadUploadContextNr   z<Foto hanya bisa diubah pada stock adjustment berstatus DRAFT%Item stock adjustment tidak ditemukan)manualdigitalz'Tipe foto harus 'manual' atau 'digital')get_spbu_coder   z*Gagal menyimpan foto item stock adjustment)app.utils.file_uploadr   r   r   r   r   rN   r   r   get_item_by_idr   rM   add_item_fotor   r   r   r   rv   )r   r&   r   r   rj   r   r   r   r   rB   rG   r   	spbu_codectxrk   s                  r#   upload_item_fotor     s     A **2vw?
?C
{;<<
zz](((WXX!00WfEED|@AA((BCC3#B00I
	7CKK
8CJ#6
6C,,R$DDDiik !**2vw?
?C!#&&? @ F 1
6 	E GkkmEFF kkm
 @s   #FD:AF9D=:2F,D?-%FEFE 3E4E EE F*F+F=F?FFE E F#E&$+FFFFfoto_idc                 *  K   t        j                  | ||       d{   }|t        d      |j                  t        j
                  k7  rt        d      t        j                  | ||       d{   }|t        d      ddlm} 	 t        j                  | ||       d{   }|t        d      | j                          d{     ||j                         d{    t        j                  | ||       d{   }t        |      S 7 7 7 q7 N# t        $ r | j                          d{  7    t        $ r% | j                          d{  7   t        d      t        $ r | j                          d{  7    w xY w7 7 w)	zd
    Delete a photo from a StockAdjustmentItem.
    Returns the refreshed full detail response.
    Nr   z=Foto hanya bisa dihapus pada stock adjustment berstatus DRAFTr   r   )delete_filezFoto tidak ditemukanz*Gagal menghapus foto item stock adjustment)r   r   r   rN   r   r   r   r   r   delete_item_fotor   r   r   r   rk   rv   )	r   r&   r   r   r   rB   rG   r   deleteds	            r#   r   r     sm     !**2vw?
?C
{;<<
zz](((XYY!00WfEED|@AA1(99"gwOO?344iik gkk
""" **2vw?
?C!#&&9 @ F P 	 kkm GkkmEFF kkm #
?s   FDAF1D2F
D $D%$D 	D
D F#F$FFFFD D F6D97!FE+FFFFF)NNNr      )F)>__doc__r   r   decimalr   
sqlalchemyr   r   sqlalchemy.excr   r   sqlalchemy.ext.asyncior	   sqlalchemy.ormr
   app.models.operationalr   r   app.models.roler   r   app.models.spbur   app.repositoriesr   r   app.schemas.stockr   r   r   r   r   r   app.utils.kalibrasir   r$   r   intdictrA   rf   rq   rv   boolr   r   tupler   r   objectr   r   r   r   strr   r   r   r   bytesr   r   r[   rg   r#   <module>r      s   Y   # : / ' A / " >  3
@L 
@& 
@W 
@Y` 
@55./5 5 
$Z	5p -D :"= 8 4Q 	 	S 	T 	 "	
, #'999 Dj	9
 D 9 9 9 4'(#-.9''"',/'"'" ;';';' ;' 	;'
 ./;' ;' ;' #;'|''' ' ./	'
 #'<4'4'"4',/4':=4'"4'n''"',/':='"'2''' ' 	'
 ' 4Z' #'@''"',/':='GJ'"':>>">/2>?F>>"6\ 6# 6$ 6
.'.'.' .' 	.'
 .' .' .' #.'b'''''' '' 	''
 '' #''rg   