§
    ®¶«j/f  ã                  óÂ   — d dl mZ 	 d dlmZmZ d dlmZ d dl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mZ  ej        d	¦  «        Z G d
„ d¦  «        ZdS )é    )Úannotations)ÚdateÚdatetime)ÚDecimalN)ÚAnyÚDictÚListÚOptionalÚTuple)ÚUUIDÚuuid4)ÚBasePostgresClient)ÚNORM_WASTAGE_PERCENTAGESÚMaterialVarianceItemÚVarianceAlertStatusÚWarehouseDispatchz!dscons.inventory.dispatch_servicec                  ó–   — e Zd ZdZd)d*d„Zd+d„Z	 	 	 	 	 	 	 	 d,d-d„Z	 	 	 	 	 d.d/d „Zd0d"„Z	 d)d1d#„Z	e
d2d&„¦   «         Ze
d3d(„¦   «         ZdS )4ÚWarehouseDispatchServiceu·   Ä�iá»�u phá»‘i quy trÃ¬nh xuáº¥t kho váº­t tÆ° linh hoáº¡t vÃ  Ä‘á»‘i chiáº¿u hao há»¥t 3 chiá»�u:
    Dá»± ToÃ¡n (BoQ) - Thá»±c Xuáº¥t (Dispatches) - Quyáº¿t ToÃ¡n (Settlement).
    NÚpostgres_clientúOptional[BasePostgresClient]ÚreturnÚNonec                ó0   — |pt          ¦   «         | _        d S ©N)r   Ú_db)Úselfr   s     úRC:\Projects\DSCons\app\modules\inventory\application\warehouse_dispatch_service.pyÚ__init__z!WarehouseDispatchService.__init__   s   € Ø"Ð:Õ&8Ñ&:Ô&:ˆŒˆˆó    c                ó0   — |                      d¦  «         dS )uo   Ä�áº£m báº£o báº£ng erp_warehouse_dispatches tá»“n táº¡i tá»± Ä‘á»™ng náº¿u chÆ°a cháº¡y migration thá»§ cÃ´ng.a  
            CREATE TABLE IF NOT EXISTS erp_warehouse_dispatches (
                id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
                inventory_item_id UUID NOT NULL REFERENCES erp_warehouse_inventory(id) ON DELETE CASCADE,
                dispatch_code VARCHAR(64) NOT NULL UNIQUE,
                project_id UUID REFERENCES projects(id) ON DELETE SET NULL,
                wbs_id UUID REFERENCES erp_project_wbs(id) ON DELETE SET NULL,
                takeoff_item_id UUID REFERENCES erp_drawing_takeoff_items(id) ON DELETE SET NULL,
                dispatch_date DATE NOT NULL DEFAULT CURRENT_DATE,
                quantity_dispatched NUMERIC(18, 4) NOT NULL CHECK (quantity_dispatched > 0),
                unit VARCHAR(50) NOT NULL,
                unit_price_vnd NUMERIC(18, 4) NOT NULL DEFAULT 0.0000,
                total_value_vnd NUMERIC(18, 4) NOT NULL DEFAULT 0.0000,
                recipient_name VARCHAR(255),
                transport_vehicle VARCHAR(100),
                purpose VARCHAR(255),
                notes TEXT,
                created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
                updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
            );
            CREATE INDEX IF NOT EXISTS idx_erp_wh_disp_inv_item ON erp_warehouse_dispatches(inventory_item_id);
            CREATE INDEX IF NOT EXISTS idx_erp_wh_disp_project ON erp_warehouse_dispatches(project_id);
            CREATE INDEX IF NOT EXISTS idx_erp_wh_disp_date ON erp_warehouse_dispatches(dispatch_date);
        N)Úexecute)r   Úcurs     r   Ú_ensure_schemaz'WarehouseDispatchService._ensure_schema!   s'   € à�Šð ñ 	ô 	ð 	ð 	ð 	r   Úinventory_item_idÚstrÚquantity_dispatchedúDecimal | floatÚ
project_idúOptional[str]Úwbs_idÚtakeoff_item_idÚdispatch_dateúOptional[date | str]Úrecipient_nameÚtransport_vehicleÚpurposeÚnotesúDict[str, Any]c                ó  — t          t          |¦  «        ¦  «        }|t          d¦  «        k    rt          d¦  «        ‚|pt          j        ¦   «         }t          |t          ¦  «        rt          j        |¦  «        }| j                             ¦   «         5 }| 	                    ¦   «         5 }|  
                    |¦  «         |                     d|f¦  «         |                     ¦   «         }|st          d|› d�¦  «        ‚t          t          |d         ¦  «        ¦  «        }||k     r(t          d|› d|d	         › d
|› d|d	         › d�	¦  «        ‚||z
  }|t          d¦  «        k    rdnd}|r
