-
+
+
+
+
+
+
-
+
diff --git a/addons/hr_payroll/models/hr_payslip.py b/addons/hr_payroll/models/hr_payslip.py
index 18eb8408c4a..639d196fe12 100644
--- a/addons/hr_payroll/models/hr_payslip.py
+++ b/addons/hr_payroll/models/hr_payslip.py
@@ -179,9 +179,9 @@ class HrPayslip(models.Model):
for day, hours, leave in day_leave_intervals:
holiday = leave.holiday_id
current_leave_struct = leaves.setdefault(holiday.holiday_status_id, {
- 'name': holiday.holiday_status_id.name,
+ 'name': holiday.holiday_status_id.name or _('Global Leaves'),
'sequence': 5,
- 'code': holiday.holiday_status_id.name,
+ 'code': holiday.holiday_status_id.name or 'GLOBAL',
'number_of_days': 0.0,
'number_of_hours': 0.0,
'contract_id': contract.id,
diff --git a/addons/iap/models/iap.py b/addons/iap/models/iap.py
index bf426114d04..0d39bbbdf83 100644
--- a/addons/iap/models/iap.py
+++ b/addons/iap/models/iap.py
@@ -67,7 +67,7 @@ def jsonrpc(url, method='call', params=None, timeout=15):
e.data = response['error']['data']
raise e
return response.get('result')
- except (ValueError, requests.exceptions.ConnectionError, requests.exceptions.MissingSchema, requests.exceptions.Timeout) as e:
+ except (ValueError, requests.exceptions.ConnectionError, requests.exceptions.MissingSchema, requests.exceptions.Timeout, requests.exceptions.HTTPError) as e:
raise exceptions.AccessError('The url that this service requested returned an error. Please contact the author the app. The url it tried to contact was ' + url)
#----------------------------------------------------------
diff --git a/addons/l10n_nl/data/account_data.xml b/addons/l10n_nl/data/account_data.xml
index 55cccae643d..78531cb730b 100644
--- a/addons/l10n_nl/data/account_data.xml
+++ b/addons/l10n_nl/data/account_data.xml
@@ -9,6 +9,9 @@
BTW 6%
+
+ BTW 9%
+
BTW 21%
@@ -18,6 +21,9 @@
BTW 6% EU
+
+ BTW 9% EU
+
BTW 21% EU
@@ -27,6 +33,9 @@
BTW 6% Niet EU
+
+ BTW 9% Niet EU
+
BTW 21% Niet EU
diff --git a/addons/l10n_nl/data/account_fiscal_position_tax_template.xml b/addons/l10n_nl/data/account_fiscal_position_tax_template.xml
index 954e098cd89..c026f355bbb 100644
--- a/addons/l10n_nl/data/account_fiscal_position_tax_template.xml
+++ b/addons/l10n_nl/data/account_fiscal_position_tax_template.xml
@@ -16,6 +16,11 @@
+
+
+
+
+
+
+
+
+
@@ -109,6 +134,11 @@
+
+
+
+
+
@@ -124,6 +154,11 @@
+
+
+
+
+
@@ -139,6 +174,11 @@
+
+
+
+
+
diff --git a/addons/l10n_nl/data/account_tax_template.xml b/addons/l10n_nl/data/account_tax_template.xml
index bebc212da6f..0eccb372d52 100644
--- a/addons/l10n_nl/data/account_tax_template.xml
+++ b/addons/l10n_nl/data/account_tax_template.xml
@@ -20,7 +20,7 @@
10
- Verkopen/omzet laag
+ Verkopen/omzet laag 6%
6% BTW
percent
@@ -30,6 +30,19 @@
+
+ 10
+
+ Verkopen/omzet laag 9%
+ 9% BTW
+
+ percent
+
+
+ sale
+
+
+
5
@@ -73,7 +86,7 @@
10
- Verkopen/omzet laag diensten
+ Verkopen/omzet laag diensten 6%
6% BTW diensten
percent
@@ -83,6 +96,19 @@
+
+ 10
+
+ Verkopen/omzet laag diensten 9%
+ 9% BTW diensten
+
+ percent
+
+
+ sale
+
+
+
6
@@ -113,7 +139,7 @@
10
- BTW te vorderen laag (inkopen)
+ BTW te vorderen laag (inkopen) 6%
6% BTW
percent
@@ -123,10 +149,24 @@
+
+ 10
+
+ BTW te vorderen laag (inkopen) 9%
+ 9% BTW
+
+ percent
+
+
+ purchase
+
+
+
+
10
- BTW te vorderen laag (inkopen incl. BTW)
+ BTW te vorderen laag (inkopen incl. BTW) 6%
6% BTW Incl.
percent
@@ -137,6 +177,20 @@
+
+ 10
+
+ BTW te vorderen laag (inkopen incl. BTW) 9%
+ 9% BTW Incl.
+
+ percent
+ True
+
+
+ purchase
+
+
+
5
@@ -181,7 +235,7 @@
10
- BTW te vorderen laag (inkopen) diensten
+ BTW te vorderen laag (inkopen) diensten 6%
6% BTW diensten
percent
@@ -191,6 +245,19 @@
+
+ 10
+
+ BTW te vorderen laag (inkopen) diensten 9%
+ 9% BTW diensten
+
+ percent
+
+
+ purchase
+
+
+
6
@@ -231,7 +298,7 @@
-
+
98
BTW te vorderen verlegd (inkopen1)
@@ -276,7 +343,7 @@
21
- Inkopen import binnen EU laag(1)
+ Inkopen import binnen EU laag(1) 6%
Inkopen import binnen EU laag(1)
percent
@@ -286,10 +353,23 @@
+
+
+ 21
+ Inkopen import binnen EU laag(1) 9%
+ Inkopen import binnen EU laag(1)
+
+ percent
+
+
+ none
+
+
+
22
- Inkopen import binnen EU laag(2)
+ Inkopen import binnen EU laag(2) 6%
Inkopen import binnen EU laag(2)
percent
@@ -299,10 +379,23 @@
+
+
+ 22
+ Inkopen import binnen EU laag(2) 9%
+ Inkopen import binnen EU laag(2)
+
+ percent
+
+
+ none
+
+
+
20
- Inkopen import binnen EU laag
+ Inkopen import binnen EU laag 6%
6% BTW import binnen EU
group
@@ -311,6 +404,18 @@
+
+ 20
+
+ Inkopen import binnen EU laag 9%
+ 9% BTW import binnen EU
+
+ group
+ purchase
+
+
+
+
21
@@ -431,7 +536,7 @@
21
- Inkopen import binnen EU laag(1) diensten
+ Inkopen import binnen EU laag(1) diensten 6%
Inkopen import binnen EU laag(1) diensten
percent
@@ -441,10 +546,23 @@
+
+
+ 21
+ Inkopen import binnen EU laag(1) diensten 9%
+ Inkopen import binnen EU laag(1) diensten
+
+ percent
+
+
+ none
+
+
+
22
- Inkopen import binnen EU laag(2) diensten
+ Inkopen import binnen EU laag(2) diensten 6%
Inkopen import binnen EU laag(2) diensten
percent
@@ -454,10 +572,23 @@
+
+
+ 22
+ Inkopen import binnen EU laag(2) diensten 9%
+ Inkopen import binnen EU laag(2) diensten
+
+ percent
+
+
+ none
+
+
+
20
- Inkopen import binnen EU laag diensten
+ Inkopen import binnen EU laag diensten 6%
6% BTW import binnen EU diensten
group
@@ -466,6 +597,18 @@
+
+ 20
+
+ Inkopen import binnen EU laag diensten 9%
+ 9% BTW import binnen EU diensten
+
+ group
+ purchase
+
+
+
+
21
@@ -547,7 +690,7 @@
- Inkopen import buiten EU laag(1)
+ Inkopen import buiten EU laag(1) 6%
Inkopen import buiten EU laag(1)
percent
@@ -558,9 +701,22 @@
+
+
+ Inkopen import buiten EU laag(1) 9%
+ Inkopen import buiten EU laag(1)
+
+ percent
+
+
+ 21
+ none
+
+
+
- Inkopen import buiten EU laag(2)
+ Inkopen import buiten EU laag(2) 6%
Inkopen import buiten EU laag(2)
percent
@@ -571,10 +727,23 @@
+
+
+ Inkopen import buiten EU laag(2) 9%
+ Inkopen import buiten EU laag(2)
+
+ percent
+
+
+ 22
+ none
+
+
+
20
- Inkopen import buiten EU laag
+ Inkopen import buiten EU laag 6%
BTW import buiten EU laag inkopen
group
@@ -583,6 +752,18 @@
+
+ 20
+
+ Inkopen import buiten EU laag 9%
+ BTW import buiten EU laag inkopen
+
+ group
+ purchase
+
+
+
+
Inkopen import buiten EU hoog(1)
@@ -689,7 +870,7 @@
- Inkopen import buiten EU laag(1) diensten
+ Inkopen import buiten EU laag(1) diensten 6%
Inkopen import buiten EU laag(1) diensten
percent
@@ -700,9 +881,22 @@
+
+
+ Inkopen import buiten EU laag(1) diensten 9%
+ Inkopen import buiten EU laag(1) diensten
+
+ percent
+
+
+ 21
+ none
+
+
+
- Inkopen import buiten EU laag(2) diensten
+ Inkopen import buiten EU laag(2) diensten 6%
Inkopen import buiten EU laag(2) diensten
percent
@@ -713,10 +907,23 @@
+
+
+ Inkopen import buiten EU laag(2) diensten 9%
+ Inkopen import buiten EU laag(2) diensten
+
+ percent
+
+
+ 22
+ none
+
+
+
20
- Inkopen import buiten EU laag diensten
+ Inkopen import buiten EU laag diensten 6%
BTW import buiten EU laag inkopen diensten
group
@@ -725,6 +932,18 @@
+
+ 20
+
+ Inkopen import buiten EU laag diensten 9%
+ BTW import buiten EU laag inkopen diensten
+
+ group
+ purchase
+
+
+
+
Inkopen import buiten EU hoog(1) diensten
diff --git a/addons/l10n_no/data/account_data.xml b/addons/l10n_no/data/account_data.xml
index 72f40f19ae7..b0c9d690ce6 100644
--- a/addons/l10n_no/data/account_data.xml
+++ b/addons/l10n_no/data/account_data.xml
@@ -9,6 +9,9 @@
MVA 10%
+
+ MVA 12%
+
MVA 15%
diff --git a/addons/l10n_no/data/account_tax_data.xml b/addons/l10n_no/data/account_tax_data.xml
index 2609ee6f0bc..814f585514e 100644
--- a/addons/l10n_no/data/account_tax_data.xml
+++ b/addons/l10n_no/data/account_tax_data.xml
@@ -103,7 +103,19 @@
-
+
+
+
+
+ Utgående 12% mva
+ Utgående 12% mva
+ 12
+ percent
+ sale
+
+
+
+
@@ -139,9 +151,20 @@
-
+
+
+
+
+ Inngående 12% mva
+ Inngående 12% mva
+ 12
+ percent
+ purchase
+
+
+
+
-
Inngående 0% mva
diff --git a/addons/product/models/product.py b/addons/product/models/product.py
index c0c4dde774f..b5bbd38b5db 100644
--- a/addons/product/models/product.py
+++ b/addons/product/models/product.py
@@ -412,17 +412,36 @@ class ProductProduct(models.Model):
self.check_access_rule("read")
result = []
+
+ # Prefetch the fields used by the `name_get`, so `browse` doesn't fetch other fields
+ # Use `load=False` to not call `name_get` for the `product_tmpl_id`
+ self.sudo().read(['name', 'default_code', 'product_tmpl_id', 'attribute_value_ids'], load=False)
+
+ product_template_ids = self.sudo().mapped('product_tmpl_id').ids
+
+ if partner_ids:
+ supplier_info = self.env['product.supplierinfo'].sudo().search([
+ ('product_tmpl_id', 'in', product_template_ids),
+ ('name', 'in', partner_ids),
+ ])
+ # Prefetch the fields used by the `name_get`, so `browse` doesn't fetch other fields
+ # Use `load=False` to not call `name_get` for the `product_tmpl_id` and `product_id`
+ supplier_info.sudo().read(['product_tmpl_id', 'product_id', 'product_name', 'product_code'], load=False)
+ supplier_info_by_template = {}
+ for r in supplier_info:
+ supplier_info_by_template.setdefault(r.product_tmpl_id, []).append(r)
for product in self.sudo():
# display only the attributes with multiple possible values on the template
- variable_attributes = product.product_tmpl_id._get_valid_product_template_attribute_lines().filtered(lambda l: len(l.value_ids) > 1).mapped('attribute_id')
+ variable_attributes = product.attribute_value_ids.filtered(lambda v: len(v.attribute_id.value_ids) > 1).mapped('attribute_id')
variant = product.attribute_value_ids._variant_name(variable_attributes)
name = variant and "%s (%s)" % (product.name, variant) or product.name
sellers = []
if partner_ids:
- sellers = [x for x in product.seller_ids if (x.name.id in partner_ids) and (x.product_id == product)]
+ product_supplier_info = supplier_info_by_template.get(product.product_tmpl_id, [])
+ sellers = [x for x in product_supplier_info if x.product_id and x.product_id == product]
if not sellers:
- sellers = [x for x in product.seller_ids if (x.name.id in partner_ids) and not x.product_id]
+ sellers = [x for x in product_supplier_info if not x.product_id]
if sellers:
for s in sellers:
seller_variant = s.product_name and (
diff --git a/addons/product/models/product_template.py b/addons/product/models/product_template.py
index 36e35f10e66..7c4bb62ddd3 100644
--- a/addons/product/models/product_template.py
+++ b/addons/product/models/product_template.py
@@ -360,6 +360,8 @@ class ProductTemplate(models.Model):
@api.multi
def name_get(self):
+ # Prefetch the fields used by the `name_get`, so `browse` doesn't fetch other fields
+ self.read(['name', 'default_code'])
return [(template.id, '%s%s' % (template.default_code and '[%s] ' % template.default_code or '', template.name))
for template in self]
diff --git a/addons/purchase/report/purchase_report.py b/addons/purchase/report/purchase_report.py
index 4b9ca15207b..28c25cecaca 100644
--- a/addons/purchase/report/purchase_report.py
+++ b/addons/purchase/report/purchase_report.py
@@ -79,10 +79,10 @@ class PurchaseReport(models.Model):
extract(epoch from age(s.date_approve,s.date_order))/(24*60*60)::decimal(16,2) as delay,
extract(epoch from age(l.date_planned,s.date_order))/(24*60*60)::decimal(16,2) as delay_pass,
count(*) as nbr_lines,
- sum(l.price_unit / COALESCE(cr.rate, 1.0) * l.product_qty)::decimal(16,2) as price_total,
- avg(100.0 * (l.price_unit / COALESCE(cr.rate,1.0) * l.product_qty) / NULLIF(ip.value_float*l.product_qty/u.factor*u2.factor, 0.0))::decimal(16,2) as negociation,
+ sum(l.price_unit / COALESCE(NULLIF(cr.rate, 0), 1.0) * l.product_qty)::decimal(16,2) as price_total,
+ avg(100.0 * (l.price_unit / COALESCE(NULLIF(cr.rate, 0),1.0) * l.product_qty) / NULLIF(ip.value_float*l.product_qty/u.factor*u2.factor, 0.0))::decimal(16,2) as negociation,
sum(ip.value_float*l.product_qty/u.factor*u2.factor)::decimal(16,2) as price_standard,
- (sum(l.product_qty * l.price_unit / COALESCE(cr.rate, 1.0))/NULLIF(sum(l.product_qty/u.factor*u2.factor),0.0))::decimal(16,2) as price_average,
+ (sum(l.product_qty * l.price_unit / COALESCE(NULLIF(cr.rate, 0), 1.0))/NULLIF(sum(l.product_qty/u.factor*u2.factor),0.0))::decimal(16,2) as price_average,
partner.country_id as country_id,
partner.commercial_partner_id as commercial_partner_id,
analytic_account.id as account_analytic_id,
diff --git a/addons/sale_stock/models/sale_order.py b/addons/sale_stock/models/sale_order.py
index 430c3cc52d9..4c2ad3262d3 100644
--- a/addons/sale_stock/models/sale_order.py
+++ b/addons/sale_stock/models/sale_order.py
@@ -285,7 +285,14 @@ class SaleOrderLine(models.Model):
@api.onchange('product_uom_qty')
def _onchange_product_uom_qty(self):
- if self.state == 'sale' and self.product_id.type in ['product', 'consu'] and self.product_uom_qty < self._origin.product_uom_qty:
+ # When modifying a one2many, _origin doesn't guarantee that its values will be the ones
+ # in database. Hence, we need to explicitly read them from there.
+ if self._origin:
+ product_uom_qty_origin = self._origin.read(["product_uom_qty"])[0]["product_uom_qty"]
+ else:
+ product_uom_qty_origin = 0
+
+ if self.state == 'sale' and self.product_id.type in ['product', 'consu'] and self.product_uom_qty < product_uom_qty_origin:
# Do not display this warning if the new quantity is below the delivered
# one; the `write` will raise an `UserError` anyway.
if self.product_uom_qty < self.qty_delivered:
diff --git a/addons/stock/models/stock_inventory.py b/addons/stock/models/stock_inventory.py
index 5ed14e6d3a0..02f86096ecd 100644
--- a/addons/stock/models/stock_inventory.py
+++ b/addons/stock/models/stock_inventory.py
@@ -425,7 +425,7 @@ class InventoryLine(models.Model):
"""
for line in self:
if line.product_id.type != 'product':
- raise UserError(_("You can only adjust storable products."))
+ raise UserError(_("You can only adjust storable products.") + '\n\n%s -> %s' % (line.product_id.display_name, line.product_id.type))
def _get_move_values(self, qty, location_id, location_dest_id, out):
self.ensure_one()
diff --git a/addons/stock/report/report_location_barcode.xml b/addons/stock/report/report_location_barcode.xml
index 7cf706d0467..a1f81d50aac 100644
--- a/addons/stock/report/report_location_barcode.xml
+++ b/addons/stock/report/report_location_barcode.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/addons/stock_picking_batch/wizard/stock_picking_to_batch.py b/addons/stock_picking_batch/wizard/stock_picking_to_batch.py
index ddf02ffc172..4739e39670e 100644
--- a/addons/stock_picking_batch/wizard/stock_picking_to_batch.py
+++ b/addons/stock_picking_batch/wizard/stock_picking_to_batch.py
@@ -8,7 +8,7 @@ class StockPickingToBatch(models.TransientModel):
_name = 'stock.picking.to.batch'
_description = 'Batch Picking Lines'
- batch_id = fields.Many2one('stock.picking.batch', string='Batch Picking', required=True, oldname="wave_id")
+ batch_id = fields.Many2one('stock.picking.batch', string='Batch Picking', oldname="wave_id")
@api.multi
def attach_pickings(self):
diff --git a/addons/stock_picking_batch/wizard/stock_picking_to_batch_views.xml b/addons/stock_picking_batch/wizard/stock_picking_to_batch_views.xml
index da393c44972..b3b7b9a0108 100644
--- a/addons/stock_picking_batch/wizard/stock_picking_to_batch_views.xml
+++ b/addons/stock_picking_batch/wizard/stock_picking_to_batch_views.xml
@@ -8,7 +8,7 @@
',
res_id: 1,
mockRPC: function (route, args) {
- assert.step(args.method + ' ' + args.model)
+ assert.step(args.method + ' ' + args.model);
return this._super(route, args);
},
viewOptions: {
@@ -12791,6 +12835,53 @@ QUnit.module('relational_fields', {
form.destroy();
});
+ QUnit.test('widget many2many_tags in one2many with display_name', function (assert) {
+ assert.expect(4);
+ this.data.turtle.records[0].partner_ids = [2];
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '',
+ archs: {
+ 'partner,false,list': '',
+ },
+ res_id: 1,
+ });
+
+ assert.strictEqual(form.$('.o_field_one2many[name="turtles"] .o_list_view .o_field_many2manytags[name="partner_ids"]').text().replace(/\s/g, ''),
+ "secondrecordaaa", "the tags should be correctly rendered");
+
+ // open the x2m form view
+ form.$('.o_field_one2many[name="turtles"] .o_list_view td.o_data_cell:first').click();
+ assert.strictEqual($('.modal .o_form_view .o_field_many2many[name="partner_ids"] .o_list_view .o_data_cell').text(),
+ "blipMy little Foo Value", "the list view should be correctly rendered with foo");
+
+ $('.modal button.o_form_button_cancel').click();
+ assert.strictEqual(form.$('.o_field_one2many[name="turtles"] .o_list_view .o_field_many2manytags[name="partner_ids"]').text().replace(/\s/g, ''),
+ "secondrecordaaa", "the tags should still be correctly rendered");
+
+ form.$buttons.find('.o_form_button_edit').click();
+ assert.strictEqual(form.$('.o_field_one2many[name="turtles"] .o_list_view .o_field_many2manytags[name="partner_ids"]').text().replace(/\s/g, ''),
+ "secondrecordaaa", "the tags should still be correctly rendered");
+
+ form.destroy();
+ });
+
QUnit.test('widget many2many_tags: tags title attribute', function (assert) {
assert.expect(1);
this.data.turtle.records[0].partner_ids = [2];
diff --git a/addons/website_blog/static/src/js/contentshare.js b/addons/website_blog/static/src/js/contentshare.js
index cfc02470f7f..944ff5c9afe 100644
--- a/addons/website_blog/static/src/js/contentshare.js
+++ b/addons/website_blog/static/src/js/contentshare.js
@@ -34,7 +34,7 @@
},
commentEdition : function(){
var positionComment = ($('#comments').position()).top-50;
- $(".o_website_chatter_form textarea").val('"' + selected_text + '" ').focus();
+ $(".o_portal_chatter_composer_form textarea").val('"' + selected_text + '" ').focus();
$('html, body').stop().animate({
'scrollTop': positionComment
}, 500, 'swing', function () {
diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py
index 93b7bef9648..aec6d3b17ed 100644
--- a/addons/website_sale/controllers/main.py
+++ b/addons/website_sale/controllers/main.py
@@ -337,7 +337,7 @@ class WebsiteSale(ProductConfiguratorController):
}
return request.render("website_sale.product", values)
- @http.route(['/shop/change_pricelist/'], type='http', auth="public", website=True)
+ @http.route(['/shop/change_pricelist/'], type='http', auth="public", website=True, sitemap=False)
def pricelist_change(self, pl_id, **post):
if (pl_id.selectable or pl_id == request.env.user.partner_id.property_product_pricelist) \
and request.website.is_pricelist_available(pl_id.id):
@@ -345,7 +345,7 @@ class WebsiteSale(ProductConfiguratorController):
request.website.sale_get_order(force_pricelist=pl_id.id)
return request.redirect(request.httprequest.referrer or '/shop')
- @http.route(['/shop/pricelist'], type='http', auth="public", website=True)
+ @http.route(['/shop/pricelist'], type='http', auth="public", website=True, sitemap=False)
def pricelist(self, promo, **post):
redirect = post.get('r', '/shop/cart')
pricelist = request.env['product.pricelist'].sudo().search([('code', '=', promo)], limit=1)
diff --git a/addons/website_sale/static/src/scss/website_sale.scss b/addons/website_sale/static/src/scss/website_sale.scss
index 087920817db..29e685b8b4a 100644
--- a/addons/website_sale/static/src/scss/website_sale.scss
+++ b/addons/website_sale/static/src/scss/website_sale.scss
@@ -80,7 +80,7 @@
right: 0;
bottom: 0;
overflow: hidden;
- padding: 0 15px 24px 10px;
+ padding: 0 15px 0 10px;
min-height: 56px;
border-top: 1px solid rgba(255,255,255,0.2);
background: rgba(255, 255, 255, 0.75);
@@ -88,8 +88,6 @@
}
.product_price {
- padding: 5px 0;
- position: absolute;
bottom: 0;
}
diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml
index d3e35b83098..31fe23ed363 100644
--- a/addons/website_sale/views/templates.xml
+++ b/addons/website_sale/views/templates.xml
@@ -1116,7 +1116,7 @@