diff --git a/stock_customer_deposit/models/sale_order.py b/stock_customer_deposit/models/sale_order.py index 4598c194ba5..53493697afa 100644 --- a/stock_customer_deposit/models/sale_order.py +++ b/stock_customer_deposit/models/sale_order.py @@ -4,6 +4,7 @@ from odoo import _, api, fields, models from odoo.exceptions import ValidationError +from odoo.tools import float_compare class SaleOrder(models.Model): @@ -25,27 +26,25 @@ def _compute_customer_deposit_count(self): def _action_confirm(self): self._check_can_customer_deposit() - deposit_lines = self.order_line.filtered( - lambda line: line.deposit_available_qty > 0.0 and line.product_uom_qty > 0.0 - ) - if not deposit_lines: - return super()._action_confirm() - if deposit_lines.filtered( - lambda line: line.route_id == line.warehouse_id.customer_deposit_route_id - ): - raise ValidationError( - _( - "You can't use customer deposit for products with the route" - " 'Customer Deposit'." - ) - ) - for order in self: + for order in self.filtered(lambda o: not o.customer_deposit): deposit_lines = order.order_line.filtered( - lambda line: line.deposit_available_qty > 0.0 - and line.product_uom_qty > 0.0 + lambda line: float_compare( + line.deposit_available_qty, + 0.0, + precision_rounding=line.product_id.uom_id.rounding, + ) + > 0 + and float_compare( + line.product_uom_qty, + 0.0, + precision_rounding=line.product_id.uom_id.rounding, + ) + > 0 ) + # Normal order if not deposit_lines: continue + # Taking from deposit order quants_by_product = self.env["stock.quant"].read_group( domain=order._get_customer_deposit_domain(), fields=["available_quantity"], @@ -58,10 +57,17 @@ def _action_confirm(self): for quant_by_product in quants_by_product } for product in deposit_lines.mapped("product_id"): - if product_deposit.get(product.id, 0.0) < sum( - deposit_lines.filtered( - lambda line: line.product_id.id == product.id - ).mapped("product_uom_qty") + if ( + float_compare( + product_deposit.get(product.id, 0.0), + sum( + deposit_lines.filtered( + lambda line: line.product_id.id == product.id + ).mapped("product_uom_qty") + ), + precision_rounding=product.uom_id.rounding, + ) + < 0 ): raise ValidationError( _( @@ -71,32 +77,38 @@ def _action_confirm(self): product=product.name, ) ) - return super(SaleOrder, self)._action_confirm() + return super()._action_confirm() def _check_can_customer_deposit(self): - if self.filtered("customer_deposit").order_line.filtered( - lambda line: line.product_id.type == "product" - and line.route_id != line.warehouse_id.customer_deposit_route_id - ): - raise ValidationError( - _( - "All lines coming from orders marked as 'Customer depot' must" - " have Customer deposit route." - ) - ) - - if self.order_line.filtered( - lambda line: line.product_id.type == "product" - and line.warehouse_id.customer_deposit_route_id - and not line.order_id.customer_deposit - and line.route_id == line.warehouse_id.customer_deposit_route_id - ): - raise ValidationError( - _( - "You cannot select Customer Deposit route in an order line if you" - " do not mark the order as a customer depot." - ) + """Check if the order is valid to perform a deposit or take from deposit""" + for order in self: + product_lines = order.order_line.filtered( + lambda line: line.product_id.type == "product" ) + if order.customer_deposit: + # Perform deposit + if product_lines.filtered( + lambda line: line.route_id + != line.warehouse_id.customer_deposit_route_id + ): + raise ValidationError( + _( + "All lines coming from orders marked as 'Customer depot' must" + " have Customer deposit route." + ) + ) + elif order.warehouse_id.customer_deposit_route_id: + # Take from deposit + if product_lines.filtered( + lambda line: line.route_id + == line.warehouse_id.customer_deposit_route_id + ): + raise ValidationError( + _( + "You cannot select Customer Deposit route in an order line if you" + " do not mark the order as a customer depot." + ) + ) def _get_customer_deposit_domain(self): return [ diff --git a/stock_customer_deposit/models/sale_order_line.py b/stock_customer_deposit/models/sale_order_line.py index 695d0bef434..3cdf08e26e2 100644 --- a/stock_customer_deposit/models/sale_order_line.py +++ b/stock_customer_deposit/models/sale_order_line.py @@ -28,6 +28,7 @@ class SaleOrderLine(models.Model): "product_id", "warehouse_id.use_customer_deposits", "order_id.customer_deposit" ) def _compute_route_id(self): + """Set route_id to customer deposit route if use_customer_deposits is True""" for line in self: line.route_id = ( line.warehouse_id.customer_deposit_route_id diff --git a/stock_customer_deposit/models/stock_move.py b/stock_customer_deposit/models/stock_move.py index 45bad8abb60..045c410f3bf 100644 --- a/stock_customer_deposit/models/stock_move.py +++ b/stock_customer_deposit/models/stock_move.py @@ -3,6 +3,7 @@ from odoo import models +from odoo.tools import float_compare class StockMove(models.Model): @@ -11,32 +12,47 @@ class StockMove(models.Model): def _action_assign(self, force_qty=False): if self.env.context.get("owner", False): return super(StockMove, self)._action_assign(force_qty=force_qty) - # moves from warehouse with customer deposits activate - moves_customer_deposits = self.filtered( - lambda move: move.warehouse_id.use_customer_deposits + + # Warehouse not configured to use customer deposits + no_deposit_config_moves = self.filtered( + lambda move: not move.warehouse_id.use_customer_deposits ) - # moves with warehouse that have deactivated customer deposits should not do anything. - super(StockMove, self - moves_customer_deposits)._action_assign( - force_qty=force_qty + super(StockMove, no_deposit_config_moves)._action_assign(force_qty=force_qty) + # Warehouse configured to use customer deposits + deposit_config_moves = self - no_deposit_config_moves + # Move needs to create deposit + moves_push_deposit_route = deposit_config_moves.filtered( + lambda move: move.warehouse_id.customer_deposit_route_id in move.route_ids ) - # moves with warehouse that have activated customer deposits must assign owner - moves_owner = moves_customer_deposits.filtered( - lambda m: self.env["stock.quant"]._get_available_quantity( - m.product_id, - m.location_id, - owner_id=m.partner_id.commercial_partner_id or m.partner_id, + # Move needs to take from deposit or stock + moves_pull_from_stock = deposit_config_moves - moves_push_deposit_route + for move in moves_pull_from_stock: + # Check if move can take from deposit + move_owner_qty = move._get_available_quantity( + move.location_id, + lot_id=None, + package_id=None, + owner_id=move.partner_id.commercial_partner_id or move.partner_id, + strict=False, + allow_negative=False, ) - > 0.0 - ) - for move in moves_owner: - super( - StockMove, - move.with_context( - owner=move.partner_id.commercial_partner_id.id or move.partner_id.id - ), - )._action_assign(force_qty=force_qty) + owner = False + if ( + float_compare( + move_owner_qty, + move.product_uom_qty, + precision_rounding=move.product_uom.rounding, + ) + >= 0 + ): + # Enough qty to take from deposit: Propagate assign with owner context + owner = move.partner_id.commercial_partner_id.id or move.partner_id.id + super(StockMove, move.with_context(owner=owner))._action_assign( + force_qty=force_qty + ) + return super( - StockMove, (moves_customer_deposits - moves_owner).with_context(owner=False) + StockMove, moves_push_deposit_route.with_context(owner=False) )._action_assign(force_qty=force_qty) def _get_out_move_lines(self): diff --git a/stock_customer_deposit/models/stock_quant.py b/stock_customer_deposit/models/stock_quant.py index 3fe75c1988b..09edaf7bc6b 100644 --- a/stock_customer_deposit/models/stock_quant.py +++ b/stock_customer_deposit/models/stock_quant.py @@ -4,6 +4,7 @@ from odoo import api, models from odoo.osv import expression +from odoo.tools import float_compare class StockQuant(models.Model): @@ -47,7 +48,9 @@ def _update_available_quantity( owner_id=None, in_date=None, ): - if quantity > 0 and self.env.context.get("owner", False): + if float_compare( + quantity, 0.0, precision_rounding=product_id.uom_id.rounding + ) > 0 and self.env.context.get("owner", False): owner_id = ( owner_id or self.env["res.partner"].browse(self.env.context.get("owner")) diff --git a/stock_customer_deposit/tests/test_sale_customer_deposit.py b/stock_customer_deposit/tests/test_sale_customer_deposit.py index 7fbc50e990e..e998c725b83 100644 --- a/stock_customer_deposit/tests/test_sale_customer_deposit.py +++ b/stock_customer_deposit/tests/test_sale_customer_deposit.py @@ -28,6 +28,16 @@ def setUpClass(cls): cls.productB: 110, }, } + cls.result_test_second_deposit = { + False: { + cls.productA: 100, + cls.productB: 80, + }, + cls.partner1: { + cls.productA: 200, + cls.productB: 220, + }, + } @users("user_customer_deposit") def test_sale_customer_deposit(self): @@ -107,6 +117,23 @@ def test_sale_customer_deposit(self): product, self.warehouse.lot_stock_id, owner_id=partner ), quantity, + "First deposit not added correctly", + ) + # Add another order to deposit + so_2 = so.copy() + so_2.action_confirm() + so_2.picking_ids.action_confirm() + so_2.picking_ids.action_assign() + so_2.picking_ids.action_set_quantities_to_reservation() + so_2.picking_ids.button_validate() + for partner, products in self.result_test_second_deposit.items(): + for product, quantity in products.items(): + self.assertEqual( + self.env["stock.quant"]._get_available_quantity( + product, self.warehouse.lot_stock_id, owner_id=partner + ), + quantity, + "Second deposit not added correctly", ) @users("user_customer_deposit")