diff --git a/.gitignore b/.gitignore index 4b38dc02ac7d81f071a41dbecf00701eb5a555e1..4c3277c521505fb8b1dec8b003c10674114bc79e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ _build/ # dotfiles .* +!.gitignore # compiled python files *.py[co] # setup.py egg_info @@ -12,7 +13,8 @@ _build/ # hg stuff *.orig status - +# odoo filestore +openerp/filestore # generated for windows installer? install/win32/*.bat install/win32/meta.py diff --git a/addons/delivery/sale.py b/addons/delivery/sale.py index 1bfeabfe15df8ffae1372203646388a6fb304ca1..4ef3224fc3e93b7bead29ddcdd90396cc7027c9c 100644 --- a/addons/delivery/sale.py +++ b/addons/delivery/sale.py @@ -74,7 +74,7 @@ class sale_order(osv.Model): if not grid_id: raise osv.except_osv(_('No Grid Available!'), _('No grid matching for this carrier!')) - if order.state != 'draft': + if order.state not 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) diff --git a/addons/edi/static/src/js/edi.js b/addons/edi/static/src/js/edi.js index 91d2b842f8ab9e71d7c08252a8e06edc28b6e699..69f06356127f50fd895c0fbd53b632e437bed321 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 { - $('<div>').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!')); + },$('<div>').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/event_sale/event_sale.py b/addons/event_sale/event_sale.py index 528e86cf16853baeb976211dec020cf606bd529c..ed99e6a39cc6ac7055cc8aae48dc0f8896c4db31 100644 --- a/addons/event_sale/event_sale.py +++ b/addons/event_sale/event_sale.py @@ -159,7 +159,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), + }), 'seats_available': fields.function(Event._get_seats, oldname='register_avail', string='Available Seats', type='integer', multi='seats_reserved', store={ diff --git a/addons/gamification/models/goal.py b/addons/gamification/models/goal.py index 904e6fafa364ac33a4cac99a5385855a85269a74..266e25a5eb8a5fa726cd9ca258d819a3a5bfc887 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 diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 3ae373624ae8bc4d5d5b711b8619c07a90612ff8..36d6ecbf6308fae161ead6e99cdf7ea872debf59 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -889,9 +889,10 @@ 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 route = self.message_route_verify( @@ -905,7 +906,6 @@ class mail_thread(osv.AbstractModel): 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 @@ -945,8 +945,9 @@ class mail_thread(osv.AbstractModel): (mail_message.model, mail_message.res_id, custom_values, uid, None), update_author=True, assert_model=True, create_fallback=True, allow_private=True, context=context) 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) + _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 @@ -975,11 +976,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 @@ -993,15 +995,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.' % @@ -1171,7 +1174,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') diff --git a/addons/mass_mailing/wizard/mail_compose_message.py b/addons/mass_mailing/wizard/mail_compose_message.py index 4aebff2d04dbe8a65f0c3b21ad404954a8dadeb2..ede3fe3780374ab6ada91db209b999742729f3c6 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 diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 22b1af1d26aff82f035b596feafda72046bb1a42..bfdd83e0872143e8b6121caa1a5ecb0b6df1f86f 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -661,6 +661,8 @@ class purchase_order(osv.osv): _('You must first cancel all invoices related to this purchase order.')) self.pool.get('account.invoice') \ .signal_invoice_cancel(cr, uid, map(attrgetter('id'), purchase.invoice_ids)) + 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'}) self.set_order_line_status(cr, uid, ids, 'cancel', context=context) self.signal_purchase_cancel(cr, uid, ids) @@ -982,6 +984,9 @@ class purchase_order_line(osv.osv): return super(purchase_order_line, self).copy_data(cr, uid, id, default, context) def unlink(self, cr, uid, ids, context=None): + 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,)) procurement_obj = self.pool.get('procurement.order') procurement_ids_to_cancel = procurement_obj.search(cr, uid, [('purchase_line_id', 'in', ids)], context=context) if procurement_ids_to_cancel: diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 70dece226514cd535f5747a55b1719e02e480a79..45c32a33b22384af596855a83d939ad5dacaad80 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -538,14 +538,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) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index e640c6daec4102ca00aea97b673ed463530dfa61..d6d28c919e62687cfceb9f97a3b81c5182169b32 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -2276,6 +2276,9 @@ class stock_move(osv.osv): #Check moves that were pushed if move.move_dest_id.state in ('waiting', 'confirmed'): + # FIXME is opw 607970 still present with new WMS? + # (see commits 1ef2c181033bd200906fb1e5ce35e234bf566ac6 + # and 41c5ceb8ebb95c1b4e98d8dd1f12b8e547a24b1d) 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 no other moves for the move that got pushed: diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 2ea29674d8e511361a392a61162fbd6930edf619..91641216c44b10290c206cf046dca0e313a17cdb 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(); }); }, diff --git a/openerp/addons/base/security/ir.model.access.csv b/openerp/addons/base/security/ir.model.access.csv index 6113f17c5aacc3235141f133e22d6e0665cb4fb2..c70733b5140a73a1dcbfd890888b0fc5a506958d 100644 --- a/openerp/addons/base/security/ir.model.access.csv +++ b/openerp/addons/base/security/ir.model.access.csv @@ -108,6 +108,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_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 diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 76b4d7ee3ffa93d8454db22222dca52b8e64449c..e85a35b790a9c5d684f3e182dc2348d7c2e8dea4 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -2449,7 +2449,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: