diff --git a/addons/google_drive/static/src/css/google_drive.css b/addons/google_drive/static/src/css/google_drive.css
new file mode 100644
index 00000000000..ce2b22599b0
--- /dev/null
+++ b/addons/google_drive/static/src/css/google_drive.css
@@ -0,0 +1,5 @@
+li.oe_share_gdoc > a:after {
+ content: url('/google_drive/static/src/img/drive_icon.png');
+ position: absolute;
+ right: 5px;
+}
diff --git a/addons/google_drive/static/src/img/drive_icon.png b/addons/google_drive/static/src/img/drive_icon.png
index 590dde867f4..1553aa319a6 100644
Binary files a/addons/google_drive/static/src/img/drive_icon.png and b/addons/google_drive/static/src/img/drive_icon.png differ
diff --git a/addons/google_drive/static/src/js/gdrive.js b/addons/google_drive/static/src/js/gdrive.js
index bc7449c5507..6da06dcd8ec 100644
--- a/addons/google_drive/static/src/js/gdrive.js
+++ b/addons/google_drive/static/src/js/gdrive.js
@@ -41,7 +41,7 @@ Sidebar.include({
}
if (!already_there){
self.add_items('other', [{
- label: res.name+ '
',
+ label: res.name,
config_id: res.id,
res_id: res_id,
res_model: view.dataset.model,
diff --git a/addons/google_drive/views/google_drive_templates.xml b/addons/google_drive/views/google_drive_templates.xml
index f9481bfd5c5..1906a27696b 100644
--- a/addons/google_drive/views/google_drive_templates.xml
+++ b/addons/google_drive/views/google_drive_templates.xml
@@ -3,6 +3,7 @@
+
diff --git a/addons/payment_paypal/controllers/main.py b/addons/payment_paypal/controllers/main.py
index 7e4aac4c036..bdf35742bec 100644
--- a/addons/payment_paypal/controllers/main.py
+++ b/addons/payment_paypal/controllers/main.py
@@ -8,6 +8,7 @@ import urllib2
import werkzeug
from odoo import http
+from odoo.addons.payment.models.payment_acquirer import ValidationError
from odoo.http import request
_logger = logging.getLogger(__name__)
@@ -86,7 +87,10 @@ class PaypalController(http.Controller):
def paypal_ipn(self, **post):
""" Paypal IPN. """
_logger.info('Beginning Paypal IPN form_feedback with post data %s', pprint.pformat(post)) # debug
- self.paypal_validate_data(**post)
+ try:
+ self.paypal_validate_data(**post)
+ except ValidationError:
+ _logger.exception('Unable to validate the Paypal payment')
return ''
@http.route('/payment/paypal/dpn', type='http', auth="none", methods=['POST', 'GET'], csrf=False)
diff --git a/addons/point_of_sale/report/pos_invoice.py b/addons/point_of_sale/report/pos_invoice.py
index 9ce8b0860ba..92e3a0350e2 100644
--- a/addons/point_of_sale/report/pos_invoice.py
+++ b/addons/point_of_sale/report/pos_invoice.py
@@ -24,4 +24,4 @@ class PosInvoiceReport(models.AbstractModel):
not_invoiced_orders_names = list(map(lambda a: a.name, not_invoiced_posorders))
raise UserError(_('No link to an invoice for %s.') % ', '.join(not_invoiced_orders_names))
- return Report.sudo().render('account.report_invoice', {'docs': self.env['account.invoice'].browse(ids_to_print)})
+ return Report.sudo().render('account.report_invoice', {'docs': self.env['account.invoice'].sudo().browse(ids_to_print)})
diff --git a/addons/point_of_sale/views/report_saledetails.xml b/addons/point_of_sale/views/report_saledetails.xml
index 9144e47fb0b..7894c3a4cef 100644
--- a/addons/point_of_sale/views/report_saledetails.xml
+++ b/addons/point_of_sale/views/report_saledetails.xml
@@ -9,7 +9,7 @@
Sales Details
- -
+ -
diff --git a/addons/product/models/product.py b/addons/product/models/product.py
index 056b113acea..0362300e557 100644
--- a/addons/product/models/product.py
+++ b/addons/product/models/product.py
@@ -96,7 +96,7 @@ class ProductPriceHistory(models.Model):
company_id = fields.Many2one('res.company', default=_get_default_company_id, required=True)
product_id = fields.Many2one('product.product', 'Product', ondelete='cascade', required=True)
- datetime = fields.Datetime('Date', default=fields.Datetime.now())
+ datetime = fields.Datetime('Date', default=fields.Datetime.now)
cost = fields.Float('Cost', digits=dp.get_precision('Product Price'))
diff --git a/addons/purchase/models/account_invoice.py b/addons/purchase/models/account_invoice.py
index a7971764734..00e11060444 100644
--- a/addons/purchase/models/account_invoice.py
+++ b/addons/purchase/models/account_invoice.py
@@ -162,10 +162,16 @@ class AccountInvoice(models.Model):
if valuation_price_unit != i_line.price_unit and line['price_unit'] == i_line.price_unit and acc:
# price with discount and without tax included
price_unit = i_line.price_unit * (1 - (i_line.discount or 0.0) / 100.0)
+ tax_ids = []
if line['tax_ids']:
#line['tax_ids'] is like [(4, tax_id, None), (4, tax_id2, None)...]
taxes = self.env['account.tax'].browse([x[1] for x in line['tax_ids']])
price_unit = taxes.compute_all(price_unit, currency=inv.currency_id, quantity=1.0)['total_excluded']
+ for tax in taxes:
+ tax_ids.append((4, tax.id, None))
+ for child in tax.children_tax_ids:
+ if child.type_tax_use != 'none':
+ tax_ids.append((4, child.id, None))
price_before = line.get('price', 0.0)
line.update({'price': round(valuation_price_unit * line['quantity'], account_prec)})
diff_res.append({
@@ -178,6 +184,7 @@ class AccountInvoice(models.Model):
'product_id': line['product_id'],
'uom_id': line['uom_id'],
'account_analytic_id': line['account_analytic_id'],
+ 'tax_ids': tax_ids,
})
return diff_res
return []
diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py
index 024c8c3871b..18fdb4a44f5 100644
--- a/addons/sale/models/sale.py
+++ b/addons/sale/models/sale.py
@@ -915,6 +915,11 @@ class SaleOrderLine(models.Model):
product_currency = None
if rule_id:
pricelist_item = PricelistItem.browse(rule_id)
+ if pricelist_item.pricelist_id.discount_policy == 'without_discount':
+ while pricelist_item.base == 'pricelist' and pricelist_item.base_pricelist_id and pricelist_item.base_pricelist_id.discount_policy == 'without_discount':
+ price, rule_id = pricelist_item.base_pricelist_id.with_context(uom=uom.id).get_product_price_rule(product, qty, self.order_id.partner_id)
+ pricelist_item = PricelistItem.browse(rule_id)
+
if pricelist_item.base == 'standard_price':
field_name = 'standard_price'
if pricelist_item.base == 'pricelist' and pricelist_item.base_pricelist_id:
@@ -951,8 +956,8 @@ class SaleOrderLine(models.Model):
self.env.user.has_group('sale.group_discount_per_so_line')):
return
- context_partner = dict(self.env.context, partner_id=self.order_id.partner_id.id)
- pricelist_context = dict(context_partner, uom=self.product_uom.id, date=self.order_id.date_order)
+ context_partner = dict(self.env.context, partner_id=self.order_id.partner_id.id, date=self.order_id.date_order)
+ pricelist_context = dict(context_partner, uom=self.product_uom.id)
price, rule_id = self.order_id.pricelist_id.with_context(pricelist_context).get_product_price_rule(self.product_id, self.product_uom_qty or 1.0, self.order_id.partner_id)
new_list_price, currency_id = self.with_context(context_partner)._get_real_price_currency(self.product_id, rule_id, self.product_uom_qty, self.product_uom, self.order_id.pricelist_id.id)
@@ -961,8 +966,7 @@ class SaleOrderLine(models.Model):
if price != 0 and new_list_price != 0:
if self.product_id.company_id and self.order_id.pricelist_id.currency_id != self.product_id.company_id.currency_id:
# new_list_price is in company's currency while price in pricelist currency
- ctx = dict(context_partner, date=self.order_id.date_order)
- new_list_price = self.env['res.currency'].browse(currency_id).with_context(ctx).compute(new_list_price, self.order_id.pricelist_id.currency_id)
+ new_list_price = self.env['res.currency'].browse(currency_id).with_context(context_partner).compute(new_list_price, self.order_id.pricelist_id.currency_id)
discount = (new_list_price - price) / new_list_price * 100
if discount > 0:
self.discount = discount
diff --git a/addons/stock/models/stock_inventory.py b/addons/stock/models/stock_inventory.py
index f6c36541990..5cc88ee7237 100644
--- a/addons/stock/models/stock_inventory.py
+++ b/addons/stock/models/stock_inventory.py
@@ -88,8 +88,13 @@ class Inventory(models.Model):
exhausted = fields.Boolean('Include Exhausted Products', readonly=True, states={'draft': [('readonly', False)]})
@api.one
+ @api.depends('product_id', 'line_ids.product_qty')
def _compute_total_qty(self):
- self.total_qty = sum(self.mapped('line_ids').mapped('product_qty'))
+ """ For single product inventory, total quantity of the counted """
+ if self.product_id:
+ self.total_qty = sum(self.mapped('line_ids').mapped('product_qty'))
+ else:
+ self.total_qty = 0
@api.model
def _selection_filter(self):
diff --git a/addons/web/static/src/js/views/list_view_editable.js b/addons/web/static/src/js/views/list_view_editable.js
index ef9bf1a6062..003b6f18d01 100644
--- a/addons/web/static/src/js/views/list_view_editable.js
+++ b/addons/web/static/src/js/views/list_view_editable.js
@@ -641,7 +641,7 @@ ListView.include(/** @lends instance.web.ListView# */{
if (saveInfo.created) {
return self.start_edition();
}
- var options = { wraparound: !self.is_action_enabled('create') };
+ options.wraparound = !self.is_action_enabled('create');
var record = self.records[next_record](saveInfo.record, options);
if (record === undefined) {
return self.start_edition();
diff --git a/addons/web/static/test/registry.js b/addons/web/static/test/registry.js
index 132444c332a..58996837ec6 100644
--- a/addons/web/static/test/registry.js
+++ b/addons/web/static/test/registry.js
@@ -1,8 +1,8 @@
odoo.define_section('web.registry', ['web.Registry'], {
beforeEach: function () {
- this.Foo = {};
- this.Bar = {};
- this.Foo2 = {};
+ this.Foo = {bar: 1};
+ this.Bar = {foo: 0};
+ this.Foo2 = {fred: -1};
},
}, function (test) {
@@ -14,6 +14,8 @@ odoo.define_section('web.registry', ['web.Registry'], {
.add('bar', this.Bar);
assert.strictEqual(registry.get('foo'), this.Foo);
+ // make sure we're comparing actual objects instead of just undefineds
+ assert.deepEqual(registry.get('foo'), {bar: 1});
});
test('extension', function (assert, Registry) {
diff --git a/addons/web/static/test/testing.js b/addons/web/static/test/testing.js
index 2ec911740b2..927ca512060 100644
--- a/addons/web/static/test/testing.js
+++ b/addons/web/static/test/testing.js
@@ -16,7 +16,7 @@ odoo.testing.start_services = function () {
};
odoo.testing.MockRPC = function (session) {
- this.responses = {};
+ this.clear();
};
odoo.testing.MockRPC.prototype.clear = function () {
@@ -81,8 +81,33 @@ odoo.define_section = function (name, section_deps) {
section_body = arguments[3];
}
- QUnit.module(name, options);
+ mock = new odoo.testing.MockRPC();
+ function dummyfunc () {};
+
+ function beforeEach(assert) {
+ var services = odoo.testing.start_services();
+ this.deps = services;
+ this.getServiceDeps = function getServiceDeps(dep_names) {
+ return _.map(dep_names, function (name) { return services[name]; })
+ }
+ services['web.core'].qweb.add_template(odoo.testing.templates);
+ this.mock = mock;
+ this.mock.interceptRPC(services['web.session']);
+ this.assert = assert;
+ (options.beforeEach || dummyfunc).apply(this, [assert].concat(this.getServiceDeps(section_deps)));
+ }
+ function afterEach(assert) {
+ (options.afterEach || dummyfunc).apply(this, [assert].concat(this.getServiceDeps(section_deps)));
+ this.mock.clear();
+ }
+
+ QUnit.module(name, {
+ before: options.before,
+ beforeEach: beforeEach,
+ afterEach: afterEach,
+ after: options.after
+ });
function test () {
var name = arguments[0],
@@ -90,21 +115,11 @@ odoo.define_section = function (name, section_deps) {
body = arguments[arguments.length - 1];
QUnit.test(name, function (assert) {
- var services = odoo.testing.start_services();
- var deps = _.map(section_deps.concat(dep_names), function (name) { return services[name]; });
- services['web.core'].qweb.add_template(odoo.testing.templates);
- mock.clear();
- mock.interceptRPC(services['web.session']);
- var info = {
- assert: assert,
- mock: mock,
- deps: _.pick.apply(null, [services].concat(section_deps).concat(dep_names)),
- }
- return body.apply(info, [assert].concat(deps));
+ var deps = this.getServiceDeps(section_deps.concat(dep_names));
+ return body.apply(this, [assert].concat(deps));
});
}
- var mock = new odoo.testing.MockRPC();
section_body(test, mock);
};
diff --git a/addons/web_editor/static/src/js/backend.js b/addons/web_editor/static/src/js/backend.js
index ce8643f04de..50551c381b4 100644
--- a/addons/web_editor/static/src/js/backend.js
+++ b/addons/web_editor/static/src/js/backend.js
@@ -288,6 +288,7 @@ var FieldTextHtml = widget.extend({
return src;
},
initialize_content: function () {
+ var self = this;
this.$el.closest('.modal-body').css('max-height', 'none');
this.$iframe = this.$el.find('iframe');
this.document = null;
@@ -296,6 +297,14 @@ var FieldTextHtml = widget.extend({
this.editor = false;
window.odoo[this.callback+"_updown"] = null;
this.$iframe.attr("src", this.get_url());
+ this.view.on("load_record", this, function(r){
+ if (!self.$body.find('.o_dirty').length){
+ var url = self.get_url();
+ if(url !== self.$iframe.attr("src")){
+ self.$iframe.attr("src", url);
+ }
+ }
+ });
},
on_content_loaded: function () {
var self = this;
diff --git a/addons/website_sale/static/src/js/website_sale.js b/addons/website_sale/static/src/js/website_sale.js
index 710ce9def28..555d4c8142e 100644
--- a/addons/website_sale/static/src/js/website_sale.js
+++ b/addons/website_sale/static/src/js/website_sale.js
@@ -204,6 +204,19 @@ odoo.define('website_sale.website_sale', function (require) {
event.preventDefault();
$(this).closest('form').submit();
}
+ if ($(this).hasClass('a-submit-disable')){
+ $(this).addClass("disabled");
+ }
+ if ($(this).hasClass('a-submit-loading')){
+ var loading = '';
+ var fa_span = $(this).find('span[class*="fa"]');
+ if (fa_span.length){
+ fa_span.replaceWith(loading);
+ }
+ else{
+ $(this).append(loading);
+ }
+ }
});
$('form.js_attributes input, form.js_attributes select', oe_website_sale).on('change', function (event) {
if (!event.isDefaultPrevented()) {
@@ -397,7 +410,7 @@ odoo.define('website_sale.website_sale', function (require) {
//$("input[name='phone']").attr('placeholder', data.phone_code !== 0 ? '+'+ data.phone_code : '');
// populate states and display
- var selectStates = $("select[name='state_id']:visible");
+ var selectStates = $("select[name='state_id']");
// dont reload state at first loading (done in qweb)
if (selectStates.data('init')===0 || selectStates.find('option').length===1) {
if (data.states.length) {
diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml
index 4492e6d5836..6f8f355acea 100644
--- a/addons/website_sale/views/templates.xml
+++ b/addons/website_sale/views/templates.xml
@@ -1276,7 +1276,7 @@
Back
-
+
Next
diff --git a/addons/website_slides/security/ir.model.access.csv b/addons/website_slides/security/ir.model.access.csv
index 39d871b2863..07d087f7db9 100644
--- a/addons/website_slides/security/ir.model.access.csv
+++ b/addons/website_slides/security/ir.model.access.csv
@@ -1,6 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_slide_slide_public,slide.slide.public,model_slide_slide,base.group_public,1,0,0,0
-access_slide_slide_portal,slide.slide.portal,model_slide_slide,base.group_portal,1,1,1,0
+access_slide_slide_portal,slide.slide.portal,model_slide_slide,base.group_portal,1,0,0,0
access_slide_slide_user,slide.slide.user,model_slide_slide,base.group_user,1,1,1,1
access_slide_tag_public,slide.tag.public,model_slide_tag,base.group_public,1,0,0,0
access_slide_tag_portal,slide.tag.portal,model_slide_tag,base.group_portal,1,0,0,0
diff --git a/addons/website_slides/security/website_slides_security.xml b/addons/website_slides/security/website_slides_security.xml
index c464e6ff578..fe39060bd3b 100644
--- a/addons/website_slides/security/website_slides_security.xml
+++ b/addons/website_slides/security/website_slides_security.xml
@@ -17,7 +17,7 @@
Channel: public: published only
-
+
[('website_published', '=', True)]
@@ -36,7 +36,7 @@
Slide: public: published only
-
+
[('channel_id.website_published', '=', True), ('website_published', '=', True)]
diff --git a/doc/cla/individual/bluesoft83.md b/doc/cla/individual/bluesoft83.md
new file mode 100644
index 00000000000..e8caabb578a
--- /dev/null
+++ b/doc/cla/individual/bluesoft83.md
@@ -0,0 +1,11 @@
+Bosnia and Herzegovina, 2017-03-02
+
+I hereby agree to the terms of the Odoo Individual Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Boško Stojaković bluesoft83@gmail.com https://github.com/bluesoft83
diff --git a/odoo/addons/base/ir/ir_model.py b/odoo/addons/base/ir/ir_model.py
index ce5c60e0025..0c5b9b92aff 100644
--- a/odoo/addons/base/ir/ir_model.py
+++ b/odoo/addons/base/ir/ir_model.py
@@ -136,10 +136,8 @@ class IrModel(models.Model):
for model in self:
if model.state != 'manual':
raise UserError(_("Model '%s' contains module data and cannot be removed!") % model.name)
-
- # prevent screwing up fields that depend on these models' fields
- for model in self:
- model.field_id._prepare_update()
+ # prevent screwing up fields that depend on these models' fields
+ model.field_id._prepare_update()
self._drop_table()
res = super(IrModel, self).unlink()
diff --git a/odoo/addons/base/res/res_users.py b/odoo/addons/base/res/res_users.py
index 45142038b27..e92aaf5315e 100644
--- a/odoo/addons/base/res/res_users.py
+++ b/odoo/addons/base/res/res_users.py
@@ -183,7 +183,7 @@ class Users(models.Model):
def _default_groups(self):
default_user = self.env.ref('base.default_user', raise_if_not_found=False)
- return (default_user or self.env['res.users']).groups_id
+ return (default_user or self.env['res.users']).sudo().groups_id
def _companies_count(self):
return self.env['res.company'].sudo().search_count([])