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" |
|||
"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