# 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.pool import Pool, PoolMeta from trytond.tools import grouped_slice from trytond.transaction import Transaction, without_check_access class Request(metaclass=PoolMeta): __name__ = 'purchase.request' sale_lines = fields.One2Many( 'sale.line', 'purchase_request', "Sale Lines", readonly=True) @classmethod def _get_origin(cls): return super()._get_origin() | {'sale.sale'} @classmethod def copy(cls, requests, default=None): default = default.copy() if default is not None else {} default.setdefault('sale_lines') return super().copy(requests, default=default) @classmethod def delete(cls, requests): pool = Pool() Sale = pool.get('sale.sale') SaleLine = pool.get('sale.line') transaction = Transaction() context = transaction.context with without_check_access(): reqs = cls.browse(requests) sales = set(r.origin for r in reqs if isinstance(r.origin, Sale)) sale_lines = [l for r in reqs for l in r.sale_lines] if sale_lines: SaleLine.write(sale_lines, { 'purchase_request': None, }) super().delete(requests) if sales: 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