diff --git a/stock_picking_report_valued/models/stock_picking.py b/stock_picking_report_valued/models/stock_picking.py index 55044ee04..5ea6b5839 100644 --- a/stock_picking_report_valued/models/stock_picking.py +++ b/stock_picking_report_valued/models/stock_picking.py @@ -41,10 +41,47 @@ def _compute_amount_all(self): records...). """ for pick in self: - amount_untaxed = amount_tax = 0.0 - for line in pick.move_line_ids: - amount_untaxed += line.sale_price_subtotal - amount_tax += line.sale_price_tax + pick = pick.with_company(pick.company_id) + move_lines = pick.move_line_ids.filtered(lambda x: x.sale_line) + + if pick.company_id.tax_calculation_rounding_method == "round_globally": + # With global rounding, we need to use the same method as sale.order + # to calculate taxes on all lines together, not sum line by line + tax_lines_data = [] + for line in move_lines: + valued_line = line.sale_line + quantity = line._get_report_valued_quantity() + different_uom = valued_line.product_uom != line.product_uom_id + different_qty = float_compare( + quantity, + valued_line.product_uom_qty, + precision_rounding=line.product_uom_id.rounding, + ) + if different_uom or different_qty: + # Create virtual sale line with move line quantity + valued_line.mapped("tax_id") # Force cache + sol_vals = valued_line._convert_to_write(valued_line._cache) + sol_vals["product_uom_qty"] = quantity + sol_vals.pop("price_subtotal", None) + valued_line = valued_line.new(sol_vals) + + tax_line_dict = valued_line._convert_to_tax_base_line_dict() + tax_lines_data.append(tax_line_dict) + + if tax_lines_data: + tax_results = pick.env["account.tax"]._compute_taxes(tax_lines_data) + totals = tax_results["totals"] + amount_untaxed = totals.get(pick.currency_id, {}).get( + "amount_untaxed", 0.0 + ) + amount_tax = totals.get(pick.currency_id, {}).get("amount_tax", 0.0) + else: + amount_untaxed = amount_tax = 0.0 + else: + # With round_per_line, sum the tax from each line (current behavior) + amount_untaxed = sum(move_lines.mapped("sale_price_subtotal")) + amount_tax = sum(move_lines.mapped("sale_price_tax")) + pick.update( { "amount_untaxed": amount_untaxed, diff --git a/stock_picking_report_valued/tests/test_stock_picking_valued.py b/stock_picking_report_valued/tests/test_stock_picking_valued.py index acdccf503..53f6795a0 100644 --- a/stock_picking_report_valued/tests/test_stock_picking_valued.py +++ b/stock_picking_report_valued/tests/test_stock_picking_valued.py @@ -182,3 +182,41 @@ def _patched_get_report_valued_total_amount(self): finally: # Restore original method StockPicking._get_report_valued_total_amount = original_method + + def test_08_partial_delivery_with_round_globally(self): + """ + Test partial delivery (different quantity) with global tax rounding. + This tests the code path that creates a virtual sale line with + different quantity when round_globally is active. + """ + self.sale_order.company_id.tax_calculation_rounding_method = "round_globally" + self.sale_order.action_confirm() + self.assertTrue(len(self.sale_order.picking_ids)) + picking = self.sale_order.picking_ids[0] + picking.action_assign() + # Change quantity to trigger the different_qty code path + picking.move_line_ids.quantity = 0.5 + # Force recompute + picking.invalidate_recordset(["amount_untaxed", "amount_tax", "amount_total"]) + # With 0.5 quantity: 100 * 0.5 = 50, tax = 50 * 0.15 = 7.5 + self.assertEqual(picking.amount_untaxed, 50.0) + self.assertEqual(picking.amount_tax, 7.5) + self.assertEqual(picking.amount_total, 57.5) + + def test_09_empty_picking_with_round_globally(self): + """ + Test picking without sale lines with global tax rounding. + This tests the code path when tax_lines_data is empty. + """ + self.sale_order.company_id.tax_calculation_rounding_method = "round_globally" + self.sale_order.action_confirm() + picking = self.sale_order.picking_ids[0] + picking.action_assign() + # Unreserve to clear move_line_ids with sale_line + picking.do_unreserve() + # Force recompute + picking.invalidate_recordset(["amount_untaxed", "amount_tax", "amount_total"]) + # Should be 0.0 when no lines + self.assertEqual(picking.amount_untaxed, 0.0) + self.assertEqual(picking.amount_tax, 0.0) + self.assertEqual(picking.amount_total, 0.0)