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.
This commit is contained in:
Cédric Krier committed 2023-12-21 12:33:00 +01:00
1 parent 956040bca9
commit d3264484c4
4 files changed
+66 -111

No files matched your search

+9 -5
View File
@@ -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):
-4
View File
@@ -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'])
+16 -81
View File
@@ -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
+41 -21
View File
@@ -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):