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 @@
+
+
+
diff --git a/view/purchase_request_form.xml b/view/purchase_request_form.xml
index 8fc7cf76a8..b2cb64d944 100644
--- a/view/purchase_request_form.xml
+++ b/view/purchase_request_form.xml
@@ -34,4 +34,9 @@ this repository contains the full copyright notices and license terms. -->
+
+
+