|d         sdnd}||g}|r|                     |¦  «         |                     |¦  «         |                     d|› d�|¦  «         |                     d¦  «         |                     ¦   «         d         pddz   }d|j        › d|d›�}t          t          |d         ¦  «        ¦  «        }||z  }|                     d||||||||d	         |||||	|
f¦  «         |                     ¦   «         }|                     ¦   «          t                                d|› d|d	         › d|d         › d|› �¦  «         i d t          |d          ¦  «        “d!|“d"|“d#|d#         “d$t%          |¦  «        “d%t%          |¦  «        “d	|d	         “dt%          |¦  «        “d&t%          |¦  «        “d'|                     ¦   «         “d(|“d)|“d*|“d+|	“d,|
“d-|“cd.d.d.¦  «         cd.d.d.¦  «         S # 1 swxY w Y   	 d.d.d.¦  «         d.S # 1 swxY w Y   d.S )/uF  Táº¡o phiáº¿u xuáº¥t kho linh hoáº¡t.
        - Kiá»ƒm tra tá»“n kho kháº£ dá»¥ng (chá»‘ng xuáº¥t Ã¢m) báº±ng SELECT ... FOR UPDATE.
        - Trá»« quantity_remaining vÃ  cáº­p nháº­t tráº¡ng thÃ¡i kho.
        - Cho phÃ©p liÃªn káº¿t linh hoáº¡t: Theo dá»± Ã¡n, WBS, hoáº·c xuáº¥t dÃ¹ng chung (khÃ´ng báº¯t buá»™c).
        Ú0u-   Sá»‘ lÆ°á»£ng xuáº¥t kho pháº£i lá»›n hÆ¡n 0.a#  
                    SELECT id, item_code, item_name, material_group, unit,
                           quantity_remaining, unit_price_vnd, allocated_project_id
                    FROM erp_warehouse_inventory
                    WHERE id = %s
                    FOR UPDATE;
                u(   KhÃ´ng tÃ¬m tháº¥y lÃ´ váº­t tÆ° cÃ³ ID z trong kho.Úquantity_remainingu   Tá»“n kho kháº£ dá»¥ng (ú Úunitu   ) khÃ´ng Ä‘á»§ Ä‘á»ƒ xuáº¥t (z).u   Ä�Ãƒ_XUáº¤T_Háº¾Tu   Ä�Ãƒ_XUáº¤T_Má»˜T_PHáº¦NÚallocated_project_idz, allocated_project_id = %sÚ zÙ
                    UPDATE erp_warehouse_inventory
                    SET quantity_remaining = %s,
                        status = %s,
                        updated_at = CURRENT_TIMESTAMP
                        z4
                    WHERE id = %s;
                z5SELECT COUNT(*) as cnt FROM erp_warehouse_dispatches;Úcntr   é   zXK-ú-Ú04dÚunit_price_vndaV  
                    INSERT INTO erp_warehouse_dispatches (
                        inventory_item_id, dispatch_code, project_id, wbs_id,
                        takeoff_item_id, dispatch_date, quantity_dispatched,
                        unit, unit_price_vnd, total_value_vnd,
                        recipient_name, transport_vehicle, purpose, notes
                    ) VALUES (
                        %s, %s, %s, %s,
                        %s, %s, %s,
                        %s, %s, %s,
                        %s, %s, %s, %s
                    ) RETURNING id, created_at;
                u    [WarehouseDispatch] Ä�Ã£ xuáº¥t u    mÃ£ Ú	item_codeu    theo phiáº¿u ÚidÚdispatch_coder$   Ú	item_namer&   Úquantity_remaining_afterÚtotal_value_vndr,   r(   r.   r/   r0   r1   ÚstatusN)r   r%   Ú
ValueErrorr   ÚtodayÚ
isinstanceÚfromisoformatr   Úget_connectionÚcursorr#   r!   ÚfetchoneÚappendÚyearÚcommitÚloggerÚinfoÚfloatÚ	isoformat)r   r$   r&   r(   r*   r+   r,   r.   r/   r0   r1   ÚqtyÚd_dateÚconnr"   ÚitemÚ	remainingÚnew_remainingÚ
new_statusÚupdate_proj_sqlÚupdate_paramsÚseqrA   Ú
unit_priceÚ	total_valÚress                             r   Úcreate_dispatchz(WarehouseDispatchService.create_dispatch<   s€  € õ$ •cÐ-Ñ.Ô.Ñ/Ô/ˆØ•'˜#‘,”,ÒÐÝÐLÑMÔMÐMàÐ.¥$¤*¡,¤,ˆÝ�f�cÑ"Ô"ð 	0ÝÔ'¨Ñ/Ô/ˆFàŒX×$Ò$Ñ&Ô&ð \	¨$Ø—’‘”ð [ #Ø×#Ò# CÑ(Ô(Ð(ð —’ð ð (Ð)ñ+ô +ð +ð —|’|‘~”~�Øð pÝ$Ð%nÐPaÐ%nÐ%nÐ%nÑoÔoÐoå#¥C¨Ð-AÔ(BÑ$CÔ$CÑDÔD�	Ø˜s’?�?Ý$ð A°9ð  Að  A¸tÀF¼|ð  Að  AÐjmð  Að  AÐptÐu{Ôp|ð  Að  Að  Añô ð ð !*¨C¡�Ø4AÅWÈSÁ\Ä\Ò4QÐ4QÐ0Ð0ÐWq�
ð DNÐ"zÐVZÐ[qÔVrÐ"zÐ"?Ð"?Ðxz�Ø!.°
Ð ;�Ø"ð 5Ø!×(Ò(¨Ñ4Ô4Ð4Ø×$Ò$Ð%6Ñ7Ô7Ð7à—’ð ð
 )ðð ð ð #ñ$ô $ð $ð —’ÐSÑTÔTÐTØ—|’|‘~”~ eÔ,Ð1°°QÑ6�Ø = f¤kÐ =Ð =°CÐ =Ð =Ð =�å$¥S¨Ð.>Ô)?Ñ%@Ô%@ÑAÔA�
Ø *Ñ,�	ð —’ð ð & }°jÀ&Ø# V¨SØ˜”L *¨iØ"Ð$5°wÀð	ñô ð ð& —l’l‘n”n�Ø—’‘”�å—’ð  I¸sð  Ið  IÀTÈ&Ä\ð  Ið  IÐX\Ð]hÔXið  Ið  Ið  zGð  Ið  Iñ  Jô  Jð  JðØ�#˜c $œi™.œ.ðà# ]ðð (Ð):ðð    kÔ!2ð	ð
 *­5°©:¬:ðð /µ°mÑ0DÔ0Dðð ˜D œLðð %¥e¨JÑ&7Ô&7ðð &¥u¨YÑ'7Ô'7ðð $ V×%5Ò%5Ñ%7Ô%7ðð ! *ðð % nðð (Ð):ðð ˜wðð ˜Uðð  ˜jð!ðU[ð [ð [ð [ñ [ô [ð [ð\	ð \	ð \	ð \	ñ \	ô \	ð \	ð \	ð[ð [ð [ð [øøøð [ð [ð [ð [ð [ð\	ð \	ð \	ñ \	ô \	ð \	ð \	ð \	ð \	ð \	ð \	ð \	øøøð \	ð \	ð \	ð \	ð \	ð \	s7   ÂM:Â+JM!ÍM:Í!M%	Í%M:Í(M%	Í)M:Í:M>ÎM>é2   r   ÚsearchÚlimitÚintÚoffsetú Tuple[List[Dict[str, Any]], int]c                óŒ  — | j                              ¦   «         5 }|                     ¦   «         5 }|                      |¦  «         dg}g }	|r*|                     d¦  «         |	                     |¦  «         |r*|                     d¦  «         |	                     |¦  «         |rF|                     d¦  «         d|                     ¦   «         › d�}
|	                     |
|
|
|
g¦  «         d                     |¦  «        }|                     d|› d�|	¦  «         | 	                    ¦   «         d	         }d
|› d�}|	                     ||g¦  «         |                     ||	¦  «         | 
                    ¦   «         }d„ |D ¦   «         |fcddd¦  «         cddd¦  «         S # 1 swxY w Y   	 ddd¦  «         dS # 1 swxY w Y   dS )uu   Truy váº¥n danh sÃ¡ch lá»‹ch sá»­ phiáº¿u xuáº¥t kho vá»›i Ä‘áº§y Ä‘á»§ liÃªn káº¿t dá»± Ã¡n vÃ  lÃ´ váº­t tÆ° gá»‘c.ú1=1úd.project_id = %szd.inventory_item_id = %sze(d.dispatch_code ILIKE %s OR w.item_name ILIKE %s OR d.recipient_name ILIKE %s OR d.purpose ILIKE %s)ú%z AND a  
                    SELECT COUNT(*) as total
                    FROM erp_warehouse_dispatches d
                    JOIN erp_warehouse_inventory w ON d.inventory_item_id = w.id
                    LEFT JOIN projects p ON d.project_id = p.id
                    WHERE ú;
                Útotala�  
                    SELECT 
                        d.id, d.dispatch_code, d.dispatch_date, d.quantity_dispatched,
                        d.unit, d.unit_price_vnd, d.total_value_vnd,
                        d.recipient_name, d.transport_vehicle, d.purpose, d.notes,
                        d.created_at,
                        w.id as inventory_item_id, w.item_code, w.item_name, w.material_group,
                        w.invoice_series_number, w.supplier_name,
                        p.id as project_id, p.project_code, p.project_name,
                        t.item_name as takeoff_work_name
                    FROM erp_warehouse_dispatches d
                    JOIN erp_warehouse_inventory w ON d.inventory_item_id = w.id
                    LEFT JOIN projects p ON d.project_id = p.id
                    LEFT JOIN erp_drawing_takeoff_items t ON d.takeoff_item_id = t.id
                    WHERE z~
                    ORDER BY d.dispatch_date DESC, d.created_at DESC
                    LIMIT %s OFFSET %s;
                c                ó,   — g | ]}t          |¦  «        ‘ŒS © ©Údict©Ú.0Úrs     r   ú
<listcomp>z<WarehouseDispatchService.list_dispatches.<locals>.<listcomp>ô   ó   € Ð.Ð.Ð. A�˜Q™œÐ.Ð.Ð.r   N)r   rJ   rK   r#   rM   ÚstripÚextendÚjoinr!   rL   Úfetchall)r   r(   r$   rc   rd   rf   rV   r"   Úwhere_clausesÚparamsÚtermÚ	where_sqlÚ	total_cntÚqueryÚrowss                  r   Úlist_dispatchesz(WarehouseDispatchService.list_dispatches´   s§  € ð ŒX×$Ò$Ñ&Ô&ð 7	:¨$Ø—’‘”ð 6: #Ø×#Ò# CÑ(Ô(Ð(à!& �Ø$&�àð .Ø!×(Ò(Ð)<Ñ=Ô=Ð=Ø—M’M *Ñ-Ô-Ð-à$ð 5Ø!×(Ò(Ð)CÑDÔDÐDØ—M’MÐ"3Ñ4Ô4Ð4àð <Ø!×(Ò(ð  *Qñ  Rô  Rð  RØ0˜vŸ|š|™~œ~Ð0Ð0Ð0�DØ—M’M 4¨¨t°TÐ":Ñ;Ô;Ð;à#ŸLšL¨Ñ7Ô7�	ð —’ð ð
 %ðð ð ð ñô ð ð  ŸLšL™NœN¨7Ô3�	ðð %ðð ð �ð$ —’˜u f˜oÑ.Ô.Ð.Ø—’˜E 6Ñ*Ô*Ð*Ø—|’|‘~”~�à.Ð.¨Ð.Ñ.Ô.°	Ð9ðm6:ð 6:ð 6:ð 6:ñ 6:ô 6:ð 6:ð7	:ð 7	:ð 7	:ð 7	:ñ 7	:ô 7	:ð 7	:ð 7	:ð6:ð 6:ð 6:ð 6:øøøð 6:ð 6:ð 6:ð 6:ð 6:ð7	:ð 7	:ð 7	:ñ 7	:ô 7	:ð 7	:ð 7	:ð 7	:ð 7	:ð 7	:ð 7	:ð 7	:øøøð 7	:ð 7	:ð 7	:ð 7	:ð 7	:ð 7	:s5   šF9¯EF ÆF9Æ F$	Æ$F9Æ'F$	Æ(F9Æ9F=Ç F=úList[Dict[str, Any]]c                óJ  — | j                              ¦   «         5 }|                     ¦   «         5 }|                     d|f¦  «         |                     ¦   «         }d„ |D ¦   «         cddd¦  «         cddd¦  «         S # 1 swxY w Y   	 ddd¦  «         dS # 1 swxY w Y   dS )uk   Láº¥y danh sÃ¡ch cÃ¡c cÃ´ng tÃ¡c bÃ³c tÃ¡ch dá»± toÃ¡n cá»§a dá»± Ã¡n Ä‘á»ƒ gá»£i Ã½ chá»�n khi xuáº¥t kho.ab  
                    SELECT t.id, t.item_order, t.wbs_code, t.norm_code, t.item_name,
                           t.category, t.unit, t.quantity as estimated_quantity,
                           t.hsmt_quantity, t.actual_quantity as settled_quantity,
                           t.unit_price_vnd, t.total_amount_vnd,
                           dt.drawing_code, dt.drawing_title
                    FROM erp_drawing_takeoff_items t
                    JOIN erp_drawing_takeoffs dt ON t.takeoff_id = dt.id
                    WHERE dt.project_id = %s
                    ORDER BY t.item_order ASC;
                c                ó,   — g | ]}t          |¦  «        ‘ŒS ro   rp   rr   s     r   ru   zHWarehouseDispatchService.get_project_takeoff_options.<locals>.<listcomp>  rv   r   N)r   rJ   rK   r!   rz   )r   r(   rV   r"   r�   s        r   Úget_project_takeoff_optionsz4WarehouseDispatchService.get_project_takeoff_optionsö   sF  € àŒX×$Ò$Ñ&Ô&ð 	/¨$Ø—’‘”ð / #Ø—’ð 
ð !�]ñ
$ô 
$ð 
$ð —|’|‘~”~�Ø.Ð.¨Ð.Ñ.Ô.ð/ð /ð /ð /ñ /ô /ð /ð	/ð 	/ð 	/ð 	/ñ 	/ô 	/ð 	/ð 	/ð/ð /ð /ð /øøøð /ð /ð /ð /ð /ð	/ð 	/ð 	/ñ 	/ô 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/øøøð 	/ð 	/ð 	/ð 	/ð 	/ð 	/s4   šB¯7A?Á&BÁ?B	ÂBÂB	ÂBÂBÂBc                ód  — | j                              ¦   «         5 }|                     ¦   «         5 }|                      |¦  «         d}g }|rd}|                     |¦  «         |                     d|› d�|¦  «         |                     ¦   «         }i }|D �]g}|                     d¦  «        pd                     ¦   «         }	|  	                    |	t          |                     d¦  «        pd¦  «        ¦  «        }
t          t          |                     d	¦  «        pd
¦  «        ¦  «        }|                     d¦  «        �"t          t          |d         ¦  «        ¦  «        n|}|                     d¦  «        }t          |t          ¦  «        �r |rþ|D ]ù}|                     dd¦  «        }|                     d|d         ¦  «        }t          t          |                     d¦  «        pd¦  «        ¦  «        }|                     d|d         ¦  «        }|                      ||	¦  «        }|› d|› �}||vr%|||t          d¦  «        t          d¦  «        dœ||<   ||         d	xx         ||z  z  cc<   ||         dxx         ||z  z  cc<   Œú�Œù|
› d|d         › �}||vr1|
|d         |d         t          d¦  «        t          d¦  «        dœ||<   ||         d	xx         |z  cc<   ||         dxx         |z  cc<   �Œid}g }|rd}|                     |¦  «         |                     d|› d�|¦  «         |                     ¦   «         }i }|D ]‘}|d         }|d         }|› d|› �}||d         |t          t          |d         pd
¦  «        ¦  «        t          t          |d         pd
¦  «        ¦  «        t          t          |d         pd
¦  «        ¦  «        dœ||<   Œ’t          |                     ¦   «         ¦  «        t          |                     ¦   «         ¦  «        z  }g }t          d¦  «        }t          d¦  «        }d
}t#          |¦  «        D �]–}|                     |i ¦  «        } |                     |i ¦  «        }!|                      d¦  «        p|!                     d¦  «        pd}|                      d¦  «        p|!                     d¦  «        pd}|!                     d¦  «        p|                      d¦  «        p|}"|                      d	t          d¦  «        ¦  «        }|                      dt          d¦  «        ¦  «        }#|!                     dt          d¦  «        ¦  «        }$|!                     d t          d¦  «        ¦  «        }%|!                     d!t          d¦  «        ¦  «        }&||&z  }t%          j        |t          d"¦  «        ¦  «        }'|#t          d¦  «        k    r|#n|}(|(t          d¦  «        k    r’|$|(z
  })|(d
k    r|)|(z  t          d#¦  «        z  }*nt          d¦  «        }*|*|'k    rt&          j        }+d$|'› d%�},nu|*|'t          d&¦  «        z  k    rt&          j        }+d'|*d(›d)|'› d*�},nHt&          j        }+|d+z  }d,|*d(›d-|'› d.�},n,t          d¦  «        })t          d¦  «        }*t&          j        }+d/},t1          t          d¦  «        |)¦  «        |%z  }-||-z  }|                     ||"|t3          t5          |d0¦  «        ¦  «        t3          t5          |#d0¦  «        ¦  «        t3          t5          |$d0¦  «        ¦  «        t3          t5          |'d1¦  «        ¦  «        t3          t5          |)d0¦  «        ¦  «        t3          t5          |*d1¦  «        ¦  «        |+j        t3          t5          |%d1¦  «        ¦  «        t3          t5          |-d1¦  «        ¦  «        |,d2œ¦  «         �Œ˜d3„ |D ¦   «         }.|.rt9          |.¦  «        t;          |.¦  «        z  nd4}/|t;          |¦  «        t3          t5          |d1¦  «        ¦  «        t3          t5          |d1¦  «        ¦  «        t5          |/d1¦  «        ||d5œcddd¦  «         cddd¦  «         S # 1 swxY w Y   	 ddd¦  «         dS # 1 swxY w Y   dS )6u  TÃ­nh toÃ¡n ma tráº­n Ä‘á»‘i chiáº¿u 3 chiá»�u: Dá»± toÃ¡n (BoQ) - Thá»±c xuáº¥t (Dispatches) - Quyáº¿t toÃ¡n (Settlement)
        vÃ  phÃ¢n tÃ­ch tá»· lá»‡ hao há»¥t váº­t tÆ° thá»±c táº¿ so vá»›i Ä‘á»‹nh má»©c nhÃ  nÆ°á»›c (ThÃ´ng tÆ° 12/2021 & Ä�á»‹nh má»©c 2026).
        ri   zdt.project_id = %saŸ  
                    SELECT t.id, t.item_name, t.category, t.unit,
                           t.quantity as estimated_quantity,
                           t.actual_quantity as settled_quantity,
                           t.material_breakdown, t.material_cost_vnd
                    FROM erp_drawing_takeoff_items t
                    JOIN erp_drawing_takeoffs dt ON t.takeoff_id = dt.id
                    WHERE rl   ÚcategoryÚotherrB   r9   Úestimated_quantityr   Úsettled_quantityNÚmaterial_breakdownÚmaterial_codeÚmaterial_nameÚnorm_quantityg      ð?r7   z::r4   )Úmaterial_grouprŽ   r7   rŠ   r‹   rj   a½  
                    SELECT w.material_group, w.item_name, d.unit,
                           SUM(d.quantity_dispatched) as total_dispatched,
                           AVG(d.unit_price_vnd) as avg_unit_price,
                           SUM(d.total_value_vnd) as total_dispatched_value
                    FROM erp_warehouse_dispatches d
                    JOIN erp_warehouse_inventory w ON d.inventory_item_id = w.id
                    WHERE zU
                    GROUP BY w.material_group, w.item_name, d.unit;
                r�   Útotal_dispatchedÚavg_unit_priceÚtotal_dispatched_value)r�   rŽ   r7   Údispatched_quantityr>   Útotal_dispatched_value_vndÚOTHER_SUPPLIESr”   r>   r•   z2.5Ú100u#   Trong Ä‘á»‹nh má»©c cho phÃ©p (â‰¤ u   %). Quáº£n lÃ½ hao há»¥t tá»‘t.z1.2u&   Tiá»‡m cáº­n ngÆ°á»¡ng Ä‘á»‹nh má»©c (z.2fz% vs u   %). Cáº§n chÃº Ã½ bÃ£i kho.r;   u   VÆ¯á»¢T Ä�á»ŠNH Má»¨C (z% > u8   %). Cáº£nh bÃ¡o tháº¥t thoÃ¡t hoáº·c hao phÃ­ thi cÃ´ng!u@   Xuáº¥t kho Ä‘á»™c láº­p (ChÆ°a gáº¯n dá»± toÃ¡n bÃ³c tÃ¡ch CAD).é   é   )r�   rŽ   r7   rŠ   r‹   r”   Únorm_wastage_pctÚactual_wastage_quantityÚactual_wastage_pctÚvariance_statusr>   Úwastage_cost_vndÚevaluation_messagec                óT   — g | ]%}|d          t           j        j        k    ¯|d         ‘Œ&S )r�   rœ   )r   ÚNO_ESTIMATEÚvalue)rs   rW   s     r   ru   zOWarehouseDispatchService.calculate_material_variance_report.<locals>.<listcomp>Å  s_   € ð  Yð  Yð  Y¸TÐW[Ð\mÔWnõ  sFô  sRô  sXò  XXð  XX˜dÐ#7Ô8ð  XXð  XXð  XXr   g        )r(   Útotal_materials_monitoredr•   Útotal_wastage_cost_vndÚaverage_wastage_pctÚexceeded_items_countÚitems)r   rJ   rK   r#   rM   r!   rz   ÚgetÚupperÚ_map_category_to_material_groupr%   r   rH   ÚlistÚ_map_material_name_to_groupÚsetÚkeysÚsortedr   r   ÚSAFEÚWARNINGÚEXCEEDEDr¡   ÚmaxrR   Úroundr¢   ÚsumÚlen)0r   r(   rV   r"   Útakeoff_whereÚtakeoff_paramsÚtakeoff_rowsÚestimated_by_groupÚrowÚcatÚ	mat_groupÚq_dtÚq_actÚ
