Merge lp:~openerp-community-reviewer/sale-wkfl/move_sale_exception_module_from_e-commerce-addons-jge into lp:~sale-core-editors/sale-wkfl/7.0

Proposed by Joël Grand-Guillaume @ camptocamp
Status: Merged
Merged at revision: 21
Proposed branch: lp:~openerp-community-reviewer/sale-wkfl/move_sale_exception_module_from_e-commerce-addons-jge
Merge into: lp:~sale-core-editors/sale-wkfl/7.0
Diff against target: 994 lines (+925/-0)
13 files modified
sale_exceptions/__init__.py (+26/-0)
sale_exceptions/__openerp__.py (+46/-0)
sale_exceptions/i18n/fr.po (+177/-0)
sale_exceptions/i18n/sale_exceptions.pot (+177/-0)
sale_exceptions/sale.py (+236/-0)
sale_exceptions/sale_exceptions_data.xml (+19/-0)
sale_exceptions/sale_view.xml (+102/-0)
sale_exceptions/sale_workflow.xml (+9/-0)
sale_exceptions/security/ir.model.access.csv (+3/-0)
sale_exceptions/settings/sale.exception.csv (+5/-0)
sale_exceptions/wizard/__init__.py (+24/-0)
sale_exceptions/wizard/sale_exception_confirm.py (+62/-0)
sale_exceptions/wizard/sale_exception_confirm_view.xml (+39/-0)
To merge this branch: bzr merge lp:~openerp-community-reviewer/sale-wkfl/move_sale_exception_module_from_e-commerce-addons-jge
Reviewer Review Type Date Requested Status
Nicolas Bessi - Camptocamp (community) no test, code review Approve
Guewen Baconnier @ Camptocamp Approve
Review via email: mp+193567@code.launchpad.net

Commit message

[MOVE] Move here the sale_exceptions module from the lp:e-commerce-addons

Description of the change

Hi,

I suggest to move the sale_exceptions module from e-commerce-addons to here because it really concerns the sales workflow in a more generic way than just the e-commerce context.

I put the branch under the reviewer team so feel free to fix stuff if you'd like.

WARNING: Merge this proposal when this one is done : https://code.launchpad.net/~openerp-community-reviewer/e-commerce-addons/move_sale_exception_to_sale_wkfl_branches-jge/+merge/193570

Regards,

Joël

To post a comment you must log in.
Revision history for this message
Pedro Manuel Baeza (pedro.baeza) wrote :

As your reasoning on community list, I approve it blindly.

Regards.

Revision history for this message
Sébastien BEAU - http://www.akretion.com (sebastien.beau) wrote :

Hi, I am working on the split of sale_exception in two module "sale_exception" and "exception_rule". The aim is to have the exception on different object like purchase_order, stock_picking, invoice ... (already implemented on purchase_order and soon on picking).
If we put the module sale_exception here where should I put the module "exception_rule" and "purchase_exception" (and later stock_picking_exception)?

I see two solution possible
- Putting exception_rule in "server-env-tools" and than all implementation in sale-wkfl, purchase-wkfl...
- Creating a project "workflow-exception" and put all exception module here

What do you prefers?

Revision history for this message
Pedro Manuel Baeza (pedro.baeza) wrote :

Hi, Sébastien, is there too much logic on exception_rule so that it is mandatory to split it in one module?, or it can be easily repeated in consequent other modules "purchase_exception", "stock_exception", etc?

Regards.

Revision history for this message
Joël Grand-Guillaume @ camptocamp (jgrandguillaume-c2c) wrote :

Hi,

+1 for Pedro. Having such a general module doesn't seems mandatory. The exception can IMO define in every module.

Moreover, the sale_exception module was already there like this. So I just move it. We can't assume doing both in one. Your suggestion may break back-compatibility so... My suggestion: move on to have it in the right project/branches, then when you'll be ready, make your MP so we'll have the opportunity to discuss that with something concrete !

What do you think ?

++

Joël

Revision history for this message
Sébastien BEAU - http://www.akretion.com (sebastien.beau) wrote :

After my split I have around 140 ligne of python in exception_rule and around 40 in sale_exception so I really think it's worth to split it because we will avoid to duplicate the 140 ligne in every module for nothing, it's not a lot but it's always better to no duplicate the code (on improvement improve all module). Moreover we use the same pop-up wizard in sale_exception and purchase_exception so it the same interface for the end user.

