@@ -108,13 +108,92 @@ def create(self, vals_list):
108108 return recs
109109
110110 def _get_aggregated_product_quantities (self , ** kwargs ):
111- aggregated_move_lines = super ()._get_aggregated_product_quantities (** kwargs )
112- if bool (self .env ['ir.config_parameter' ].sudo ().get_param ('stock_ux.delivery_slip_use_origin' , 'False' )) == True :
113- for line in aggregated_move_lines :
114- moves = self .filtered (
115- lambda sml : sml .product_id == aggregated_move_lines [line ]['product' ]
116- ).mapped ('move_id' ).filtered (lambda m : m .origin_description )
117- if moves :
118- aggregated_move_lines [line ]['description' ] = False
119- aggregated_move_lines [line ]['name' ] = ', ' .join (moves .mapped ('origin_description' ))
111+ """ Returns a dictionary of products (key = id+name+description+uom) and corresponding values of interest.
112+
113+ Allows aggregation of data across separate move lines for the same product. This is expected to be useful
114+ in things such as delivery reports. Dict key is made as a combination of values we expect to want to group
115+ the products by (i.e. so data is not lost). This function purposely ignores lots/SNs because these are
116+ expected to already be properly grouped by line.
117+
118+ returns: dictionary {product_id+name+description+uom: {product, name, description, qty_done, product_uom}, ...}
119+ """
120+ aggregated_move_lines = {}
121+
122+ def get_aggregated_properties (move_line = False , move = False ):
123+ move = move or move_line .move_id
124+ uom = move .product_uom or move_line .product_uom_id
125+ name = move .product_id .display_name
126+ secondary_uom_qty = move .secondary_uom_qty
127+ secondary_uom_id = move .secondary_uom_id
128+ description = move .description_picking
129+ if description == name or description == move .product_id .name :
130+ description = False
131+ product = move .product_id
132+ line_key = f'{ product .id } _{ product .display_name } _{ description or "" } _{ uom .id } _{ move_line } '
133+ return (line_key , name , description , uom , secondary_uom_qty , secondary_uom_id )
134+
135+ # Loops to get backorders, backorders' backorders, and so and so...
136+ backorders = self .env ['stock.picking' ]
137+ pickings = self .picking_id
138+ while pickings .backorder_ids :
139+ backorders |= pickings .backorder_ids
140+ pickings = pickings .backorder_ids
141+
142+ for move_line in self :
143+ if kwargs .get ('except_package' ) and move_line .result_package_id :
144+ continue
145+ line_key , name , description , uom , secondary_uom_qty , secondary_uom_id = get_aggregated_properties (move_line = move_line )
146+ qty_done = move_line .product_uom_id ._compute_quantity (move_line .qty_done , uom )
147+ if line_key not in aggregated_move_lines :
148+ qty_ordered = None
149+ if backorders and not kwargs .get ('strict' ):
150+ qty_ordered = move_line .move_id .product_uom_qty
151+ # Filters on the aggregation key (product, description and uom) to add the
152+ # quantities delayed to backorders to retrieve the original ordered qty.
153+ following_move_lines = backorders .move_line_ids .filtered (
154+ lambda ml : get_aggregated_properties (move = ml .move_id )[0 ] == line_key
155+ )
156+ qty_ordered += sum (following_move_lines .move_id .mapped ('product_uom_qty' ))
157+ # Remove the done quantities of the other move lines of the stock move
158+ previous_move_lines = move_line .move_id .move_line_ids .filtered (
159+ lambda ml : get_aggregated_properties (move = ml .move_id )[0 ] == line_key and ml .id != move_line .id
160+ )
161+ qty_ordered -= sum (map (lambda m : m .product_uom_id ._compute_quantity (m .qty_done , uom ), previous_move_lines ))
162+ aggregated_move_lines [line_key ] = {'name' : name ,
163+ 'description' : description ,
164+ 'qty_done' : qty_done ,
165+ 'qty_ordered' : qty_ordered or qty_done ,
166+ 'product_uom' : uom ,
167+ 'product' : move_line .product_id ,
168+ 'secondary_uom_qty' : secondary_uom_qty ,
169+ 'secondary_uom_id' : secondary_uom_id }
170+ else :
171+ aggregated_move_lines [line_key ]['qty_ordered' ] += qty_done
172+ aggregated_move_lines [line_key ]['qty_done' ] += qty_done
173+
174+ # Does the same for empty move line to retrieve the ordered qty. for partially done moves
175+ # (as they are splitted when the transfer is done and empty moves don't have move lines).
176+ if kwargs .get ('strict' ):
177+ return aggregated_move_lines
178+ pickings = (self .picking_id | backorders )
179+ for empty_move in pickings .move_ids :
180+ if not (empty_move .state == "cancel" and empty_move .product_uom_qty
181+ and float_is_zero (empty_move .quantity_done , precision_rounding = empty_move .product_uom .rounding )):
182+ continue
183+ line_key , name , description , uom , secondary_uom_qty , secondary_uom_id = get_aggregated_properties (move = empty_move )
184+
185+ if line_key not in aggregated_move_lines :
186+ qty_ordered = empty_move .product_uom_qty
187+ aggregated_move_lines [line_key ] = {
188+ 'name' : name ,
189+ 'description' : description ,
190+ 'qty_done' : False ,
191+ 'qty_ordered' : qty_ordered ,
192+ 'product_uom' : uom ,
193+ 'product' : empty_move .product_id ,
194+ 'secondary_uom_qty' : secondary_uom_qty ,
195+ 'secondary_uom_id' : secondary_uom_id ,
196+ }
197+ else :
198+ aggregated_move_lines [line_key ]['qty_ordered' ] += empty_move .product_uom_qty
120199 return aggregated_move_lines
0 commit comments