You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
6.3 KiB
187 lines
6.3 KiB
# -*- coding: utf-8 -*-
|
|
# © 2011 Raphaël Valyi, Renato Lima, Guewen Baconnier, Sodexis
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
|
|
|
import time
|
|
|
|
from openerp import api, models, fields, _
|
|
from openerp.exceptions import UserError
|
|
from openerp.tools.safe_eval import safe_eval
|
|
|
|
|
|
class SaleException(models.Model):
|
|
_name = 'sale.exception'
|
|
_description = "Sale Exceptions"
|
|
_order = 'active desc, sequence asc'
|
|
|
|
name = fields.Char('Exception Name', required=True, translate=True)
|
|
description = fields.Text('Description', translate=True)
|
|
sequence = fields.Integer(
|
|
string='Sequence',
|
|
help="Gives the sequence order when applying the test")
|
|
model = fields.Selection(
|
|
[('sale.order', 'Sale Order'),
|
|
('sale.order.line', 'Sale Order Line')],
|
|
string='Apply on', required=True)
|
|
active = fields.Boolean('Active')
|
|
code = fields.Text(
|
|
'Python Code',
|
|
help="Python code executed to check if the exception apply or "
|
|
"not. The code must apply block = True to apply the "
|
|
"exception.",
|
|
default="""
|
|
# Python code. Use failed = True to block the sale order.
|
|
# You can use the following variables :
|
|
# - self: ORM model of the record which is checked
|
|
# - order or line: browse_record of the sale order or sale order line
|
|
# - object: same as order or line, browse_record of the sale order or
|
|
# sale order line
|
|
# - pool: ORM model pool (i.e. self.pool)
|
|
# - time: Python time module
|
|
# - cr: database cursor
|
|
# - uid: current user id
|
|
# - context: current context
|
|
""")
|
|
sale_order_ids = fields.Many2many(
|
|
'sale.order',
|
|
'sale_order_exception_rel', 'exception_id', 'sale_order_id',
|
|
string='Sale Orders',
|
|
readonly=True)
|
|
|
|
|
|
class SaleOrder(models.Model):
|
|
_inherit = 'sale.order'
|
|
|
|
_order = 'main_exception_id asc, date_order desc, name desc'
|
|
|
|
main_exception_id = fields.Many2one(
|
|
'sale.exception',
|
|
compute='_get_main_error',
|
|
string='Main Exception',
|
|
store=True)
|
|
exception_ids = fields.Many2many(
|
|
'sale.exception',
|
|
'sale_order_exception_rel', 'sale_order_id', 'exception_id',
|
|
string='Exceptions')
|
|
ignore_exceptions = fields.Boolean('Ignore Exceptions', copy=False)
|
|
|
|
@api.one
|
|
@api.depends('state', 'exception_ids')
|
|
def _get_main_error(self):
|
|
if self.state == 'draft' and self.exception_ids:
|
|
self.main_exception_id = self.exception_ids[0]
|
|
else:
|
|
self.main_exception_id = False
|
|
|
|
@api.model
|
|
def test_all_draft_orders(self):
|
|
order_set = self.search([('state', '=', 'draft')])
|
|
order_set.test_exceptions()
|
|
return True
|
|
|
|
@api.multi
|
|
def _popup_exceptions(self):
|
|
action = self.env.ref('sale_exception.action_sale_exception_confirm')
|
|
action = action.read()[0]
|
|
action.update({
|
|
'context': {
|
|
'active_id': self.ids[0],
|
|
'active_ids': self.ids
|
|
}
|
|
})
|
|
return action
|
|
|
|
@api.multi
|
|
def action_confirm(self):
|
|
if self.detect_exceptions():
|
|
return self._popup_exceptions()
|
|
else:
|
|
return super(SaleOrder, self).action_confirm()
|
|
|
|
@api.multi
|
|
def action_cancel(self):
|
|
for order in self:
|
|
if order.ignore_exceptions:
|
|
order.ignore_exceptions = False
|
|
return super(SaleOrder, self).action_cancel()
|
|
|
|
@api.multi
|
|
def test_exceptions(self):
|
|
"""
|
|
Condition method for the workflow from draft to confirm
|
|
"""
|
|
if self.detect_exceptions():
|
|
return False
|
|
return True
|
|
|
|
@api.multi
|
|
def detect_exceptions(self):
|
|
"""returns the list of exception_ids for all the considered sale orders
|
|
|
|
as a side effect, the sale order's exception_ids column is updated with
|
|
the list of exceptions related to the SO
|
|
"""
|
|
exception_obj = self.env['sale.exception']
|
|
order_exceptions = exception_obj.search(
|
|
[('model', '=', 'sale.order')])
|
|
line_exceptions = exception_obj.search(
|
|
[('model', '=', 'sale.order.line')])
|
|
|
|
all_exception_ids = []
|
|
for order in self:
|
|
if order.ignore_exceptions:
|
|
continue
|
|
exception_ids = order._detect_exceptions(order_exceptions,
|
|
line_exceptions)
|
|
order.exception_ids = [(6, 0, exception_ids)]
|
|
all_exception_ids += exception_ids
|
|
return all_exception_ids
|
|
|
|
@api.model
|
|
def _exception_rule_eval_context(self, obj_name, rec):
|
|
user = self.env['res.users'].browse(self._uid)
|
|
return {obj_name: rec,
|
|
'self': self.pool.get(rec._name),
|
|
'object': rec,
|
|
'obj': rec,
|
|
'pool': self.pool,
|
|
'cr': self._cr,
|
|
'uid': self._uid,
|
|
'user': user,
|
|
'time': time,
|
|
# copy context to prevent side-effects of eval
|
|
'context': self._context.copy()}
|
|
|
|
@api.model
|
|
def _rule_eval(self, rule, obj_name, rec):
|
|
expr = rule.code
|
|
space = self._exception_rule_eval_context(obj_name, rec)
|
|
try:
|
|
safe_eval(expr,
|
|
space,
|
|
mode='exec',
|
|
nocopy=True) # nocopy allows to return 'result'
|
|
except Exception, e:
|
|
raise UserError(
|
|
_('Error when evaluating the sale exception '
|
|
'rule:\n %s \n(%s)') % (rule.name, e))
|
|
return space.get('failed', False)
|
|
|
|
@api.multi
|
|
def _detect_exceptions(self, order_exceptions,
|
|
line_exceptions):
|
|
self.ensure_one()
|
|
exception_ids = []
|
|
for rule in order_exceptions:
|
|
if self._rule_eval(rule, 'order', self):
|
|
exception_ids.append(rule.id)
|
|
|
|
for order_line in self.order_line:
|
|
for rule in line_exceptions:
|
|
if rule.id in exception_ids:
|
|
# we do not matter if the exception as already been
|
|
# found for an order line of this order
|
|
continue
|
|
if self._rule_eval(rule, 'line', order_line):
|
|
exception_ids.append(rule.id)
|
|
return exception_ids
|