Here is my work : http://bazaar.launchpad.net/~sebastien.beau/e-commerce-addons/e-commerce-addons-split-sale-exception/view/head:/sale_exceptions/sale.py
(not ready to be merge yet, I have to be sure that I do not miss any improvement done by guewen here https://code.launchpad.net/~extra-addons-commiter/e-commerce-addons/7.0)

Revision history for this message
Sébastien BEAU - http://www.akretion.com (sebastien.beau) wrote :

Sorry I made a mistake around 180 line in the generic module

Revision history for this message
Joël Grand-Guillaume @ camptocamp (jgrandguillaume-c2c) wrote :

Hi Sébastien,

Your work looks great. Can you please make another MP once this one is done with your work ? This we move forward on this one.

@Pedro : Would you add your review here please ?

Regards,

Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote :

I approve for the move

review: Approve
46. By Nicolas Bessi - Camptocamp

[PEP8] and community conventions

47. By Nicolas Bessi - Camptocamp

[PEP8]

48. By Nicolas Bessi - Camptocamp

[PEP8] + removing class instantiation

Revision history for this message
Nicolas Bessi - Camptocamp (nbessi-c2c-deactivatedaccount) wrote :

LGTM

I also committed PEP8 and community convention fixes.

Moving the module is OK.

review: Approve (no test, code review)
49. By Nicolas Bessi - Camptocamp

[FIX] licence

50. By Nicolas Bessi - Camptocamp

[MRG] migration of sale_exceptions from original branch

Revision history for this message
Nicolas Bessi - Camptocamp (nbessi-c2c-deactivatedaccount) wrote :

Hello,

I have merge back migration/fixes recently commited in lp:e-commerce-addons

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory 'sale_exceptions'
=== added file 'sale_exceptions/__init__.py'
--- sale_exceptions/__init__.py 1970-01-01 00:00:00 +0000
+++ sale_exceptions/__init__.py 2013-11-15 12:55:43 +0000
@@ -0,0 +1,26 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2011 Akretion LTDA.
6# authors: Raphaël Valyi, Renato Lima
7# Copyright (C) 2010-2012 Akretion Sébastien BEAU <sebastien.beau@akretion.com>
8# Copyright (C) 2012 Camptocamp SA (Guewen Baconnier)
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU Affero General Public License as
12# published by the Free Software Foundation, either version 3 of the
13# License, or (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU Affero General Public License for more details.
19#
20# You should have received a copy of the GNU Affero General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22#
23##############################################################################
24
25from . import sale
26from . import wizard
027
=== added file 'sale_exceptions/__openerp__.py'
--- sale_exceptions/__openerp__.py 1970-01-01 00:00:00 +0000
+++ sale_exceptions/__openerp__.py 2013-11-15 12:55:43 +0000
@@ -0,0 +1,46 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2011 Akretion LTDA.
6# authors: Raphaël Valyi, Renato Lima
7# Copyright (C) 2010-2012 Akretion Sébastien BEAU <sebastien.beau@akretion.com>
8# Copyright (C) 2012 Camptocamp SA (Guewen Baconnier)
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU Affero General Public License as
12# published by the Free Software Foundation, either version 3 of the
13# License, or (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU Affero General Public License for more details.
19#
20# You should have received a copy of the GNU Affero General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22#
23##############################################################################
24{'name': 'Sale Exceptions',
25 'version': '0.1',
26 'category': 'Generic Modules/Sale',
27 'description': """
28This module allows you attach several customizable exceptions to your sale order
29 in a way that you can filter orders by exceptions type and fix them.
30
31This is especially useful in an order importation scenario such as with
32the base_sale_multi_channels module, because it's likely a few orders have errors
33when you import them (like product not found in OpenERP, wrong line format etc...)
34""",
35 'author': 'Akretion',
36 'website': 'http://www.akretion.com',
37 'depends': ['sale'],
38 'init_xml': ['settings/sale.exception.csv'],
39 'update_xml': ['sale_workflow.xml',
40 'sale_view.xml',
41 'sale_exceptions_data.xml',
42 'wizard/sale_exception_confirm_view.xml',
43 'security/ir.model.access.csv'],
44 'demo_xml': [],
45 'installable': True,
46 }
047
=== added directory 'sale_exceptions/i18n'
=== added file 'sale_exceptions/i18n/fr.po'
--- sale_exceptions/i18n/fr.po 1970-01-01 00:00:00 +0000
+++ sale_exceptions/i18n/fr.po 2013-11-15 12:55:43 +0000
@@ -0,0 +1,177 @@
1# Translation of OpenERP Server.
2# This file contains the translation of the following modules:
3# * sale_exceptions
4#
5msgid ""
6msgstr ""
7"Project-Id-Version: OpenERP Server 7.0\n"
8"Report-Msgid-Bugs-To: \n"
9"POT-Creation-Date: 2013-05-02 06:57+0000\n"
10"PO-Revision-Date: 2013-05-02 06:57+0000\n"
11"Last-Translator: <>\n"
12"Language-Team: \n"
13"MIME-Version: 1.0\n"
14"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Transfer-Encoding: \n"
16"Plural-Forms: \n"
17
18#. module: sale_exceptions
19#: model:ir.model,name:sale_exceptions.model_sale_exception_confirm
20msgid "sale.exception.confirm"
21msgstr ""
22
23#. module: sale_exceptions
24#: selection:sale.exception,model:0
25msgid "Sale Order Line"
26msgstr "Ligne de commande"
27
28#. module: sale_exceptions
29#: field:sale.exception,model:0
30msgid "Apply on"
31msgstr "Appliquer sur"
32
33#. module: sale_exceptions
34#: model:sale.exception,name:sale_exceptions.excep_no_stock
35msgid "Not Enough Virtual Stock"
36msgstr "Pas assez de quantité de stock prévue"
37
38#. module: sale_exceptions
39#: field:sale.exception,description:0
40msgid "Description"
41msgstr "Description"
42
43#. module: sale_exceptions
44#: help:sale.exception,sequence:0
45msgid "Gives the sequence order when applying the test"
46msgstr "Définit l'ordre d'application des contrôles"
47
48#. module: sale_exceptions
49#: view:sale.exception.confirm:0
50msgid "Sale Exceptions On Sale Order"
51msgstr "Restrictions sur la commande"
52
53#. module: sale_exceptions
54#: field:sale.exception.confirm,exception_ids:0
55msgid "Exceptions to resolve"
56msgstr "Restrictions à résoudre"
57
58#. module: sale_exceptions
59#: view:sale.exception.confirm:0
60msgid "_Ok"
61msgstr "_Ok"
62
63#. module: sale_exceptions
64#: view:sale.exception:0
65#: view:sale.exception.confirm:0
66msgid "Sale Exception"
67msgstr "Restriction de vente"
68
69#. module: sale_exceptions
70#: view:sale.order:0
71msgid "TO FIX"
72msgstr "A CORRIGER"
73
74#. module: sale_exceptions
75#: help:sale.exception,code:0
76msgid "Python code executed to check if the exception apply or not. The code must apply block = True to apply the exception."
77msgstr "Code Python exécuté pour déterminer si la restriction s'applique. The bloc de code doit retourner block = True pour appliquer la restriction."
78
79#. module: sale_exceptions
80#: view:sale.order:0
81msgid "Exception"
82msgstr "Restriction"
83
84#. module: sale_exceptions
85#: view:sale.order:0
86msgid "Error:"
87msgstr "Erreur :"
88
89#. module: sale_exceptions
90#: selection:sale.exception,model:0
91msgid "Sale Order"
92msgstr "Bon de commande"
93
94#. module: sale_exceptions
95#: field:sale.exception.confirm,sale_id:0
96msgid "Sale"
97msgstr "Commande"
98
99#. module: sale_exceptions
100#: field:sale.exception,active:0
101msgid "Active"
102msgstr "Actif"
103
104#. module: sale_exceptions
105#: field:sale.exception,name:0
106msgid "Exception Name"
107msgstr "Nom de la restriction"
108
109#. module: sale_exceptions
110#: field:sale.order,exceptions_ids:0
111msgid "Exceptions"
112msgstr "Restrictions"
113
114#. module: sale_exceptions
115#: model:ir.actions.act_window,name:sale_exceptions.action_sale_exception_confirm
116#: model:ir.model,name:sale_exceptions.model_sale_exception
117#: view:sale.exception.confirm:0
118msgid "Sale Exceptions"
119msgstr "Restrictions de vente"
120
121#. module: sale_exceptions
122#: model:ir.actions.act_window,name:sale_exceptions.action_sale_test_tree
123#: model:ir.ui.menu,name:sale_exceptions.menu_sale_test
124msgid "Exception Rules"
125msgstr "Règles de restriction"
126
127#. module: sale_exceptions
128#: model:ir.model,name:sale_exceptions.model_sale_order
129msgid "Sales Order"
130msgstr "Bon de commande"
131
132#. module: sale_exceptions
133#: field:sale.exception,sequence:0
134msgid "Sequence"
135msgstr "Séquence"
136
137#. module: sale_exceptions
138#: field:sale.exception,code:0
139msgid "Python Code"
140msgstr "Code Python"
141
142#. module: sale_exceptions
143#: view:sale.order:0
144msgid "Sales"
145msgstr "Bons de commande"
146
147#. module: sale_exceptions
148#: model:sale.exception,name:sale_exceptions.excep_no_zip
149msgid "No ZIP code on destination"
150msgstr "Code postal manquant sur la destination"
151
152#. module: sale_exceptions
153#: view:sale.exception:0
154msgid "Sale Exception Setup"
155msgstr "Configuration des restrictions de vente"
156
157#. module: sale_exceptions
158#: view:sale.exception:0
159msgid "Affected Sales Orders"
160msgstr "Bons de commande affectés"
161
162#. module: sale_exceptions
163#: field:sale.exception,sale_order_ids:0
164msgid "Sale Orders"
165msgstr "Bons de commande"
166
167#. module: sale_exceptions
168#: field:sale.exception.confirm,ignore:0
169#: field:sale.order,ignore_exceptions:0
170msgid "Ignore Exceptions"
171msgstr "Ignorer la restriction"
172
173#. module: sale_exceptions
174#: field:sale.order,main_exception_id:0
175msgid "Main Exception"
176msgstr "Restriction principale"
177
0178
=== added file 'sale_exceptions/i18n/sale_exceptions.pot'
--- sale_exceptions/i18n/sale_exceptions.pot 1970-01-01 00:00:00 +0000
+++ sale_exceptions/i18n/sale_exceptions.pot 2013-11-15 12:55:43 +0000
@@ -0,0 +1,177 @@
1# Translation of OpenERP Server.
2# This file contains the translation of the following modules:
3# * sale_exceptions
4#
5msgid ""
6msgstr ""
7"Project-Id-Version: OpenERP Server 7.0\n"
8"Report-Msgid-Bugs-To: \n"
9"POT-Creation-Date: 2013-05-02 06:57+0000\n"
10"PO-Revision-Date: 2013-05-02 06:57+0000\n"
11"Last-Translator: <>\n"
12"Language-Team: \n"
13"MIME-Version: 1.0\n"
14"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Transfer-Encoding: \n"
16"Plural-Forms: \n"
17
18#. module: sale_exceptions
19#: model:ir.model,name:sale_exceptions.model_sale_exception_confirm
20msgid "sale.exception.confirm"
21msgstr ""
22
23#. module: sale_exceptions
24#: selection:sale.exception,model:0
25msgid "Sale Order Line"
26msgstr ""
27
28#. module: sale_exceptions
29#: field:sale.exception,model:0
30msgid "Apply on"
31msgstr ""
32
33#. module: sale_exceptions
34#: model:sale.exception,name:sale_exceptions.excep_no_stock
35msgid "Not Enough Virtual Stock"
36msgstr ""
37
38#. module: sale_exceptions
39#: field:sale.exception,description:0
40msgid "Description"
41msgstr ""
42
43#. module: sale_exceptions
44#: help:sale.exception,sequence:0
45msgid "Gives the sequence order when applying the test"
46msgstr ""
47
48#. module: sale_exceptions
49#: view:sale.exception.confirm:0
50msgid "Sale Exceptions On Sale Order"
51msgstr ""
52
53#. module: sale_exceptions
54#: field:sale.exception.confirm,exception_ids:0
55msgid "Exceptions to resolve"
56msgstr ""
57
58#. module: sale_exceptions
59#: view:sale.exception.confirm:0
60msgid "_Ok"
61msgstr ""
62
63#. module: sale_exceptions
64#: view:sale.exception:0
65#: view:sale.exception.confirm:0
66msgid "Sale Exception"
67msgstr ""
68
69#. module: sale_exceptions
70#: view:sale.order:0
71msgid "TO FIX"
72msgstr ""
73
74#. module: sale_exceptions
75#: help:sale.exception,code:0
76msgid "Python code executed to check if the exception apply or not. The code must apply block = True to apply the exception."
77msgstr ""
78
79#. module: sale_exceptions
80#: view:sale.order:0
81msgid "Exception"
82msgstr ""
83
84#. module: sale_exceptions
85#: view:sale.order:0
86msgid "Error:"
87msgstr ""
88
89#. module: sale_exceptions
90#: selection:sale.exception,model:0
91msgid "Sale Order"
92msgstr ""
93
94#. module: sale_exceptions
95#: field:sale.exception.confirm,sale_id:0
96msgid "Sale"
97msgstr ""
98
99#. module: sale_exceptions
100#: field:sale.exception,active:0
101msgid "Active"
102msgstr ""
103
104#. module: sale_exceptions
105#: field:sale.exception,name:0
106msgid "Exception Name"
107msgstr ""
108
109#. module: sale_exceptions
110#: field:sale.order,exceptions_ids:0
111msgid "Exceptions"
112msgstr ""
113
114#. module: sale_exceptions
115#: model:ir.actions.act_window,name:sale_exceptions.action_sale_exception_confirm
116#: model:ir.model,name:sale_exceptions.model_sale_exception
117#: view:sale.exception.confirm:0
118msgid "Sale Exceptions"
119msgstr ""
120
121#. module: sale_exceptions
122#: model:ir.actions.act_window,name:sale_exceptions.action_sale_test_tree
123#: model:ir.ui.menu,name:sale_exceptions.menu_sale_test
124msgid "Exception Rules"
125msgstr ""
126
127#. module: sale_exceptions
128#: model:ir.model,name:sale_exceptions.model_sale_order
129msgid "Sales Order"
130msgstr ""
131
132#. module: sale_exceptions
133#: field:sale.exception,sequence:0
134msgid "Sequence"
135msgstr ""
136
137#. module: sale_exceptions
138#: field:sale.exception,code:0
139msgid "Python Code"
140msgstr ""
141
142#. module: sale_exceptions
143#: view:sale.order:0
144msgid "Sales"
145msgstr ""
146
147#. module: sale_exceptions
148#: model:sale.exception,name:sale_exceptions.excep_no_zip
149msgid "No ZIP code on destination"
150msgstr ""
151
152#. module: sale_exceptions
153#: view:sale.exception:0
154msgid "Sale Exception Setup"
155msgstr ""
156
157#. module: sale_exceptions
158#: view:sale.exception:0
159msgid "Affected Sales Orders"
160msgstr ""
161
162#. module: sale_exceptions
163#: field:sale.exception,sale_order_ids:0
164msgid "Sale Orders"
165msgstr ""
166
167#. module: sale_exceptions
168#: field:sale.exception.confirm,ignore:0
169#: field:sale.order,ignore_exceptions:0
170msgid "Ignore Exceptions"
171msgstr ""
172
173#. module: sale_exceptions
174#: field:sale.order,main_exception_id:0
175msgid "Main Exception"
176msgstr ""
177
0178
=== added file 'sale_exceptions/sale.py'
--- sale_exceptions/sale.py 1970-01-01 00:00:00 +0000
+++ sale_exceptions/sale.py 2013-11-15 12:55:43 +0000
@@ -0,0 +1,236 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2011 Akretion LTDA.
6# Copyright (C) 2010-2012 Akretion Sébastien BEAU <sebastien.beau@akretion.com>
7# Copyright (C) 2012 Camptocamp SA (Guewen Baconnier)
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23
24import time
25
26from openerp.osv import orm, fields
27from openerp.tools.safe_eval import safe_eval
28from openerp.tools.translate import _
29
30
31class sale_exception(orm.Model):
32 _name = "sale.exception"
33 _description = "Sale Exceptions"
34 _order = "active desc, sequence asc"
35 _columns = {
36 'name': fields.char('Exception Name', required=True, translate=True),
37 'description': fields.text('Description', translate=True),
38 'sequence': fields.integer(
39 'Sequence',
40 help="Gives the sequence order when applying the test"),
41 'model': fields.selection([('sale.order', 'Sale Order'),
42 ('sale.order.line', 'Sale Order Line')],
43 string='Apply on', required=True),
44 'active': fields.boolean('Active'),
45 'code': fields.text(
46 'Python Code',
47 help="Python code executed to check if the exception apply or not. "
48 "The code must apply block = True to apply the exception."),
49 'sale_order_ids': fields.many2many(
50 'sale.order',
51 'sale_order_exception_rel', 'exception_id', 'sale_order_id',
52 string='Sale Orders',
53 readonly=True),
54 }
55
56 _defaults = {
57 'code': """# Python code. Use failed = True to block the sale order.
58# You can use the following variables :
59# - self: ORM model of the record which is checked
60# - order or line: browse_record of the sale order or sale order line
61# - object: same as order or line, browse_record of the sale order or sale order line
62# - pool: ORM model pool (i.e. self.pool)
63# - time: Python time module
64# - cr: database cursor
65# - uid: current user id
66# - context: current context
67"""
68 }
69
70
71class sale_order(orm.Model):
72 _inherit = "sale.order"
73
74 _order = 'main_exception_id asc, date_order desc, name desc'
75
76 def _get_main_error(self, cr, uid, ids, name, args, context=None):
77 res = {}
78 for sale_order in self.browse(cr, uid, ids, context=context):
79 if sale_order.state == 'draft' and sale_order.exceptions_ids:
80 res[sale_order.id] = sale_order.exceptions_ids[0].id
81 else:
82 res[sale_order.id] = False
83 return res
84
85 _columns = {
86 'main_exception_id': fields.function(
87 _get_main_error,
88 type='many2one',
89 relation="sale.exception",
90 string='Main Exception',
91 store={
92 'sale.order': (lambda self, cr, uid, ids, c=None: ids,
93 ['exceptions_ids', 'state'], 10),
94 }),
95 'exceptions_ids': fields.many2many(
96 'sale.exception',
97 'sale_order_exception_rel', 'sale_order_id', 'exception_id',
98 string='Exceptions'),
99 'ignore_exceptions': fields.boolean('Ignore Exceptions'),
100 }
101
102 def test_all_draft_orders(self, cr, uid, context=None):
103 ids = self.search(cr, uid, [('state', '=', 'draft')], context=context)
104 self.test_exceptions(cr, uid, ids, context=context)
105 return True
106
107 def _popup_exceptions(self, cr, uid, order_id, context=None):
108 if context is None:
109 context = {}
110 model_data_obj = self.pool.get('ir.model.data')
111 list_obj = self.pool.get('sale.exception.confirm')
112 ctx = context.copy()
113 ctx.update({'active_id': order_id,
114 'active_ids': [order_id]})
115 list_id = list_obj.create(cr, uid, {}, context=ctx)
116 view_id = model_data_obj.get_object_reference(
117 cr, uid, 'sale_exceptions', 'view_sale_exception_confirm')[1]
118 action = {
119 'name': _("Blocked in draft due to exceptions"),
120 'type': 'ir.actions.act_window',
121 'view_type': 'form',
122 'view_mode': 'form',
123 'res_model': 'sale.exception.confirm',
124 'view_id': [view_id],
125 'target': 'new',
126 'nodestroy': True,
127 'res_id': list_id,
128 }
129 return action
130
131 def action_button_confirm(self, cr, uid, ids, context=None):
132 exception_ids = self.detect_exceptions(cr, uid, ids, context=context)
133 if exception_ids:
134 return self._popup_exceptions(cr, uid, ids[0], context=context)
135 else:
136 return super(sale_order, self).action_button_confirm(cr, uid, ids,
137 context=context)
138
139 def test_exceptions(self, cr, uid, ids, context=None):
140 """
141 Condition method for the workflow from draft to confirm
142 """
143 exception_ids = self.detect_exceptions(cr, uid, ids, context=context)
144 if exception_ids:
145 return False
146 return True
147
148 def detect_exceptions(self, cr, uid, ids, context=None):
149 exception_obj = self.pool.get('sale.exception')
150 order_exception_ids = exception_obj.search(
151 cr, uid,
152 [('model', '=', 'sale.order')],
153 context=context)
154 line_exception_ids = exception_obj.search(
155 cr, uid,
156 [('model', '=', 'sale.order.line')],
157 context=context)
158
159 order_exceptions = exception_obj.browse(cr, uid, order_exception_ids,
160 context=context)
161 line_exceptions = exception_obj.browse(cr, uid, line_exception_ids,
162 context=context)
163
164 exception_ids = False
165 for order in self.browse(cr, uid, ids, context=context):
166 if order.ignore_exceptions:
167 continue
168 exception_ids = self._detect_exceptions(cr, uid,
169 order,
170 order_exceptions,
171 line_exceptions,
172 context=context)
173
174 self.write(cr, uid, [order.id],
175 {'exceptions_ids': [(6, 0, exception_ids)]},
176 context=context)
177 return exception_ids
178
179 def _exception_rule_eval_context(self, cr, uid, obj_name, obj, context=None):
180 if context is None:
181 context = {}
182
183 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
184 return {obj_name: obj,
185 'self': self.pool.get(obj._name),
186 'object': obj,
187 'obj': obj,
188 'pool': self.pool,
189 'cr': cr,
190 'uid': uid,
191 'user': user,
192 'time': time,
193 # copy context to prevent side-effects of eval
194 'context': context.copy()}
195
196 def _rule_eval(self, cr, uid, rule, obj_name, obj, context):
197 expr = rule.code
198 space = self._exception_rule_eval_context(cr, uid, obj_name, obj,
199 context=context)
200 try:
201 safe_eval(expr,
202 space,
203 mode='exec',
204 nocopy=True) # nocopy allows to return 'result'
205 except Exception, e:
206 raise orm.except_orm(
207 _('Error'),
208 _('Error when evaluating the sale exception '
209 'rule:\n %s \n(%s)') % (rule.name, e))
210 return space.get('failed', False)
211
212 def _detect_exceptions(self, cr, uid, order, order_exceptions,
213 line_exceptions, context=None):
214 exception_ids = []
215 for rule in order_exceptions:
216 if self._rule_eval(cr, uid, rule, 'order', order, context):
217 exception_ids.append(rule.id)
218
219 for order_line in order.order_line:
220 for rule in line_exceptions:
221 if rule.id in exception_ids:
222 # we do not matter if the exception as already been
223 # found for an order line of this order
224 continue
225 if self._rule_eval(cr, uid, rule, 'line', order_line, context):
226 exception_ids.append(rule.id)
227
228 return exception_ids
229
230 def copy(self, cr, uid, id, default=None, context=None):
231 if default is None:
232 default = {}
233 default.update({
234 'ignore_exceptions': False,
235 })
236 return super(sale_order, self).copy(cr, uid, id, default=default, context=context)
0237
=== added file 'sale_exceptions/sale_exceptions_data.xml'
--- sale_exceptions/sale_exceptions_data.xml 1970-01-01 00:00:00 +0000
+++ sale_exceptions/sale_exceptions_data.xml 2013-11-15 12:55:43 +0000
@@ -0,0 +1,19 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data noupdate="1">
4
5 <record forcecreate="True" id="ir_cron_test_orders" model="ir.cron">
6 <field name="name">Test Draft Orders</field>
7 <field eval="False" name="active"/>
8 <field name="user_id" ref="base.user_root"/>
9 <field name="interval_number">20</field>
10 <field name="interval_type">minutes</field>
11 <field name="numbercall">-1</field>
12 <field eval="False" name="doall"/>
13 <field eval="'sale.order'" name="model"/>
14 <field eval="'test_all_draft_orders'" name="function"/>
15 <field eval="'()'" name="args"/>
16 </record>
17
18 </data>
19</openerp>
020
=== added file 'sale_exceptions/sale_view.xml'
--- sale_exceptions/sale_view.xml 1970-01-01 00:00:00 +0000
+++ sale_exceptions/sale_view.xml 2013-11-15 12:55:43 +0000
@@ -0,0 +1,102 @@
1<?xml version="1.0" ?>
2<openerp>
3 <data>
4
5 <record id="view_sale_exception_tree" model="ir.ui.view">
6 <field name="name">sale.exception.tree</field>
7 <field name="model">sale.exception</field>
8 <field name="arch" type="xml">
9 <tree string="Sale Exception">
10 <field name="active"/>
11 <field name="name"/>
12 <field name="description"/>
13 <field name="model"/>
14 <field name="sequence"/>
15 </tree>
16 </field>
17 </record>
18
19 <record id="view_sale_exception_form" model="ir.ui.view">
20 <field name="name">sale.exception.form</field>
21 <field name="model">sale.exception</field>
22 <field name="arch" type="xml">
23 <form string="Sale Exception Setup">
24 <group colspan="4" col="2">
25 <field name="name"/>
26 <field name="description"/>
27 </group>
28 <group col="4" colspan="4" groups="base.group_sale_manager">
29 <field name="active"/>
30 <field name="sequence"/>
31 <group colspan="4" col="2" groups="base.group_system">
32 <field name="model"/>
33 <field name="code"/>
34 </group>
35 </group>
36 <group colspan="4" col="2">
37 <separator string="Affected Sales Orders"/>
38 <newline/>
39 <field name="sale_order_ids" nolabel="1" domain="[('state', '=', 'draft')]"/>
40 </group>
41 </form>
42 </field>
43 </record>
44
45 <record id="action_sale_test_tree" model="ir.actions.act_window">
46 <field name="name">Exception Rules</field>
47 <field name="res_model">sale.exception</field>
48 <field name="view_type">form</field>
49 <field name="view_mode">tree,form</field>
50 <field name="view_id" ref="view_sale_exception_tree"/>
51 <field name="context">{'active_test': False}</field>
52 </record>
53
54 <menuitem action="action_sale_test_tree" id="menu_sale_test" parent="base.menu_sale_config_sales" />
55
56
57 <record id="view_order_form" model="ir.ui.view">
58 <field name="name">sale_exceptions.view_order_form</field>
59 <field name="model">sale.order</field>
60 <field name="inherit_id" ref="sale.view_order_form"/>
61 <field name="arch" type="xml">
62 <field name="name" position="after">
63 <group>
64 <field name="main_exception_id" options='{"no_open": True}'
65 class="oe_inline" string="Error:"
66 attrs="{'invisible':[('main_exception_id','=', False)]}"/>
67 </group>
68 </field>
69 <xpath expr="//page[@string='Other Information']/group"
70 position="inside">
71 <group name="exception" colspan="2" col="2">
72 <separator string="Exception" colspan="2"/>
73 <field name="exceptions_ids" colspan="2" nolabel="1"/>
74 </group>
75 </xpath>
76 </field>
77 </record>
78
79 <record id="view_order_tree" model="ir.ui.view">
80 <field name="name">sale_exceptions.view_order_tree</field>
81 <field name="model">sale.order</field>
82 <field name="inherit_id" ref="sale.view_order_tree"/>
83 <field name="arch" type="xml">
84 <field name="state" position="after">
85 <field name="main_exception_id"/>
86 </field>
87 </field>
88 </record>
89
90 <record id="view_sales_order_filter" model="ir.ui.view">
91 <field name="name">sale_exceptions.view_sales_order_filter</field>
92 <field name="model">sale.order</field>
93 <field name="inherit_id" ref="sale.view_sales_order_filter" />
94 <field name="arch" type="xml">
95 <filter name="sales" position="after">
96 <separator orientation="vertical"/>
97 <filter icon="terp-emblem-important" name="tofix" string="Blocked in draft" domain="[('main_exception_id','!=',False)]"/>
98 </filter>
99 </field>
100 </record>
101 </data>
102</openerp>
0103
=== added file 'sale_exceptions/sale_workflow.xml'
--- sale_exceptions/sale_workflow.xml 1970-01-01 00:00:00 +0000
+++ sale_exceptions/sale_workflow.xml 2013-11-15 12:55:43 +0000
@@ -0,0 +1,9 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4 <record id="sale.trans_draft_router" model="workflow.transition">
5 <field name="signal">order_confirm</field>
6 <field name="condition">test_exceptions()</field>
7 </record>
8 </data>
9</openerp>
010
=== added directory 'sale_exceptions/security'
=== added file 'sale_exceptions/security/ir.model.access.csv'
--- sale_exceptions/security/ir.model.access.csv 1970-01-01 00:00:00 +0000
+++ sale_exceptions/security/ir.model.access.csv 2013-11-15 12:55:43 +0000
@@ -0,0 +1,3 @@
1"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
2"access_sale_exception","sale.exception","model_sale_exception","base.group_user",1,0,0,0
3"access_sale_exception_manager","sale.exception","model_sale_exception","base.group_sale_manager",1,1,1,1
04
=== added directory 'sale_exceptions/settings'
=== added file 'sale_exceptions/settings/sale.exception.csv'
--- sale_exceptions/settings/sale.exception.csv 1970-01-01 00:00:00 +0000
+++ sale_exceptions/settings/sale.exception.csv 2013-11-15 12:55:43 +0000
@@ -0,0 +1,5 @@
1"id","name","description","sequence","model","code","active"
2"excep_no_zip","No ZIP code on destination",,50,"sale.order","if not order.partner_shipping_id.zip:
3 failed=True",False
4"excep_no_stock","Not Enough Virtual Stock",,50,"sale.order.line","if line.product_id and line.product_id.type == 'product' and line.product_id.virtual_available < line.product_uom_qty:
5 failed=True",False
06
=== added directory 'sale_exceptions/wizard'
=== added file 'sale_exceptions/wizard/__init__.py'
--- sale_exceptions/wizard/__init__.py 1970-01-01 00:00:00 +0000
+++ sale_exceptions/wizard/__init__.py 2013-11-15 12:55:43 +0000
@@ -0,0 +1,24 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2011 Akretion LTDA.
6# authors: Raphaël Valyi, Renato Lima
7# Copyright (C) 2010-2012 Akretion Sébastien BEAU <sebastien.beau@akretion.com>
8# Copyright (C) 2012 Camptocamp SA (Guewen Baconnier)
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU Affero General Public License as
12# published by the Free Software Foundation, either version 3 of the
13# License, or (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU Affero General Public License for more details.
19#
20# You should have received a copy of the GNU Affero General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22#
23##############################################################################
24from . import sale_exception_confirm
025
=== added file 'sale_exceptions/wizard/sale_exception_confirm.py'
--- sale_exceptions/wizard/sale_exception_confirm.py 1970-01-01 00:00:00 +0000
+++ sale_exceptions/wizard/sale_exception_confirm.py 2013-11-15 12:55:43 +0000
@@ -0,0 +1,62 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Copyright Camptocamp SA
5# @author: Guewen Baconnier
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#
20##############################################################################
21from openerp.osv import orm, fields
22
23
24class SaleExceptionConfirm(orm.TransientModel):
25
26 _name = 'sale.exception.confirm'
27
28 _columns = {
29 'sale_id': fields.many2one('sale.order', 'Sale'),
30 'exception_ids': fields.many2many('sale.exception',
31 string='Exceptions to resolve',
32 readonly=True),
33 'ignore': fields.boolean('Ignore Exceptions'),
34 }
35
36 def default_get(self, cr, uid, fields, context=None):
37 if context is None:
38 context = {}
39 res = super(SaleExceptionConfirm, self).default_get(
40 cr, uid, fields, context=context)
41 order_obj = self.pool.get('sale.order')
42 sale_id = context.get('active_ids')
43 assert len(sale_id) == 1, "Only 1 ID accepted, got %r" % sale_id
44 sale_id = sale_id[0]
45 sale = order_obj.browse(cr, uid, sale_id, context=context)
46 exception_ids = [e.id for e in sale.exceptions_ids]
47 res.update({'exception_ids': [(6, 0, exception_ids)]})
48 res.update({'sale_id': sale_id})
49 return res
50
51 def action_confirm(self, cr, uid, ids, context=None):
52 if hasattr(ids, '__iter__'):
53 assert len(ids) == 1, "Only 1 ID accepted, got %r" % ids
54 ids = ids[0]
55 form = self.browse(cr, uid, ids, context=context)
56 if form.ignore:
57 self.pool.get('sale.order').write(
58 cr, uid,
59 form.sale_id.id,
60 {'ignore_exceptions': True},
61 context=context)
62 return {'type': 'ir.actions.act_window_close'}
063
=== added file 'sale_exceptions/wizard/sale_exception_confirm_view.xml'
--- sale_exceptions/wizard/sale_exception_confirm_view.xml 1970-01-01 00:00:00 +0000
+++ sale_exceptions/wizard/sale_exception_confirm_view.xml 2013-11-15 12:55:43 +0000
@@ -0,0 +1,39 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4
5 <record id="view_sale_exception_confirm" model="ir.ui.view">
6 <field name="name">Sale Exceptions</field>
7 <field name="model">sale.exception.confirm</field>
8 <field name="arch" type="xml">
9 <form string="Blocked in draft due to exceptions" version="7.0">
10 <group>
11 <field name="exception_ids" nolabel="1" colspan="4">
12 <tree string="Sale Exceptions">
13 <field name="name"/>
14 <field name="description"/>
15 </tree>
16 </field>
17 <newline/>
18 <field name="ignore" groups='base.group_sale_manager'/>
19 </group>
20 <footer>
21 <button name="action_confirm" string="_Close"
22 colspan="1" type="object" icon="gtk-ok" />
23 </footer>
24 </form>
25 </field>
26 </record>
27
28 <record id="action_sale_exception_confirm" model="ir.actions.act_window">
29 <field name="name">Blocked in draft due to exceptions</field>
30 <field name="type">ir.actions.act_window</field>
31 <field name="res_model">sale.exception.confirm</field>
32 <field name="view_type">form</field>
33 <field name="view_mode">form</field>
34 <field name="view_id" ref="view_sale_exception_confirm"/>
35 <field name="target">new</field>
36 </record>
37
38 </data>
39</openerp>

Subscribers

People subscribed via source and target branches