[MERGE] forward port of branch saas-3 up to fe8106f
This commit is contained in:
commit
e2e60bf4eb
|
@ -878,7 +878,7 @@ class account_voucher(osv.osv):
|
||||||
currency_id = journal.currency.id
|
currency_id = journal.currency.id
|
||||||
else:
|
else:
|
||||||
currency_id = journal.company_id.currency_id.id
|
currency_id = journal.company_id.currency_id.id
|
||||||
vals['value'].update({'currency_id': currency_id})
|
vals['value'].update({'currency_id': currency_id, 'payment_rate_currency_id': currency_id})
|
||||||
#in case we want to register the payment directly from an invoice, it's confusing to allow to switch the journal
|
#in case we want to register the payment directly from an invoice, it's confusing to allow to switch the journal
|
||||||
#without seeing that the amount is expressed in the journal currency, and not in the invoice currency. So to avoid
|
#without seeing that the amount is expressed in the journal currency, and not in the invoice currency. So to avoid
|
||||||
#this common mistake, we simply reset the amount to 0 if the currency is not the invoice currency.
|
#this common mistake, we simply reset the amount to 0 if the currency is not the invoice currency.
|
||||||
|
|
|
@ -222,6 +222,7 @@
|
||||||
|
|
||||||
<field name="pay_now" invisible="1"/>
|
<field name="pay_now" invisible="1"/>
|
||||||
<field name="type" invisible="True"/>
|
<field name="type" invisible="True"/>
|
||||||
|
<field name="payment_rate_currency_id" invisible="1"/>
|
||||||
<group>
|
<group>
|
||||||
<group>
|
<group>
|
||||||
<field name="partner_id" domain="[('supplier','=',True)]" string="Supplier" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type, date, context)" context="{'default_customer': 0, 'search_default_supplier': 1, 'default_supplier': 1}" />
|
<field name="partner_id" domain="[('supplier','=',True)]" string="Supplier" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type, date, context)" context="{'default_customer': 0, 'search_default_supplier': 1, 'default_supplier': 1}" />
|
||||||
|
|
|
@ -249,7 +249,7 @@ class users(osv.osv):
|
||||||
return user_id
|
return user_id
|
||||||
registry = RegistryManager.get(db)
|
registry = RegistryManager.get(db)
|
||||||
with registry.cursor() as cr:
|
with registry.cursor() as cr:
|
||||||
cr.execute("SELECT id, active FROM res_users WHERE lower(login)=%s", (login,))
|
cr.execute("SELECT id FROM res_users WHERE lower(login)=%s", (login,))
|
||||||
res = cr.fetchone()
|
res = cr.fetchone()
|
||||||
if res:
|
if res:
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -160,6 +160,10 @@ class crm_lead(format_address, osv.osv):
|
||||||
return result, fold
|
return result, fold
|
||||||
|
|
||||||
def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
|
def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
|
||||||
|
if context and context.get('opportunity_id'):
|
||||||
|
action = self._get_formview_action(cr, user, context['opportunity_id'], context=context)
|
||||||
|
if action.get('views') and any(view_id for view_id in action['views'] if view_id[1] == view_type):
|
||||||
|
view_id = next(view_id[0] for view_id in action['views'] if view_id[1] == view_type)
|
||||||
res = super(crm_lead, self).fields_view_get(cr, user, view_id, view_type, context, toolbar=toolbar, submenu=submenu)
|
res = super(crm_lead, self).fields_view_get(cr, user, view_id, view_type, context, toolbar=toolbar, submenu=submenu)
|
||||||
if view_type == 'form':
|
if view_type == 'form':
|
||||||
res['arch'] = self.fields_view_get_address(cr, user, res['arch'], context=context)
|
res['arch'] = self.fields_view_get_address(cr, user, res['arch'], context=context)
|
||||||
|
@ -586,6 +590,13 @@ class crm_lead(format_address, osv.osv):
|
||||||
attachment.write(values)
|
attachment.write(values)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _merge_opportunity_phonecalls(self, cr, uid, opportunity_id, opportunities, context=None):
|
||||||
|
phonecall_obj = self.pool['crm.phonecall']
|
||||||
|
for opportunity in opportunities:
|
||||||
|
for phonecall_id in phonecall_obj.search(cr, uid, [('opportunity_id', '=', opportunity.id)], context=context):
|
||||||
|
phonecall_obj.write(cr, uid, phonecall_id, {'opportunity_id': opportunity_id}, context=context)
|
||||||
|
return True
|
||||||
|
|
||||||
def get_duplicated_leads(self, cr, uid, ids, partner_id, include_lost=False, context=None):
|
def get_duplicated_leads(self, cr, uid, ids, partner_id, include_lost=False, context=None):
|
||||||
"""
|
"""
|
||||||
Search for opportunities that have the same partner and that arent done or cancelled
|
Search for opportunities that have the same partner and that arent done or cancelled
|
||||||
|
@ -616,6 +627,7 @@ class crm_lead(format_address, osv.osv):
|
||||||
self._merge_notify(cr, uid, highest, opportunities, context=context)
|
self._merge_notify(cr, uid, highest, opportunities, context=context)
|
||||||
self._merge_opportunity_history(cr, uid, highest, opportunities, context=context)
|
self._merge_opportunity_history(cr, uid, highest, opportunities, context=context)
|
||||||
self._merge_opportunity_attachments(cr, uid, highest, opportunities, context=context)
|
self._merge_opportunity_attachments(cr, uid, highest, opportunities, context=context)
|
||||||
|
self._merge_opportunity_phonecalls(cr, uid, highest, opportunities, context=context)
|
||||||
|
|
||||||
def merge_opportunity(self, cr, uid, ids, user_id=False, section_id=False, context=None):
|
def merge_opportunity(self, cr, uid, ids, user_id=False, section_id=False, context=None):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -104,7 +104,7 @@
|
||||||
domain="[('object_id.model', '=', 'crm.phonecall')]"/>
|
domain="[('object_id.model', '=', 'crm.phonecall')]"/>
|
||||||
<field name="partner_mobile"/>
|
<field name="partner_mobile"/>
|
||||||
<field name="priority" widget="priority"/>
|
<field name="priority" widget="priority"/>
|
||||||
<field name="opportunity_id" on_change="on_change_opportunity(opportunity_id)"/>
|
<field name="opportunity_id" on_change="on_change_opportunity(opportunity_id)" context="{'opportunity_id': opportunity_id}"/>
|
||||||
</group>
|
</group>
|
||||||
<field name="description" placeholder="Description..."/>
|
<field name="description" placeholder="Description..."/>
|
||||||
</sheet>
|
</sheet>
|
||||||
|
|
|
@ -108,7 +108,7 @@
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Payment Transactions">
|
<form string="Payment Transactions">
|
||||||
<sheet>
|
<sheet>
|
||||||
<group col="6">
|
<group>
|
||||||
<group>
|
<group>
|
||||||
<field name="reference"/>
|
<field name="reference"/>
|
||||||
<field name="amount"/>
|
<field name="amount"/>
|
||||||
|
|
|
@ -74,7 +74,7 @@
|
||||||
<field name="res_model">account.invoice</field>
|
<field name="res_model">account.invoice</field>
|
||||||
<field name="view_mode">tree,form</field>
|
<field name="view_mode">tree,form</field>
|
||||||
<field name="domain">[('type','in',['out_invoice','out_refund'])]</field>
|
<field name="domain">[('type','in',['out_invoice','out_refund'])]</field>
|
||||||
<field name="context">{'type':['out_invoice','out_refund'], 'journal_type': 'sale'}</field>
|
<field name="context">{}</field>
|
||||||
<field name="search_view_id" ref="account.view_account_invoice_filter"/>
|
<field name="search_view_id" ref="account.view_account_invoice_filter"/>
|
||||||
<field name="help">We haven't sent you any invoice.</field>
|
<field name="help">We haven't sent you any invoice.</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
|
@ -20,9 +20,6 @@
|
||||||
##############################################################################
|
##############################################################################
|
||||||
from openerp import tools
|
from openerp import tools
|
||||||
|
|
||||||
import math
|
|
||||||
|
|
||||||
|
|
||||||
def rounding(f, r):
|
def rounding(f, r):
|
||||||
# TODO for trunk: log deprecation warning
|
# TODO for trunk: log deprecation warning
|
||||||
# _logger.warning("Deprecated rounding method, please use tools.float_round to round floats.")
|
# _logger.warning("Deprecated rounding method, please use tools.float_round to round floats.")
|
||||||
|
@ -32,4 +29,4 @@ def rounding(f, r):
|
||||||
def ceiling(f, r):
|
def ceiling(f, r):
|
||||||
if not r:
|
if not r:
|
||||||
return f
|
return f
|
||||||
return math.ceil(f / r) * r
|
return tools.float_round(f, precision_rounding=r, rounding_method='UP')
|
||||||
|
|
|
@ -32,6 +32,7 @@ from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
|
||||||
import psycopg2
|
import psycopg2
|
||||||
|
|
||||||
import openerp.addons.decimal_precision as dp
|
import openerp.addons.decimal_precision as dp
|
||||||
|
from openerp.tools.float_utils import float_round
|
||||||
|
|
||||||
def ean_checksum(eancode):
|
def ean_checksum(eancode):
|
||||||
"""returns the checksum of an ean string of length 13, returns -1 if the string has the wrong length"""
|
"""returns the checksum of an ean string of length 13, returns -1 if the string has the wrong length"""
|
||||||
|
@ -178,7 +179,10 @@ class product_uom(osv.osv):
|
||||||
raise osv.except_osv(_('Error!'), _('Conversion from Product UoM %s to Default UoM %s is not possible as they both belong to different Category!.') % (from_unit.name,to_unit.name,))
|
raise osv.except_osv(_('Error!'), _('Conversion from Product UoM %s to Default UoM %s is not possible as they both belong to different Category!.') % (from_unit.name,to_unit.name,))
|
||||||
else:
|
else:
|
||||||
return qty
|
return qty
|
||||||
amount = qty / from_unit.factor
|
# First round to the precision of the original unit, so that
|
||||||
|
# float representation errors do not bias the following ceil()
|
||||||
|
# e.g. with 1 / (1/12) we could get 12.0000048, ceiling to 13!
|
||||||
|
amount = float_round(qty/from_unit.factor, precision_rounding=from_unit.rounding)
|
||||||
if to_unit:
|
if to_unit:
|
||||||
amount = amount * to_unit.factor
|
amount = amount * to_unit.factor
|
||||||
if round:
|
if round:
|
||||||
|
|
|
@ -2903,7 +2903,7 @@ instance.web.form.FieldTextHtml = instance.web.form.AbstractField.extend(instanc
|
||||||
if (! this.get("effective_readonly")) {
|
if (! this.get("effective_readonly")) {
|
||||||
self._updating_editor = false;
|
self._updating_editor = false;
|
||||||
this.$textarea = this.$el.find('textarea');
|
this.$textarea = this.$el.find('textarea');
|
||||||
var width = ((this.node.attrs || {}).editor_width || 'auto');
|
var width = ((this.node.attrs || {}).editor_width || 'calc(100% - 4px)');
|
||||||
var height = ((this.node.attrs || {}).editor_height || 250);
|
var height = ((this.node.attrs || {}).editor_height || 250);
|
||||||
this.$textarea.cleditor({
|
this.$textarea.cleditor({
|
||||||
width: width, // width not including margins, borders or padding
|
width: width, // width not including margins, borders or padding
|
||||||
|
|
|
@ -664,6 +664,13 @@ openerp.web_calendar = function(instance) {
|
||||||
domain: self.get_range_domain(domain, start, end),
|
domain: self.get_range_domain(domain, start, end),
|
||||||
context: context,
|
context: context,
|
||||||
}).done(function(events) {
|
}).done(function(events) {
|
||||||
|
if (self.dataset.index === null) {
|
||||||
|
if (events.length) {
|
||||||
|
self.dataset.index = 0;
|
||||||
|
}
|
||||||
|
} else if (self.dataset.index >= events.length) {
|
||||||
|
self.dataset.index = events.length ? 0 : null;
|
||||||
|
}
|
||||||
|
|
||||||
if (self.event_source !== current_event_source) {
|
if (self.event_source !== current_event_source) {
|
||||||
console.log("Consecutive ``do_search`` called. Cancelling.");
|
console.log("Consecutive ``do_search`` called. Cancelling.");
|
||||||
|
|
|
@ -85,6 +85,8 @@ instance.web_graph.GraphView = instance.web.View.extend({
|
||||||
measures = groupbys.measures;
|
measures = groupbys.measures;
|
||||||
|
|
||||||
if (!this.graph_widget) {
|
if (!this.graph_widget) {
|
||||||
|
this.widget_config.context = _.clone(context);
|
||||||
|
this.widget_config.context.group_by_no_leaf = true;
|
||||||
if (group_by.length) {
|
if (group_by.length) {
|
||||||
this.widget_config.row_groupby = group_by;
|
this.widget_config.row_groupby = group_by;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ openerp.web_graph.PivotTable = openerp.web.Class.extend({
|
||||||
init: function (model, domain, fields, options) {
|
init: function (model, domain, fields, options) {
|
||||||
this.cells = [];
|
this.cells = [];
|
||||||
this.domain = domain;
|
this.domain = domain;
|
||||||
|
this.context = options.context;
|
||||||
this.no_data = true;
|
this.no_data = true;
|
||||||
this.updating = false;
|
this.updating = false;
|
||||||
this.model = model;
|
this.model = model;
|
||||||
|
@ -412,6 +413,7 @@ openerp.web_graph.PivotTable = openerp.web.Class.extend({
|
||||||
var self = this;
|
var self = this;
|
||||||
return this.model.query(_.without(fields, '__count'))
|
return this.model.query(_.without(fields, '__count'))
|
||||||
.filter(domain)
|
.filter(domain)
|
||||||
|
.context(this.context)
|
||||||
.lazy(false)
|
.lazy(false)
|
||||||
.group_by(groupbys)
|
.group_by(groupbys)
|
||||||
.then(function (groups) {
|
.then(function (groups) {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<field name="name">Blog Post: public: published only</field>
|
<field name="name">Blog Post: public: published only</field>
|
||||||
<field name="model_id" ref="model_blog_post"/>
|
<field name="model_id" ref="model_blog_post"/>
|
||||||
<field name="domain_force">[('website_published', '=', True)]</field>
|
<field name="domain_force">[('website_published', '=', True)]</field>
|
||||||
<field name="groups" eval="[(4, ref('base.group_public'))]"/>
|
<field name="groups" eval="[(4, ref('base.group_public')), (4, ref('base.group_portal'))]"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
|
|
|
@ -85,7 +85,6 @@ class ir_cron(osv.osv):
|
||||||
'interval_type' : 'months',
|
'interval_type' : 'months',
|
||||||
'numbercall' : 1,
|
'numbercall' : 1,
|
||||||
'active' : 1,
|
'active' : 1,
|
||||||
'doall' : 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def _check_args(self, cr, uid, ids, context=None):
|
def _check_args(self, cr, uid, ids, context=None):
|
||||||
|
|
|
@ -373,6 +373,12 @@ class res_users(osv.osv):
|
||||||
default['login'] = _("%s (copy)") % user2copy['login']
|
default['login'] = _("%s (copy)") % user2copy['login']
|
||||||
return super(res_users, self).copy(cr, uid, id, default, context)
|
return super(res_users, self).copy(cr, uid, id, default, context)
|
||||||
|
|
||||||
|
def copy_data(self, cr, uid, ids, default=None, context=None):
|
||||||
|
if default is None:
|
||||||
|
default = {}
|
||||||
|
default.update({'login_date': False})
|
||||||
|
return super(res_users, self).copy_data(cr, uid, ids, default, context=context)
|
||||||
|
|
||||||
@tools.ormcache(skiparg=2)
|
@tools.ormcache(skiparg=2)
|
||||||
def context_get(self, cr, uid, context=None):
|
def context_get(self, cr, uid, context=None):
|
||||||
user = self.browse(cr, SUPERUSER_ID, uid, context)
|
user = self.browse(cr, SUPERUSER_ID, uid, context)
|
||||||
|
|
|
@ -203,8 +203,8 @@
|
||||||
-
|
-
|
||||||
!python {model: res.currency}: |
|
!python {model: res.currency}: |
|
||||||
from openerp.tools import float_compare, float_is_zero, float_round, float_repr
|
from openerp.tools import float_compare, float_is_zero, float_round, float_repr
|
||||||
def try_round(amount, expected, precision_digits=3, float_round=float_round, float_repr=float_repr):
|
def try_round(amount, expected, precision_digits=3, float_round=float_round, float_repr=float_repr, rounding_method='HALF-UP'):
|
||||||
result = float_repr(float_round(amount, precision_digits=precision_digits),
|
result = float_repr(float_round(amount, precision_digits=precision_digits, rounding_method=rounding_method),
|
||||||
precision_digits=precision_digits)
|
precision_digits=precision_digits)
|
||||||
assert result == expected, 'Rounding error: got %s, expected %s' % (result, expected)
|
assert result == expected, 'Rounding error: got %s, expected %s' % (result, expected)
|
||||||
try_round(2.6745, '2.675')
|
try_round(2.6745, '2.675')
|
||||||
|
@ -218,6 +218,15 @@
|
||||||
try_round(457.4554, '457.455')
|
try_round(457.4554, '457.455')
|
||||||
try_round(-457.4554, '-457.455')
|
try_round(-457.4554, '-457.455')
|
||||||
|
|
||||||
|
# Try some rounding value with rounding method UP instead of HALF-UP
|
||||||
|
# We use 8.175 because when normalizing 8.175 with precision_digits=3 it gives
|
||||||
|
# us 8175,0000000001234 as value, and if not handle correctly the rounding UP
|
||||||
|
# value will be incorrect (should be 8,175 and not 8,176)
|
||||||
|
try_round(8.175, '8.175', rounding_method='UP')
|
||||||
|
try_round(8.1751, '8.176', rounding_method='UP')
|
||||||
|
try_round(-8.175, '-8.175', rounding_method='UP')
|
||||||
|
try_round(-8.1751, '-8.175', rounding_method='UP')
|
||||||
|
|
||||||
# Extended float range test, inspired by Cloves Almeida's test on bug #882036.
|
# Extended float range test, inspired by Cloves Almeida's test on bug #882036.
|
||||||
fractions = [.0, .015, .01499, .675, .67499, .4555, .4555, .45555]
|
fractions = [.0, .015, .01499, .675, .67499, .4555, .4555, .45555]
|
||||||
expecteds = ['.00', '.02', '.01', '.68', '.67', '.46', '.456', '.4556']
|
expecteds = ['.00', '.02', '.01', '.68', '.67', '.46', '.456', '.4556']
|
||||||
|
|
|
@ -29,10 +29,11 @@ def _float_check_precision(precision_digits=None, precision_rounding=None):
|
||||||
return 10 ** -precision_digits
|
return 10 ** -precision_digits
|
||||||
return precision_rounding
|
return precision_rounding
|
||||||
|
|
||||||
def float_round(value, precision_digits=None, precision_rounding=None):
|
def float_round(value, precision_digits=None, precision_rounding=None, rounding_method='HALF-UP'):
|
||||||
"""Return ``value`` rounded to ``precision_digits``
|
"""Return ``value`` rounded to ``precision_digits`` decimal digits,
|
||||||
decimal digits, minimizing IEEE-754 floating point representation
|
minimizing IEEE-754 floating point representation errors, and applying
|
||||||
errors, and applying HALF-UP (away from zero) tie-breaking rule.
|
the tie-breaking rule selected with ``rounding_method``, by default
|
||||||
|
HALF-UP (away from zero).
|
||||||
Precision must be given by ``precision_digits`` or ``precision_rounding``,
|
Precision must be given by ``precision_digits`` or ``precision_rounding``,
|
||||||
not both!
|
not both!
|
||||||
|
|
||||||
|
@ -41,6 +42,9 @@ def float_round(value, precision_digits=None, precision_rounding=None):
|
||||||
:param float precision_rounding: decimal number representing the minimum
|
:param float precision_rounding: decimal number representing the minimum
|
||||||
non-zero value at the desired precision (for example, 0.01 for a
|
non-zero value at the desired precision (for example, 0.01 for a
|
||||||
2-digit precision).
|
2-digit precision).
|
||||||
|
:param rounding_method: the rounding method used: 'HALF-UP' or 'UP', the first
|
||||||
|
one rounding up to the closest number with the rule that number>=0.5 is
|
||||||
|
rounded up to 1, and the latest one always rounding up.
|
||||||
:return: rounded float
|
:return: rounded float
|
||||||
"""
|
"""
|
||||||
rounding_factor = _float_check_precision(precision_digits=precision_digits,
|
rounding_factor = _float_check_precision(precision_digits=precision_digits,
|
||||||
|
@ -52,7 +56,7 @@ def float_round(value, precision_digits=None, precision_rounding=None):
|
||||||
# we normalize the value before rounding it as an integer, and de-normalize
|
# we normalize the value before rounding it as an integer, and de-normalize
|
||||||
# after rounding: e.g. float_round(1.3, precision_rounding=.5) == 1.5
|
# after rounding: e.g. float_round(1.3, precision_rounding=.5) == 1.5
|
||||||
|
|
||||||
# TIE-BREAKING: HALF-UP
|
# TIE-BREAKING: HALF-UP (for normal rounding)
|
||||||
# We want to apply HALF-UP tie-breaking rules, i.e. 0.5 rounds away from 0.
|
# We want to apply HALF-UP tie-breaking rules, i.e. 0.5 rounds away from 0.
|
||||||
# Due to IEE754 float/double representation limits, the approximation of the
|
# Due to IEE754 float/double representation limits, the approximation of the
|
||||||
# real value may be slightly below the tie limit, resulting in an error of
|
# real value may be slightly below the tie limit, resulting in an error of
|
||||||
|
@ -66,8 +70,19 @@ def float_round(value, precision_digits=None, precision_rounding=None):
|
||||||
normalized_value = value / rounding_factor # normalize
|
normalized_value = value / rounding_factor # normalize
|
||||||
epsilon_magnitude = math.log(abs(normalized_value), 2)
|
epsilon_magnitude = math.log(abs(normalized_value), 2)
|
||||||
epsilon = 2**(epsilon_magnitude-53)
|
epsilon = 2**(epsilon_magnitude-53)
|
||||||
normalized_value += cmp(normalized_value,0) * epsilon
|
if rounding_method == 'HALF-UP':
|
||||||
rounded_value = round(normalized_value) # round to integer
|
normalized_value += cmp(normalized_value,0) * epsilon
|
||||||
|
rounded_value = round(normalized_value) # round to integer
|
||||||
|
|
||||||
|
# TIE-BREAKING: UP (for ceiling operations)
|
||||||
|
# When rounding the value up, we instead subtract the epsilon value
|
||||||
|
# as the the approximation of the real value may be slightly *above* the
|
||||||
|
# tie limit, this would result in incorrectly rounding up to the next number
|
||||||
|
|
||||||
|
elif rounding_method == 'UP':
|
||||||
|
normalized_value -= cmp(normalized_value,0) * epsilon
|
||||||
|
rounded_value = math.ceil(normalized_value) # ceil to integer
|
||||||
|
|
||||||
result = rounded_value * rounding_factor # de-normalize
|
result = rounded_value * rounding_factor # de-normalize
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue