diff --git a/addons/account/models/account.py b/addons/account/models/account.py index 3d17f6ef6b7..d666b9df1df 100644 --- a/addons/account/models/account.py +++ b/addons/account/models/account.py @@ -688,7 +688,18 @@ class AccountTax(models.Model): # the 'Account' decimal precision + 5), and that way it's like # rounding after the sum of the tax amounts of each line prec = currency.decimal_places - if company_id.tax_calculation_rounding_method == 'round_globally' or not bool(self.env.context.get("round", True)): + + # In some cases, it is necessary to force/prevent the rounding of the tax and the total + # amounts. For example, in SO/PO line, we don't want to round the price unit at the + # precision of the currency. + # The context key 'round' allows to force the standard behavior. + round_tax = False if company_id.tax_calculation_rounding_method == 'round_globally' else True + round_total = True + if 'round' in self.env.context: + round_tax = bool(self.env.context['round']) + round_total = bool(self.env.context['round']) + + if not round_tax: prec += 5 total_excluded = total_included = base = round(price_unit * quantity, prec) @@ -707,7 +718,7 @@ class AccountTax(models.Model): continue tax_amount = tax._compute_amount(base, price_unit, quantity, product, partner) - if company_id.tax_calculation_rounding_method == 'round_globally' or not bool(self.env.context.get("round", True)): + if not round_tax: tax_amount = round(tax_amount, prec) else: tax_amount = currency.round(tax_amount) @@ -733,8 +744,8 @@ class AccountTax(models.Model): return { 'taxes': sorted(taxes, key=lambda k: k['sequence']), - 'total_excluded': currency.round(total_excluded) if bool(self.env.context.get("round", True)) else total_excluded, - 'total_included': currency.round(total_included) if bool(self.env.context.get("round", True)) else total_included, + 'total_excluded': currency.round(total_excluded) if round_total else total_excluded, + 'total_included': currency.round(total_included) if round_total else total_included, 'base': base, } diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index fae900807e3..d09238f4f7d 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -1097,7 +1097,7 @@ class AccountMoveLine(models.Model): taxes = self.env['account.tax'].browse(tax_ids) currency = self.env['res.currency'].browse(vals.get('currency_id')) partner = self.env['res.partner'].browse(vals.get('partner_id')) - res = taxes.compute_all(amount, + res = taxes.with_context(round=True).compute_all(amount, currency, 1, vals.get('product_id'), partner) # Adjust line amount if any tax is price_include if abs(res['total_excluded']) < abs(amount): diff --git a/addons/account/static/src/js/account_reconciliation_widgets.js b/addons/account/static/src/js/account_reconciliation_widgets.js index e35bfdf38f9..0df8aaa5c40 100644 --- a/addons/account/static/src/js/account_reconciliation_widgets.js +++ b/addons/account/static/src/js/account_reconciliation_widgets.js @@ -18,6 +18,7 @@ var pyeval = require('web.pyeval'); var web_client = require('web.web_client'); var parse_value = require('web.web_client'); var Widget = require('web.Widget'); +var session = require('web.session'); var FieldMany2One = core.form_widget_registry.get('many2one'); var FieldChar = core.form_widget_registry.get('char'); @@ -73,8 +74,6 @@ var abstractReconciliation = Widget.extend(ControlPanelMixin, { this.action_manager = this.findAncestor(function(ancestor){ return ancestor instanceof ActionManager }); this.crash_manager = new CrashManager(); - // Method that tests if a monetary amount == 0, we use 4 digits because no currency uses more - this.monetaryIsZero = _.partial(utils.float_is_zero, _, 4); this.formatCurrencies; // Method that formats the currency ; loaded from the server this.model_res_users = new Model("res.users"); this.model_tax = new Model("account.tax"); @@ -367,6 +366,11 @@ var abstractReconciliation = Widget.extend(ControlPanelMixin, { if (line.ref && line.ref !== line.name) line.q_label = line.q_label + " : " + line.ref; }, + + // Method that tests if a monetary amount == 0, we use 4 digits because no currency uses more + monetaryIsZero: function(amount, digits) { + return utils.float_is_zero(amount, digits === undefined ? 4 : digits); + }, }); var abstractReconciliationLine = Widget.extend({ @@ -1870,7 +1874,11 @@ var bankStatementReconciliationLine = abstractReconciliationLine.extend({ // Find out if the counterpart is lower than, equal or greater than the transaction being reconciled var balance_type = undefined; - if (self.monetaryIsZero(self.get("balance"))) balance_type = "equal"; + var digits = 4; + if (this.get("currency_id") && session.get_currency(this.get("currency_id"))) { + digits = session.get_currency(this.get("currency_id")).digits[1]; + } + if (self.monetaryIsZero(self.get("balance"), digits)) balance_type = "equal"; else if (self.get("balance") * self.st_line.amount > 0) balance_type = "greater"; else if (self.get("balance") * self.st_line.amount < 0) balance_type = "lower"; @@ -2065,7 +2073,11 @@ var bankStatementReconciliationLine = abstractReconciliationLine.extend({ var payment_aml = _.filter(this.get("mv_lines_selected"), function(line) { return line.already_paid }); var payment_aml_ids = _.collect(payment_aml, function(line) { return line.id }); var new_aml_dicts = this.prepareCreatedMoveLinesForPersisting(this.getCreatedLines()); - if (! self.monetaryIsZero(this.get("balance"))) new_aml_dicts.push(this.prepareOpenBalanceForPersisting()); + var digits = 4; + if (this.get("currency_id") && session.get_currency(this.get("currency_id"))) { + digits = session.get_currency(this.get("currency_id")).digits[1]; + } + if (! self.monetaryIsZero(this.get("balance"), digits)) new_aml_dicts.push(this.prepareOpenBalanceForPersisting()); return { 'counterpart_aml_dicts': counterpart_aml_dicts, 'payment_aml_ids': payment_aml_ids, diff --git a/addons/barcodes/static/src/js/form_view_barcode_handler.js b/addons/barcodes/static/src/js/form_view_barcode_handler.js index a6fa4b55adf..b00bce51c5e 100644 --- a/addons/barcodes/static/src/js/form_view_barcode_handler.js +++ b/addons/barcodes/static/src/js/form_view_barcode_handler.js @@ -52,6 +52,11 @@ var FormViewBarcodeHandler = common.AbstractField.extend(BarcodeHandlerMixin, { } }, + destroy: function () { + this.stop_listening(); + this._super.apply(this, arguments); + }, + _display_no_edit_mode_warning: function() { this.do_warn(_t('Error : Document not editable'), _t('To modify this document, please first start edition.')); }, diff --git a/addons/stock/models/stock_pack_operation.py b/addons/stock/models/stock_pack_operation.py index c306eac1dae..c992978923c 100644 --- a/addons/stock/models/stock_pack_operation.py +++ b/addons/stock/models/stock_pack_operation.py @@ -164,8 +164,9 @@ class PackOperation(models.Model): def split_quantities(self): for operation in self: if float_compare(operation.product_qty, operation.qty_done, precision_rounding=operation.product_uom_id.rounding) == 1: - operation.copy(default={'qty_done': 0.0, 'product_qty': operation.product_qty - operation.qty_done}) + cpy = operation.copy(default={'qty_done': 0.0, 'product_qty': operation.product_qty - operation.qty_done}) operation.write({'product_qty': operation.qty_done}) + operation._copy_remaining_pack_lot_ids(cpy) else: raise UserError(_('The quantity to split should be smaller than the quantity To Do. ')) return True @@ -238,6 +239,19 @@ class PackOperation(models.Model): raise UserError(_('You should provide a different serial number for each piece')) check_tracking = _check_serial_number + @api.multi + def _copy_remaining_pack_lot_ids(self, new_operation): + for op in self: + for lot in op.pack_lot_ids: + new_qty_todo = lot.qty_todo - lot.qty + + if float_compare(new_qty_todo, 0, precision_rounding=op.product_uom_id.rounding) > 0: + lot.copy({ + 'operation_id': new_operation.id, + 'qty_todo': new_qty_todo, + 'qty': 0, + }) + class PackOperationLot(models.Model): _name = "stock.pack.operation.lot" diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py index d810a5f7ffd..c5c664dcae3 100644 --- a/addons/stock/models/stock_picking.py +++ b/addons/stock/models/stock_picking.py @@ -999,6 +999,10 @@ class Picking(models.Model): packlots_transfer = [(4, x.id) for x in operation.pack_lot_ids] new_operation.write({'pack_lot_ids': packlots_transfer}) + # the stock.pack.operation.lot records now belong to the new, packaged stock.pack.operation + # we have to create new ones with new quantities for our original, unfinished stock.pack.operation + new_operation._copy_remaining_pack_lot_ids(operation) + op = new_operation pack_operation_ids |= op if operations: diff --git a/addons/stock/models/stock_warehouse.py b/addons/stock/models/stock_warehouse.py index bfdfb0147b7..066a9a5afe0 100644 --- a/addons/stock/models/stock_warehouse.py +++ b/addons/stock/models/stock_warehouse.py @@ -808,7 +808,7 @@ class Orderpoint(models.Model): def _get_date_planned(self, start_date): days = self.lead_days or 0.0 - if self.lead_type == 'purchase': + if self.lead_type == 'supplier': # These days will be substracted when creating the PO days += self.product_id._select_seller(self.product_id).delay or 0.0 date_planned = start_date + relativedelta.relativedelta(days=days) diff --git a/addons/survey/views/survey_views.xml b/addons/survey/views/survey_views.xml index a85100f4f5b..bb9e5677ca0 100644 --- a/addons/survey/views/survey_views.xml +++ b/addons/survey/views/survey_views.xml @@ -237,6 +237,7 @@ survey.survey + diff --git a/addons/web/static/src/js/views/list_view.js b/addons/web/static/src/js/views/list_view.js index c6e72cc0596..28288a14747 100644 --- a/addons/web/static/src/js/views/list_view.js +++ b/addons/web/static/src/js/views/list_view.js @@ -1092,8 +1092,8 @@ ListView.List = Class.extend({ _(names).pluck(1).join(', ')); record.set(column.id, ids); }); - // temp empty value - record.set(column.id, false); + // temporary empty display name + record.set(column.id + '__display', false); } } return column.format(record.toForm().data, { diff --git a/addons/web/static/src/js/views/tree_view.js b/addons/web/static/src/js/views/tree_view.js index aeb206257fe..604c9204b42 100644 --- a/addons/web/static/src/js/views/tree_view.js +++ b/addons/web/static/src/js/views/tree_view.js @@ -49,7 +49,7 @@ var TreeView = View.extend({ }, willStart: function () { _(this.fields_view.arch.children).each(function (field) { - if (field.attrs.modifiers) { + if (field.attrs.modifiers && typeof field.attrs.modifiers === "string") { field.attrs.modifiers = JSON.parse(field.attrs.modifiers); } }); diff --git a/addons/web_editor/static/src/js/snippets.editor.js b/addons/web_editor/static/src/js/snippets.editor.js index 66789ef635e..5a67b11707d 100644 --- a/addons/web_editor/static/src/js/snippets.editor.js +++ b/addons/web_editor/static/src/js/snippets.editor.js @@ -321,6 +321,9 @@ data.Class = Widget.extend({ self.make_snippet_draggable(self.$snippets); this.associate_snippet_names(this.$snippets); + + this.show_blocks(); + this.$el.on("snippet-dropped snippet-removed", this.show_blocks.bind(this)); }, associate_snippet_names: function ($snippets) { @@ -1087,6 +1090,8 @@ data.Editor = Class.extend({ $(".note-control-selection").hide(); $('.o_table_handler').remove(); + this.buildingBlock.$el.trigger("snippet-removed"); + return false; }, diff --git a/addons/web_editor/static/src/xml/snippets.xml b/addons/web_editor/static/src/xml/snippets.xml index f1cb9fc0fed..20a234a1b15 100644 --- a/addons/web_editor/static/src/xml/snippets.xml +++ b/addons/web_editor/static/src/xml/snippets.xml @@ -60,7 +60,7 @@ -