breakdownsÚmÚcodeÚm_nameÚnorm_qtyr7   Ú	sub_groupÚkeyÚ
disp_whereÚdisp_paramsÚdispatch_rowsÚdispatched_by_grouprt   ÚgrpÚall_keysÚvariance_itemsÚtotal_dispatched_valÚtotal_wastage_costÚexceeded_countÚest_dataÚ	disp_dataÚmat_nameÚq_qtÚq_txr^   Úval_txÚnorm_pctÚ
baseline_qÚactual_wastage_qtyrœ   rE   Úeval_msgÚwastage_costÚ
valid_pctsÚavg_pcts0                                                   r   Ú"calculate_material_variance_reportz;WarehouseDispatchService.calculate_material_variance_report  s\
  € ð ŒX×$Ò$Ñ&Ô&ð B	¨$Ø—’‘”ð A #Ø×#Ò# CÑ(Ô(Ð(ð
 !&�Ø,.�Øð 6Ø$8�MØ"×)Ò)¨*Ñ5Ô5Ð5à—’ð ð )ðð ð ð $ñ%ô %ð %ð  #Ÿ|š|™~œ~�ð ACÐ"Ø'ð *Mñ *M�CØŸ7š7 :Ñ.Ô.Ð9°'×@Ò@ÑBÔB�CØ $× DÒ DÀSÍ#ÈcÏgÊgÐVaÑNbÔNbÐNhÐfhÑJiÔJiÑ jÔ j�Iõ #¥3 s§w¢wÐ/CÑ'DÔ'DÐ'IÈÑ#JÔ#JÑKÔK�DàEHÇWÂWÐM_ÑE`ÔE`ÐEl�G¥C¨Ð,>Ô(?Ñ$@Ô$@ÑAÔAÐAÐrv�Eð "%§¢Ð)=Ñ!>Ô!>�JÝ! *­dÑ3Ô3ñ M¸
