From d3264484c4c0a7cd3fb4c90d6e191a4cc30d12c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 21 Dec 2023 12:33:00 +0100 Subject: [PATCH] Update sale based on purchase request update state It is simpler if the purchase triggers the update of the request and the update of the request triggers the process of the sale. --- modules/purchase_request/purchase.py | 14 ++-- modules/sale_supply/__init__.py | 4 -- modules/sale_supply/purchase.py | 97 +++++----------------------- modules/sale_supply/sale.py | 62 ++++++++++++------ 4 files changed, 66 insertions(+), 111 deletions(-) diff --git a/modules/purchase_request/purchase.py b/modules/purchase_request/purchase.py index 96725ea0fb..d5cf3d2573 100644 --- a/modules/purchase_request/purchase.py +++ b/modules/purchase_request/purchase.py @@ -20,8 +20,12 @@ def process_request(func): pool = Pool() Request = pool.get('purchase.request') func(cls, purchases) - requests = [r for p in purchases for l in p.lines for r in l.requests] - Request.update_state(requests) + with without_check_access(): + requests = [ + r for p in cls.browse(purchases) + for l in p.lines + for r in l.requests] + Request.update_state(requests) return wrapper @@ -77,10 +81,10 @@ class Purchase(metaclass=PoolMeta): super(Purchase, cls).cancel(purchases) @classmethod + @ModelView.button @process_request - @Workflow.transition('done') - def do(cls, purchases): - super(Purchase, cls).do(purchases) + def process(cls, purchases): + super().process(purchases) class Line(metaclass=PoolMeta): diff --git a/modules/sale_supply/__init__.py b/modules/sale_supply/__init__.py index bdb0dfb946..3761891064 100644 --- a/modules/sale_supply/__init__.py +++ b/modules/sale_supply/__init__.py @@ -11,14 +11,10 @@ def register(): sale.Sale, sale.Line, purchase.Request, - purchase.Purchase, stock.ShipmentIn, product.Template, product.Product, module='sale_supply', type_='model') - Pool.register( - purchase.HandlePurchaseCancellationException, - module='sale_supply', type_='wizard') Pool.register( stock.OrderPoint, module='sale_supply', type_='model', depends=['stock_supply']) diff --git a/modules/sale_supply/purchase.py b/modules/sale_supply/purchase.py index 4ed25eb64f..9984f39cb7 100644 --- a/modules/sale_supply/purchase.py +++ b/modules/sale_supply/purchase.py @@ -1,10 +1,8 @@ # 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 functools import wraps -from trytond.model import ModelView, Workflow, fields +from trytond.model import fields from trytond.pool import Pool, PoolMeta -from trytond.tools import grouped_slice from trytond.transaction import Transaction, without_check_access @@ -18,6 +16,21 @@ class Request(metaclass=PoolMeta): def _get_origin(cls): return super()._get_origin() | {'sale.sale'} + @classmethod + def update_state(cls, requests): + pool = Pool() + Sale = pool.get('sale.sale') + transaction = Transaction() + context = transaction.context + + super().update_state(requests) + + sales = {r.origin for r in requests if isinstance(r.origin, Sale)} + if sales: + with transaction.set_context( + queue_batch=context.get('queue_batch', True)): + Sale.__queue__.process(Sale.browse(sales)) + @classmethod def delete(cls, requests): pool = Pool() @@ -41,81 +54,3 @@ class Request(metaclass=PoolMeta): with transaction.set_context( queue_batch=context.get('queue_batch', True)): Sale.__queue__.process(sales) - - -def process_sale_supply(func): - @wraps(func) - def wrapper(cls, purchases): - pool = Pool() - Request = pool.get('purchase.request') - Sale = pool.get('sale.sale') - transaction = Transaction() - context = transaction.context - - sales = set() - with without_check_access(): - for sub_purchases in grouped_slice(purchases): - ids = [x.id for x in sub_purchases] - requests = Request.search([ - ('purchase_line.purchase.id', 'in', ids), - ('origin', 'like', 'sale.sale,%'), - ]) - sales.update(r.origin.id for r in requests) - func(cls, purchases) - if sales: - with transaction.set_context( - queue_batch=context.get('queue_batch', True)): - Sale.__queue__.process(sales) - return wrapper - - -class Purchase(metaclass=PoolMeta): - __name__ = 'purchase.purchase' - - @classmethod - @ModelView.button - @process_sale_supply - def process(cls, purchases): - super(Purchase, cls).process(purchases) - - @classmethod - @ModelView.button - @Workflow.transition('cancelled') - @process_sale_supply - def cancel(cls, purchases): - super(Purchase, cls).cancel(purchases) - - -class HandlePurchaseCancellationException(metaclass=PoolMeta): - __name__ = 'purchase.request.handle.purchase.cancellation' - - def transition_cancel_request(self): - pool = Pool() - SaleLine = pool.get('sale.line') - Move = pool.get('stock.move') - Shipment = pool.get('stock.shipment.out') - - next_state = super(HandlePurchaseCancellationException, - self).transition_cancel_request() - moves = [] - shipments = set() - for sub_ids in grouped_slice(list(map(int, self.records))): - sale_lines = SaleLine.search([ - ('purchase_request', 'in', sub_ids), - ]) - for sale_line in sale_lines: - moves.extend(sale_line.moves) - for shipment in sale_line.sale.shipments: - if shipment.state in ['draft', 'waiting']: - shipments.add(shipment) - if moves: - shipments_waiting = [s for s in shipments if s.state == 'waiting'] - Shipment.draft(shipments) # clear inventory moves - Move.cancel(moves) - Shipment.wait([ - s for s in shipments_waiting - if any(m.state != 'cancelled' for m in s.outgoing_moves)]) - Shipment.cancel([ - s for s in shipments_waiting - if all(m.state == 'cancelled' for m in s.outgoing_moves)]) - return next_state diff --git a/modules/sale_supply/sale.py b/modules/sale_supply/sale.py index 38df1b68c5..81ab2bba00 100644 --- a/modules/sale_supply/sale.py +++ b/modules/sale_supply/sale.py @@ -51,25 +51,18 @@ class Sale(metaclass=PoolMeta): def _process_supply(cls, sales, product_quantities): pool = Pool() Line = pool.get('sale.line') - Move = pool.get('stock.move') PurchaseRequest = pool.get('purchase.request') - ShipmentOut = pool.get('stock.shipment.out') requests, lines = [], [] - moves_to_draft, shipments_to_wait = [], set() for sale in sales: reqs, lns = sale.create_purchase_requests(product_quantities) requests.extend(reqs) lines.extend(lns) - moves, shipments = sale.create_move_from_supply() - moves_to_draft.extend(moves) - shipments_to_wait.update(shipments) - shipments_to_wait = ShipmentOut.browse(list(shipments_to_wait)) PurchaseRequest.save(requests) Line.save(lines) - Move.draft(moves_to_draft) - ShipmentOut.wait(shipments_to_wait) + + cls._update_moves_from_supply(sales) def create_purchase_requests(self, product_quantities): requests, lines = [], [] @@ -83,19 +76,46 @@ class Sale(metaclass=PoolMeta): lines.append(line) return requests, lines - def create_move_from_supply(self): - 'Set to draft move linked to supply' + @classmethod + def _update_moves_from_supply(cls, sales): pool = Pool() - ShipmentOut = pool.get('stock.shipment.out') - moves = [] - for line in self.lines: - if line.supply_state in {'supplied', 'cancelled'}: - for move in line.moves: - if move.state == 'staging': - moves.append(move) - shipments = {m.shipment for m in moves - if isinstance(m.shipment, ShipmentOut)} - return moves, shipments + Shipment = pool.get('stock.shipment.out') + Move = pool.get('stock.move') + + shipments = set() + + def add_shipment(shipment): + if (isinstance(shipment, Shipment) + and shipment.state in {'draft', 'waiting'}): + shipments.add(shipment) + + moves_to_draft, moves_to_cancel = [], [] + for sale in sales: + for line in sale.lines: + if line.supply_state == 'supplied': + for move in line.moves: + if move.state == 'staging': + moves_to_draft.append(move) + add_shipment(move.shipment) + elif line.supply_state == 'cancelled': + for move in line.moves: + moves_to_cancel.append(move) + add_shipment(move.shipment) + if moves_to_draft: + Move.draft(moves_to_draft) + shipments = Shipment.browse(shipments) + shipments_waiting = [s for s in shipments if s.state == 'waiting'] + if shipments: + Shipment.draft(shipments) # clear inventory moves + if moves_to_cancel: + Move.cancel(moves_to_cancel) + if shipments: + Shipment.wait([ + s for s in shipments_waiting + if any(m.state != 'cancelled' for m in s.moves)]) + Shipment.cancel([ + s for s in shipments + if all(m.state == 'cancelled' for m in s.moves)]) class Line(metaclass=PoolMeta):