Merge lp:~camptocamp/openobject-addons/trunk-extra-sale-exceptions-2.0 into lp:openobject-addons/extra-trunk
- trunk-extra-sale-exceptions-2.0
- Merge into trunk-extra-addons
Status: | Merged |
---|---|
Merge reported by: | Guewen Baconnier @ Camptocamp |
Merged at revision: | not available |
Proposed branch: | lp:~camptocamp/openobject-addons/trunk-extra-sale-exceptions-2.0 |
Merge into: | lp:openobject-addons/extra-trunk |
Diff against target: |
543 lines (+326/-72) 8 files modified
sale_exceptions/__init__.py (+1/-0) sale_exceptions/__openerp__.py (+3/-2) sale_exceptions/sale.py (+124/-51) sale_exceptions/sale_exceptions_data.xml (+48/-13) sale_exceptions/sale_view.xml (+46/-6) sale_exceptions/wizard/__init__.py (+1/-0) sale_exceptions/wizard/sale_exception_confirm.py (+58/-0) sale_exceptions/wizard/sale_exception_confirm_view.xml (+45/-0) |
To merge this branch: | bzr merge lp:~camptocamp/openobject-addons/trunk-extra-sale-exceptions-2.0 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Sébastien BEAU - http://www.akretion.com | Pending | ||
Review via email: mp+94061@code.launchpad.net |
Commit message
Description of the change
Hi,
Here is my improvements on the module sale_exceptions.
I want to commit you my work before any merge, because I think it may have some impact on your clients.
In short :
- Instead of a method, each exception has now a python code, evaluated (safe_eval) during the execution. Indeed, you need super rights (group_system) to modify the exceptions code. So it let add, modify or deactivate (active field) each sale exception rule. Each rule is applied on the order or the sale order.
- The test_exceptions method (workflow condition) does not anymore raise an error, it returns only False when some exceptions have been found
- The confirm button on the sale order now open a nice popup (osv_memory) with the list of exceptions to fix. The Sale Manager has the possibility to bypass the exceptions and confirm it anyway.
Now, one remark:
I guess that this module has been created for a specific need, and that it contains default rules which are not really generic ('unknown' in line.product_
I propose to clean up all this base exceptions, do you agree? we should keep some good examples like No ZIP on Destination or Not Enough Virtual Stock.
Thanks
See you
Guewen
- 5627. By Guewen Baconnier @ Camptocamp
-
[FIX] removed necessary import
Preview Diff
1 | === modified file 'sale_exceptions/__init__.py' |
2 | --- sale_exceptions/__init__.py 2011-03-07 12:19:42 +0000 |
3 | +++ sale_exceptions/__init__.py 2012-02-21 21:55:24 +0000 |
4 | @@ -20,5 +20,6 @@ |
5 | ############################################################################## |
6 | |
7 | import sale |
8 | +import wizard |
9 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |
10 | |
11 | |
12 | === modified file 'sale_exceptions/__openerp__.py' |
13 | --- sale_exceptions/__openerp__.py 2012-02-13 09:31:57 +0000 |
14 | +++ sale_exceptions/__openerp__.py 2012-02-21 21:55:24 +0000 |
15 | @@ -23,11 +23,11 @@ |
16 | |
17 | { |
18 | 'name': 'Sale Exceptions', |
19 | - 'version': '1.0', |
20 | + 'version': '2.0', |
21 | 'category': 'Generic Modules/Sale', |
22 | 'description': """ |
23 | This module allows you attach several customizable exceptions to your sale order in a way that you can filter orders by exceptions type and fix them. |
24 | -This is escpecially useful in an order importation scenario such as with the base_sale_multi_channels module, because it's likely a few orders have errors when you import them (like product not found in OpenERP, wrong line format etc...) |
25 | +This is especially useful in an order importation scenario such as with the base_sale_multi_channels module, because it's likely a few orders have errors when you import them (like product not found in OpenERP, wrong line format etc...) |
26 | """, |
27 | 'author': 'Akretion', |
28 | 'website': 'http://www.akretion.com', |
29 | @@ -36,6 +36,7 @@ |
30 | 'update_xml': ['sale_workflow.xml', |
31 | 'sale_view.xml', |
32 | 'sale_exceptions_data.xml', |
33 | + 'wizard/sale_exception_confirm_view.xml', |
34 | 'security/ir.model.access.csv'], |
35 | 'demo_xml': [], |
36 | 'installable': True, |
37 | |
38 | === modified file 'sale_exceptions/sale.py' |
39 | --- sale_exceptions/sale.py 2012-02-13 09:33:17 +0000 |
40 | +++ sale_exceptions/sale.py 2012-02-21 21:55:24 +0000 |
41 | @@ -3,6 +3,7 @@ |
42 | # |
43 | # OpenERP, Open Source Management Solution |
44 | # Copyright (C) 2011 Akretion LTDA. |
45 | +# Copyright (C) 2012 Camptocamp SA (Guewen Baconnier) |
46 | # |
47 | # This program is free software: you can redistribute it and/or modify |
48 | # it under the terms of the GNU Affero General Public License as |
49 | @@ -19,20 +20,40 @@ |
50 | # |
51 | ############################################################################## |
52 | |
53 | -from datetime import datetime, timedelta |
54 | -from dateutil.relativedelta import relativedelta |
55 | import time |
56 | +import netsvc |
57 | |
58 | from osv import fields, osv |
59 | +from tools.safe_eval import safe_eval as eval |
60 | from tools.translate import _ |
61 | -import netsvc |
62 | |
63 | class sale_exception(osv.osv): |
64 | _name = "sale.exception" |
65 | _description = "Sale Exceptions" |
66 | _columns = { |
67 | 'name': fields.char('Exception Name', size=64, required=True, translate=True), |
68 | - 'sale_order_ids': fields.many2many('sale.order', 'sale_order_exception_rel', 'exception_id', 'sale_order_id', 'Sale Orders'), |
69 | + 'note': fields.text('Note'), |
70 | + 'sale_order_ids': fields.many2many('sale.order', 'sale_order_exception_rel', |
71 | + 'exception_id', 'sale_order_id', 'Sale Orders'), |
72 | + 'model': fields.selection([('sale.order', 'Sale Order'), ('sale.order.line', 'Sale Order Line')], |
73 | + string='Apply on', required=True), |
74 | + 'active': fields.boolean('Active'), |
75 | + 'code': fields.text('Python Code', |
76 | + help="Python code executed to check if the exception apply or not. The code must apply block = True to apply the exception."), |
77 | + } |
78 | + |
79 | + _defaults = { |
80 | + 'code': """# Python code. Use block = True to block the sale order. |
81 | +# You can use the following variables : |
82 | +# - self: ORM model of the record which is checked |
83 | +# - order or line: browse_record of the sale order or sale order line |
84 | +# - object: same as order or line, browse_record of the sale order or sale order line |
85 | +# - pool: ORM model pool (i.e. self.pool) |
86 | +# - time: Python time module |
87 | +# - cr: database cursor |
88 | +# - uid: current user id |
89 | +# - context: current context |
90 | +""" |
91 | } |
92 | |
93 | sale_exception() |
94 | @@ -41,58 +62,110 @@ |
95 | _inherit = "sale.order" |
96 | _columns = { |
97 | 'exceptions_ids': fields.many2many('sale.exception', 'sale_order_exception_rel', 'sale_order_id', 'exception_id', 'Exceptions'), |
98 | + 'ignore_exceptions': fields.boolean('Ignore Exceptions'), |
99 | } |
100 | |
101 | - def __add_exception(self, cr, uid, exceptions_list, exception_name): |
102 | - ir_model_data_id = self.pool.get('ir.model.data').search(cr, uid, [('name', '=', exception_name)])[0] |
103 | - except_id = self.pool.get('ir.model.data').read(cr, uid, ir_model_data_id, ['res_id'])['res_id'] |
104 | - if except_id not in exceptions_list: |
105 | - exceptions_list.append(except_id) |
106 | - |
107 | def test_all_draft_orders(self, cr, uid, context=None): |
108 | ids = self.search(cr, uid, [('state', '=', 'draft')]) |
109 | - for id in ids: |
110 | - try: |
111 | - self.test_exceptions(cr, uid, [id]) |
112 | - except Exception: |
113 | - pass |
114 | - return True |
115 | - |
116 | - def test_exceptions(self, cr, uid, ids, *args): |
117 | + self.test_exceptions(cr, uid, ids) |
118 | + return True |
119 | + |
120 | + def button_order_confirm(self, cr, uid, ids, context=None): |
121 | + exception_ids = self.detect_exceptions(cr, uid, ids, context=context) |
122 | + if exception_ids: |
123 | + model_data_obj = self.pool.get('ir.model.data') |
124 | + action = { |
125 | + 'type': 'ir.actions.act_window', |
126 | + 'view_type': 'form', |
127 | + 'view_mode': 'form', |
128 | + 'res_model': 'sale.exception.confirm', |
129 | + 'view_id': model_data_obj.get_object_reference(cr, uid, 'sale_exceptions', 'view_sale_exception_confirm')[1], |
130 | + 'target': 'new', |
131 | + } |
132 | + return action |
133 | + else: |
134 | + wf_service = netsvc.LocalService("workflow") |
135 | + wf_service.trg_validate(uid, 'sale.order', ids[0], 'order_confirm', cr) |
136 | + return True |
137 | + |
138 | + def test_exceptions(self, cr, uid, ids, context=None): |
139 | + """ |
140 | + Condition method for the workflow from draft to confirm |
141 | + """ |
142 | + exception_ids = self.detect_exceptions(cr, uid, ids, context=context) |
143 | + if exception_ids: |
144 | + return False |
145 | + return True |
146 | + |
147 | + def detect_exceptions(self, cr, uid, ids, context=None): |
148 | + exception_obj = self.pool.get('sale.exception') |
149 | + order_exception_ids = exception_obj.search(cr, uid, |
150 | + [('model', '=', 'sale.order')], context=context) |
151 | + line_exception_ids = exception_obj.search(cr, uid, |
152 | + [('model', '=', 'sale.order.line')], context=context) |
153 | + order_exceptions = exception_obj.browse(cr, uid, order_exception_ids, context=context) |
154 | + line_exceptions = exception_obj.browse(cr, uid, line_exception_ids, context=context) |
155 | + |
156 | + exception_ids = False |
157 | for order in self.browse(cr, uid, ids): |
158 | - new_exceptions = [] |
159 | - self.add_custom_order_exception(cr, uid, ids, order, new_exceptions, *args) |
160 | - self.write(cr, uid, [order.id], {'exceptions_ids': [(6, 0, new_exceptions)]}) |
161 | - cr.commit() |
162 | - if len(new_exceptions) != 0: |
163 | - raise osv.except_osv(_('Order has errors!'), "\n".join(ex.name for ex in self.pool.get('sale.exception').browse(cr, uid, new_exceptions))) |
164 | - return True |
165 | - |
166 | - def add_custom_order_exception(self, cr, uid, ids, order, exceptions, *args): |
167 | - self.detect_invalid_destination(cr, uid, order, exceptions) |
168 | - self.detect_no_zip(cr, uid, order, exceptions) |
169 | + if order.ignore_exceptions: |
170 | + continue |
171 | + exception_ids = self._detect_exceptions(cr, uid, order, |
172 | + order_exceptions, line_exceptions, context=context) |
173 | + |
174 | + self.write(cr, uid, [order.id], {'exceptions_ids': [(6, 0, exception_ids)]}) |
175 | + return exception_ids |
176 | + |
177 | + def _exception_rule_eval_context(self, cr, uid, obj_name, obj, context=None): |
178 | + if context is None: |
179 | + context = {} |
180 | + |
181 | + return {obj_name: obj, |
182 | + 'self': self.pool.get(obj._name), |
183 | + 'object': obj, |
184 | + 'obj': obj, |
185 | + 'pool': self.pool, |
186 | + 'cr': cr, |
187 | + 'uid': uid, |
188 | + 'user': self.pool.get('res.users').browse(cr, uid, uid), |
189 | + 'time': time, |
190 | + # copy context to prevent side-effects of eval |
191 | + 'context': dict(context),} |
192 | + |
193 | + def _rule_eval(self, cr, uid, rule, obj_name, obj, context): |
194 | + expr = rule.code |
195 | + space = self._exception_rule_eval_context(cr, uid, obj_name, obj, |
196 | + context=context) |
197 | + try: |
198 | + eval(expr, space, |
199 | + mode='exec', nocopy=True) # nocopy allows to return 'result' |
200 | + except Exception, e: |
201 | + raise osv.except_osv(_('Error'), _('Error when evaluating the sale exception rule :\n %s \n(%s)') % |
202 | + (rule.name, e)) |
203 | + return space.get('block', False) |
204 | + |
205 | + def _detect_exceptions(self, cr, uid, order, order_exceptions, line_exceptions, context=None): |
206 | + exception_ids = [] |
207 | + for rule in order_exceptions: |
208 | + if self._rule_eval(cr, uid, rule, 'order', order, context): |
209 | + exception_ids.append(rule.id) |
210 | + |
211 | for order_line in order.order_line: |
212 | - self.detect_wrong_product(cr, uid, order_line, exceptions) |
213 | - self.detect_not_enough_virtual_stock(cr, uid, order_line, exceptions) |
214 | - return exceptions |
215 | - |
216 | - def detect_invalid_destination(self, cr, uid, order, exceptions): |
217 | - no_delivery_carrier = self.pool.get('delivery.carrier').search(cr, uid, [('name', '=', 'No Delivery')]) |
218 | - if no_delivery_carrier: |
219 | - no_delivery_carrier_grid = self.pool.get('delivery.carrier').grid_get(cr, uid, no_delivery_carrier, order.partner_shipping_id.id) |
220 | - if no_delivery_carrier_grid: |
221 | - self.__add_exception(cr, uid, exceptions, 'excep_invalid_location') |
222 | - |
223 | - def detect_no_zip(self, cr, uid, order, exceptions): |
224 | - if not order.partner_shipping_id.zip: |
225 | - self.__add_exception(cr, uid, exceptions, 'excep_no_zip') |
226 | - |
227 | - def detect_wrong_product(self, cr, uid, order_line, exceptions): |
228 | - if order_line.product_id and 'unknown' in order_line.product_id.name.lower() or not order_line.product_id: |
229 | - self.__add_exception(cr, uid, exceptions, 'excep_product') |
230 | - |
231 | - def detect_not_enough_virtual_stock(self, cr, uid, order_line, exceptions): |
232 | - if order_line.product_id and order_line.product_id.type == 'product' and order_line.product_id.virtual_available < order_line.product_uom_qty: |
233 | - self.__add_exception(cr, uid, exceptions, 'excep_no_stock') |
234 | + for rule in line_exceptions: |
235 | + if rule.id in exception_ids: |
236 | + continue # we do not matter if the exception as already been |
237 | + # found for an order line of this order |
238 | + if self._rule_eval(cr, uid, rule, 'line', order_line, context): |
239 | + exception_ids.append(rule.id) |
240 | + |
241 | + return exception_ids |
242 | + |
243 | + def copy(self, cr, uid, id, default=None, context=None): |
244 | + if default is None: |
245 | + default = {} |
246 | + default.update({ |
247 | + 'ignore_exceptions': False, |
248 | + }) |
249 | + return super(sale_order, self).copy(cr, uid, id, default=default, context=context) |
250 | |
251 | sale_order() |
252 | |
253 | === modified file 'sale_exceptions/sale_exceptions_data.xml' |
254 | --- sale_exceptions/sale_exceptions_data.xml 2011-08-22 21:10:53 +0000 |
255 | +++ sale_exceptions/sale_exceptions_data.xml 2012-02-21 21:55:24 +0000 |
256 | @@ -1,30 +1,49 @@ |
257 | <?xml version="1.0" encoding="utf-8"?> |
258 | <openerp> |
259 | - <data> |
260 | + <data noupdate="1"> |
261 | <record id="excep_product" model="sale.exception"> |
262 | - <field name="name">Error: Product Not Found</field> |
263 | - </record> |
264 | - |
265 | - <record id="excep_invalid_location" model="sale.exception"> |
266 | - <field name="name">Error: Invalid Destination</field> |
267 | + <field name="name">Product Not Found</field> |
268 | + <field name="code">if line.product_id and 'unknown' in line.product_id.name.lower() or not line.product_id: |
269 | + block = True</field> |
270 | + <field name="model">sale.order.line</field> |
271 | + <field name="note"></field> |
272 | + <field name="active" eval="True"/> |
273 | </record> |
274 | |
275 | <record id="excep_no_zip" model="sale.exception"> |
276 | - <field name="name">Error: No Destination zip</field> |
277 | + <field name="name">No ZIP on Destination</field> |
278 | + <field name="code">if not order.partner_shipping_id.zip: |
279 | + block = True</field> |
280 | + <field name="model">sale.order</field> |
281 | + <field name="note"></field> |
282 | + <field name="active" eval="True"/> |
283 | </record> |
284 | |
285 | <record id="excep_wrong_line" model="sale.exception"> |
286 | - <field name="name">Error: Wrong Order Line Format</field> |
287 | + <field name="name">Wrong Order Line Format</field> |
288 | + <field name="code"></field> |
289 | + <field name="model">sale.order.line</field> |
290 | + <field name="note"></field> |
291 | + <field name="active" eval="False"/> |
292 | </record> |
293 | |
294 | <record id="excep_no_stock" model="sale.exception"> |
295 | - <field name="name">Error: Not enough virtual Stock</field> |
296 | + <field name="name">Not Enough Virtual Stock</field> |
297 | + <field name="code">if line.product_id and line.product_id.type == 'product' and line.product_id.virtual_available < line.product_uom_qty: |
298 | + block = True</field> |
299 | + <field name="model">sale.order.line</field> |
300 | + <field name="note"></field> |
301 | + <field name="active" eval="True"/> |
302 | </record> |
303 | |
304 | <record id="excep_no_component_stock" model="sale.exception"> |
305 | - <field name="name">Error: Not enough virtual stock of components</field> |
306 | + <field name="name">Not enough virtual stock of components</field> |
307 | + <field name="code"></field> |
308 | + <field name="model">sale.order.line</field> |
309 | + <field name="active" eval="False"/>could |
310 | + <field name="note"></field> |
311 | </record> |
312 | - |
313 | + |
314 | <record id="no_delivery_partner" model="res.partner"> |
315 | <field name="name">No Delivery</field> |
316 | </record> |
317 | @@ -35,11 +54,27 @@ |
318 | <field model="product.category" name="categ_id" search="[]"/> |
319 | </record> |
320 | |
321 | - <record id="no_delivery_carrier" model="delivery.carrier"> |
322 | + <!-- |
323 | + induce a dependency on delivery, is it really necessary ? |
324 | + |
325 | + <record id="excep_invalid_location" model="sale.exception"> |
326 | + <field name="name">Invalid Destination</field> |
327 | + <field name="code">carrier_obj = pool.get('delivery.carrier') |
328 | +no_delivery_carrier = carrier_obj.search(cr, uid, [('name', '=', 'No Delivery')]) |
329 | +if no_delivery_carrier: |
330 | + no_delivery_carrier_grid = carrier_obj.grid_get(cr, uid, no_delivery_carrier, order.partner_shipping_id.id) |
331 | + if no_delivery_carrier_grid: |
332 | + block=True</field> |
333 | + <field name="model">sale.order</field> |
334 | + <field name="note"></field> |
335 | + <field name="active" eval="True"/> |
336 | + </record> |
337 | + |
338 | + <record id="no_delivery_carrier" model="delivery.carrier"> |
339 | <field name="name">No Delivery</field> |
340 | <field name="partner_id" ref="no_delivery_partner"/> |
341 | <field name="product_id" ref="no_delivery_product"/> |
342 | - </record> |
343 | + </record>--> |
344 | |
345 | <record forcecreate="True" id="ir_cron_test_orders" model="ir.cron"> |
346 | <field name="name">Test Draft Orders</field> |
347 | |
348 | === modified file 'sale_exceptions/sale_view.xml' |
349 | --- sale_exceptions/sale_view.xml 2011-03-07 12:19:42 +0000 |
350 | +++ sale_exceptions/sale_view.xml 2012-02-21 21:55:24 +0000 |
351 | @@ -1,6 +1,39 @@ |
352 | <?xml version="1.0" ?> |
353 | <openerp> |
354 | <data> |
355 | + |
356 | + <record id="view_sale_exception_tree" model="ir.ui.view"> |
357 | + <field name="name">sale.exception.tree</field> |
358 | + <field name="model">sale.exception</field> |
359 | + <field name="type">tree</field> |
360 | + <field name="arch" type="xml"> |
361 | + <tree string="Sale Exception"> |
362 | + <field name="name"/> |
363 | + <field name="note"/> |
364 | + </tree> |
365 | + </field> |
366 | + </record> |
367 | + |
368 | + <record id="view_sale_exception_form" model="ir.ui.view"> |
369 | + <field name="name">sale.exception.form</field> |
370 | + <field name="model">sale.exception</field> |
371 | + <field name="type">form</field> |
372 | + <field name="arch" type="xml"> |
373 | + <form string="Sale Exception Setup"> |
374 | + <field name="name" colspan="4"/> |
375 | + <field name="note" colspan="4"/> |
376 | + <newline/> |
377 | + <group colspan="4" col="2" groups="base.group_sale_manager"> |
378 | + <field name="active"/> |
379 | + <group colspan="2" col="2" groups="base.group_system"> |
380 | + <field name="model"/> |
381 | + <field name="code"/> |
382 | + </group> |
383 | + </group> |
384 | + </form> |
385 | + </field> |
386 | + </record> |
387 | + |
388 | <record id="ir_actions_act_window_exceptions0" model="ir.actions.act_window"> |
389 | <field eval="[(6,0,[])]" name="groups_id"/> |
390 | <field name="context">{}</field> |
391 | @@ -14,6 +47,7 @@ |
392 | <field eval="0" name="multi"/> |
393 | <field name="type">ir.actions.act_window</field> |
394 | <field name="name">Exceptions</field> |
395 | + <field name="view_id" ref="view_sale_exception_tree"/> |
396 | </record> |
397 | |
398 | <menuitem action="ir_actions_act_window_exceptions0" id="ir_ui_menu_exceptions0" |
399 | @@ -24,12 +58,18 @@ |
400 | <field name="inherit_id" ref="sale.view_order_form"/> |
401 | <field name="name">sale.order.68956.inherit</field> |
402 | <field name="arch" type="xml"> |
403 | - <field name="note" position="replace"> |
404 | - <group colspan="6" col="6"> |
405 | - <field name="exceptions_ids" colspan="2"/> |
406 | - <field name="note" colspan="4"/> |
407 | - </group> |
408 | - </field> |
409 | + <data> |
410 | + <field name="note" position="after"> |
411 | + <group colspan="6" col="6"> |
412 | + <field name="exceptions_ids" colspan="2"/> |
413 | + <field name="note" colspan="4"/> |
414 | + </group> |
415 | + </field> |
416 | + <button name="order_confirm" position="attributes"> |
417 | + <attribute name="name">button_order_confirm</attribute> |
418 | + <attribute name="type">object</attribute> |
419 | + </button> |
420 | + </data> |
421 | </field> |
422 | <field name="model">sale.order</field> |
423 | <field name="type">form</field> |
424 | |
425 | === added directory 'sale_exceptions/wizard' |
426 | === added file 'sale_exceptions/wizard/__init__.py' |
427 | --- sale_exceptions/wizard/__init__.py 1970-01-01 00:00:00 +0000 |
428 | +++ sale_exceptions/wizard/__init__.py 2012-02-21 21:55:24 +0000 |
429 | @@ -0,0 +1,1 @@ |
430 | +import sale_exception_confirm |
431 | |
432 | === added file 'sale_exceptions/wizard/sale_exception_confirm.py' |
433 | --- sale_exceptions/wizard/sale_exception_confirm.py 1970-01-01 00:00:00 +0000 |
434 | +++ sale_exceptions/wizard/sale_exception_confirm.py 2012-02-21 21:55:24 +0000 |
435 | @@ -0,0 +1,58 @@ |
436 | +# -*- encoding: utf-8 -*- |
437 | +############################################################################## |
438 | +# |
439 | +# Copyright Camptocamp SA |
440 | +# @author: Guewen Baconnier |
441 | +# |
442 | +# This program is free software: you can redistribute it and/or modify |
443 | +# it under the terms of the GNU General Public License as published by |
444 | +# the Free Software Foundation, either version 3 of the License, or |
445 | +# (at your option) any later version. |
446 | +# |
447 | +# This program is distributed in the hope that it will be useful, |
448 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
449 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
450 | +# GNU General Public License for more details. |
451 | +# |
452 | +# You should have received a copy of the GNU General Public License |
453 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
454 | +# |
455 | +############################################################################## |
456 | + |
457 | +import netsvc |
458 | + |
459 | +from osv import osv, fields |
460 | + |
461 | + |
462 | +class SaleExceptionConfirm(osv.osv_memory): |
463 | + |
464 | + _name = 'sale.exception.confirm' |
465 | + |
466 | + _columns = { |
467 | + 'sale_id': fields.many2one('sale.order', 'Sale'), |
468 | + 'exception_ids': fields.many2many('sale.exception', string='Exceptions to resolve', readonly=True), |
469 | + 'ignore': fields.boolean('Ignore Exceptions'), |
470 | + } |
471 | + |
472 | + def default_get(self, cr, uid, fields, context=None): |
473 | + res = super(SaleExceptionConfirm, self).default_get(cr, uid, fields, context=context) |
474 | + order_obj = self.pool.get('sale.order') |
475 | + sale_id = context.get('active_id', False) |
476 | + if sale_id: |
477 | + sale = order_obj.browse(cr, uid, sale_id, context=context) |
478 | + exception_ids = [e.id for e in sale.exceptions_ids] |
479 | + res.update({'exception_ids': [(6, 0, exception_ids)]}) |
480 | + |
481 | + res.update({'sale_id': sale_id}) |
482 | + return res |
483 | + |
484 | + def action_confirm(self, cr, uid, ids, context=None): |
485 | + form = self.browse(cr, uid, ids[0], context=context) |
486 | + if form.ignore: |
487 | + self.pool.get('sale.order').write(cr, uid, form.sale_id.id, |
488 | + {'ignore_exceptions': True}, context=context) |
489 | + wf_service = netsvc.LocalService("workflow") |
490 | + wf_service.trg_validate(uid, 'sale.order', form.sale_id.id, 'order_confirm', cr) |
491 | + return {'type': 'ir.actions.act_window_close'} |
492 | + |
493 | +SaleExceptionConfirm() |
494 | |
495 | === added file 'sale_exceptions/wizard/sale_exception_confirm_view.xml' |
496 | --- sale_exceptions/wizard/sale_exception_confirm_view.xml 1970-01-01 00:00:00 +0000 |
497 | +++ sale_exceptions/wizard/sale_exception_confirm_view.xml 2012-02-21 21:55:24 +0000 |
498 | @@ -0,0 +1,45 @@ |
499 | +<?xml version="1.0" encoding="utf-8"?> |
500 | +<openerp> |
501 | + <data> |
502 | + |
503 | + <record id="view_sale_exception_confirm" model="ir.ui.view"> |
504 | + <field name="name">Sale Exceptions</field> |
505 | + <field name="model">sale.exception.confirm</field> |
506 | + <field name="type">form</field> |
507 | + <field name="arch" type="xml"> |
508 | + <form string="Sale Exceptions"> |
509 | + <separator colspan="4" string="Exceptions on the sale order" /> |
510 | + <field name="exception_ids" colspan="4"> |
511 | + <form string="Sale Exception"> |
512 | + <field name="name" colspan="4"/> |
513 | + <field name="note" colspan="4"/> |
514 | + </form> |
515 | + <tree string="Sale Exceptions"> |
516 | + <field name="name"/> |
517 | + <field name="note"/> |
518 | + </tree> |
519 | + </field> |
520 | + <newline/> |
521 | + <field name="ignore" groups='base.group_sale_manager'/> |
522 | + <newline/> |
523 | + <separator colspan="4"/> |
524 | + <group col="2" colspan="4"> |
525 | + <button name="action_confirm" string="_Ok" |
526 | + colspan="1" type="object" icon="gtk-ok" /> |
527 | + </group> |
528 | + </form> |
529 | + </field> |
530 | + </record> |
531 | + |
532 | + <record id="action_sale_exception_confirm" model="ir.actions.act_window"> |
533 | + <field name="name">Sale Exceptions</field> |
534 | + <field name="type">ir.actions.act_window</field> |
535 | + <field name="res_model">sale.exception.confirm</field> |
536 | + <field name="view_type">form</field> |
537 | + <field name="view_mode">form</field> |
538 | + <field name="view_id" ref="view_sale_exception_confirm"/> |
539 | + <field name="target">new</field> |
540 | + </record> |
541 | + |
542 | + </data> |
543 | +</openerp> |