ð MØ!+ð \ð \˜AØ#$§5¢5¨¸"Ñ#=Ô#=˜DØ%&§U¢U¨?¸CÀÔ<LÑ%MÔ%M˜FÝ'.­s°1·5²5¸Ñ3IÔ3IÐ3PÈSÑ/QÔ/QÑ'RÔ'R˜HØ#$§5¢5¨°°V´Ñ#=Ô#=˜DØ(,×(HÒ(HÈÐQTÑ(UÔ(U˜Ià%.Ð"8Ð"8°$Ð"8Ð"8˜CØ"Ð*<Ð<Ð<à6?Ø5;Ø,0Ý:AÀ#¹,¼,Ý8?À¹¼ð;"ð ;"Ð 2°3Ñ 7ð /¨sÔ3Ð4HÐIÐIÔIÈTÐT\É_Ñ\ÐIÐIÑIØ.¨sÔ3Ð4FÐGÐGÔGÈ5ÐS[ÑK[Ñ[ÐGÐGÑGÐGñ#\ð( "+Ð;Ð;¨c°&¬kÐ;Ð;˜ØÐ&8Ð8Ð8à2;Ø14°[Ô1AØ(+¨F¬Ý6=¸c±l´lÝ4;¸C±L´Lð7ð 7Ð.¨sÑ3ð +¨3Ô/Ð0DÐEÐEÔEÈÑMÐEÐEÑEØ*¨3Ô/Ð0BÐCÐCÔCÀuÑLÐCÐCÑCÑCð
 #�
