From 95ec33db65f4e6dba31d49ed45b899dba2d3c58a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=C3=89vrard?= Date: Tue, 17 Nov 2015 17:30:42 -0300 Subject: [PATCH] Adding exception state on the purchase requests issue5107 review16811002 --- CHANGELOG | 2 + __init__.py | 4 ++ purchase.py | 38 ++++++++++ purchase_request.py | 72 ++++++++++++++++++- purchase_request.xml | 26 +++++++ ...scenario_stock_supply_purchase_request.rst | 49 ++++++++++++- ...andle_purchase_cancellation_start_form.xml | 8 +++ view/purchase_request_form.xml | 5 ++ 8 files changed, 200 insertions(+), 4 deletions(-) create mode 100644 purchase.py create mode 100644 view/handle_purchase_cancellation_start_form.xml diff --git a/CHANGELOG b/CHANGELOG index e1a199d208..74432aa278 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +* Adding exception state on the purchase requests + Version 3.8.0 - 2015-11-02 * Bug fixes (see mercurial logs for details) * Generate purchase requests even if rounding set the quantity to 0 diff --git a/__init__.py b/__init__.py index 6e25edfb69..1380578146 100644 --- a/__init__.py +++ b/__init__.py @@ -7,6 +7,7 @@ from .product import * from .purchase_request import * from .shipment import * from .location import * +from .purchase import * def register(): @@ -16,12 +17,15 @@ def register(): PurchaseRequest, CreatePurchaseRequestStart, CreatePurchaseAskParty, + HandlePurchaseCancellationExceptionStart, ShipmentInternal, CreateShipmentInternalStart, Location, + Purchase, module='stock_supply', type_='model') Pool.register( CreatePurchaseRequest, CreatePurchase, CreateShipmentInternal, + HandlePurchaseCancellationException, module='stock_supply', type_='wizard') diff --git a/purchase.py b/purchase.py new file mode 100644 index 0000000000..b99ff58c38 --- /dev/null +++ b/purchase.py @@ -0,0 +1,38 @@ +# 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.tools import grouped_slice +from trytond.pool import Pool, PoolMeta + + +class Purchase: + __name__ = 'purchase.purchase' + __metaclass__ = PoolMeta + + @classmethod + def __setup__(cls): + super(Purchase, cls).__setup__() + cls._error_messages.update({ + 'delete_purchase_request': ('You can not delete the purchase' + ' "%(purchase)s" because it is linked to at least one' + ' purchase request."'), + }) + + @classmethod + def delete(cls, purchases): + pool = Pool() + PurchaseRequest = pool.get('purchase.request') + + purchase_lines = [pl.id + for purchase in purchases for pl in purchase.lines] + for sub_lines in grouped_slice(purchase_lines): + requests = PurchaseRequest.search([ + ('purchase_line', 'in', list(sub_lines)), + ]) + if requests: + purchase = requests[0].purchase + cls.raise_user_error('delete_purchase_request', { + 'purchase': purchase.rec_name, + }) + + super(Purchase, cls).delete(purchases) diff --git a/purchase_request.py b/purchase_request.py index 81b4bc892a..e6566d40d6 100644 --- a/purchase_request.py +++ b/purchase_request.py @@ -19,7 +19,10 @@ from trytond.tools import grouped_slice __all__ = ['PurchaseRequest', 'CreatePurchaseRequestStart', 'CreatePurchaseRequest', - 'CreatePurchaseAskParty', 'CreatePurchase'] + 'CreatePurchaseAskParty', 'CreatePurchase', + 'HandlePurchaseCancellationException', + 'HandlePurchaseCancellationExceptionStart', + ] STATES = { 'readonly': Eval('state') != 'draft', @@ -70,11 +73,13 @@ class PurchaseRequest(ModelSQL, ModelView): ]) origin = fields.Reference('Origin', selection='get_origin', readonly=True, required=True) + exception_ignored = fields.Boolean('Ignored Exception') state = fields.Function(fields.Selection([ ('purchased', 'Purchased'), ('done', 'Done'), ('draft', 'Draft'), ('cancel', 'Cancel'), + ('exception', 'Exception'), ], 'State'), 'get_state', searcher='search_state') @classmethod @@ -87,6 +92,11 @@ class PurchaseRequest(ModelSQL, ModelView): 'delete_purchase_line': ('You can not delete purchased ' 'request.'), }) + cls._buttons.update({ + 'handle_purchase_cancellation_exception': { + 'invisible': Eval('state') != 'exception', + }, + }) @classmethod def __register__(cls, module_name): @@ -124,6 +134,10 @@ class PurchaseRequest(ModelSQL, ModelView): def default_company(): return Transaction().context.get('company') + @staticmethod + def default_exception_ignored(): + return False + def get_purchase(self, name): if self.purchase_line: return self.purchase_line.purchase.id @@ -134,7 +148,10 @@ class PurchaseRequest(ModelSQL, ModelView): def get_state(self, name): if self.purchase_line: - if self.purchase_line.purchase.state == 'cancel': + if (self.purchase_line.purchase.state == 'cancel' + and not self.exception_ignored): + return 'exception' + elif self.purchase_line.purchase.state == 'cancel': return 'cancel' elif self.purchase_line.purchase.state == 'done': return 'done' @@ -155,7 +172,10 @@ class PurchaseRequest(ModelSQL, ModelView): _, operator_, state = clause Operator = fields.SQL_OPERATORS[operator_] state_case = Case( - (purchase.state == 'cancel', 'cancel'), + ((purchase.state == 'cancel') + & (request.exception_ignored == False), 'exception'), + ((purchase.state == 'cancel') + & (request.exception_ignored == True), 'cancel'), (purchase.state == 'done', 'done'), (request.purchase_line != Null, 'purchased'), else_='draft') @@ -525,6 +545,12 @@ class PurchaseRequest(ModelSQL, ModelView): cls.raise_user_error('delete_purchase_line') super(PurchaseRequest, cls).delete(requests) + @classmethod + @ModelView.button_action( + 'stock_supply.wizard_purchase_cancellation_handle_exception') + def handle_purchase_cancellation_exception(cls, purchases): + pass + class CreatePurchaseRequestStart(ModelView): 'Create Purchase Request' @@ -738,3 +764,43 @@ class CreatePurchase(Wizard): taxes.append(tax.id) line.taxes = taxes return line + + +class HandlePurchaseCancellationException(Wizard): + 'Handle Purchase Cancellation Exception' + __name__ = 'purchase.request.handle.purchase.cancellation' + + start = StateView('purchase.request.handle.purchase.cancellation.start', + 'stock_supply.handle_purchase_cancellation_start', [ + Button('Cancel', 'end', 'tryton-cancel'), + Button('Reset to draft', 'reset', 'tryton-clear'), + Button('Cancel Request', 'cancel_request', 'tryton-delete', + default=True), + ]) + reset = StateTransition() + cancel_request = StateTransition() + + def transition_reset(self): + pool = Pool() + PurchaseRequest = pool.get('purchase.request') + + requests = PurchaseRequest.browse(Transaction().context['active_ids']) + PurchaseRequest.write(requests, { + 'purchase_line': None, + }) + return 'end' + + def transition_cancel_request(self): + pool = Pool() + PurchaseRequest = pool.get('purchase.request') + + requests = PurchaseRequest.browse(Transaction().context['active_ids']) + PurchaseRequest.write(requests, { + 'exception_ignored': True, + }) + return 'end' + + +class HandlePurchaseCancellationExceptionStart(ModelView): + 'Handle Purchase Cancellation Exception - Start' + __name__ = 'purchase.request.handle.purchase.cancellation.start' diff --git a/purchase_request.xml b/purchase_request.xml index 1d96e6ca27..9f5142a3cb 100644 --- a/purchase_request.xml +++ b/purchase_request.xml @@ -40,6 +40,14 @@ this repository contains the full copyright notices and license terms. --> + + Exception + + + + All @@ -98,6 +106,24 @@ this repository contains the full copyright notices and license terms. --> + + purchase.request.handle.purchase.cancellation.start + form + handle_purchase_cancellation_start_form + + + Handle Purchase Cancellation + purchase.request.handle.purchase.cancellation + purchase.request + + + + form_action + purchase.request,-1 + + diff --git a/tests/scenario_stock_supply_purchase_request.rst b/tests/scenario_stock_supply_purchase_request.rst index 3bf06bae6b..fe19854d08 100644 --- a/tests/scenario_stock_supply_purchase_request.rst +++ b/tests/scenario_stock_supply_purchase_request.rst @@ -43,11 +43,13 @@ Create chart of accounts:: >>> accounts = get_accounts(company) >>> expense = accounts['expense'] -Create a customer:: +Create parties:: >>> Party = Model.get('party.party') >>> customer = Party(name='Customer') >>> customer.save() + >>> supplier = Party(name='Supplier') + >>> supplier.save() Create stock admin user:: @@ -163,3 +165,48 @@ There is now a draft purchase request:: True >>> pr.quantity 1.0 + +Create the purchase then cancel it:: + + >>> create_purchase = Wizard('purchase.request.create_purchase', + ... [pr]) + >>> create_purchase.form.party = supplier + >>> create_purchase.execute('start') + >>> pr.state + 'purchased' + + >>> Purchase = Model.get('purchase.purchase') + >>> purchase, = Purchase.find() + >>> purchase.click('cancel') + >>> pr.reload() + >>> pr.state + 'exception' + +Handle the exception:: + + >>> handle_exception = Wizard( + ... 'purchase.request.handle.purchase.cancellation', [pr]) + >>> handle_exception.execute('reset') + >>> pr.state + 'draft' + +Recreate a purchase and cancel it again:: + + >>> create_purchase = Wizard('purchase.request.create_purchase', + ... [pr]) + >>> pr.state + 'purchased' + + >>> purchase, = Purchase.find([('state', '=', 'draft')]) + >>> purchase.click('cancel') + >>> pr.reload() + >>> pr.state + 'exception' + +Handle again the exception:: + + >>> handle_exception = Wizard( + ... 'purchase.request.handle.purchase.cancellation', [pr]) + >>> handle_exception.execute('cancel_request') + >>> pr.state + 'cancel' diff --git a/view/handle_purchase_cancellation_start_form.xml b/view/handle_purchase_cancellation_start_form.xml new file mode 100644 index 0000000000..ba285cba30 --- /dev/null +++ b/view/handle_purchase_cancellation_start_form.xml @@ -0,0 +1,8 @@ + + +
+ +