product_obj = self.pool.get('product.product')
analytic_journal_obj =self.pool.get('account.analytic.journal')
product_price_type_obj = self.pool.get('product.price.type')
+ product_uom_obj = self.pool.get('product.uom')
j_id = analytic_journal_obj.browse(cr, uid, journal_id, context=context)
prod = product_obj.browse(cr, uid, prod_id, context=context)
result = 0.0
if prod_id:
- unit = prod.uom_id.id
+ unit_obj = False
+ if unit:
+ unit_obj = product_uom_obj.browse(cr, uid, unit, context=context)
+ if not unit_obj or prod.uom_id.category_id.id != unit_obj.category_id.id:
+ unit = prod.uom_id.id
if j_id.type == 'purchase':
- unit = prod.uom_po_id.id
+ if not unit_obj or prod.uom_po_id.category_id.id != unit_obj.category_id.id:
+ unit = prod.uom_po_id.id
if j_id.type <> 'sale':
a = prod.property_account_expense.id
if not a:
assert len(self) == 1, 'This option should only be used for a single id at a time.'
template = self.env.ref('account.email_template_edi_invoice', False)
compose_form = self.env.ref('mail.email_compose_message_wizard_form', False)
- ctx = dict(self._context,
+ ctx = dict(
default_model='account.invoice',
default_res_id=self.id,
default_use_template=bool(template),
continue
ctx = dict(self._context, lang=inv.partner_id.lang)
- date_invoice = inv.date_invoice or fields.Date.context_today(self)
+
+ if not inv.date_invoice:
+ inv.with_context(ctx).write({'date_invoice': fields.Date.context_today(self)})
+ date_invoice = inv.date_invoice
company_currency = inv.company_id.currency_id
# create the analytical lines, one move line per invoice line
move = account_move.with_context(ctx).create(move_vals)
# make the invoice point to that move
vals = {
- 'date_invoice': date_invoice,
'move_id': move.id,
'period_id': period.id,
'move_name': move.name,
if inv.type in ('in_invoice', 'in_refund'):
ref = inv.reference
else:
- ref = self._convert_ref(cr, uid, inv.number)
+ ref = self._convert_ref(inv.number)
obj_move_line = acct_ins_obj.browse(cr, uid, il['analytics_id'], context=context)
ctx = context.copy()
ctx.update({'date': inv.date_invoice})
""" Wrap the methods `create` and `write` of the models specified by
the rules given by `ids` (or all existing rules if `ids` is `None`.)
"""
+ #
+ # Note: the patched methods create and write must be defined inside
+ # another function, otherwise their closure may be wrong. For instance,
+ # the function create refers to the outer variable 'create', which you
+ # expect to be bound to create itself. But that expectation is wrong if
+ # create is defined inside a loop; in that case, the variable 'create'
+ # is bound to the last function defined by the loop.
+ #
+
+ def make_create():
+ """ instanciate a create method that processes action rules """
+ def create(self, cr, uid, vals, context=None, **kwargs):
+ # avoid loops or cascading actions
+ if context and context.get('action'):
+ return create.origin(self, cr, uid, vals, context=context)
+
+ # call original method with a modified context
+ context = dict(context or {}, action=True)
+ new_id = create.origin(self, cr, uid, vals, context=context, **kwargs)
+
+ # as it is a new record, we do not consider the actions that have a prefilter
+ action_model = self.pool.get('base.action.rule')
+ action_dom = [('model', '=', self._name),
+ ('kind', 'in', ['on_create', 'on_create_or_write'])]
+ action_ids = action_model.search(cr, uid, action_dom, context=context)
+
+ # check postconditions, and execute actions on the records that satisfy them
+ for action in action_model.browse(cr, uid, action_ids, context=context):
+ if action_model._filter(cr, uid, action, action.filter_id, [new_id], context=context):
+ action_model._process(cr, uid, action, [new_id], context=context)
+ return new_id
+
+ return create
+
+ def make_write():
+ """ instanciate a write method that processes action rules """
+ def write(self, cr, uid, ids, vals, context=None, **kwargs):
+ # avoid loops or cascading actions
+ if context and context.get('action'):
+ return write.origin(self, cr, uid, ids, vals, context=context)
+
+ # modify context
+ context = dict(context or {}, action=True)
+ ids = [ids] if isinstance(ids, (int, long, str)) else ids
+
+ # retrieve the action rules to possibly execute
+ action_model = self.pool.get('base.action.rule')
+ action_dom = [('model', '=', self._name),
+ ('kind', 'in', ['on_write', 'on_create_or_write'])]
+ action_ids = action_model.search(cr, uid, action_dom, context=context)
+ actions = action_model.browse(cr, uid, action_ids, context=context)
+
+ # check preconditions
+ pre_ids = {}
+ for action in actions:
+ pre_ids[action] = action_model._filter(cr, uid, action, action.filter_pre_id, ids, context=context)
+
+ # call original method
+ write.origin(self, cr, uid, ids, vals, context=context, **kwargs)
+
+ # check postconditions, and execute actions on the records that satisfy them
+ for action in actions:
+ post_ids = action_model._filter(cr, uid, action, action.filter_id, pre_ids[action], context=context)
+ if post_ids:
+ action_model._process(cr, uid, action, post_ids, context=context)
+ return True
+
+ return write
+
updated = False
if ids is None:
ids = self.search(cr, SUPERUSER_ID, [])
model_obj = self.pool[model]
if not hasattr(model_obj, 'base_action_ruled'):
# monkey-patch methods create and write
-
- def create(self, cr, uid, vals, context=None, **kwargs):
- # avoid loops or cascading actions
- if context and context.get('action'):
- return create.origin(self, cr, uid, vals, context=context)
-
- # call original method with a modified context
- context = dict(context or {}, action=True)
- new_id = create.origin(self, cr, uid, vals, context=context, **kwargs)
-
- # as it is a new record, we do not consider the actions that have a prefilter
- action_model = self.pool.get('base.action.rule')
- action_dom = [('model', '=', self._name),
- ('kind', 'in', ['on_create', 'on_create_or_write'])]
- action_ids = action_model.search(cr, uid, action_dom, context=context)
-
- # check postconditions, and execute actions on the records that satisfy them
- for action in action_model.browse(cr, uid, action_ids, context=context):
- if action_model._filter(cr, uid, action, action.filter_id, [new_id], context=context):
- action_model._process(cr, uid, action, [new_id], context=context)
- return new_id
-
- def write(self, cr, uid, ids, vals, context=None, **kwargs):
- # avoid loops or cascading actions
- if context and context.get('action'):
- return write.origin(self, cr, uid, ids, vals, context=context)
-
- # modify context
- context = dict(context or {}, action=True)
- ids = [ids] if isinstance(ids, (int, long, str)) else ids
-
- # retrieve the action rules to possibly execute
- action_model = self.pool.get('base.action.rule')
- action_dom = [('model', '=', self._name),
- ('kind', 'in', ['on_write', 'on_create_or_write'])]
- action_ids = action_model.search(cr, uid, action_dom, context=context)
- actions = action_model.browse(cr, uid, action_ids, context=context)
-
- # check preconditions
- pre_ids = {}
- for action in actions:
- pre_ids[action] = action_model._filter(cr, uid, action, action.filter_pre_id, ids, context=context)
-
- # call original method
- write.origin(self, cr, uid, ids, vals, context=context, **kwargs)
-
- # check postconditions, and execute actions on the records that satisfy them
- for action in actions:
- post_ids = action_model._filter(cr, uid, action, action.filter_id, pre_ids[action], context=context)
- if post_ids:
- action_model._process(cr, uid, action, post_ids, context=context)
- return True
-
- model_obj._patch_method('create', create)
- model_obj._patch_method('write', write)
+ model_obj._patch_method('create', make_create())
+ model_obj._patch_method('write', make_write())
model_obj.base_action_ruled = True
updated = True
return {'type': 'ir.actions.act_window_close'}
def _generate_query(self, fields, maximum_group=100):
- group_fields = ', '.join(fields)
+ sql_fields = []
+ for field in fields:
+ if field in ['email', 'name']:
+ sql_fields.append('lower(%s)' % field)
+ elif field in ['vat']:
+ sql_fields.append("replace(%s, ' ', '')" % field)
+ else:
+ sql_fields.append(field)
+
+ group_fields = ', '.join(sql_fields)
filters = []
for field in fields:
- if field in ['email', 'name']:
+ if field in ['email', 'name', 'vat']:
filters.append((field, 'IS NOT', 'NULL'))
criteria = ' AND '.join('%s %s %s' % (field, operator, value)
from openerp.addons.base.res.res_partner import format_address
from openerp.osv import fields, osv, orm
from openerp.tools.translate import _
+from openerp.tools import email_re
CRM_LEAD_FIELDS_TO_MERGE = ['name',
'partner_id',
return {'value':{'country_id':country_id}}
return {}
+ def message_partner_info_from_emails(self, cr, uid, id, emails, link_mail=False, context=None):
+ res = super(crm_lead, self).message_partner_info_from_emails(cr, uid, id, emails, link_mail=link_mail, context=context)
+ lead = self.browse(cr, uid, id, context=context)
+ for partner_info in res:
+ if not partner_info.get('partner_id') and (lead.partner_name or lead.contact_name):
+ emails = email_re.findall(partner_info['full_name'] or '')
+ email = emails and emails[0] or ''
+ if email and lead.email_from and email.lower() == lead.email_from.lower():
+ partner_info['full_name'] = '%s <%s>' % (lead.partner_name or lead.contact_name, email)
+ break
+ return res
+
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
('done', 'Done')
], string='Status', default='draft', readonly=True, required=True, copy=False,
help="If event is created, the status is 'Draft'. If event is confirmed for the particular dates the status is set to 'Confirmed'. If the event is over, the status is set to 'Done'. If event is cancelled the status is set to 'Cancelled'.")
- email_registration_id = fields.Many2one('email.template', string='Registration Confirmation Email',
+ email_registration_id = fields.Many2one(
+ 'email.template', string='Registration Confirmation Email',
+ domain=[('model', '=', 'event.registration')],
help='This field contains the template of the mail that will be automatically sent each time a registration for this event is confirmed.')
- email_confirmation_id = fields.Many2one('email.template', string='Event Confirmation Email',
+ email_confirmation_id = fields.Many2one(
+ 'email.template', string='Event Confirmation Email',
+ domain=[('model', '=', 'event.registration')],
help="If you set an email template, each participant will receive this email announcing the confirmation of the event.")
reply_to = fields.Char(string='Reply-To Email',
readonly=False, states={'done': [('readonly', True)]},
class event_registration(models.Model):
- """Event Registration"""
- _name= 'event.registration'
+ _name = 'event.registration'
+ _description = 'Event Registration'
_inherit = ['mail.thread', 'ir.needaction_mixin']
_order = 'name, create_date desc'
# 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 = mail_header_msgid_re.findall(thread_references)
mail_message_ids = mail_msg_obj.search(cr, uid, [('message_id', 'in', msg_references)], context=context)
if ref_match and mail_message_ids:
original_msg = mail_msg_obj.browse(cr, SUPERUSER_ID, mail_message_ids[0], context=context)
email_from, email_to, message_id, model, thread_id, custom_values, uid)
return [route]
- # 2. Reply to a private message
+ # 3. Reply to a private message
if in_reply_to:
mail_message_ids = mail_msg_obj.search(cr, uid, [
('message_id', '=', in_reply_to),
email_from, email_to, message_id, mail_message.id, custom_values, uid)
return [route]
- # 3. Look for a matching mail.alias entry
+ # 4. 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
# for all the odd MTAs out there, as there is no standard header for the envelope's `rcpt_to` value.
rcpt_tos = \
routes.append(route)
return routes
- # 4. Fallback to the provided parameters, if they work
+ # 5. Fallback to the provided parameters, if they work
if not thread_id:
# Legacy: fallback to matching [ID] in the Subject
match = tools.res_re.search(decode_header(message, 'Subject'))
}
.openerp .oe_group_button {
- position: absolute;
- padding-bottom: 50px;
bottom: 0;
}
contact_ids = Contacts.search(cr, SUPERUSER_ID, [('list_id', '=', int(list_id)), ('email', '=', email)], context=context)
if not contact_ids:
- contact_ng = Contacts.name_create(cr, SUPERUSER_ID, email, context=context)
- Contacts.write(cr, SUPERUSER_ID, [contact_ng[0]], {'list_id': int(list_id)}, context=context)
+ Contacts.add_to_list(cr, SUPERUSER_ID, email, int(list_id), context=context)
# add email to session
request.session['mass_mailing_email'] = email
return True
'list_id': _get_latest_list
}
- def name_create(self, cr, uid, name, context=None):
+ def get_name_email(self, name, context):
name, email = self.pool['res.partner']._parse_partner_name(name, context=context)
if name and not email:
email = name
if email and not name:
name = email
+ return name, email
+
+ def name_create(self, cr, uid, name, context=None):
+ name, email = self.get_name_email(name, context=context)
rec_id = self.create(cr, uid, {'name': name, 'email': email}, context=context)
return self.name_get(cr, uid, [rec_id], context)[0]
+ def add_to_list(self, cr, uid, name, list_id, context=None):
+ name, email = self.get_name_email(name, context=context)
+ rec_id = self.create(cr, uid, {'name': name, 'email': email, 'list_id': list_id}, context=context)
+ return self.name_get(cr, uid, [rec_id], context)[0]
+
def message_get_default_recipients(self, cr, uid, ids, context=None):
res = {}
for record in self.browse(cr, uid, ids, context=context):
font-weight: normal;
font-style: normal;
}
+
@font-face {
font-family: "EntypoRegular";
src: url("/web/static/src/font/entypo-webfont.eot") format("eot");
font-weight: normal;
font-style: normal;
}
+
#oe_main_menu_navbar {
min-height: 34px;
z-index: 1001;
vertical-align: top;
}
.openerp .oe_title {
- width: 38%;
float: left;
}
.openerp .oe_title:after {
.openerp .oe_form_dirty button.oe_highlight_on_dirty:hover {
background: #ed6f6a;
}
+.openerp .oe_warning_redirect {
+ border: none !important;
+ padding: 0 !important;
+ margin-left: 20px !important;
+ background: #f5f7f9 !important;
+ box-shadow: none !important;
+}
+.openerp .oe_warning_redirect:hover {
+ text-decoration: underline !important;
+}
.openerp .oe_stat_button {
font-weight: normal;
width: 132px !important;
display: inline;
vertical-align: middle;
}
-.openerp .oe_warning_redirect {
- border: none !important;
- padding: 0 !important;
- margin-left: 20px !important;
- background: #f5f7f9 !important;
- box-shadow: none !important;
-}
-.openerp .oe_warning_redirect:hover {
- text-decoration: underline !important;
-}
.openerp .oe_stat_button:hover {
background: #7c7bad;
color: white;
top: 0px;
}
}
+
.kitten-mode-activated {
background-size: cover;
background-attachment: fixed;
td
vertical-align: top
.oe_title
- width: 38%
- float: left
+ float: left
.oe_title:after
content: "."
display: block
this.init_dialog();
}
this.$buttons.insertAfter(this.$dialog_box.find(".modal-body"));
+ $('.tooltip').remove(); //remove open tooltip if any to prevent them staying when modal is opened
//add to list of currently opened modal
opened_modal.push(this.$dialog_box);
return this;
}, 0);
});
instance.web.bus.on('click', this, function(ev) {
- $.fn.tooltip('destroy');
+ $('.tooltip').remove();
if (!$(ev.target).is('input[type=file]')) {
self.$el.find('.oe_dropdown_menu.oe_opened, .oe_dropdown_toggle.oe_opened').removeClass('oe_opened');
}
/* Bootstrap defaults overwrite */
$.fn.tooltip.Constructor.DEFAULTS.placement = 'auto top';
$.fn.tooltip.Constructor.DEFAULTS.html = true;
-$.fn.tooltip.Constructor.DEFAULTS.container = 'body';
+$.fn.tooltip.Constructor.DEFAULTS.trigger = 'hover focus click';
//overwrite bootstrap tooltip method to prevent showing 2 tooltip at the same time
var bootstrap_show_function = $.fn.tooltip.Constructor.prototype.show;
$.fn.tooltip.Constructor.prototype.show = function () {
if (e.isDefaultPrevented() || !inDom) return;
return bootstrap_show_function.call(this);
};
+//overwrite bootstrap tooltip init method in order to check if tooltip is in a modal or not and
+//if so it needs to have a container body in order to be visible
+var bootstrap_init_tooltip_fnct = $.fn.tooltip.Constructor.prototype.init;
+$.fn.tooltip.Constructor.prototype.init = function (type, element, options) {
+ options = options || {}
+ if ($('.modal[aria-hidden="false"]').length !== 0){
+ if (options && !options.container){
+ options = _.extend({container: 'body'},options);
+ }
+ }
+ return bootstrap_init_tooltip_fnct.call(this, type, element, options);
+}
/**
* Registry for all the client actions key: tag value: widget
trigger = trigger || this.$el;
options = _.extend({
delay: { show: 500, hide: 0 },
- trigger: 'hover',
title: function() {
var template = widget.template + '.tooltip';
if (!QWeb.has_template(template)) {
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 class="modal-title"><t t-raw="title"/></h3>
</div>
- <div class="modal-body" style="overflow-y: auto;">
+ <div class="modal-body">
</div>
</div>
</div>
if country_id:
domain += [('country_id', '=', country_id)]
if not any(x['country_id'][0] == country_id for x in countries):
- country = country_obj.browse(cr, uid, country_id, context)
- countries.append({
- 'country_id_count': 0,
- 'country_id': (country_id, country.name)
- })
+ country = country_obj.read(cr, uid, country_id, ['name'], context)
+ if country:
+ countries.append({
+ 'country_id_count': 0,
+ 'country_id': (country_id, country['name'])
+ })
countries.sort(key=lambda d: d['country_id'][1])
countries.insert(0, {
order_by = self._generate_order_by(order, query)
from_clause, where_clause, where_clause_params = query.get_sql()
- limit_str = limit and ' limit %d' % limit or ''
- offset_str = offset and ' offset %d' % offset or ''
where_str = where_clause and (" WHERE %s" % where_clause) or ''
- query_str = 'SELECT "%s".id FROM ' % self._table + from_clause + where_str + order_by + limit_str + offset_str
if count:
- # /!\ the main query must be executed as a subquery, otherwise
- # offset and limit apply to the result of count()!
- cr.execute('SELECT count(*) FROM (%s) AS count' % query_str, where_clause_params)
+ # Ignore order, limit and offset when just counting, they don't make sense and could
+ # hurt performance
+ query_str = 'SELECT count(1) FROM ' + from_clause + where_str
+ cr.execute(query_str, where_clause_params)
res = cr.fetchone()
return res[0]
+ limit_str = limit and ' limit %d' % limit or ''
+ offset_str = offset and ' offset %d' % offset or ''
+ query_str = 'SELECT "%s".id FROM ' % self._table + from_clause + where_str + order_by + limit_str + offset_str
cr.execute(query_str, where_clause_params)
res = cr.fetchall()