@@ -107,6 +107,17 @@ public void importStock(StockImportRequest request) {
107107 @ Transactional
108108 // Trừ stock.
109109 public void deductStock (ProductVariant variant , int quantity , Long orderId , String notes ) {
110+ if (!variant .isBaseUnit ()) {
111+ var directVariantStocks = inventoryStockRepository .findByVariantId (variant .getId ());
112+ boolean hasDirectVariantStock = directVariantStocks .stream ()
113+ .anyMatch (stock -> stock .getQuantity () != null && stock .getQuantity () > 0 );
114+
115+ if (hasDirectVariantStock ) {
116+ deductFromStocks (variant , quantity , directVariantStocks , orderId , notes );
117+ return ;
118+ }
119+ }
120+
110121 ProductVariant baseVariant = variant ;
111122 int deductQuantity = quantity ;
112123
@@ -123,35 +134,37 @@ public void deductStock(ProductVariant variant, int quantity, Long orderId, Stri
123134 deductQuantity = quantity * factor ;
124135 }
125136
126- // Ưu tiên trừ theo lô cũ (FIFO đơn giản)
127- int remainingToDeduct = deductQuantity ;
128- var stocks = inventoryStockRepository .findByVariantId (baseVariant .getId ());
137+ var baseStocks = inventoryStockRepository .findByVariantId (baseVariant .getId ());
138+ deductFromStocks (baseVariant , deductQuantity , baseStocks , orderId , notes );
139+ }
140+
141+ private void deductFromStocks (ProductVariant stockVariant , int quantityToDeduct , java .util .List <InventoryStock > stocks ,
142+ Long orderId , String notes ) {
143+ int remainingToDeduct = quantityToDeduct ;
129144
130145 for (InventoryStock stock : stocks ) {
131- if (remainingToDeduct <= 0 )
146+ if (remainingToDeduct <= 0 ) {
132147 break ;
148+ }
133149
134- if (stock .getQuantity () > 0 ) {
135- int oldQty = stock .getQuantity ();
136- int qtyToTake = Math .min (oldQty , remainingToDeduct );
137- stock .setQuantity (oldQty - qtyToTake );
138- InventoryStock savedStock = inventoryStockRepository .save (stock );
139- outOfStockNotificationService .handleStockTransition (savedStock , oldQty , savedStock .getQuantity (), "SALE_ORDER" );
150+ int currentQty = stock .getQuantity () != null ? stock .getQuantity () : 0 ;
151+ if (currentQty <= 0 ) {
152+ continue ;
153+ }
140154
141- // Ghi nhận biến động cho lô này
142- recordMovement (baseVariant , stock .getBatch (), stock .getLocation (),
143- StockTransactionType .SALE , -qtyToTake , "SALE_ORDER" , orderId , notes );
155+ int qtyToTake = Math .min (currentQty , remainingToDeduct );
156+ stock .setQuantity (currentQty - qtyToTake );
157+ InventoryStock savedStock = inventoryStockRepository .save (stock );
158+ outOfStockNotificationService .handleStockTransition (savedStock , currentQty , savedStock .getQuantity (), "SALE_ORDER" );
144159
145- remainingToDeduct -= qtyToTake ;
146- }
160+ recordMovement (stockVariant , stock .getBatch (), stock .getLocation (),
161+ StockTransactionType .SALE , -qtyToTake , "SALE_ORDER" , orderId , notes );
162+
163+ remainingToDeduct -= qtyToTake ;
147164 }
148165
149166 if (remainingToDeduct > 0 ) {
150- // Vẫn chưa trừ hết (Tồn kho bị âm - Tùy cấu hình hệ thống POS có thể bắn lỗi)
151- // Trong bối cảnh bán lẻ, thường ta ghi log cảnh báo và cho phép bán âm, hoặc
152- // trừ âm vào một location mặc định.
153- // Để an toàn, chúng ta throw exception ở đây trừ khi requirements cho phép âm.
154- throw new RuntimeException ("Not enough stock available for variant: " + baseVariant .getSku ());
167+ throw new RuntimeException ("Not enough stock available for variant: " + stockVariant .getSku ());
155168 }
156169 }
157170
0 commit comments