diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py
index ea07610662e..9ca7859339f 100644
--- a/addons/account/models/account_invoice.py
+++ b/addons/account/models/account_invoice.py
@@ -500,12 +500,12 @@ class AccountInvoice(models.Model):
msg = _('Cannot find a chart of accounts for this company, You should configure it. \nPlease go to Account Configuration.')
raise RedirectWarning(msg, action.id, _('Go to the configuration panel'))
- if type in ('out_invoice', 'out_refund'):
- account_id = rec_account.id
- payment_term_id = p.property_payment_term_id.id
- else:
+ if type in ('in_invoice', 'in_refund'):
account_id = pay_account.id
payment_term_id = p.property_supplier_payment_term_id.id
+ else:
+ account_id = rec_account.id
+ payment_term_id = p.property_payment_term_id.id
delivery_partner_id = self.get_delivery_partner_id()
fiscal_position = self.env['account.fiscal.position'].get_fiscal_position(self.partner_id.id, delivery_id=delivery_partner_id)
diff --git a/addons/barcodes/static/src/js/field_float_scannable.js b/addons/barcodes/static/src/js/field_float_scannable.js
index 801cf32b5e2..5497a488c79 100644
--- a/addons/barcodes/static/src/js/field_float_scannable.js
+++ b/addons/barcodes/static/src/js/field_float_scannable.js
@@ -22,6 +22,10 @@ var FieldFloatScannable = form_widgets.FieldFloat.extend({
return formats.parse_value(val, {type: "float"}, def);
},
+ format_value: function(val, def) {
+ return formats.format_value(val, {type: "float"}, def);
+ },
+
simulateKeypress: function (e) {
/* only simulate a keypress if it has been previously prevented */
if (e.originalEvent.dispatched_by_barcode_reader !== true) {
diff --git a/addons/hr_gamification/wizard/gamification_badge_user_wizard.py b/addons/hr_gamification/wizard/gamification_badge_user_wizard.py
index 5bbb6848bd3..4c66db60db9 100644
--- a/addons/hr_gamification/wizard/gamification_badge_user_wizard.py
+++ b/addons/hr_gamification/wizard/gamification_badge_user_wizard.py
@@ -10,7 +10,7 @@ class GamificationBadgeUserWizard(models.TransientModel):
employee_id = fields.Many2one('hr.employee', string='Employee', required=True)
user_id = fields.Many2one('res.users', string='User',
- related='employee_id.user_id', store=True, readonly=True)
+ related='employee_id.user_id', store=True, readonly=True, compute_sudo=True)
# TODO 12.0/master remove this hack by changing the model
@api.model
diff --git a/addons/hr_holidays/models/hr_holidays.py b/addons/hr_holidays/models/hr_holidays.py
index a4b9a48c67c..0573211a517 100644
--- a/addons/hr_holidays/models/hr_holidays.py
+++ b/addons/hr_holidays/models/hr_holidays.py
@@ -174,7 +174,7 @@ class Holidays(models.Model):
payslip_status = fields.Boolean('Reported in last payslips',
help='Green this button when the leave has been taken into account in the payslip.')
report_note = fields.Text('HR Comments')
- user_id = fields.Many2one('res.users', string='User', related='employee_id.user_id', related_sudo=True, store=True, default=lambda self: self.env.uid, readonly=True)
+ user_id = fields.Many2one('res.users', string='User', related='employee_id.user_id', related_sudo=True, compute_sudo=True, store=True, default=lambda self: self.env.uid, readonly=True)
date_from = fields.Datetime('Start Date', readonly=True, index=True, copy=False,
states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, track_visibility='onchange')
date_to = fields.Datetime('End Date', readonly=True, copy=False,
diff --git a/addons/hr_timesheet_sheet/models/hr_timesheet_sheet.py b/addons/hr_timesheet_sheet/models/hr_timesheet_sheet.py
index f970d38ea32..75bb0cd9175 100644
--- a/addons/hr_timesheet_sheet/models/hr_timesheet_sheet.py
+++ b/addons/hr_timesheet_sheet/models/hr_timesheet_sheet.py
@@ -46,7 +46,7 @@ class HrTimesheetSheet(models.Model):
name = fields.Char(string="Note", states={'confirm': [('readonly', True)], 'done': [('readonly', True)]})
employee_id = fields.Many2one('hr.employee', string='Employee', default=_default_employee, required=True)
- user_id = fields.Many2one('res.users', related='employee_id.user_id', string='User', store=True, readonly=True)
+ user_id = fields.Many2one('res.users', related='employee_id.user_id', string='User', store=True, readonly=True, compute_sudo=True)
date_from = fields.Date(string='Date From', default=_default_date_from, required=True,
index=True, readonly=True, states={'new': [('readonly', False)]})
date_to = fields.Date(string='Date To', default=_default_date_to, required=True,
diff --git a/addons/point_of_sale/static/src/css/pos.css b/addons/point_of_sale/static/src/css/pos.css
index d4d48d1578a..dbe43fe5011 100644
--- a/addons/point_of_sale/static/src/css/pos.css
+++ b/addons/point_of_sale/static/src/css/pos.css
@@ -1472,6 +1472,8 @@ td {
body {
margin: 0;
color: white !important;
+ /* avoid black background if backgrounds are printed */
+ background: initial;
}
.oe_leftbar,
.oe_loading,
diff --git a/addons/sale/models/res_partner.py b/addons/sale/models/res_partner.py
index 0ac1409cd00..05d3f21f05f 100644
--- a/addons/sale/models/res_partner.py
+++ b/addons/sale/models/res_partner.py
@@ -14,11 +14,17 @@ class ResPartner(models.Model):
sale_warn_msg = fields.Text('Message for Sales Order')
def _compute_sale_order_count(self):
- sale_data = self.env['sale.order'].read_group(domain=[('partner_id', 'child_of', self.ids)],
- fields=['partner_id'], groupby=['partner_id'])
- mapped_data = dict([(m['partner_id'][0], m['partner_id_count']) for m in sale_data])
- for partner in self:
- # let's obtain the partner id and all its child ids
- partner_ids = [partner.id] + partner.child_ids.ids
- # then we can sum for all the partner's child
- partner.sale_order_count = sum(mapped_data.get(child, 0) for child in partner_ids)
+ # retrieve all children partners and prefetch 'parent_id' on them
+ all_partners = self.search([('id', 'child_of', self.ids)])
+ all_partners.read(['parent_id'])
+
+ sale_order_groups = self.env['sale.order'].read_group(
+ domain=[('partner_id', 'in', all_partners.ids)],
+ fields=['partner_id'], groupby=['partner_id']
+ )
+ for group in sale_order_groups:
+ partner = self.browse(group['partner_id'][0])
+ while partner:
+ if partner in self:
+ partner.sale_order_count += group['partner_id_count']
+ partner = partner.parent_id
diff --git a/addons/stock/models/product.py b/addons/stock/models/product.py
index 8192b4eec4c..a04ad250d9b 100644
--- a/addons/stock/models/product.py
+++ b/addons/stock/models/product.py
@@ -4,6 +4,7 @@
from odoo import api, fields, models, _
from odoo.addons import decimal_precision as dp
from odoo.exceptions import UserError
+from odoo.osv import expression
from odoo.tools.float_utils import float_round
from datetime import datetime
import operator as py_operator
@@ -370,6 +371,15 @@ class Product(models.Model):
def action_view_routes(self):
return self.mapped('product_tmpl_id').action_view_routes()
+ @api.model
+ def search(self, domain=None, *args, **kwargs):
+ # ONLY FOR 10.0 UP TO SAAS-15
+ # ignore dummy fields used for search context²
+ for index in range(len(domain or [])):
+ if domain[index][0] in ('location_id', 'warehouse_id'):
+ domain[index] = expression.TRUE_LEAF
+ return super(Product, self).search(domain, *args, **kwargs)
+
@api.multi
def write(self, values):
res = super(Product, self).write(values)
@@ -507,6 +517,15 @@ class ProductTemplate(models.Model):
def onchange_tracking(self):
return self.mapped('product_variant_ids').onchange_tracking()
+ @api.model
+ def search(self, domain=None, *args, **kwargs):
+ # ONLY FOR 10.0 UP TO SAAS-15
+ # ignore dummy fields used for search context
+ for index in range(len(domain or [])):
+ if domain[index][0] in ('location_id', 'warehouse_id'):
+ domain[index] = expression.TRUE_LEAF
+ return super(ProductTemplate, self).search(domain, *args, **kwargs)
+
@api.multi
def write(self, vals):
if 'uom_id' in vals:
diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py
index 5babba5f035..a88cc22c484 100644
--- a/addons/stock/models/stock_move.py
+++ b/addons/stock/models/stock_move.py
@@ -193,8 +193,8 @@ class StockMove(models.Model):
if self.state == 'done':
self.availability = self.product_qty
else:
- quants = self.env['stock.quant'].search([('location_id', 'child_of', self.location_id.id), ('product_id', '=', self.product_id.id), ('reservation_id', '=', False)])
- self.availability = min(self.product_qty, sum(quants.mapped('qty')))
+ qty_tot = self.env['stock.quant'].read_group([('location_id', 'child_of', self.location_id.id), ('product_id', '=', self.product_id.id), ('reservation_id', '=', False)], ['qty'], [])[0]
+ self.availability = min(self.product_qty, qty_tot['qty'] or 0)
@api.multi
def _compute_string_qty_information(self):
diff --git a/addons/stock/models/stock_quant.py b/addons/stock/models/stock_quant.py
index b427acbd748..ab567103eaa 100644
--- a/addons/stock/models/stock_quant.py
+++ b/addons/stock/models/stock_quant.py
@@ -110,12 +110,11 @@ class Quant(models.Model):
res = super(Quant, self).read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy)
if 'inventory_value' in fields:
for line in res:
- if '__domain' in line:
- lines = self.search(line['__domain'])
- inv_value = 0.0
- for line2 in lines:
- inv_value += line2.inventory_value
- line['inventory_value'] = inv_value
+ lines = self.search(line.get('__domain', domain))
+ inv_value = 0.0
+ for line2 in lines:
+ inv_value += line2.inventory_value
+ line['inventory_value'] = inv_value
return res
@api.multi
diff --git a/addons/web/static/src/js/framework/formats.js b/addons/web/static/src/js/framework/formats.js
index c4d001a12e1..6d8e94d6541 100644
--- a/addons/web/static/src/js/framework/formats.js
+++ b/addons/web/static/src/js/framework/formats.js
@@ -47,6 +47,7 @@ function format_value (value, descriptor, value_if_empty) {
_.str.sprintf('%d', value));
case 'monetary':
case 'float':
+ case 'field_float_scannable':
var digits = descriptor.digits ? descriptor.digits : [69,2];
digits = typeof digits === "string" ? py.eval(digits) : digits;
var precision = digits[1];
@@ -135,6 +136,7 @@ function parse_value (value, descriptor, value_if_empty) {
return tmp;
case 'monetary':
case 'float':
+ case 'field_float_scannable':
var tmp2 = value;
do {
tmp = tmp2;
diff --git a/addons/web/static/src/js/views/form_relational_widgets.js b/addons/web/static/src/js/views/form_relational_widgets.js
index 466b92c1350..fb7bcafc630 100644
--- a/addons/web/static/src/js/views/form_relational_widgets.js
+++ b/addons/web/static/src/js/views/form_relational_widgets.js
@@ -265,7 +265,7 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
});
// avoid ignoring autocomplete="off" by obfuscating placeholder, see #30439
- if (this.$input.attr('placeholder')) {
+ if ($.browser.chrome && this.$input.attr('placeholder')) {
this.$input.attr('placeholder', function (index, val) {
return val.split('').join('\ufeff');
});
diff --git a/addons/website_mail/views/website_mail_templates.xml b/addons/website_mail/views/website_mail_templates.xml
index 824abeb0727..ee03fd2d722 100644
--- a/addons/website_mail/views/website_mail_templates.xml
+++ b/addons/website_mail/views/website_mail_templates.xml
@@ -77,7 +77,7 @@
on