Ø)+�Øð 3Ø!4�JØ×&Ò& zÑ2Ô2Ð2à—’ð 	ð &ð	ð 	ð 	ð !ñ	"ô 	"ð 	"ð !$§¢¡¤�àACÐ#Ø&ð ð �AØÐ,Ô-�CØ˜Vœ9�DØ Ð*Ð* DÐ*Ð*�Cà*-Ø)*¨;¬Ø $Ý/6µs¸1Ð=OÔ;PÐ;UÐTUÑ7VÔ7VÑ/WÔ/WÝ*1µ#°aÐ8HÔ6IÐ6NÈQÑ2OÔ2OÑ*PÔ*PÝ6=½cÀ!ÐD\ÔB]ÐBbÐabÑ>cÔ>cÑ6dÔ6dð0ð 0Ð'¨Ñ,Ð,õ Ð1×6Ò6Ñ8Ô8Ñ9Ô9½CÐ@S×@XÒ@XÑ@ZÔ@ZÑ<[Ô<[Ñ[�Ø79�å'.¨s¡|¤|Ð$Ý%,¨S¡\¤\Ð"Ø!"�å! (Ñ+Ô+ð Añ A�CØ1×5Ò5°c¸2Ñ>Ô>�HØ 3× 7Ò 7¸¸RÑ @Ô @�Ià"Ÿ,š,Ð'7Ñ8Ô8Ðo¸I¿MºMÐJZÑ<[Ô<[ÐoÐ_o�CØ#Ÿ<š<¨Ñ/Ô/ÐN°9·=²=ÀÑ3HÔ3HÐNÈB�DØ(Ÿ}š}¨_Ñ=Ô=ÐeÀÇÂÈoÑA^ÔA^ÐeÐbe�Hà#Ÿ<š<Ð(<½gÀc¹l¼lÑKÔK�DØ#Ÿ<š<Ð(:½GÀC¹L¼LÑIÔI�DØ$Ÿ=š=Ð)>ÅÈÁÄÑMÔM�DØ!*§¢Ð/?ÅÈÁÄÑ!NÔ!N�JØ&Ÿ]š]Ð+GÍÐQTÉÌÑVÔV�FØ(¨FÑ2Ð(å7Ô;¸CÅÈÁÄÑPÔP�Hð *.µ¸±´Ò)<Ð)<  À$�Jà!¥G¨C¡L¤LÒ0Ð0à-1°JÑ->Ð*Ø%¨š>˜>Ø2DÀzÑ2QÕU\Ð]bÑUcÔUcÑ1cÐ.Ð.å18¸±´Ð.ð .°Ò9Ð9Ý%8Ô%=˜FØ'vÈXÐ'vÐ'vÐ'v˜H˜HØ/°8½gÀe¹n¼nÑ3LÒLÐLÝ%8Ô%@˜Fð (SÐPbð  (Sð  (Sð  (SÐmuð  (Sð  (Sð  (S˜H˜Hå%8Ô%A˜FØ*¨aÑ/˜Nð (`ÐASð  (`ð  (`ð  (`Ð]eð  (`ð  (`ð  (`˜H˜Hõ .5°S©\¬\Ð*Ý-4°S©\¬\Ð*Ý!4Ô!@˜Ø#e˜õ %(­°©¬Ð6HÑ$IÔ$IÈZÑ#W�LØ&¨,Ñ6Ð&à"×)Ò)Ø*-Ø)1Ø $Ý.3µE¸$À±N´NÑ.CÔ.CÝ,1µ%¸¸a±.´.Ñ,AÔ,AÝ/4µU¸4À±^´^Ñ/DÔ/DÝ,1µ%¸À!Ñ2DÔ2DÑ,EÔ,EÝ38½Ð?QÐSTÑ9UÔ9UÑ3VÔ3VÝ.3µEÐ:LÈaÑ4PÔ4PÑ.QÔ.QØ+1¬<Ý*/µ°jÀ!Ñ0DÔ0DÑ*EÔ*EÝ,1µ%¸ÀaÑ2HÔ2HÑ,IÔ,IØ.6ð+ð +ñ ô ð ñ ð" Yð  YÀ^ð  Yñ  Yô  Y�
Ø?IÐR�#˜j™/œ/­C°
©O¬OÑ;Ð;Ès�ð #-Ý14°^Ñ1DÔ1DÝ27½Ð>RÐTUÑ8VÔ8VÑ2WÔ2WÝ.3µEÐ:LÈaÑ4PÔ4PÑ.QÔ.QÝ+0°¸!Ñ+<Ô+<Ø,:Ø+ðð ðsAð Að Að Añ Aô Að AðB	ð B	ð B	ð B	ñ B	ô B	ð B	ð B	ðAð Að Að Aøøøð Að Að Að Að AðB	ð B	ð B	ñ B	ô B	ð B	ð B	ð B	ð B	ð B	ð B	ð B	øøøð B	ð B	ð B	ð B	ð B	ð B	s5   šb%¯abá3b%âb	âb%âb	âb%â%b)â,b)rˆ   Únamec                ó  — |                       ¦   «         }|                      ¦   «         }d|v sd|v sd|v rdS d|v sd|v rdS d|v sd	|v rd
S d|v sd|v rdS d|v sd|v sd|v sd|v rdS d|v sd|v sd|v rdS d|v sd|v rdS dS )u<   Ã�nh xáº¡ danh má»¥c cÃ´ng tÃ¡c sang nhÃ³m váº­t tÆ° chÃ­nh.Úrebarõ   thÃ©pu   cá»‘t thÃ©pÚSTEELÚconcreteõ	   bÃª tÃ´ngÚCONCRETEÚcementõ   xi mÄƒngÚCEMENTÚmasonryõ   gáº¡chÚBRICKÚ	earthworku   Ä‘áº¥tõ   cÃ¡tu   Ä‘Ã¡Ú	AGGREGATEÚpaintõ   sÆ¡nõ   chá»‘ng tháº¥mÚFINISHING_PAINTÚpipeõ   á»‘ngÚPIPES_FITTINGSr–   )Úlower)rˆ   rß   ÚcÚns       r   rª   z8WarehouseDispatchService._map_category_to_material_groupÒ  së   € ð �NŠNÑÔˆØ�JŠJ‰LŒLˆØ�aˆ<ˆ<˜7 a˜<˜<¨=¸AÐ+=Ð+=Ø�7Ø˜ˆ?ˆ?˜k¨QÐ.Ð.Ø�:Ø�qˆ=ˆ=˜J¨!˜O˜OØ�8Ø˜ˆ>ˆ>˜X¨˜]˜]Ø�7Ø˜!ÐÐ˜x¨1˜}˜}°¸!°°¸vÈ¸{¸{Ø�;Ø�aˆ<ˆ<˜6 Q˜;˜;Ð*:¸aÐ*?Ð*?Ø$Ð$Ø�Qˆ;ˆ;˜' Q˜,˜,Ø#Ð#ØÐr   Úfallback_catc                ó2  ‡— |                       ¦   «         Št          ˆfd„dD ¦   «         ¦  «        rdS t          ˆfd„dD ¦   «         ¦  «        rdS t          ˆfd„dD ¦   «         ¦  «        rd	S t          ˆfd
„dD ¦   «         ¦  «        rdS t          ˆfd„dD ¦   «         ¦  «        rdS t          ˆfd„dD ¦   «         ¦  «        rdS t          ˆfd„dD ¦   «         ¦  «        rdS t          ˆfd„dD ¦   «         ¦  «        rdS t                               || ¦  «        S )uX   Nháº­n diá»‡n nhÃ³m váº­t tÆ° tá»« tÃªn chi tiáº¿t trong báº£ng phÃ¢n rÃ£ Ä‘á»‹nh má»©c.c              3  ó    •K  — | ]}|‰v V — Œ	d S r   ro   ©rs   ÚkÚnls     €r   ú	<genexpr>zGWarehouseDispatchService._map_material_name_to_group.<locals>.<genexpr>ë  ó'   øè è € Ð=Ð=˜1ˆq�BˆwÐ=Ð=Ð=Ð=Ð=Ð=r   )râ   rá   u   buá»™crã   c              3  ó    •K  — | ]}|‰v V — Œ	d S r   ro   rý   s     €r   r   zGWarehouseDispatchService._map_material_name_to_group.<locals>.<genexpr>í  s'   øè è € ÐAÐA˜1ˆq�BˆwÐAÐAÐAÐAÐAÐAr   )rè   Úpcb40Úchinfonré   c              3  ó    •K  — | ]}|‰v V — Œ	d S r   ro   rý   s     €r   r   zGWarehouseDispatchService._map_material_name_to_group.<locals>.<genexpr>ï  s'   øè è € ÐNÐN˜1ˆq�BˆwÐNÐNÐNÐNÐNÐNr   )rî   u   Ä‘Ã¡ 1x2u   Ä‘Ã¡ 4x6u	   Ä‘Ã¡ dÄƒmrï   c              3  ó    •K  — | ]}|‰v V — Œ	d S r   ro   rý   s     €r   r   zGWarehouseDispatchService._map_material_name_to_group.<locals>.<genexpr>ñ  s'   øè è € Ð:Ð:˜1ˆq�BˆwÐ:Ð:Ð:Ð:Ð:Ð:r   )rå   rä   ræ   c              3  ó    •K  — | ]}|‰v V — Œ	d S r   ro   rý   s     €r   r   zGWarehouseDispatchService._map_material_name_to_group.<locals>.<genexpr>ó  s'   øè è € Ð4Ð4˜1ˆq�BˆwÐ4Ð4Ð4Ð4Ð4Ð4r   )rë   u   ngÃ³irì   c              3  ó    •K  — | ]}|‰v V — Œ	d S r   ro   rý   s     €r   r   zGWarehouseDispatchService._map_material_name_to_group.<locals>.<genexpr>õ  s'   øè è € ÐCÐC˜1ˆq�BˆwÐCÐCÐCÐCÐCÐCr   )rñ   Úsikarò   ró   c              3  ó    •K  — | ]}|‰v V — Œ	d S r   ro   rý   s     €r   r   zGWarehouseDispatchService._map_material_name_to_group.<locals>.<genexpr>÷  s'   øè è € Ð<Ð<˜1ˆq�BˆwÐ<Ð<Ð<Ð<Ð<Ð<r   )rõ   u   nhá»±au   cÃºtrö   c              3  ó    •K  — | ]}|‰v V — Œ	d S r   ro   rý   s     €r   r   zGWarehouseDispatchService._map_material_name_to_group.<locals>.<genexpr>ù  r  r   )u   dáº§uu   xÄƒngÚdieselÚFUEL_OIL)r÷   Úanyr   rª   )rß   rú   rÿ   s     @r   r¬   z4WarehouseDispatchService._map_material_name_to_groupç  sƒ  ø€ ð �ZŠZ‰\Œ\ˆÝÐ=Ð=Ð=Ð=Ð <Ð=Ñ=Ô=Ñ=Ô=ð 	Ø�7ÝÐAÐAÐAÐAÐ @ÐAÑAÔAÑAÔAð 	Ø�8ÝÐNÐNÐNÐNÐ MÐNÑNÔNÑNÔNð 	Ø�;ÝÐ:Ð:Ð:Ð:Ð 9Ð:Ñ:Ô:Ñ:Ô:ð 	Ø�:ÝÐ4Ð4Ð4Ð4Ð 3Ð4Ñ4Ô4Ñ4Ô4ð 	Ø�7ÝÐCÐCÐCÐCÐ BÐCÑCÔCÑCÔCð 	%Ø$Ð$ÝÐ<Ð<Ð<Ð<Ð ;Ð<Ñ<Ô<Ñ<Ô<ð 	$Ø#Ð#ÝÐ=Ð=Ð=Ð=Ð <Ð=Ñ=Ô=Ñ=Ô=ð 	Ø�:Ý'×GÒGÈÐVZÑ[Ô[Ð[r   r   )r   r   r   r   )r   r   )NNNNNNNN)r$   r%   r&   r'   r(   r)   r*   r)   r+   r)   r,   r-   r.   r)   r/   r)   r0   r)   r1   r)   r   r2   )NNNrb   r   )r(   r)   r$   r)   rc   r)   rd   re   rf   re   r   rg   )r(   r%   r   rƒ   )r(   r)   r   r2   )rˆ   r%   rß   r%   r   r%   )rß   r%   rú   r%   r   r%   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r#   ra   r‚   r†   rÞ   Ústaticmethodrª   r¬   ro   r   r   r   r      s2  € € € € € ðð ð;ð ;ð ;ð ;ð ;ðð ð ð ð> %)Ø $Ø)-Ø.2Ø(,Ø+/Ø!%Ø#ðvð vð vð vð vðt %)Ø+/Ø $ØØð@:ð @:ð @:ð @:ð @:ðD/ð /ð /ð /ð& +/ðHð Hð Hð Hð HðT ð ð  ð  ñ „\ð ð( ð\ð \ð \ñ „\ð\ð \ð \r   r   )Ú
__future__r   r   r   Údecimalr   ÚloggingÚtypingr   r   r	   r
   r   Úuuidr   r   Ú&app.core.postgres.base_pkg.base_clientr   Ú.app.modules.inventory.domain.dispatch_entitiesr   r   r   r   Ú	getLoggerrP   r   ro   r   r   ú<module>r     s<  ðØ "Ð "Ð "Ð "Ð "Ð "ðð
 $Ð #Ð #Ð #Ð #Ð #Ð #Ð #Ø Ð Ð Ð Ð Ð Ø €€€Ø 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ø Ð Ð Ð Ð Ð Ð Ð à EÐ EÐ EÐ EÐ EÐ Eðð ð ð ð ð ð ð ð ð ð ð ð 
ˆÔ	Ð>Ñ	?Ô	?€ðb\ð b\ð b\ð b\ð b\ñ b\ô b\ð b\ð b\ð b\r   