[MERGE] forward port of branch saas-3 up to fdc6271
This commit is contained in:
commit
56f2b7ae0f
|
@ -145,7 +145,7 @@ class email_template(osv.osv):
|
|||
# - img src -> check URL
|
||||
# - a href -> check URL
|
||||
for node in root.iter():
|
||||
if node.tag == 'a':
|
||||
if node.tag == 'a' and node.get('href'):
|
||||
node.set('href', _process_link(node.get('href')))
|
||||
elif node.tag == 'img' and not node.get('src', 'data').startswith('data'):
|
||||
node.set('src', _process_link(node.get('src')))
|
||||
|
|
|
@ -285,7 +285,6 @@ class hr_expense_expense(osv.osv):
|
|||
if not mres:
|
||||
continue
|
||||
res.append(mres)
|
||||
tax_code_found= False
|
||||
|
||||
#Calculate tax according to default tax on product
|
||||
taxes = []
|
||||
|
@ -303,32 +302,28 @@ class hr_expense_expense(osv.osv):
|
|||
a = product.categ_id.property_account_expense_categ.id
|
||||
a = fpos_obj.map_account(cr, uid, fpos, a)
|
||||
taxes = a and self.pool.get('account.account').browse(cr, uid, a, context=context).tax_ids or False
|
||||
tax_id = fpos_obj.map_tax(cr, uid, fpos, taxes)
|
||||
if not taxes:
|
||||
continue
|
||||
tax_l = []
|
||||
#Calculating tax on the line and creating move?
|
||||
for tax in tax_obj.compute_all(cr, uid, taxes,
|
||||
line.unit_amount ,
|
||||
line.unit_quantity, line.product_id,
|
||||
exp.user_id.partner_id)['taxes']:
|
||||
tax_code_id = tax['base_code_id']
|
||||
tax_amount = line.total_amount * tax['base_sign']
|
||||
if tax_code_found:
|
||||
if not tax_code_id:
|
||||
continue
|
||||
res.append(self.move_line_get_item(cr, uid, line, context))
|
||||
res[-1]['price'] = 0.0
|
||||
res[-1]['account_analytic_id'] = False
|
||||
elif not tax_code_id:
|
||||
if not tax_code_id:
|
||||
continue
|
||||
tax_code_found = True
|
||||
res[-1]['tax_code_id'] = tax_code_id
|
||||
res[-1]['tax_amount'] = cur_obj.compute(cr, uid, exp.currency_id.id, company_currency, tax_amount, context={'date': exp.date_confirm})
|
||||
##
|
||||
is_price_include = tax_obj.read(cr,uid,tax['id'],['price_include'],context)['price_include']
|
||||
if is_price_include:
|
||||
## We need to deduce the price for the tax
|
||||
res[-1]['price'] = res[-1]['price'] - (tax['amount'] * tax['base_sign'] or 0.0)
|
||||
# tax amount countains base amount without the tax
|
||||
tax_amount = (line.total_amount - tax['amount']) * tax['base_sign']
|
||||
else:
|
||||
tax_amount = line.total_amount * tax['base_sign']
|
||||
res[-1]['tax_amount'] = cur_obj.compute(cr, uid, exp.currency_id.id, company_currency, tax_amount, context={'date': exp.date_confirm})
|
||||
assoc_tax = {
|
||||
'type':'tax',
|
||||
'name':tax['name'],
|
||||
|
@ -339,7 +334,8 @@ class hr_expense_expense(osv.osv):
|
|||
'tax_code_id': tax['tax_code_id'],
|
||||
'tax_amount': tax['amount'] * tax['base_sign'],
|
||||
}
|
||||
res.append(assoc_tax)
|
||||
tax_l.append(assoc_tax)
|
||||
res += tax_l
|
||||
return res
|
||||
|
||||
def move_line_get_item(self, cr, uid, line, context=None):
|
||||
|
|
|
@ -28,26 +28,15 @@ class res_partner(osv.osv):
|
|||
'nrc' : fields.char('NRC', size=16, help='Registration number at the Registry of Commerce'),
|
||||
}
|
||||
|
||||
# The SQL constraints are no-ops but present only to display the right error message to the
|
||||
# user when the partial unique indexes defined below raise errors/
|
||||
# The real constraints need to be implemented with PARTIAL UNIQUE INDEXES (see auto_init),
|
||||
# due to the way accounting data is delegated by contacts to their companies in OpenERP 7.0.
|
||||
_sql_constraints = [
|
||||
('vat_uniq', 'unique (id)', 'The vat of the partner must be unique !'),
|
||||
('nrc_uniq', 'unique (id)', 'The code of the partner must be unique !')
|
||||
]
|
||||
|
||||
def _auto_init(self, cr, context=None):
|
||||
result = super(res_partner, self)._auto_init(cr, context=context)
|
||||
# Real implementation of the vat/nrc constraints: only "commercial entities" need to have
|
||||
# unique numbers, and the condition for being a commercial entity is "is_company or parent_id IS NULL".
|
||||
# Contacts inside a company automatically have a copy of the company's commercial fields
|
||||
# (see _commercial_fields()), so they are automatically consistent.
|
||||
# Remove constrains for vat, nrc on "commercial entities" because is not mandatory by legislation
|
||||
# Even that VAT numbers are unique, the NRC field is not unique, and there are certain entities that
|
||||
# doesn't have a NRC number plus the formatting was changed few times, so we cannot have a base rule for
|
||||
# checking if available and emmited by the Ministry of Finance, only online on their website.
|
||||
cr.execute("""
|
||||
DROP INDEX IF EXISTS res_partner_vat_uniq_for_companies;
|
||||
DROP INDEX IF EXISTS res_partner_nrc_uniq_for_companies;
|
||||
CREATE UNIQUE INDEX res_partner_vat_uniq_for_companies ON res_partner (vat) WHERE is_company OR parent_id IS NULL;
|
||||
CREATE UNIQUE INDEX res_partner_nrc_uniq_for_companies ON res_partner (nrc) WHERE is_company OR parent_id IS NULL;
|
||||
""")
|
||||
return result
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ class mail_thread(osv.AbstractModel):
|
|||
model = context.get('empty_list_help_model')
|
||||
res_id = context.get('empty_list_help_id')
|
||||
ir_config_parameter = self.pool.get("ir.config_parameter")
|
||||
catchall_domain = ir_config_parameter.get_param(cr, uid, "mail.catchall.domain", context=context)
|
||||
catchall_domain = ir_config_parameter.get_param(cr, SUPERUSER_ID, "mail.catchall.domain", context=context)
|
||||
document_name = context.get('empty_list_help_document_name', _('document'))
|
||||
alias = None
|
||||
|
||||
|
|
|
@ -356,7 +356,7 @@ class mrp_repair(osv.osv):
|
|||
'origin': repair.name,
|
||||
'type': 'out_invoice',
|
||||
'account_id': account_id,
|
||||
'partner_id': repair.partner_id.id,
|
||||
'partner_id': repair.partner_invoice_id.id or repair.partner_id.id,
|
||||
'currency_id': repair.pricelist_id.currency_id.id,
|
||||
'comment': repair.quotation_notes,
|
||||
'fiscal_position': repair.partner_id.property_account_position.id
|
||||
|
|
|
@ -252,7 +252,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
|
|||
// Load the company Logo
|
||||
|
||||
self.company_logo = new Image();
|
||||
self.company_logo.crossOrigin = 'anonymous';
|
||||
var logo_loaded = new $.Deferred();
|
||||
self.company_logo.onload = function(){
|
||||
var img = self.company_logo;
|
||||
|
@ -274,13 +273,12 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
|
|||
ctx.drawImage(self.company_logo,0,0, width, height);
|
||||
|
||||
self.company_logo_base64 = c.toDataURL();
|
||||
window.logo64 = self.company_logo_base64;
|
||||
logo_loaded.resolve();
|
||||
};
|
||||
self.company_logo.onerror = function(){
|
||||
logo_loaded.reject();
|
||||
};
|
||||
self.company_logo.src = window.location.origin + '/web/binary/company_logo';
|
||||
self.company_logo.src = '/web/binary/company_logo'+'?_'+Math.random();
|
||||
|
||||
return logo_loaded;
|
||||
});
|
||||
|
|
|
@ -294,6 +294,7 @@
|
|||
<field name="view_mode">kanban,tree,form</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_id" ref="product_template_kanban_view"/>
|
||||
<field name="context">{"search_default_filter_to_sell":1}</field>
|
||||
</record>
|
||||
|
||||
<menuitem action="product_template_action"
|
||||
|
|
|
@ -995,6 +995,7 @@
|
|||
<field name="product_id" on_change="onchange_product_id(product_id,location_id,location_dest_id, False)"/>
|
||||
<field name="product_uom_qty" on_change="onchange_quantity(product_id, product_uom_qty, product_uom, product_uos)"/>
|
||||
<field name="product_uom" string="Unit of Measure" groups="product.group_uom"/>
|
||||
<field name="product_uos_qty" groups="product.group_uos"/>
|
||||
<field name="product_uos" groups="product.group_uos"/>
|
||||
<button name="%(stock.move_scrap)d"
|
||||
string="Scrap Products" type="action"
|
||||
|
@ -1019,6 +1020,7 @@
|
|||
<field name="product_id"/>
|
||||
<field name="product_uom_qty" on_change="onchange_quantity(product_id, product_uom_qty, product_uom, product_uos)"/>
|
||||
<field name="product_uom" string="Unit of Measure" groups="product.group_uom"/>
|
||||
<field name="product_uos_qty" groups="product.group_uos"/>
|
||||
<field name="product_uos" groups="product.group_uos"/>
|
||||
<field name="location_id" groups="stock.group_locations" invisible="1"/>
|
||||
<field name="picking_id" invisible="1" />
|
||||
|
|
|
@ -119,7 +119,7 @@ def ensure_db(redirect='/web/database/selector'):
|
|||
abort_and_redirect(url_redirect)
|
||||
|
||||
# if db not provided, use the session one
|
||||
if not db:
|
||||
if not db and http.db_filter([request.session.db]):
|
||||
db = request.session.db
|
||||
|
||||
# if no database provided and no database in session, use monodb
|
||||
|
@ -545,6 +545,8 @@ class Home(http.Controller):
|
|||
|
||||
@http.route('/login', type='http', auth="none")
|
||||
def login(self, db, login, key, redirect="/web", **kw):
|
||||
if not http.db_filter([db]):
|
||||
return werkzeug.utils.redirect('/', 303)
|
||||
return login_and_redirect(db, login, key, redirect_url=redirect)
|
||||
|
||||
@http.route('/web/js/<xmlid>', type='http', auth="public")
|
||||
|
@ -1283,7 +1285,7 @@ class Binary(http.Controller):
|
|||
'/logo',
|
||||
'/logo.png',
|
||||
], type='http', auth="none")
|
||||
def company_logo(self, dbname=None):
|
||||
def company_logo(self, dbname=None, **kw):
|
||||
# TODO add etag, refactor to use /image code for etag
|
||||
uid = None
|
||||
if request.session.db:
|
||||
|
|
|
@ -1935,17 +1935,17 @@
|
|||
.openerp .oe_application .oe_form_sheet .oe_notebook_page {
|
||||
padding: 0 16px;
|
||||
}
|
||||
.openerp .oe_form > :not(.oe_form_nosheet) header {
|
||||
.openerp .oe_form > :not(.oe_form_nosheet) header, .openerp .oe_form > .oe_form_nosheet header {
|
||||
padding-left: 2px;
|
||||
}
|
||||
.openerp .oe_form > :not(.oe_form_nosheet) header ul:not(.oe_tooltip_technical):not(.oe_dropdown_menu) {
|
||||
.openerp .oe_form > :not(.oe_form_nosheet) header ul:not(.oe_tooltip_technical):not(.oe_dropdown_menu), .openerp .oe_form > .oe_form_nosheet header ul:not(.oe_tooltip_technical):not(.oe_dropdown_menu) {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
}
|
||||
.openerp .oe_form > :not(.oe_form_nosheet) header .oe_button {
|
||||
.openerp .oe_form > :not(.oe_form_nosheet) header .oe_button, .openerp .oe_form > .oe_form_nosheet header .oe_button {
|
||||
margin: 3px 2px 1px;
|
||||
}
|
||||
.openerp .oe_form > :not(.oe_form_nosheet) header .oe_button:first-child {
|
||||
.openerp .oe_form > :not(.oe_form_nosheet) header .oe_button:first-child, .openerp .oe_form > .oe_form_nosheet header .oe_button:first-child {
|
||||
margin-left: 6px;
|
||||
}
|
||||
.openerp .oe_form header {
|
||||
|
@ -2666,7 +2666,9 @@
|
|||
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_float.oe_readonly, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_view_integer.oe_readonly {
|
||||
padding: 6px 0px 0px;
|
||||
text-align: right;
|
||||
max-width: 100px;
|
||||
}
|
||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_float span, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_view_integer span {
|
||||
padding: 0px 6px;
|
||||
}
|
||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_float input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_view_integer input {
|
||||
width: 100% !important;
|
||||
|
|
|
@ -1577,7 +1577,7 @@ $sheet-padding: 16px
|
|||
padding: 0 16px
|
||||
// }}}
|
||||
// FormView.header {{{
|
||||
.oe_form > :not(.oe_form_nosheet) header
|
||||
.oe_form > :not(.oe_form_nosheet) header, .oe_form > .oe_form_nosheet header
|
||||
padding-left: 2px
|
||||
ul:not(.oe_tooltip_technical):not(.oe_dropdown_menu)
|
||||
display: inline-block
|
||||
|
@ -2148,7 +2148,8 @@ $sheet-padding: 16px
|
|||
&.oe_readonly
|
||||
padding: 6px 0px 0px
|
||||
text-align: right
|
||||
max-width: 100px
|
||||
span
|
||||
padding: 0px 6px
|
||||
input
|
||||
width: 100% !important
|
||||
text-align: right
|
||||
|
|
|
@ -1008,7 +1008,7 @@ instance.web.UserMenu = instance.web.Widget.extend({
|
|||
this.update_promise = this.update_promise.then(fct, fct);
|
||||
},
|
||||
on_menu_help: function() {
|
||||
window.open('http://help.openerp.com', '_blank');
|
||||
window.open('http://help.odoo.com', '_blank');
|
||||
},
|
||||
on_menu_logout: function() {
|
||||
this.trigger('user_logout');
|
||||
|
@ -1037,10 +1037,10 @@ instance.web.UserMenu = instance.web.Widget.extend({
|
|||
state: JSON.stringify(state),
|
||||
scope: 'userinfo',
|
||||
};
|
||||
instance.web.redirect('https://accounts.openerp.com/oauth2/auth?'+$.param(params));
|
||||
instance.web.redirect('https://accounts.odoo.com/oauth2/auth?'+$.param(params));
|
||||
}).fail(function(result, ev){
|
||||
ev.preventDefault();
|
||||
instance.web.redirect('https://accounts.openerp.com/web');
|
||||
instance.web.redirect('https://accounts.odoo.com/web');
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -946,6 +946,8 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
|
|||
sign = -1;
|
||||
field = field.slice(1);
|
||||
}
|
||||
if(!a[field] && a[field] !== 0){ return sign}
|
||||
if(!b[field] && b[field] !== 0){ return (sign == -1) ? 1 : -1}
|
||||
//m2o should be searched based on value[1] not based whole value(i.e. [id, value])
|
||||
if(_.isArray(a[field]) && a[field].length == 2 && _.isString(a[field][1])){
|
||||
return sign * compare(a[field][1], b[field][1]);
|
||||
|
|
|
@ -43,6 +43,8 @@ class website_event(website_event):
|
|||
ticket = ticket_obj.browse(cr, SUPERUSER_ID, ticket_id, context=context)
|
||||
request.website.sale_get_order(force_create=1)._cart_update(
|
||||
product_id=ticket.product_id.id, add_qty=quantity, context=dict(context, event_ticket_id=ticket.id))
|
||||
if 'tax_id' in _values:
|
||||
_values['tax_id'] = [(6, 0, _values['tax_id'])]
|
||||
|
||||
if not sale:
|
||||
return request.redirect("/event/%s" % event_id)
|
||||
|
|
|
@ -236,6 +236,8 @@ class ir_attachment(osv.osv):
|
|||
for model, mids in res_ids.items():
|
||||
# ignore attachments that are not attached to a resource anymore when checking access rights
|
||||
# (resource was deleted but attachment was not)
|
||||
if not self.pool.get(model):
|
||||
continue
|
||||
mids = self.pool[model].exists(cr, uid, mids)
|
||||
ima.check(cr, uid, model, mode)
|
||||
self.pool[model].check_access_rule(cr, uid, mids, mode, context=context)
|
||||
|
|
|
@ -24,7 +24,7 @@ from email.MIMEBase import MIMEBase
|
|||
from email.MIMEMultipart import MIMEMultipart
|
||||
from email.Charset import Charset
|
||||
from email.Header import Header
|
||||
from email.Utils import formatdate, make_msgid, COMMASPACE
|
||||
from email.Utils import formatdate, make_msgid, COMMASPACE, parseaddr
|
||||
from email import Encoders
|
||||
import logging
|
||||
import re
|
||||
|
@ -120,6 +120,7 @@ def encode_header_param(param_text):
|
|||
return param_text_ascii if param_text_ascii\
|
||||
else Charset('utf8').header_encode(param_text_utf8)
|
||||
|
||||
# TODO master, remove me, no longer used internaly
|
||||
name_with_email_pattern = re.compile(r'("[^<@>]+")\s*<([^ ,<@]+@[^> ,]+)>')
|
||||
address_pattern = re.compile(r'([^ ,<@]+@[^> ,]+)')
|
||||
|
||||
|
@ -143,15 +144,16 @@ def encode_rfc2822_address_header(header_text):
|
|||
header_text_ascii = try_coerce_ascii(header_text_utf8)
|
||||
if header_text_ascii:
|
||||
return header_text_ascii
|
||||
|
||||
name, email = parseaddr(header_text_utf8)
|
||||
if not name:
|
||||
return email
|
||||
|
||||
# non-ASCII characters are present, attempt to
|
||||
# replace all "Name" patterns with the RFC2047-
|
||||
# encoded version
|
||||
def replace(match_obj):
|
||||
name, email = match_obj.group(1), match_obj.group(2)
|
||||
name_encoded = str(Header(name, 'utf-8'))
|
||||
return "%s <%s>" % (name_encoded, email)
|
||||
header_text_utf8 = name_with_email_pattern.sub(replace,
|
||||
header_text_utf8)
|
||||
name_encoded = str(Header(name, 'utf-8'))
|
||||
header_text_utf8 = "%s <%s>" % (name_encoded, email)
|
||||
# try again after encoding
|
||||
header_text_ascii = try_coerce_ascii(header_text_utf8)
|
||||
if header_text_ascii:
|
||||
|
|
|
@ -2181,13 +2181,19 @@ class BaseModel(object):
|
|||
# same ordering, and can be merged in one pass.
|
||||
result = []
|
||||
known_values = {}
|
||||
|
||||
if len(groupby_list) < 2 and context.get('group_by_no_leaf'):
|
||||
count_attr = '_'
|
||||
else:
|
||||
count_attr = groupby
|
||||
count_attr += '_count'
|
||||
|
||||
def append_left(left_side):
|
||||
grouped_value = left_side[groupby] and left_side[groupby][0]
|
||||
if not grouped_value in known_values:
|
||||
result.append(left_side)
|
||||
known_values[grouped_value] = left_side
|
||||
else:
|
||||
count_attr = groupby + '_count'
|
||||
known_values[grouped_value].update({count_attr: left_side[count_attr]})
|
||||
def append_right(right_side):
|
||||
grouped_value = right_side[0]
|
||||
|
@ -3730,6 +3736,7 @@ class BaseModel(object):
|
|||
self.check_access_rights(cr, uid, 'unlink')
|
||||
|
||||
ir_property = self.pool.get('ir.property')
|
||||
ir_attachment_obj = self.pool.get('ir.attachment')
|
||||
|
||||
# Check if the records are used as default properties.
|
||||
domain = [('res_id', '=', False),
|
||||
|
@ -3768,6 +3775,13 @@ class BaseModel(object):
|
|||
if ir_value_ids:
|
||||
ir_values_obj.unlink(cr, uid, ir_value_ids, context=context)
|
||||
|
||||
# For the same reason, removing the record relevant to ir_attachment
|
||||
# The search is performed with sql as the search method of ir_attachment is overridden to hide attachments of deleted records
|
||||
cr.execute('select id from ir_attachment where res_model = %s and res_id in %s', (self._name, sub_ids))
|
||||
ir_attachment_ids = [ir_attachment[0] for ir_attachment in cr.fetchall()]
|
||||
if ir_attachment_ids:
|
||||
ir_attachment_obj.unlink(cr, uid, ir_attachment_ids, context=context)
|
||||
|
||||
for order, obj_name, store_ids, fields in result_store:
|
||||
if obj_name == self._name:
|
||||
effective_store_ids = list(set(store_ids) - set(ids))
|
||||
|
|
|
@ -597,7 +597,7 @@ command_re = re.compile("^Set-([a-z]+) *: *(.+)$", re.I + re.UNICODE)
|
|||
# Updated in 7.0 to match the model name as well
|
||||
# Typical form of references is <timestamp-openerp-record_id-model_name@domain>
|
||||
# group(1) = the record ID ; group(2) = the model (if any) ; group(3) = the domain
|
||||
reference_re = re.compile("<.*-open(?:object|erp)-(\\d+)(?:-([\w.]+))?.*@(.*)>", re.UNICODE)
|
||||
reference_re = re.compile("<.*-open(?:object|erp)-(\\d+)(?:-([\w.]+))?[^>]*@([^>]*)>", re.UNICODE)
|
||||
|
||||
# Bounce regex
|
||||
# Typical form of bounce is bounce-128-crm.lead-34@domain
|
||||
|
|
Loading…
Reference in New Issue