Mourad Elhadj Mimoune
8 years ago
15 changed files with 332 additions and 480 deletions
-
14base_exception/__manifest__.py
-
39base_exception/data/sale_exception_data.xml
-
2base_exception/models/__init__.py
-
215base_exception/models/base_exception.py
-
201base_exception/models/sale.py
-
9base_exception/security/base_exception_security.xml
-
6base_exception/security/ir.model.access.csv
-
5base_exception/tests/__init__.py
-
62base_exception/tests/test_sale_exception.py
-
51base_exception/views/base_exception_view.xml
-
116base_exception/views/sale_view.xml
-
2base_exception/wizard/__init__.py
-
35base_exception/wizard/base_exception_confirm.py
-
20base_exception/wizard/base_exception_confirm_view.xml
-
35base_exception/wizard/sale_exception_confirm.py
@ -1,39 +0,0 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||
<openerp> |
|
||||
<data noupdate="1"> |
|
||||
|
|
||||
<record forcecreate="True" id="ir_cron_test_orders" model="ir.cron"> |
|
||||
<field name="name">Test Draft Orders</field> |
|
||||
<field eval="False" name="active"/> |
|
||||
<field name="user_id" ref="base.user_root"/> |
|
||||
<field name="interval_number">20</field> |
|
||||
<field name="interval_type">minutes</field> |
|
||||
<field name="numbercall">-1</field> |
|
||||
<field eval="False" name="doall"/> |
|
||||
<field eval="'sale.order'" name="model"/> |
|
||||
<field eval="'test_all_draft_orders'" name="function"/> |
|
||||
<field eval="'()'" name="args"/> |
|
||||
</record> |
|
||||
|
|
||||
<record id ="excep_no_zip" model="sale.exception"> |
|
||||
<field name="name">No ZIP code on destination</field> |
|
||||
<field name="description">No ZIP code on destination</field> |
|
||||
<field name="sequence">50</field> |
|
||||
<field name="model">sale.order</field> |
|
||||
<field name="code">if not order.partner_shipping_id.zip: |
|
||||
failed=True</field> |
|
||||
<field name="active" eval="False"/> |
|
||||
</record> |
|
||||
|
|
||||
<record id ="excep_no_stock" model="sale.exception"> |
|
||||
<field name="name">Not Enough Virtual Stock</field> |
|
||||
<field name="description">Not Enough Virtual Stock</field> |
|
||||
<field name="sequence">50</field> |
|
||||
<field name="model">sale.order.line</field> |
|
||||
<field name="code">if line.product_id and line.product_id.type == 'product' and line.product_id.virtual_available < line.product_uom_qty: |
|
||||
failed=True</field> |
|
||||
<field name="active" eval="False"/> |
|
||||
</record> |
|
||||
|
|
||||
</data> |
|
||||
</openerp> |
|
@ -0,0 +1,215 @@ |
|||||
|
# -*- 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 functools import wraps |
||||
|
|
||||
|
from odoo import api, models, fields, _ |
||||
|
from odoo.exceptions import UserError, ValidationError |
||||
|
from odoo.tools.safe_eval import safe_eval |
||||
|
|
||||
|
|
||||
|
def implemented_by_base_exception(func): |
||||
|
"""Call a prefixed function based on 'namespace'.""" |
||||
|
@wraps(func) |
||||
|
def wrapper(cls, *args, **kwargs): |
||||
|
fun_name = func.__name__ |
||||
|
fun = '_%s%s' % (cls.rule_group, fun_name) |
||||
|
if not hasattr(cls, fun): |
||||
|
fun = '_default%s' % (fun_name) |
||||
|
return getattr(cls, fun)(*args, **kwargs) |
||||
|
return wrapper |
||||
|
|
||||
|
|
||||
|
class ExceptionRule(models.Model): |
||||
|
_name = 'exception.rule' |
||||
|
_description = "Exception Rules" |
||||
|
_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") |
||||
|
rule_group = fields.Selection( |
||||
|
[], |
||||
|
help="Rule group is used the group the rules that must validated " |
||||
|
"at same time for a target object. Ex: " |
||||
|
"validate sale.order.line rules with sale order rules.", |
||||
|
required=True) |
||||
|
model = fields.Selection( |
||||
|
[], |
||||
|
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 base.exception. |
||||
|
# You can use the following variables : |
||||
|
# - self: ORM model of the record which is checked |
||||
|
# - "rule_group" or "rule_group_"line: |
||||
|
# browse_record of the base.exception or |
||||
|
# base.exception line (ex rule_group = sale for sale order) |
||||
|
# - object: same as order or line, browse_record of the base.exception or |
||||
|
# base.exception line |
||||
|
# - pool: ORM model pool (i.e. self.pool) |
||||
|
# - time: Python time module |
||||
|
# - cr: database cursor |
||||
|
# - uid: current user id |
||||
|
# - context: current context |
||||
|
""") |
||||
|
|
||||
|
|
||||
|
class BaseException(models.AbstractModel): |
||||
|
_name = 'base.exception' |
||||
|
|
||||
|
_order = 'main_exception_id asc' |
||||
|
|
||||
|
main_exception_id = fields.Many2one( |
||||
|
'exception.rule', |
||||
|
compute='_compute_main_error', |
||||
|
string='Main Exception', |
||||
|
store=True) |
||||
|
rule_group = fields.Selection( |
||||
|
[], |
||||
|
readonly=True, |
||||
|
) |
||||
|
exception_ids = fields.Many2many( |
||||
|
'exception.rule', |
||||
|
string='Exceptions') |
||||
|
ignore_exception = fields.Boolean('Ignore Exceptions', copy=False) |
||||
|
|
||||
|
@api.depends('exception_ids', 'ignore_exception') |
||||
|
def _compute_main_error(self): |
||||
|
for obj in self: |
||||
|
if not obj.ignore_exception and obj.exception_ids: |
||||
|
obj.main_exception_id = obj.exception_ids[0] |
||||
|
else: |
||||
|
obj.main_exception_id = False |
||||
|
|
||||
|
@api.multi |
||||
|
def _popup_exceptions(self): |
||||
|
action = self._get_popup_action() |
||||
|
action = action.read()[0] |
||||
|
action.update({ |
||||
|
'context': { |
||||
|
'active_id': self.ids[0], |
||||
|
'active_ids': self.ids |
||||
|
} |
||||
|
}) |
||||
|
return action |
||||
|
|
||||
|
@api.model |
||||
|
def _get_popup_action(self): |
||||
|
action = self.env.ref('base_exception.action_exception_rule_confirm') |
||||
|
return action |
||||
|
|
||||
|
@api.model |
||||
|
def _check_exception(self): |
||||
|
""" |
||||
|
This method must be used in a constraint that must be created in the |
||||
|
object that inherits for base.exception. |
||||
|
for sale : |
||||
|
@api.constrains('ignore_exception',) |
||||
|
def sale_check_exception(self): |
||||
|
... |
||||
|
... |
||||
|
self._check_exception |
||||
|
""" |
||||
|
exception_ids = self.detect_exceptions() |
||||
|
if exception_ids: |
||||
|
exceptions = self.env['exception.rule'].browse(exception_ids) |
||||
|
raise ValidationError('\n'.join(exceptions.mapped('name'))) |
||||
|
|
||||
|
@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 base.exceptions |
||||
|
""" |
||||
|
exception_obj = self.env['exception.rule'] |
||||
|
model_exceptions = exception_obj.sudo().search( |
||||
|
[('model', '=', self._name)]) |
||||
|
sub_exceptions = exception_obj.sudo().search( |
||||
|
[('rule_group', '=', self.rule_group), |
||||
|
('id', 'not in', model_exceptions.ids), |
||||
|
]) |
||||
|
|
||||
|
all_exception_ids = [] |
||||
|
for obj in self: |
||||
|
if obj.ignore_exception: |
||||
|
continue |
||||
|
exception_ids = obj._detect_exceptions( |
||||
|
model_exceptions, sub_exceptions) |
||||
|
obj.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 exception.rule ' |
||||
|
'rule:\n %s \n(%s)') % (rule.name, e)) |
||||
|
return space.get('failed', False) |
||||
|
|
||||
|
@api.multi |
||||
|
def _detect_exceptions(self, model_exceptions, |
||||
|
sub_exceptions): |
||||
|
self.ensure_one() |
||||
|
exception_ids = [] |
||||
|
for rule in model_exceptions: |
||||
|
if self._rule_eval(rule, self.rule_group, self): |
||||
|
exception_ids.append(rule.id) |
||||
|
if sub_exceptions: |
||||
|
for obj_line in self._get_lines(): |
||||
|
for rule in sub_exceptions: |
||||
|
if rule.id in exception_ids: |
||||
|
# we do not matter if the exception as already been |
||||
|
# found for an line of this object |
||||
|
# (ex sale order line if obj is sale order) |
||||
|
continue |
||||
|
group_line = self.rule_group + '_line' |
||||
|
if self._rule_eval(rule, group_line, obj_line): |
||||
|
exception_ids.append(rule.id) |
||||
|
return exception_ids |
||||
|
|
||||
|
@implemented_by_base_exception |
||||
|
def _get_lines(self): |
||||
|
pass |
||||
|
|
||||
|
def _default_get_lines(self): |
||||
|
return [] |
@ -1,201 +0,0 @@ |
|||||
# -*- 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, ValidationError |
|
||||
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_exception = fields.Boolean('Ignore Exceptions', copy=False) |
|
||||
|
|
||||
@api.one |
|
||||
@api.depends('exception_ids', 'ignore_exception') |
|
||||
def _get_main_error(self): |
|
||||
if not self.ignore_exception 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.one |
|
||||
@api.constrains('ignore_exception', 'order_line', 'state') |
|
||||
def check_sale_exception_constrains(self): |
|
||||
if self.state == 'sale': |
|
||||
exception_ids = self.detect_exceptions() |
|
||||
if exception_ids: |
|
||||
exceptions = self.env['sale.exception'].browse(exception_ids) |
|
||||
raise ValidationError('\n'.join(exceptions.mapped('name'))) |
|
||||
|
|
||||
@api.onchange('order_line') |
|
||||
def onchange_ignore_exception(self): |
|
||||
if self.state == 'sale': |
|
||||
self.ignore_exception = False |
|
||||
|
|
||||
@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_exception: |
|
||||
order.ignore_exception = 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_exception: |
|
||||
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 |
|
@ -0,0 +1,9 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
|
||||
|
<record id="group_exception_rule_manager" model="res.groups"> |
||||
|
<field name="name">Exception manager</field> |
||||
|
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/> |
||||
|
</record> |
||||
|
|
||||
|
</odoo> |
@ -1,3 +1,5 @@ |
|||||
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" |
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" |
||||
"access_sale_exception","sale.exception","model_sale_exception","base.group_user",1,0,0,0 |
|
||||
"access_sale_exception_manager","sale.exception","model_sale_exception","base.group_sale_manager",1,1,1,1 |
|
||||
|
"access_exception_rule","base.exception","model_exception_rule","base.group_user",1,0,0,0 |
||||
|
"access_exception_rule_manager","base.exception","model_exception_rule","base_exception.group_exception_rule_manager",1,1,1,1 |
||||
|
"access_base_exception","base.exception","model_base_exception","base.group_user",1,0,0,0 |
||||
|
"access_base_exception_manager","base.exception","model_base_exception","base_exception.group_exception_rule_manager",1,1,1,1 |
@ -1,5 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
# (c) 2015 Oihane Crucelaegui - AvanzOSC |
|
||||
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html |
|
||||
|
|
||||
from . import test_sale_exception |
|
@ -1,62 +0,0 @@ |
|||||
from openerp.exceptions import ValidationError |
|
||||
from openerp.addons.sale.tests.test_sale_order import TestSaleOrder |
|
||||
|
|
||||
|
|
||||
class TestSaleException(TestSaleOrder): |
|
||||
|
|
||||
def test_sale_order_exception(self): |
|
||||
exception = self.env.ref('sale_exception.excep_no_zip') |
|
||||
exception.active = True |
|
||||
partner = self.env.ref('base.res_partner_1') |
|
||||
partner.zip = False |
|
||||
p = self.env.ref('product.product_product_6') |
|
||||
so = self.env['sale.order'].create({ |
|
||||
'partner_id': partner.id, |
|
||||
'partner_invoice_id': partner.id, |
|
||||
'partner_shipping_id': partner.id, |
|
||||
'order_line': [(0, 0, {'name': p.name, |
|
||||
'product_id': p.id, |
|
||||
'product_uom_qty': 2, |
|
||||
'product_uom': p.uom_id.id, |
|
||||
'price_unit': p.list_price})], |
|
||||
'pricelist_id': self.env.ref('product.list0').id, |
|
||||
}) |
|
||||
|
|
||||
# confirm quotation |
|
||||
so.action_confirm() |
|
||||
self.assertTrue(so.state == 'draft') |
|
||||
|
|
||||
# Set ignore_exception flag (Done after ignore is selected at wizard) |
|
||||
so.ignore_exception = True |
|
||||
so.action_confirm() |
|
||||
self.assertTrue(so.state == 'sale') |
|
||||
|
|
||||
# Add a order line to test after SO is confirmed |
|
||||
p = self.env.ref('product.product_product_7') |
|
||||
|
|
||||
# set ignore_exception = False (Done by onchange of order_line) |
|
||||
self.assertRaises( |
|
||||
ValidationError, |
|
||||
so.write, |
|
||||
{ |
|
||||
'ignore_exception': False, |
|
||||
'order_line': [(0, 0, {'name': p.name, |
|
||||
'product_id': p.id, |
|
||||
'product_uom_qty': 2, |
|
||||
'product_uom': p.uom_id.id, |
|
||||
'price_unit': p.list_price})] |
|
||||
}, |
|
||||
) |
|
||||
|
|
||||
p = self.env.ref('product.product_product_7') |
|
||||
|
|
||||
# Set ignore exception True (Done manually by user) |
|
||||
so.write({ |
|
||||
'ignore_exception': True, |
|
||||
'order_line': [(0, 0, {'name': p.name, |
|
||||
'product_id': p.id, |
|
||||
'product_uom_qty': 2, |
|
||||
'product_uom': p.uom_id.id, |
|
||||
'price_unit': p.list_price})] |
|
||||
}) |
|
||||
exception.active = False |
|
@ -0,0 +1,51 @@ |
|||||
|
<?xml version="1.0" ?> |
||||
|
<odoo> |
||||
|
|
||||
|
<record id="view_exception_rule_tree" model="ir.ui.view"> |
||||
|
<field name="name">exception.rule.tree</field> |
||||
|
<field name="model">exception.rule</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<tree string="Exception Rule"> |
||||
|
<field name="active"/> |
||||
|
<field name="name"/> |
||||
|
<field name="description"/> |
||||
|
<field name="model"/> |
||||
|
<field name="sequence"/> |
||||
|
</tree> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="view_exception_rule_form" model="ir.ui.view"> |
||||
|
<field name="name">exception.rule.form</field> |
||||
|
<field name="model">exception.rule</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form string="Exception Rule Setup" name="exception_rule"> |
||||
|
<group colspan="4" col="2"> |
||||
|
<field name="name"/> |
||||
|
<field name="description"/> |
||||
|
</group> |
||||
|
<group colspan="4" groups="base_exception.group_exception_rule_manager"> |
||||
|
<field name="active"/> |
||||
|
<field name="sequence"/> |
||||
|
</group> |
||||
|
<group colspan="4" col="2" groups="base.group_system"> |
||||
|
<field name="rule_group"/> |
||||
|
<field name="model"/> |
||||
|
<field name="code"/> |
||||
|
</group> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="action_exception_rule_tree" model="ir.actions.act_window"> |
||||
|
<field name="name">Exception Rules</field> |
||||
|
<field name="res_model">exception.rule</field> |
||||
|
<field name="view_type">form</field> |
||||
|
<field name="view_mode">tree,form</field> |
||||
|
<field name="view_id" ref="view_exception_rule_tree"/> |
||||
|
<field name="context">{'active_test': False}</field> |
||||
|
</record> |
||||
|
|
||||
|
<menuitem action="action_exception_rule_tree" id="menu_action_exception" parent="base_setup.menu_config" /> |
||||
|
|
||||
|
</odoo> |
@ -1,116 +0,0 @@ |
|||||
<?xml version="1.0" ?> |
|
||||
<odoo> |
|
||||
|
|
||||
<record id="view_sale_exception_tree" model="ir.ui.view"> |
|
||||
<field name="name">sale.exception.tree</field> |
|
||||
<field name="model">sale.exception</field> |
|
||||
<field name="arch" type="xml"> |
|
||||
<tree string="Sale Exception"> |
|
||||
<field name="active"/> |
|
||||
<field name="name"/> |
|
||||
<field name="description"/> |
|
||||
<field name="model"/> |
|
||||
<field name="sequence"/> |
|
||||
</tree> |
|
||||
</field> |
|
||||
</record> |
|
||||
|
|
||||
<record id="view_sale_exception_form" model="ir.ui.view"> |
|
||||
<field name="name">sale.exception.form</field> |
|
||||
<field name="model">sale.exception</field> |
|
||||
<field name="arch" type="xml"> |
|
||||
<form string="Sale Exception Setup" name="sale_exception"> |
|
||||
<group colspan="4" col="2"> |
|
||||
<field name="name"/> |
|
||||
<field name="description"/> |
|
||||
</group> |
|
||||
<group colspan="4" groups="base.group_sale_manager"> |
|
||||
<field name="active"/> |
|
||||
<field name="sequence"/> |
|
||||
</group> |
|
||||
<group colspan="4" col="2" groups="base.group_system"> |
|
||||
<field name="model"/> |
|
||||
<field name="code"/> |
|
||||
</group> |
|
||||
<group colspan="4" col="2"> |
|
||||
<separator string="Affected Sales Orders"/> |
|
||||
<newline/> |
|
||||
<field name="sale_order_ids" nolabel="1" domain="[('state', '=', 'draft')]"/> |
|
||||
</group> |
|
||||
</form> |
|
||||
</field> |
|
||||
</record> |
|
||||
|
|
||||
<record id="action_sale_test_tree" model="ir.actions.act_window"> |
|
||||
<field name="name">Exception Rules</field> |
|
||||
<field name="res_model">sale.exception</field> |
|
||||
<field name="view_type">form</field> |
|
||||
<field name="view_mode">tree,form</field> |
|
||||
<field name="view_id" ref="view_sale_exception_tree"/> |
|
||||
<field name="context">{'active_test': False}</field> |
|
||||
</record> |
|
||||
|
|
||||
<menuitem action="action_sale_test_tree" id="menu_sale_test" parent="base.menu_sale_general_settings" /> |
|
||||
|
|
||||
|
|
||||
<record id="view_order_form" model="ir.ui.view"> |
|
||||
<field name="name">sale_exception.view_order_form</field> |
|
||||
<field name="model">sale.order</field> |
|
||||
<field name="inherit_id" ref="sale.view_order_form"/> |
|
||||
<field name="arch" type="xml"> |
|
||||
<field name="name" position="after"> |
|
||||
<group> |
|
||||
<field name="main_exception_id" options='{"no_open": True}' |
|
||||
class="oe_inline" string="Error:" |
|
||||
attrs="{'invisible':[('main_exception_id','=', False)]}"/> |
|
||||
</group> |
|
||||
</field> |
|
||||
<xpath expr="//field[@name='date_order']/.." position="inside"> |
|
||||
<field name="ignore_exception" states="sale" /> |
|
||||
</xpath> |
|
||||
<xpath expr="//group[@name='sales_person']/.." |
|
||||
position="inside"> |
|
||||
<newline /> |
|
||||
<group name="exception" colspan="2" col="2"> |
|
||||
<separator string="Exception" colspan="2"/> |
|
||||
<field name="exception_ids" colspan="2" nolabel="1"/> |
|
||||
</group> |
|
||||
</xpath> |
|
||||
</field> |
|
||||
</record> |
|
||||
|
|
||||
<record id="view_order_tree" model="ir.ui.view"> |
|
||||
<field name="name">sale_exception.view_order_tree</field> |
|
||||
<field name="model">sale.order</field> |
|
||||
<field name="inherit_id" ref="sale.view_order_tree"/> |
|
||||
<field name="arch" type="xml"> |
|
||||
<field name="state" position="after"> |
|
||||
<field name="main_exception_id"/> |
|
||||
</field> |
|
||||
</field> |
|
||||
</record> |
|
||||
|
|
||||
<record id="view_quotation_tree" model="ir.ui.view"> |
|
||||
<field name="name">sale_exception.view_order_tree</field> |
|
||||
<field name="model">sale.order</field> |
|
||||
<field name="inherit_id" ref="sale.view_quotation_tree"/> |
|
||||
<field name="arch" type="xml"> |
|
||||
<field name="state" position="after"> |
|
||||
<field name="main_exception_id"/> |
|
||||
</field> |
|
||||
</field> |
|
||||
</record> |
|
||||
|
|
||||
<record id="view_sales_order_filter" model="ir.ui.view"> |
|
||||
<field name="name">sale_exception.view_sales_order_filter</field> |
|
||||
<field name="model">sale.order</field> |
|
||||
<field name="inherit_id" ref="sale.view_sales_order_filter" /> |
|
||||
<field name="arch" type="xml"> |
|
||||
<filter name="sales" position="after"> |
|
||||
<separator orientation="vertical"/> |
|
||||
<filter icon="terp-emblem-important" name="tofix" string="Blocked in draft" domain="[('main_exception_id','!=',False)]"/> |
|
||||
</filter> |
|
||||
</field> |
|
||||
</record> |
|
||||
|
|
||||
</odoo> |
|
@ -0,0 +1,35 @@ |
|||||
|
# -*- 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). |
||||
|
|
||||
|
from odoo import api, fields, models |
||||
|
|
||||
|
|
||||
|
class ExceptionRuleConfirm(models.AbstractModel): |
||||
|
|
||||
|
_name = 'exception.rule.confirm' |
||||
|
|
||||
|
related_model_id = fields.Many2one('base.exception',) |
||||
|
exception_ids = fields.Many2many('exception.rule', |
||||
|
string='Exceptions to resolve', |
||||
|
readonly=True) |
||||
|
ignore = fields.Boolean('Ignore Exceptions') |
||||
|
|
||||
|
@api.model |
||||
|
def default_get(self, field_list): |
||||
|
res = super(ExceptionRuleConfirm, self).default_get(field_list) |
||||
|
current_model = self._context.get('active_model') |
||||
|
model_except_obj = self.env[current_model] |
||||
|
active_ids = self._context.get('active_ids') |
||||
|
assert len(active_ids) == 1, "Only 1 ID accepted, got %r" % active_ids |
||||
|
active_id = active_ids[0] |
||||
|
related_model_except = model_except_obj.browse(active_id) |
||||
|
exception_ids = [e.id for e in related_model_except.exception_ids] |
||||
|
res.update({'exception_ids': [(6, 0, exception_ids)]}) |
||||
|
res.update({'related_model_id': active_id}) |
||||
|
return res |
||||
|
|
||||
|
@api.multi |
||||
|
def action_confirm(self): |
||||
|
self.ensure_one() |
||||
|
return {'type': 'ir.actions.act_window_close'} |
@ -1,35 +0,0 @@ |
|||||
# -*- 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). |
|
||||
|
|
||||
from openerp import models, fields, api |
|
||||
|
|
||||
|
|
||||
class SaleExceptionConfirm(models.TransientModel): |
|
||||
|
|
||||
_name = 'sale.exception.confirm' |
|
||||
|
|
||||
sale_id = fields.Many2one('sale.order', 'Sale') |
|
||||
exception_ids = fields.Many2many('sale.exception', |
|
||||
string='Exceptions to resolve', |
|
||||
readonly=True) |
|
||||
ignore = fields.Boolean('Ignore Exceptions') |
|
||||
|
|
||||
@api.model |
|
||||
def default_get(self, field_list): |
|
||||
res = super(SaleExceptionConfirm, self).default_get(field_list) |
|
||||
order_obj = self.env['sale.order'] |
|
||||
sale_id = self._context.get('active_ids') |
|
||||
assert len(sale_id) == 1, "Only 1 ID accepted, got %r" % sale_id |
|
||||
sale_id = sale_id[0] |
|
||||
sale = order_obj.browse(sale_id) |
|
||||
exception_ids = [e.id for e in sale.exception_ids] |
|
||||
res.update({'exception_ids': [(6, 0, exception_ids)]}) |
|
||||
res.update({'sale_id': sale_id}) |
|
||||
return res |
|
||||
|
|
||||
@api.one |
|
||||
def action_confirm(self): |
|
||||
if self.ignore: |
|
||||
self.sale_id.ignore_exception = True |
|
||||
return {'type': 'ir.actions.act_window_close'} |
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue