diff --git a/sale.py b/sale.py index 0fd1666d9b..127dda810e 100644 --- a/sale.py +++ b/sale.py @@ -1,8 +1,9 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -from trytond.model import fields +from trytond.model import Model, fields from trytond.pool import Pool, PoolMeta from trytond.pyson import Eval +from trytond.transaction import Transaction class Sale(metaclass=PoolMeta): @@ -169,7 +170,7 @@ class Line(metaclass=PoolMeta): origin=self.sale, ) - def assign_supplied(self, location_quantities): + def assign_supplied(self, quantities, grouping=('product',)): ''' Assign supplied move @@ -182,20 +183,50 @@ class Line(metaclass=PoolMeta): if self.supply_state != 'supplied': return + + def get_key(move, location_id): + key = (location_id,) + for field in grouping: + value = getattr(move, field) + if isinstance(value, Model): + value = value.id + key += (value,) + return key + + def get_values(key, location_name): + yield location_name, key[0] + for field, value in zip(grouping, key[1:]): + if value is not None and '.' not in field: + yield field, value + + def match(key, pattern): + for k, p in zip(key, pattern): + if p is None or k == p: + continue + else: + return False + else: + return True + moves = set() for move in self.moves: for inv_move in move.shipment.inventory_moves: if inv_move.product.id == self.product.id: moves.add(inv_move) + to_write = [] + to_assign = [] for move in moves: if move.state != 'draft': continue - location_qties_converted = {} - for location_id, quantity in location_quantities.items(): - location_qties_converted[location_id] = ( - Uom.compute_qty(move.product.default_uom, - quantity, move.uom, round=False)) - to_pick = move.pick_product(location_qties_converted) + qties_converted = [] + for key, quantity in quantities.items(): + move_key = get_key(move, key[0]) + if match(key, move_key): + qty = Uom.compute_qty( + move.product.default_uom, quantity, move.uom, + round=False) + qties_converted.append((key, qty)) + to_pick = move.pick_product(qties_converted) picked_qties = sum(qty for _, qty in to_pick) if picked_qties < move.quantity: @@ -205,18 +236,22 @@ class Line(metaclass=PoolMeta): }) else: first = True - for from_location, qty in to_pick: - values = { - 'from_location': from_location.id, - 'quantity': qty, - } + for key, qty in to_pick: + values = dict(get_values(key, 'from_location')) + values['quantity'] = move.uom.round(qty) if first: - Move.write([move], values) - Move.assign([move]) + to_write.extend([[move], values]) + to_assign.append(move) + first = False else: - Move.assign(Move.copy([move], default=values)) + with Transaction().set_context(_stock_move_split=True): + to_assign.extend(Move.copy([move], default=values)) qty_default_uom = Uom.compute_qty(move.uom, qty, move.product.default_uom, round=False) - location_quantities[from_location] -= qty_default_uom + quantities[key] -= qty_default_uom + if to_write: + Move.write(*to_write) + if to_assign: + Move.assign(to_assign) diff --git a/stock.py b/stock.py index c7644e91a1..4d2494489b 100644 --- a/stock.py +++ b/stock.py @@ -2,7 +2,7 @@ # this repository contains the full copyright notices and license terms. from collections import defaultdict -from trytond.model import ModelView, Workflow +from trytond.model import Model, ModelView, Workflow from trytond.pool import Pool, PoolMeta @@ -13,20 +13,42 @@ class ShipmentIn(metaclass=PoolMeta): @ModelView.button @Workflow.transition('done') def done(cls, shipments): - SaleLine = Pool().get('sale.line') - super(ShipmentIn, cls).done(shipments) # Assigned sale move lines for shipment in shipments: - move_ids = [x.id for x in shipment.incoming_moves] - sale_lines = SaleLine.search([ - ('purchase_request.purchase_line.moves', - 'in', move_ids), - ('purchase_request.origin', 'like', 'sale.sale,%'), - ]) - pbl = defaultdict(lambda: defaultdict(int)) - for move in shipment.inventory_moves: - pbl[move.product][move.to_location] += move.internal_quantity - for sale_line in sale_lines: - sale_line.assign_supplied(pbl[sale_line.product]) + shipment.assign_supplied() + + def assign_supplied(self, grouping=('product',), filter_=None): + pool = Pool() + SaleLine = pool.get('sale.line') + + def filter_func(move): + if filter_ is None: + return True + for fieldname, values in filter_: + value = getattr(move, fieldname) + if isinstance(value, Model): + value = value.id + if value not in values: + return False + + def get_key(move): + key = (move.to_location.id,) + for field in grouping: + value = getattr(move, field) + if isinstance(value, Model): + value = value.id + key += (value,) + return key + move_ids = [m.id for m in filter(filter_func, self.incoming_moves)] + sale_lines = SaleLine.search([ + ('purchase_request.purchase_line.moves', 'in', move_ids), + ('purchase_request.origin', 'like', 'sale.sale,%'), + ]) + pbl = defaultdict(lambda: defaultdict(int)) + for move in filter(filter_func, self.inventory_moves): + pbl[move.product][get_key(move)] += move.internal_quantity + for sale_line in sale_lines: + sale_line.assign_supplied( + pbl[sale_line.product], grouping=grouping)