44# Copyright 2018 Simone Rubino - Agile Business Group
55# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
66
7+ from contextlib import contextmanager
8+
79from odoo import _ , api , fields , models
810from odoo .exceptions import ValidationError
911
@@ -66,9 +68,8 @@ def _discount_fields(self):
6668
6769 @api .depends ("discount2" , "discount3" , "discounting_type" )
6870 def _compute_amount (self ):
69- prev_values = self .triple_discount_preprocess ()
70- res = super ()._compute_amount ()
71- self .triple_discount_postprocess (prev_values )
71+ with self ._aggregated_discount () as lines :
72+ res = super (SaleOrderLine , lines )._compute_amount ()
7273 return res
7374
7475 _sql_constraints = [
@@ -93,37 +94,40 @@ def _prepare_invoice_line(self, **kwargs):
9394 res .update ({"discount2" : self .discount2 , "discount3" : self .discount3 })
9495 return res
9596
96- def triple_discount_preprocess (self ):
97- """Prepare data for post processing.
98-
99- Save the values of the discounts in a dictionary,
100- to be restored in postprocess.
101- Resetting every discount except the main one to 0.0 avoids issues if
102- this method is called multiple times.
103- Updating the cache provides consistency through re-computations."""
104- prev_values = dict ()
105- self .invalidate_recordset (self ._discount_fields ())
106- for line in self :
107- prev_values [line ] = {
108- fname : line [fname ] for fname in self ._discount_fields ()
109- }
110-
111- vals = {fname : 0 for fname in self ._discount_fields ()}
112- vals ["discount" ] = line ._get_final_discount ()
113-
114- line ._cache .update (vals )
115- return prev_values
116-
117- @api .model
118- def triple_discount_postprocess (self , prev_values ):
119- """Restore the discounts of the lines in the dictionary prev_values.
120- Updating the cache provides consistency through re-computations."""
121- self .invalidate_recordset (self ._discount_fields ())
122- for line , prev_vals_dict in list (prev_values .items ()):
123- line .update (prev_vals_dict )
97+ @contextmanager
98+ def _aggregated_discount (self ):
99+ """A context manager to temporarily change the discount value on the
100+ records and restore it after the context is exited. It temporarily
101+ changes the discount value to the aggregated discount value so that
102+ methods that depend on the discount value will use the aggregated
103+ discount value instead of the original one.
104+ """
105+ discount_field = self ._fields ["discount" ]
106+ # Protect discount field from triggering recompute of totals. We don't want
107+ # to invalidate the cache to avoid to flush the records to the database.
108+ # This is safe because we are going to restore the original value at the end
109+ # of the method.
110+ with self .env .protecting ([discount_field ], self ):
111+ old_values = {}
112+ for line in self :
113+ old_values [line .id ] = line .discount
114+ aggregated_discount = line ._get_final_discount ()
115+ line .update ({"discount" : aggregated_discount })
116+ yield self .with_context (discount_is_aggregated = True )
117+ for line in self :
118+ if line .id not in old_values :
119+ continue
120+ line .with_context (
121+ restoring_triple_discount = True ,
122+ ).update ({"discount" : old_values [line .id ]})
124123
125124 def _convert_to_tax_base_line_dict (self ):
126125 self .ensure_one ()
126+ discount = (
127+ self .discount
128+ if self .env .context .get ("discount_is_aggregated" )
129+ else self ._get_final_discount ()
130+ )
127131 return self .env ["account.tax" ]._convert_to_tax_base_line_dict (
128132 self ,
129133 partner = self .order_id .partner_id ,
@@ -132,6 +136,6 @@ def _convert_to_tax_base_line_dict(self):
132136 taxes = self .tax_id ,
133137 price_unit = self .price_unit ,
134138 quantity = self .product_uom_qty ,
135- discount = self . _get_final_discount () ,
139+ discount = discount ,
136140 price_subtotal = self .price_subtotal ,
137141 )
0 commit comments