diff --git a/addons/account/tests/__init__.py b/addons/account/tests/__init__.py index e4a6224852d..01e519c02cf 100644 --- a/addons/account/tests/__init__.py +++ b/addons/account/tests/__init__.py @@ -12,6 +12,7 @@ from . import test_chart_of_account from . import test_fiscal_position from . import test_manual_reconciliation from . import test_payment +from . import test_product_id_change from . import test_reconciliation from . import test_search from . import test_tax diff --git a/addons/account/tests/test_product_id_change.py b/addons/account/tests/test_product_id_change.py index 1bd5154f4ba..a2008b64bc7 100644 --- a/addons/account/tests/test_product_id_change.py +++ b/addons/account/tests/test_product_id_change.py @@ -1,50 +1,91 @@ -from openerp.tests.common import TransactionCase +from openerp.addons.account.tests.account_test_classes import AccountingTestCase +import time -class TestProductIdChange(TransactionCase): +class TestProductIdChange(AccountingTestCase): """Test that when an included tax is mapped by a fiscal position, the included tax must be subtracted to the price of the product. """ def setUp(self): super(TestProductIdChange, self).setUp() - self.fiscal_position_model = self.registry('account.fiscal.position') - self.fiscal_position_tax_model = self.registry('account.fiscal.position.tax') - self.tax_model = self.registry('account.tax') - self.pricelist_model = self.registry('product.pricelist') - self.res_partner_model = self.registry('res.partner') - self.product_tmpl_model = self.registry('product.template') - self.product_model = self.registry('product.product') - self.invoice_line_model = self.registry('account.invoice.line') + self.invoice_model = self.env['account.invoice'] + self.fiscal_position_model = self.env['account.fiscal.position'] + self.fiscal_position_tax_model = self.env['account.fiscal.position.tax'] + self.tax_model = self.env['account.tax'] + self.pricelist_model = self.env['product.pricelist'] + self.res_partner_model = self.env['res.partner'] + self.product_tmpl_model = self.env['product.template'] + self.product_model = self.env['product.product'] + self.invoice_line_model = self.env['account.invoice.line'] + self.account_receivable = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_receivable').id)], limit=1) + self.account_revenue = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_revenue').id)], limit=1) def test_product_id_change(self): - cr, uid = self.cr, self.uid - partner_id = self.res_partner_model.create(cr, uid, dict(name="George")) - tax_include_id = self.tax_model.create(cr, uid, dict(name="Include tax", - type='percent', - amount='0.21', - price_include=True)) - tax_exclude_id = self.tax_model.create(cr, uid, dict(name="Exclude tax", - type='percent', - amount='0.00')) - product_tmpl_id = self.product_tmpl_model.create(cr, uid, dict(name="Voiture", - list_price='121', - standard_price='121', - taxes_id=[(6, 0, [tax_include_id])], - supplier_taxes_id=[(6, 0, [tax_include_id])])) - product_id = self.product_model.create(cr, uid, dict(product_tmpl_id=product_tmpl_id)) - product = self.product_model.browse(cr, uid, [product_id]) - fp_id = self.fiscal_position_model.create(cr, uid, dict(name="fiscal position", - sequence=1)) - fp_tax_id = self.fiscal_position_tax_model.create(cr, uid, dict(position_id=fp_id, - tax_src_id=tax_include_id, - tax_dest_id=tax_exclude_id)) + partner = self.res_partner_model.create(dict(name="George")) + tax_include_sale = self.tax_model.create(dict(name="Include tax", + type_tax_use='sale', + amount='21.00', + price_include=True)) + tax_include_purchase = self.tax_model.create(dict(name="Include tax", + type_tax_use='purchase', + amount='21.00', + price_include=True)) + tax_exclude_sale = self.tax_model.create(dict(name="Exclude tax", + type_tax_use='sale', + amount='0.00')) + tax_exclude_purchase = self.tax_model.create(dict(name="Exclude tax", + type_tax_use='purchase', + amount='0.00')) + product_tmpl = self.product_tmpl_model.create(dict(name="Voiture", + list_price='121', + standard_price='121', + taxes_id=[(6, 0, [tax_include_sale.id])], + supplier_taxes_id=[(6, 0, [tax_include_purchase.id])])) + product = self.product_model.create(dict(product_tmpl_id=product_tmpl.id)) + fp = self.fiscal_position_model.create(dict(name="fiscal position", sequence=1)) + fp_tax_sale = self.fiscal_position_tax_model.create(dict(position_id=fp.id, + tax_src_id=tax_include_sale.id, + tax_dest_id=tax_exclude_sale.id)) + fp_tax_purchase = self.fiscal_position_tax_model.create(dict(position_id=fp.id, + tax_src_id=tax_include_purchase.id, + tax_dest_id=tax_exclude_purchase.id)) - res = self.invoice_line_model.product_id_change(cr, uid, [], product.id, product.uom_id.id, - qty=1, type='out_invoice', partner_id=partner_id, - fposition_id=fp_id) - self.assertEquals(100, res['value']['price_unit'], "The included tax must be subtracted to the price") + out_invoice = self.invoice_model.create({ + 'partner_id': partner.id, + 'reference_type': 'none', + 'name': 'invoice to client', + 'account_id': self.account_receivable.id, + 'type': 'out_invoice', + 'date_invoice': time.strftime('%Y') + '-06-26', + 'fiscal_position_id': fp.id, + }) + out_line = self.invoice_line_model.create({ + 'product_id': product.id, + 'quantity': 1, + 'price_unit': 121.0, + 'invoice_id': out_invoice.id, + 'name': 'something out', + 'account_id': self.account_revenue.id, + }) - res = self.invoice_line_model.product_id_change(cr, uid, [], product.id, product.uom_id.id, - qty=1, type='in_invoice', partner_id=partner_id, - fposition_id=fp_id) - self.assertEquals(100, res['value']['price_unit'], "The included tax must be subtracted to the price") + in_invoice = self.invoice_model.create({ + 'partner_id': partner.id, + 'reference_type': 'none', + 'name': 'invoice to supplier', + 'account_id': self.account_receivable.id, + 'type': 'in_invoice', + 'date_invoice': time.strftime('%Y') + '-06-26', + 'fiscal_position_id': fp.id, + }) + in_line = self.invoice_line_model.create({ + 'product_id': product.id, + 'quantity': 1, + 'price_unit': 121.0, + 'invoice_id': in_invoice.id, + 'name': 'something in', + 'account_id': self.account_revenue.id, + }) + out_line._onchange_product_id() + self.assertEquals(100, out_line.price_unit, "The included tax must be subtracted to the price") + in_line._onchange_product_id() + self.assertEquals(100, in_line.price_unit, "The included tax must be subtracted to the price") diff --git a/addons/im_livechat/controllers/main.py b/addons/im_livechat/controllers/main.py index 43e547e2fb1..30ae212c8b2 100644 --- a/addons/im_livechat/controllers/main.py +++ b/addons/im_livechat/controllers/main.py @@ -30,7 +30,8 @@ class LivechatController(http.Controller): username = kwargs.get("username", _("Visitor")) channel = request.env['im_livechat.channel'].sudo().browse(channel_id) info = request.env['im_livechat.channel'].get_livechat_info(channel.id, username=username) - return request.render('im_livechat.loader', {'info': info, 'web_session_required': True}) if info['available'] else False + js = request.render('im_livechat.loader', {'info': info, 'web_session_required': True}) + return request.make_response(js, headers=[('Content-Type', 'application/javascript')]) @http.route('/im_livechat/init', type='json', auth="public") def livechat_init(self, channel_id): diff --git a/addons/im_livechat/models/im_livechat_channel.py b/addons/im_livechat/models/im_livechat_channel.py index e15d4212a43..ac894a2c7ab 100644 --- a/addons/im_livechat/models/im_livechat_channel.py +++ b/addons/im_livechat/models/im_livechat_channel.py @@ -195,7 +195,7 @@ class ImLivechatChannel(models.Model): @api.model def get_livechat_info(self, channel_id, username='Visitor'): info = {} - info['available'] = len(self.browse(channel_id).get_available_users()) + info['available'] = len(self.browse(channel_id).get_available_users()) > 0 if info['available']: info['server_url'] = self.env['ir.config_parameter'].get_param('web.base.url') info['options'] = self.sudo().get_channel_infos(channel_id) diff --git a/addons/im_livechat/views/im_livechat_channel_templates.xml b/addons/im_livechat/views/im_livechat_channel_templates.xml index d97df04989e..0efd2310a14 100644 --- a/addons/im_livechat/views/im_livechat_channel_templates.xml +++ b/addons/im_livechat/views/im_livechat_channel_templates.xml @@ -143,7 +143,6 @@ - document.addEventListener("DOMContentLoaded", function(event) { odoo.define('web.session', function (require) { @@ -154,6 +153,7 @@ odoo.define('im_livechat.livesupport', function (require) { + var im_livechat = require('im_livechat.im_livechat'); var button = new im_livechat.LivechatButton( $('body'), @@ -162,9 +162,9 @@ ); button.appendTo($('body')); window.livechat_button = button; + }); }); - diff --git a/addons/mail/wizard/mail_compose_message_view.xml b/addons/mail/wizard/mail_compose_message_view.xml index 54d8e599240..73429e48962 100644 --- a/addons/mail/wizard/mail_compose_message_view.xml +++ b/addons/mail/wizard/mail_compose_message_view.xml @@ -96,6 +96,7 @@ context="{ 'default_composition_mode': 'mass_mail', 'default_partner_to': '${object.id or \'\'}', + 'default_active_domain': [['id', 'in', context.get('active_ids', [])]], }"/> diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py index cf1d7ecb2c9..8ba3e79b05c 100644 --- a/addons/point_of_sale/point_of_sale.py +++ b/addons/point_of_sale/point_of_sale.py @@ -645,7 +645,7 @@ class pos_order(osv.osv): _order = "id desc" def _amount_line_tax(self, cr, uid, line, fiscal_position_id, context=None): - taxes = line.product_id.taxes_id.filtered(lambda t: t.company_id.id == line.order_id.company_id.id) + taxes = line.tax_ids.filtered(lambda t: t.company_id.id == line.order_id.company_id.id) if fiscal_position_id: taxes = fiscal_position_id.map_tax(taxes) price = line.price_unit * (1 - (line.discount or 0.0) / 100.0) @@ -1399,7 +1399,7 @@ class pos_order_line(osv.osv): account_tax_obj = self.pool.get('account.tax') for line in self.browse(cr, uid, ids, context=context): cur = line.order_id.pricelist_id.currency_id - taxes = [ tax for tax in line.product_id.taxes_id if tax.company_id.id == line.order_id.company_id.id ] + taxes = [ tax for tax in line.tax_ids if tax.company_id.id == line.order_id.company_id.id ] fiscal_position_id = line.order_id.fiscal_position_id if fiscal_position_id: taxes = fiscal_position_id.map_tax(taxes) diff --git a/addons/point_of_sale/static/src/js/gui.js b/addons/point_of_sale/static/src/js/gui.js index 1885d2ef13e..5e8c8bf71d1 100644 --- a/addons/point_of_sale/static/src/js/gui.js +++ b/addons/point_of_sale/static/src/js/gui.js @@ -8,6 +8,7 @@ odoo.define('point_of_sale.gui', function (require) { var core = require('web.core'); var Model = require('web.DataModel'); +var formats = require('web.formats'); var _t = core._t; @@ -411,8 +412,8 @@ var Gui = core.Class.extend({ numpad_input: function(buffer, input, options) { var newbuf = buffer.slice(0); options = options || {}; - var newbuf_float = instance.web.parse_value(newbuf, {type: "float"}, 0); - var decimal_point = instance.web._t.database.parameters.decimal_point; + var newbuf_float = formats.parse_value(newbuf, {type: "float"}, 0); + var decimal_point = _t.database.parameters.decimal_point; if (input === decimal_point) { if (options.firstinput) { diff --git a/addons/point_of_sale/static/src/js/popups.js b/addons/point_of_sale/static/src/js/popups.js index 16dc7413865..17a3fd6ac7c 100644 --- a/addons/point_of_sale/static/src/js/popups.js +++ b/addons/point_of_sale/static/src/js/popups.js @@ -205,7 +205,7 @@ var NumberPopupWidget = PopupWidget.extend({ this._super(options); this.inputbuffer = '' + (options.value || ''); - this.decimal_separator = instance.web._t.database.parameters.decimal_point; + this.decimal_separator = _t.database.parameters.decimal_point; this.renderElement(); this.firstinput = true; }, diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js index 273f6764643..4626f3b4ff3 100644 --- a/addons/point_of_sale/static/src/js/screens.js +++ b/addons/point_of_sale/static/src/js/screens.js @@ -1706,7 +1706,7 @@ var PaymentScreenWidget = ScreenWidget.extend({ 'title': tip ? _t('Change Tip') : _t('Add Tip'), 'value': self.format_currency_no_symbol(value), 'confirm': function(value) { - order.set_tip(instance.web.parse_value(value, {type: "float"}, 0)); + order.set_tip(formats.parse_value(value, {type: "float"}, 0)); self.order_changes(); self.render_paymentlines(); } diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index fd9479f519a..7ba0b01f663 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -150,7 +150,7 @@ class PurchaseOrder(models.Model): picking_type_id = fields.Many2one('stock.picking.type', 'Deliver To', states=READONLY_STATES, required=True, default=_default_picking_type,\ help="This will determine picking type of incoming shipment") default_location_dest_id_usage = fields.Selection(related='picking_type_id.default_location_dest_id.usage', string='Destination Location Type',\ - help="Technical field used to display the Drop Ship Address") + help="Technical field used to display the Drop Ship Address", readonly=True) group_id = fields.Many2one('procurement.group', string="Procurement Group") @api.model diff --git a/addons/web_editor/static/src/js/backend.js b/addons/web_editor/static/src/js/backend.js index 3d3b6cb27ee..eca913e1309 100644 --- a/addons/web_editor/static/src/js/backend.js +++ b/addons/web_editor/static/src/js/backend.js @@ -194,7 +194,7 @@ var FieldTextHtml = widget.extend({ return def; }, get_url: function (_attr) { - var src = this.options.editor_url ? this.options.editor_url+"?" : "/web_editor/field/html?"; + var src = this.options.editor_url || "/web_editor/field/html"; var datarecord = this.view.get_fields_values(); var attr = { @@ -211,6 +211,9 @@ var FieldTextHtml = widget.extend({ if (this.options.snippets) { attr.snippets = this.options.snippets; } + if (this.options.template) { + attr.template = this.options.template; + } if (!this.get("effective_readonly")) { attr.enable_editor = 1; } @@ -227,6 +230,10 @@ var FieldTextHtml = widget.extend({ attr[k] = _attr[k]; } + if (src.indexOf('?') === -1) { + src += "?"; + } + for (var k in attr) { if (attr[k] !== null) { src += "&"+k+"="+(_.isBoolean(attr[k]) ? +attr[k] : attr[k]); diff --git a/doc/cla/individual/stephen144.md b/doc/cla/individual/stephen144.md new file mode 100644 index 00000000000..1caf0b28b4f --- /dev/null +++ b/doc/cla/individual/stephen144.md @@ -0,0 +1,11 @@ +United States of America, 2015-10-22 + +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, + +Stephen Medina stephen@lilmail.xyz https://github.com/stephen144 diff --git a/doc/howtos/web.rst b/doc/howtos/web.rst index a2641cbfdef..d58091e964e 100644 --- a/doc/howtos/web.rst +++ b/doc/howtos/web.rst @@ -78,7 +78,7 @@ not be fetched by browsers. The ``src/css``, ``src/js`` and ``src/xml`` sub-folders are conventional and not strictly necessary. ``oepetstore/static/css/petstore.css`` - currently empty, will hold the CSS_ for pet store content + Currently empty, will hold the CSS_ for pet store content ``oepetstore/static/xml/petstore.xml`` Mostly empty, will hold :ref:`reference/qweb` templates ``oepetstore/static/js/petstore.js`` @@ -103,6 +103,8 @@ sub-folders are conventional and not strictly necessary. Which only prints a small message in the browser's console. +The files in the ``static`` folder, need to be defined within the module in order for them to be loaded correctly. Everything in ``src/xml`` is defined in ``__openerp__.py`` while the contents of ``src/css`` and ``src/js`` are defined in ``petstore.xml``, or a similar file. + .. warning:: All JavaScript files are concatenated and :term:`minified` to improve