[MERGE] forward port of branch saas-4 up to 65f68c1

This commit is contained in:
Christophe Simonis
2014-06-03 19:45:19 +02:00
13 changed files with 75 additions and 42 deletions
+3 -1
View File
@@ -3,6 +3,7 @@ _build/
# dotfiles # dotfiles
.* .*
!.gitignore
# compiled python files # compiled python files
*.py[co] *.py[co]
# setup.py egg_info # setup.py egg_info
@@ -12,7 +13,8 @@ _build/
# hg stuff # hg stuff
*.orig *.orig
status status
# odoo filestore
openerp/filestore
# generated for windows installer? # generated for windows installer?
install/win32/*.bat install/win32/*.bat
install/win32/meta.py install/win32/meta.py
+1 -1
View File
@@ -74,7 +74,7 @@ class sale_order(osv.Model):
if not grid_id: if not grid_id:
raise osv.except_osv(_('No Grid Available!'), _('No grid matching for this carrier!')) 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.')) 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) grid = grid_obj.browse(cr, uid, grid_id, context=context)
+14 -16
View File
@@ -39,16 +39,15 @@ instance.edi.EdiImport = instance.web.Widget.extend({
}); });
} }
else { else {
$('<div>').dialog({ new instance.web.Dialog(this,{
modal: true, title: 'Import Successful!',
title: 'Import Successful!', buttons: {
buttons: { Ok: function() {
Ok: function() { this.parents('.modal').modal('hide');
$(this).dialog("close"); window.location = "/";
window.location = "/"; }
} }
} },$('<div>').html(_t('The document has been successfully imported!'))).open();
}).html(_t('The document has been successfully imported!'));
} }
}, },
on_imported_error: function(response){ on_imported_error: function(response){
@@ -58,13 +57,12 @@ instance.edi.EdiImport = instance.web.Widget.extend({
msg += "\n " + _t("Reason:") + response.data.message; msg += "\n " + _t("Reason:") + response.data.message;
} }
var params = {error: response, message: msg}; var params = {error: response, message: msg};
$(instance.web.qweb.render("CrashManager.warning", params)).dialog({ new instance.web.Dialog(this,{
title: _t("Document Import Notification"), title: _t("Document Import Notification"),
modal: true, buttons: {
buttons: { Ok: function() { this.parents('.modal').modal('hide');}
Ok: function() { $(this).dialog("close"); } }
} },$(instance.web.qweb.render("CrashManager.warning", params))).open();
});
} }
}); });
+5 -1
View File
@@ -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." + 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 )", "If you have too much registrations you are not able to confirm your event. (0 to ignore this rule )",
type='integer', 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', 'seats_available': fields.function(Event._get_seats, oldname='register_avail', string='Available Seats',
type='integer', multi='seats_reserved', type='integer', multi='seats_reserved',
store={ store={
+2 -1
View File
@@ -333,7 +333,8 @@ class gamification_goal(osv.Model):
if definition.computation_mode == 'sum': if definition.computation_mode == 'sum':
field_name = definition.field_id.name 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 new_value = res and res[0][field_name] or 0.0
else: # computation mode = count else: # computation mode = count
+20 -10
View File
@@ -889,9 +889,10 @@ class mail_thread(osv.AbstractModel):
thread_references = references or in_reply_to thread_references = references or in_reply_to
# 1. message is a reply to an existing message (exact match of message_id) # 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() msg_references = thread_references.split()
mail_message_ids = mail_msg_obj.search(cr, uid, [('message_id', 'in', msg_references)], context=context) 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) 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 model, thread_id = original_msg.model, original_msg.res_id
route = self.message_route_verify( route = self.message_route_verify(
@@ -905,7 +906,6 @@ class mail_thread(osv.AbstractModel):
return [route] return [route]
# 2. message is a reply to an existign thread (6.1 compatibility) # 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: if ref_match:
reply_thread_id = int(ref_match.group(1)) reply_thread_id = int(ref_match.group(1))
reply_model = ref_match.group(2) or fallback_model 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), (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) update_author=True, assert_model=True, create_fallback=True, allow_private=True, context=context)
if route: 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', _logger.info(
email_from, email_to, message_id, mail_message.id, custom_values, uid) '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] return [route]
# 3. Look for a matching mail.alias entry # 3. Look for a matching mail.alias entry
@@ -975,11 +976,12 @@ class mail_thread(osv.AbstractModel):
user_id = uid user_id = uid
_logger.info('No matching user_id for the alias %s', alias.alias_name) _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) 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, route = self.message_route_verify(cr, uid, message, message_dict, route,
update_author=True, assert_model=True, create_fallback=True, context=context) update_author=True, assert_model=True, create_fallback=True, context=context)
if route: 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) routes.append(route)
return routes return routes
@@ -993,15 +995,16 @@ class mail_thread(osv.AbstractModel):
thread_id = int(thread_id) thread_id = int(thread_id)
except: except:
thread_id = False 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, route = self.message_route_verify(cr, uid, message, message_dict,
(fallback_model, thread_id, custom_values, uid, None), (fallback_model, thread_id, custom_values, uid, None),
update_author=True, assert_model=True, context=context) update_author=True, assert_model=True, context=context)
if route: 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] return [route]
# AssertionError if no routes found and if no bounce occured # ValueError if no routes found and if no bounce occured
raise ValueError( raise ValueError(
'No possible route found for incoming message from %s to %s (Message-Id %s:). ' 'No possible route found for incoming message from %s to %s (Message-Id %s:). '
'Create an appropriate mail.alias or force the destination model.' % 'Create an appropriate mail.alias or force the destination model.' %
@@ -1171,7 +1174,14 @@ class mail_thread(osv.AbstractModel):
body = u'' body = u''
if save_original: if save_original:
attachments.append(('original_email.eml', message.as_string())) 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() encoding = message.get_content_charset()
body = message.get_payload(decode=True) body = message.get_payload(decode=True)
body = tools.ustr(body, encoding, errors='replace') body = tools.ustr(body, encoding, errors='replace')
@@ -43,10 +43,15 @@ class MailComposeMessage(osv.TransientModel):
}, context=context) }, context=context)
mass_mailing = self.pool['mail.mass_mailing'].browse(cr, uid, mass_mailing_id, context=context) mass_mailing = self.pool['mail.mass_mailing'].browse(cr, uid, mass_mailing_id, context=context)
for res_id in res_ids: for res_id in res_ids:
res[res_id]['mailing_id'] = mass_mailing.id res[res_id].update({
res[res_id]['statistics_ids'] = [(0, 0, { 'mailing_id': mass_mailing.id,
'model': wizard.model, 'statistics_ids': [(0, 0, {
'res_id': res_id, 'model': wizard.model,
'mass_mailing_id': mass_mailing.id, '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 return res
+5
View File
@@ -661,6 +661,8 @@ class purchase_order(osv.osv):
_('You must first cancel all invoices related to this purchase order.')) _('You must first cancel all invoices related to this purchase order.'))
self.pool.get('account.invoice') \ self.pool.get('account.invoice') \
.signal_invoice_cancel(cr, uid, map(attrgetter('id'), purchase.invoice_ids)) .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.write(cr, uid, ids, {'state': 'cancel'})
self.set_order_line_status(cr, uid, ids, 'cancel', context=context) self.set_order_line_status(cr, uid, ids, 'cancel', context=context)
self.signal_purchase_cancel(cr, uid, ids) 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) return super(purchase_order_line, self).copy_data(cr, uid, id, default, context)
def unlink(self, cr, uid, ids, context=None): 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_obj = self.pool.get('procurement.order')
procurement_ids_to_cancel = procurement_obj.search(cr, uid, [('purchase_line_id', 'in', ids)], context=context) procurement_ids_to_cancel = procurement_obj.search(cr, uid, [('purchase_line_id', 'in', ids)], context=context)
if procurement_ids_to_cancel: if procurement_ids_to_cancel:
+7 -3
View File
@@ -538,14 +538,18 @@ class sale_order(osv.osv):
if grouped: 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) 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 = '' invoice_ref = ''
origin_ref = ''
for o, l in val: 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'}) 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)) cr.execute('insert into sale_order_invoice_rel (order_id,invoice_id) values (%s,%s)', (o.id, res))
#remove last '|' in invoice_ref #remove last '|' in invoice_ref
if len(invoice_ref) >= 1: if len(invoice_ref) >= 1:
invoice_ref = 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: else:
for order, il in val: for order, il in val:
res = self._make_invoice(cr, uid, order, il, context=context) res = self._make_invoice(cr, uid, order, il, context=context)
+3
View File
@@ -2276,6 +2276,9 @@ class stock_move(osv.osv):
#Check moves that were pushed #Check moves that were pushed
if move.move_dest_id.state in ('waiting', 'confirmed'): 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']), 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) ('move_dest_id', '=', move.move_dest_id.id)], context=context)
#If no other moves for the move that got pushed: #If no other moves for the move that got pushed:
+2 -2
View File
@@ -779,9 +779,9 @@ instance.web_kanban.KanbanGroup = instance.web.Widget.extend({
}); });
var am = instance.webclient.action_manager; var am = instance.webclient.action_manager;
var form = am.dialog_widget.views.form.controller; 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() { form.on('record_saved', self, function() {
am.dialog.close(); am.dialog.$dialog_box.modal('hide');
self.view.do_reload(); self.view.do_reload();
}); });
}, },
@@ -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_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_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",,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_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_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 "access_ir_needaction_mixin","ir_needaction_mixin","model_ir_needaction_mixin",,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
108 access_multi_company_default manager multi_company_default Manager model_multi_company_default group_erp_manager 1 1 1 1
109 access_ir_filter all ir_filters all model_ir_filters 1 1 1 1
110 access_ir_config_parameter ir_config_parameter model_ir_config_parameter 1 0 0 0
111 access_ir_config_parameter_system ir_config_parameter_system model_ir_config_parameter group_system 1 1 1 1
112 access_ir_mail_server ir_mail_server model_ir_mail_server group_system 1 1 1 1
113 access_ir_actions_client ir_actions_client all model_ir_actions_client 1 0 0 0
114 access_ir_needaction_mixin ir_needaction_mixin model_ir_needaction_mixin 1 1 1 1
+1 -1
View File
@@ -2449,7 +2449,7 @@ class BaseModel(object):
fetched_data = cr.dictfetchall() fetched_data = cr.dictfetchall()
if not groupby_fields: 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'] many2onefields = [gb['field'] for gb in annotated_groupbys if gb['type'] == 'many2one']
if many2onefields: if many2onefields: