From f96a03247b371c4d2e9317a46d645a4b66e2f1fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ana=C3=ABl=20Closson?= Date: Thu, 22 May 2014 09:26:40 +0200 Subject: [PATCH 01/15] [FIX] stock: auto_validate move not set to done when multiple parent move set to done at the same time - opw 607970 --- addons/stock/stock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index d2d5ba3fab1..64a43852ad4 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -2413,7 +2413,7 @@ class stock_move(osv.osv): # Downstream move should only be triggered if this move is the last pending upstream move other_upstream_move_ids = self.search(cr, uid, [('id','!=',move.id),('state','not in',['done','cancel']), ('move_dest_id','=',move.move_dest_id.id)], context=context) - if not other_upstream_move_ids: + if not set(other_upstream_move_ids) - set(move_ids): self.write(cr, uid, [move.id], {'move_history_ids': [(4, move.move_dest_id.id)]}) if move.move_dest_id.state in ('waiting', 'confirmed'): self.force_assign(cr, uid, [move.move_dest_id.id], context=context) From 41c5ceb8ebb95c1b4e98d8dd1f12b8e547a24b1d Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 27 May 2014 17:56:30 +0200 Subject: [PATCH 02/15] [FIX] stock: clean previous commit --- addons/stock/stock.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 64a43852ad4..4c45de266d7 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -2411,9 +2411,9 @@ class stock_move(osv.osv): picking_ids.append(move.picking_id.id) if move.move_dest_id.id and (move.state != 'done'): # Downstream move should only be triggered if this move is the last pending upstream move - other_upstream_move_ids = self.search(cr, uid, [('id','!=',move.id),('state','not in',['done','cancel']), + other_upstream_move_ids = self.search(cr, uid, [('id','not in',move_ids),('state','not in',['done','cancel']), ('move_dest_id','=',move.move_dest_id.id)], context=context) - if not set(other_upstream_move_ids) - set(move_ids): + if not other_upstream_move_ids: self.write(cr, uid, [move.id], {'move_history_ids': [(4, move.move_dest_id.id)]}) if move.move_dest_id.state in ('waiting', 'confirmed'): self.force_assign(cr, uid, [move.move_dest_id.id], context=context) From 65d7cc524d9f3a5dc833cb483ccaaf86fab4819d Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Wed, 28 May 2014 18:00:11 +0200 Subject: [PATCH 03/15] [FIX] purchase: Do not allow to delete a purchase order line which is validated This restriction behavior copied from the sale.order model Moreover, the purchase.order lines state were not set to cancel when the purchase order was cancelled. This is now the case, this behavior is also coped from the sale.order model When the purchase order is reset to draft, we also reset the order lines state to draft --- addons/purchase/purchase.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 6767f9185ac..d258eee7431 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -499,6 +499,8 @@ class purchase_order(osv.osv): if not len(ids): return False self.write(cr, uid, ids, {'state':'draft','shipped':0}) + for purchase in self.browse(cr, uid, ids, context=context): + self.pool['purchase.order.line'].write(cr, uid, [l.id for l in purchase.order_line], {'state': 'draft'}) wf_service = netsvc.LocalService("workflow") for p_id in ids: # Deleting the existing instance of workflow for PO @@ -596,6 +598,8 @@ class purchase_order(osv.osv): _('You must first cancel all receptions related to this purchase order.')) if inv: wf_service.trg_validate(uid, 'account.invoice', inv.id, 'invoice_cancel', cr) + self.pool['purchase.order.line'].write(cr, uid, [l.id for l in purchase.order_line], + {'state': 'cancel'}) self.write(cr,uid,ids,{'state':'cancel'}) for (id, name) in self.name_get(cr, uid, ids): @@ -901,6 +905,8 @@ class purchase_order_line(osv.osv): def unlink(self, cr, uid, ids, context=None): procurement_ids_to_cancel = [] for line in self.browse(cr, uid, ids, context=context): + if line.state not in ['draft', 'cancel']: + raise osv.except_osv(_('Invalid Action!'), _('Cannot delete a purchase order line which is in state \'%s\'.') %(line.state,)) if line.move_dest_id: procurement_ids_to_cancel.extend(procurement.id for procurement in line.move_dest_id.procurements) if procurement_ids_to_cancel: From 3f6f43214ff363e3f39160828ca4e6dfa80128dd Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Wed, 28 May 2014 18:17:33 +0200 Subject: [PATCH 04/15] [FIX] replace .bzrignore by .gitignore, as we are now working with git --- .bzrignore | 21 --------------------- .gitignore | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 21 deletions(-) delete mode 100644 .bzrignore create mode 100644 .gitignore diff --git a/.bzrignore b/.bzrignore deleted file mode 100644 index e7618c7b0ce..00000000000 --- a/.bzrignore +++ /dev/null @@ -1,21 +0,0 @@ -.*.swp -.bzrignore -.idea -.project -.pydevproject -.ropeproject -.settings -.DS_Store -openerp/addons/* -openerp/filestore* -.Python -*.pyc -*.pyo -bin/* -build/ -include/ -lib/ -share/ -doc/_build/* -win32/*.bat -win32/meta.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..7828a605ef0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# sphinx build directories +_build/ + +# dotfiles +.* +!.gitignore +# compiled python files +*.py[co] +# setup.py egg_info +*.egg-info +# emacs backup files +*~ +# hg stuff +*.orig +status +# odoo filestore +openerp/filestore +# generated for windows installer? +install/win32/*.bat +install/win32/meta.py + +# various virtualenv +/bin/ +/build/ +/dist/ +/include/ +/lib/ +/man/ +/share/ +/src/ From 310ecb0d60123094bdc1da6d7915e71749a1e480 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Quenot Date: Wed, 28 May 2014 15:45:29 +0200 Subject: [PATCH 05/15] Fix detection of MIME type in message_parse() Be careful, content-type may contain tricky content like in the following example so test the MIME type with startswith() Content-Type: multipart/related; boundary="_004_3f1e4da175f349248b8d43cdeb9866f1AMSPR06MB343eurprd06pro_"; type="text/html" --- addons/mail/mail_thread.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 90ae3cf0aa3..942daddd439 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -780,7 +780,14 @@ class mail_thread(osv.AbstractModel): body = u'' if save_original: attachments.append(('original_email.eml', message.as_string())) - if not message.is_multipart() or 'text/' in message.get('content-type', ''): + + # Be careful, content-type may contain tricky content like in the + # following example so test the MIME type with startswith() + # + # Content-Type: multipart/related; + # boundary="_004_3f1e4da175f349248b8d43cdeb9866f1AMSPR06MB343eurprd06pro_"; + # type="text/html" + if not message.is_multipart() or message.get('content-type', '').startswith("text/"): encoding = message.get_content_charset() body = message.get_payload(decode=True) body = tools.ustr(body, encoding, errors='replace') From a7a51cf55d9bd514328e23e3af6f9dac6d57d532 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Mon, 2 Jun 2014 12:57:57 +0200 Subject: [PATCH 06/15] [FIX] base: security, group settings is authorized to alter ir.config_parameter --- openerp/addons/base/security/ir.model.access.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/openerp/addons/base/security/ir.model.access.csv b/openerp/addons/base/security/ir.model.access.csv index e17ffb314e1..5e288903629 100644 --- a/openerp/addons/base/security/ir.model.access.csv +++ b/openerp/addons/base/security/ir.model.access.csv @@ -112,6 +112,7 @@ "access_multi_company_default manager","multi_company_default Manager","model_multi_company_default","group_erp_manager",1,1,1,1 "access_ir_filter all","ir_filters all","model_ir_filters",,1,1,1,1 "access_ir_config_parameter","ir_config_parameter","model_ir_config_parameter",,1,0,0,0 +"access_ir_config_parameter","ir_config_parameter","model_ir_config_parameter","group_system",1,1,1,1 "access_ir_mail_server","ir_mail_server","model_ir_mail_server","group_system",1,1,1,1 "access_ir_actions_client","ir_actions_client all","model_ir_actions_client",,1,0,0,0 "access_ir_needaction_mixin","ir_needaction_mixin","model_ir_needaction_mixin",,1,1,1,1 From e61fb4d80afb7b5cdf1db99a51129693c450f49b Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Mon, 2 Jun 2014 13:00:36 +0200 Subject: [PATCH 07/15] [FIX] base: security, do not override basic ir_config_paramter ACL --- openerp/addons/base/security/ir.model.access.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/addons/base/security/ir.model.access.csv b/openerp/addons/base/security/ir.model.access.csv index 5e288903629..3fa980f4368 100644 --- a/openerp/addons/base/security/ir.model.access.csv +++ b/openerp/addons/base/security/ir.model.access.csv @@ -112,7 +112,7 @@ "access_multi_company_default manager","multi_company_default Manager","model_multi_company_default","group_erp_manager",1,1,1,1 "access_ir_filter all","ir_filters all","model_ir_filters",,1,1,1,1 "access_ir_config_parameter","ir_config_parameter","model_ir_config_parameter",,1,0,0,0 -"access_ir_config_parameter","ir_config_parameter","model_ir_config_parameter","group_system",1,1,1,1 +"access_ir_config_parameter_system","ir_config_parameter_system","model_ir_config_parameter","group_system",1,1,1,1 "access_ir_mail_server","ir_mail_server","model_ir_mail_server","group_system",1,1,1,1 "access_ir_actions_client","ir_actions_client all","model_ir_actions_client",,1,0,0,0 "access_ir_needaction_mixin","ir_needaction_mixin","model_ir_needaction_mixin",,1,1,1,1 From cbdf7830b1cc75a57f3a473f6199032cd4123378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 2 Jun 2014 13:52:27 +0200 Subject: [PATCH 08/15] [FIX] mass_mailing: fixes: - fixed keeping the original message for routing, only when choosing to reply in the original thread (notification=True) - auto delete sent emails explicitely - mail_thread: routing: fixed replies always choosen even when replying to emails with a specified reply_to (using ref_match in the algorithm) - mail_thread: routing: instead of exclusive routing heuristics, use each case as a fallback of the previous. --- addons/mail/mail_thread.py | 44 +++++++++++-------- addons/mass_mailing/models/mass_mailing.py | 9 +++- .../wizard/mail_compose_message.py | 17 ++++--- 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 3cc2f60ed69..9acfb07c13b 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -879,22 +879,23 @@ class mail_thread(osv.AbstractModel): thread_references = references or in_reply_to # 1. message is a reply to an existing message (exact match of message_id) + ref_match = thread_references and tools.reference_re.search(thread_references) msg_references = thread_references.split() mail_message_ids = mail_msg_obj.search(cr, uid, [('message_id', 'in', msg_references)], context=context) - if mail_message_ids: + if ref_match and mail_message_ids: original_msg = mail_msg_obj.browse(cr, SUPERUSER_ID, mail_message_ids[0], context=context) model, thread_id = original_msg.model, original_msg.res_id - _logger.info( - 'Routing mail from %s to %s with Message-Id %s: direct reply to msg: model: %s, thread_id: %s, custom_values: %s, uid: %s', - email_from, email_to, message_id, model, thread_id, custom_values, uid) route = self.message_route_verify( cr, uid, message, message_dict, (model, thread_id, custom_values, uid, None), - update_author=True, assert_model=True, create_fallback=True, context=context) - return route and [route] or [] + update_author=True, assert_model=False, create_fallback=True, context=context) + if route: + _logger.info( + 'Routing mail from %s to %s with Message-Id %s: direct reply to msg: model: %s, thread_id: %s, custom_values: %s, uid: %s', + email_from, email_to, message_id, model, thread_id, custom_values, uid) + return [route] # 2. message is a reply to an existign thread (6.1 compatibility) - ref_match = thread_references and tools.reference_re.search(thread_references) if ref_match: reply_thread_id = int(ref_match.group(1)) reply_model = ref_match.group(2) or fallback_model @@ -912,14 +913,15 @@ class mail_thread(osv.AbstractModel): ('res_id', '=', thread_id), ], context=context) if compat_mail_msg_ids and model_obj.exists(cr, uid, thread_id) and hasattr(model_obj, 'message_update'): - _logger.info( - 'Routing mail from %s to %s with Message-Id %s: direct thread reply (compat-mode) to model: %s, thread_id: %s, custom_values: %s, uid: %s', - email_from, email_to, message_id, model, thread_id, custom_values, uid) route = self.message_route_verify( cr, uid, message, message_dict, (model, thread_id, custom_values, uid, None), update_author=True, assert_model=True, create_fallback=True, context=context) - return route and [route] or [] + if route: + _logger.info( + 'Routing mail from %s to %s with Message-Id %s: direct thread reply (compat-mode) to model: %s, thread_id: %s, custom_values: %s, uid: %s', + email_from, email_to, message_id, model, thread_id, custom_values, uid) + return [route] # 2. Reply to a private message if in_reply_to: @@ -929,12 +931,14 @@ class mail_thread(osv.AbstractModel): ], limit=1, context=context) if mail_message_ids: mail_message = mail_msg_obj.browse(cr, uid, mail_message_ids[0], context=context) - _logger.info('Routing mail from %s to %s with Message-Id %s: direct reply to a private message: %s, custom_values: %s, uid: %s', - email_from, email_to, message_id, mail_message.id, custom_values, uid) route = self.message_route_verify(cr, uid, message, message_dict, (mail_message.model, mail_message.res_id, custom_values, uid, None), update_author=True, assert_model=True, create_fallback=True, context=context) - return route and [route] or [] + if route: + _logger.info( + 'Routing mail from %s to %s with Message-Id %s: direct reply to a private message: %s, custom_values: %s, uid: %s', + email_from, email_to, message_id, mail_message.id, custom_values, uid) + return [route] # 3. Look for a matching mail.alias entry # Delivered-To is a safe bet in most modern MTAs, but we have to fallback on To + Cc values @@ -962,11 +966,12 @@ class mail_thread(osv.AbstractModel): user_id = uid _logger.info('No matching user_id for the alias %s', alias.alias_name) route = (alias.alias_model_id.model, alias.alias_force_thread_id, eval(alias.alias_defaults), user_id, alias) - _logger.info('Routing mail from %s to %s with Message-Id %s: direct alias match: %r', - email_from, email_to, message_id, route) route = self.message_route_verify(cr, uid, message, message_dict, route, update_author=True, assert_model=True, create_fallback=True, context=context) if route: + _logger.info( + 'Routing mail from %s to %s with Message-Id %s: direct alias match: %r', + email_from, email_to, message_id, route) routes.append(route) return routes @@ -980,15 +985,16 @@ class mail_thread(osv.AbstractModel): thread_id = int(thread_id) except: thread_id = False - _logger.info('Routing mail from %s to %s with Message-Id %s: fallback to model:%s, thread_id:%s, custom_values:%s, uid:%s', - email_from, email_to, message_id, fallback_model, thread_id, custom_values, uid) route = self.message_route_verify(cr, uid, message, message_dict, (fallback_model, thread_id, custom_values, uid, None), update_author=True, assert_model=True, context=context) if route: + _logger.info( + 'Routing mail from %s to %s with Message-Id %s: fallback to model:%s, thread_id:%s, custom_values:%s, uid:%s', + email_from, email_to, message_id, fallback_model, thread_id, custom_values, uid) return [route] - # AssertionError if no routes found and if no bounce occured + # ValueError if no routes found and if no bounce occured raise ValueError( 'No possible route found for incoming message from %s to %s (Message-Id %s:). ' 'Create an appropriate mail.alias or force the destination model.' % diff --git a/addons/mass_mailing/models/mass_mailing.py b/addons/mass_mailing/models/mass_mailing.py index f721ed2f385..bfeb3aeeafe 100644 --- a/addons/mass_mailing/models/mass_mailing.py +++ b/addons/mass_mailing/models/mass_mailing.py @@ -532,8 +532,12 @@ class MassMailing(osv.Model): #------------------------------------------------------ def get_recipients(self, cr, uid, mailing, context=None): - domain = eval(mailing.mailing_domain) - res_ids = self.pool[mailing.mailing_model].search(cr, uid, domain, context=context) + if mailing.mailing_domain: + domain = eval(mailing.mailing_domain) + res_ids = self.pool[mailing.mailing_model].search(cr, uid, domain, context=context) + else: + res_ids = [] + domain = [('id', 'in', res_ids)] # randomly choose a fragment if mailing.contact_ab_pc < 100: @@ -567,6 +571,7 @@ class MassMailing(osv.Model): 'composition_mode': 'mass_mail', 'mass_mailing_id': mailing.id, 'mailing_list_ids': [(4, l.id) for l in mailing.contact_list_ids], + 'same_thread': mailing.reply_to_mode == 'thread', } if mailing.reply_to_mode == 'email': composer_values['reply_to'] = mailing.reply_to diff --git a/addons/mass_mailing/wizard/mail_compose_message.py b/addons/mass_mailing/wizard/mail_compose_message.py index 4aebff2d04d..ede3fe37803 100644 --- a/addons/mass_mailing/wizard/mail_compose_message.py +++ b/addons/mass_mailing/wizard/mail_compose_message.py @@ -43,10 +43,15 @@ class MailComposeMessage(osv.TransientModel): }, context=context) mass_mailing = self.pool['mail.mass_mailing'].browse(cr, uid, mass_mailing_id, context=context) for res_id in res_ids: - res[res_id]['mailing_id'] = mass_mailing.id - res[res_id]['statistics_ids'] = [(0, 0, { - 'model': wizard.model, - 'res_id': res_id, - 'mass_mailing_id': mass_mailing.id, - })] + res[res_id].update({ + 'mailing_id': mass_mailing.id, + 'statistics_ids': [(0, 0, { + 'model': wizard.model, + 'res_id': res_id, + 'mass_mailing_id': mass_mailing.id, + })], + # email-mode: keep original message for routing + 'notification': mass_mailing.reply_to_mode == 'thread', + 'auto_delete': True, + }) return res From 772a5d6b75c6da4114e923972b218c89a9f8cadd Mon Sep 17 00:00:00 2001 From: Cedric Snauwaert Date: Mon, 2 Jun 2014 16:41:58 +0200 Subject: [PATCH 09/15] [FIX]modal close function in kanban and edi --- addons/edi/static/src/js/edi.js | 30 +++++++++++------------ addons/web_kanban/static/src/js/kanban.js | 4 +-- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/addons/edi/static/src/js/edi.js b/addons/edi/static/src/js/edi.js index 91d2b842f8a..69f06356127 100644 --- a/addons/edi/static/src/js/edi.js +++ b/addons/edi/static/src/js/edi.js @@ -39,16 +39,15 @@ instance.edi.EdiImport = instance.web.Widget.extend({ }); } else { - $('
').dialog({ - modal: true, - title: 'Import Successful!', - buttons: { - Ok: function() { - $(this).dialog("close"); - window.location = "/"; + new instance.web.Dialog(this,{ + title: 'Import Successful!', + buttons: { + Ok: function() { + this.parents('.modal').modal('hide'); + window.location = "/"; + } } - } - }).html(_t('The document has been successfully imported!')); + },$('
').html(_t('The document has been successfully imported!'))).open(); } }, on_imported_error: function(response){ @@ -58,13 +57,12 @@ instance.edi.EdiImport = instance.web.Widget.extend({ msg += "\n " + _t("Reason:") + response.data.message; } var params = {error: response, message: msg}; - $(instance.web.qweb.render("CrashManager.warning", params)).dialog({ - title: _t("Document Import Notification"), - modal: true, - buttons: { - Ok: function() { $(this).dialog("close"); } - } - }); + new instance.web.Dialog(this,{ + title: _t("Document Import Notification"), + buttons: { + Ok: function() { this.parents('.modal').modal('hide');} + } + },$(instance.web.qweb.render("CrashManager.warning", params))).open(); } }); diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index db4056167a7..5e5d39203a6 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -779,9 +779,9 @@ instance.web_kanban.KanbanGroup = instance.web.Widget.extend({ }); var am = instance.webclient.action_manager; var form = am.dialog_widget.views.form.controller; - form.on("on_button_cancel", am.dialog, am.dialog.close); + form.on("on_button_cancel", am.dialog, function() { return am.dialog.$dialog_box.modal('hide'); }); form.on('record_saved', self, function() { - am.dialog.close(); + am.dialog.$dialog_box.modal('hide'); self.view.do_reload(); }); }, From 6e5bef9bd2b2f4644d1c10b01e8228fd29665bdd Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Mon, 2 Jun 2014 18:00:25 +0200 Subject: [PATCH 10/15] [FIX] calendar: avoid double popup, opw 606297 Some browsers (e.g. chrome) trigger onEmptyClick as well as onBeforeLightbox during drag&drop which calls two slow_create calls (and two popups). Workaround to kill the second one. --- addons/web_calendar/static/src/js/calendar.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/addons/web_calendar/static/src/js/calendar.js b/addons/web_calendar/static/src/js/calendar.js index f7f08c3f6e3..f2cc0f74fcd 100644 --- a/addons/web_calendar/static/src/js/calendar.js +++ b/addons/web_calendar/static/src/js/calendar.js @@ -57,6 +57,7 @@ instance.web_calendar.CalendarView = instance.web.View.extend({ this.range_stop = null; this.update_range_dates(Date.today()); this.selected_filters = []; + this.is_slow_open = false; }, view_loading: function(r) { return this.load_calendar(r); @@ -465,6 +466,13 @@ instance.web_calendar.CalendarView = instance.web.View.extend({ }, slow_create: function(event_id, event_obj) { var self = this; + // Workaround, some browsers trigger onEmptyClick as well as onBeforeLightbox + // during drag&drop which calls two slow_create calls, kills the second one + if (this.is_slow_open) { + scheduler.deleteEvent(event_id); + return; + } + this.is_slow_open = true; if (this.current_mode() === 'month') { event_obj['start_date'].addHours(8); if (event_obj._length === 1) { @@ -487,6 +495,7 @@ instance.web_calendar.CalendarView = instance.web.View.extend({ view_id: pop_infos.view_id, }); pop.on('closed', self, function() { + this.is_slow_open = false; if (!something_saved) { scheduler.deleteEvent(event_id); } From 3cb551bf50618d3b481789c430359ba705b42db0 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 3 Jun 2014 10:20:55 +0200 Subject: [PATCH 11/15] [FIX] read_group: do not change the return format of the read group in case of no groupby_fields and keep the same return format as with groupby fields (a list of dict) --- openerp/osv/orm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index cb033ed7994..fdd39d4cb76 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -2459,7 +2459,7 @@ class BaseModel(object): fetched_data = cr.dictfetchall() if not groupby_fields: - return {r.pop('id'): r for r in fetched_data} + return fetched_data many2onefields = [gb['field'] for gb in annotated_groupbys if gb['type'] == 'many2one'] if many2onefields: From aa1165441c880f248cb4c7df23e5590a7e8a43ca Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 3 Jun 2014 10:48:26 +0200 Subject: [PATCH 12/15] [FIX] gamification: do not group on agregated field for sum The read_group behaviour has changed and read_group no longer sum on the field you are grouping. Instead agregate on the field we want to sum and use no groupby. For future version, grouping on a user such as what is done in batch mode is a better way to process. --- addons/gamification/models/goal.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/gamification/models/goal.py b/addons/gamification/models/goal.py index 904e6fafa36..266e25a5eb8 100644 --- a/addons/gamification/models/goal.py +++ b/addons/gamification/models/goal.py @@ -333,7 +333,8 @@ class gamification_goal(osv.Model): if definition.computation_mode == 'sum': field_name = definition.field_id.name - res = obj.read_group(cr, uid, domain, [field_name], [field_name], context=context) + # TODO for master: group on user field in batch mode + res = obj.read_group(cr, uid, domain, [field_name], [], context=context) new_value = res and res[0][field_name] or 0.0 else: # computation mode = count From e7a0b1bb9095048914e11eb1915c20d336f74c08 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Tue, 3 Jun 2014 12:21:34 +0200 Subject: [PATCH 13/15] [FIX] event_sale: stored seats_max field seats_max is a stored field in event, and should remain stored when overrided in event_sale --- addons/event_sale/event_sale.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/addons/event_sale/event_sale.py b/addons/event_sale/event_sale.py index b9db63e125c..90cac8168ec 100644 --- a/addons/event_sale/event_sale.py +++ b/addons/event_sale/event_sale.py @@ -137,6 +137,11 @@ class event_event(osv.osv): pass return [] + def _get_ticket_events(self, cr, uid, ids, context=None): + # `self` is the event.event.ticket model when called by ORM! + return list(set(ticket.event_id.id + for ticket in self.browse(cr, uid, ids, context))) + _columns = { 'event_ticket_ids': fields.one2many('event.event.ticket', "event_id", "Event Ticket"), 'seats_max': fields.function(_get_seats_max, @@ -144,7 +149,11 @@ class event_event(osv.osv): help="The maximum registration level is equal to the sum of the maximum registration of event ticket." + "If you have too much registrations you are not able to confirm your event. (0 to ignore this rule )", type='integer', - readonly=True) + readonly=True, + store={ + 'event.event': (lambda self, cr, uid, ids, c = {}: ids, ['event_ticket_ids'], 20), + 'event.event.ticket': (_get_ticket_events, ['seats_max'], 10), + }), } _defaults = { 'event_ticket_ids': _get_tickets From 7a157dd35e414968282596cb6e0cf1c09da249ec Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Tue, 3 Jun 2014 17:12:07 +0200 Subject: [PATCH 14/15] [FIX] delivery: allow to add delivery method in quotation if quotation in quotation sent stage opw-607920 --- addons/delivery/sale.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/delivery/sale.py b/addons/delivery/sale.py index da5e8fd3fd6..d2916eb2085 100644 --- a/addons/delivery/sale.py +++ b/addons/delivery/sale.py @@ -53,7 +53,7 @@ class sale_order(osv.osv): if not grid_id: raise osv.except_osv(_('No Grid Available!'), _('No grid matching for this carrier!')) - if not order.state in ('draft'): + if not order.state in ('draft', 'sent'): raise osv.except_osv(_('Order not in Draft State!'), _('The order state have to be draft to add delivery lines.')) grid = grid_obj.browse(cr, uid, grid_id, context=context) From 0c4bc1cf72d69110246fd133f6c9f55245fa3bd0 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 3 Jun 2014 16:56:07 +0200 Subject: [PATCH 15/15] [FIX] sale: keep customer reference in grouped invoice (opw 606523) --- addons/sale/sale.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 364ca36b1c2..af62b014c05 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -525,14 +525,18 @@ class sale_order(osv.osv): if grouped: res = self._make_invoice(cr, uid, val[0][0], reduce(lambda x, y: x + y, [l for o, l in val], []), context=context) invoice_ref = '' + origin_ref = '' for o, l in val: - invoice_ref += o.name + '|' + invoice_ref += (o.client_order_ref or o.name) + '|' + origin_ref += (o.origin or o.name) + '|' self.write(cr, uid, [o.id], {'state': 'progress'}) cr.execute('insert into sale_order_invoice_rel (order_id,invoice_id) values (%s,%s)', (o.id, res)) #remove last '|' in invoice_ref - if len(invoice_ref) >= 1: + if len(invoice_ref) >= 1: invoice_ref = invoice_ref[:-1] - invoice.write(cr, uid, [res], {'origin': invoice_ref, 'name': invoice_ref}) + if len(origin_ref) >= 1: + origin_ref = origin_ref[:-1] + invoice.write(cr, uid, [res], {'origin': origin_ref, 'name': invoice_ref}) else: for order, il in val: res = self._make_invoice(cr, uid, order, il, context=context)