diff options
34 files changed, 142 insertions, 48 deletions
diff --git a/addons/account/data/invoice_action_data.xml b/addons/account/data/invoice_action_data.xml index 96fa2342f06..96e55557c1e 100644 --- a/addons/account/data/invoice_action_data.xml +++ b/addons/account/data/invoice_action_data.xml @@ -33,7 +33,7 @@ amounting in <strong>${object.amount_total} ${object.currency_id.name}</strong> from ${object.company_id.name}. </p> -% if is_inline: +% if is_online: <br/><br/> <center> <a href="${access_url}" style="background-color: #1abc9c; padding: 20px; text-decoration: none; color: #fff; border-radius: 5px; font-size: 16px;" class="o_default_snippet_text">View Invoice</a> diff --git a/addons/account/models/account.py b/addons/account/models/account.py index eb0b26b6963..15e25f1d9fb 100644 --- a/addons/account/models/account.py +++ b/addons/account/models/account.py @@ -699,7 +699,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) @@ -718,7 +729,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) @@ -744,8 +755,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 2b82cef6c85..8e4d908b176 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -1057,7 +1057,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/report/account_aged_partner_balance.py b/addons/account/report/account_aged_partner_balance.py index cf4fe079eba..fb5f90f96ec 100644 --- a/addons/account/report/account_aged_partner_balance.py +++ b/addons/account/report/account_aged_partner_balance.py @@ -61,7 +61,7 @@ class ReportAgedPartnerBalance(models.AbstractModel): # Build a string like (1,2,3) for easy use in SQL query partner_ids = [partner['partner_id'] for partner in partners if partner['partner_id']] - lines = dict([(partner['partner_id'], []) for partner in partners if partner['partner_id']]) + lines = dict((partner['partner_id'] or False, []) for partner in partners) if not partner_ids: return [], [], [] @@ -80,7 +80,7 @@ class ReportAgedPartnerBalance(models.AbstractModel): aml_ids = cr.fetchall() aml_ids = aml_ids and [x[0] for x in aml_ids] or [] for line in self.env['account.move.line'].browse(aml_ids): - partner_id = line.partner_id.id or None + partner_id = line.partner_id.id or False if partner_id not in undue_amounts: undue_amounts[partner_id] = 0.0 line_amount = line.balance @@ -131,7 +131,7 @@ class ReportAgedPartnerBalance(models.AbstractModel): aml_ids = cr.fetchall() aml_ids = aml_ids and [x[0] for x in aml_ids] or [] for line in self.env['account.move.line'].browse(aml_ids): - partner_id = line.partner_id.id or None + partner_id = line.partner_id.id or False if partner_id not in partners_amount: partners_amount[partner_id] = 0.0 line_amount = line.balance diff --git a/addons/account/static/src/js/account_reconciliation_widgets.js b/addons/account/static/src/js/account_reconciliation_widgets.js index 848b9b5093a..95cd682a9ee 100644 --- a/addons/account/static/src/js/account_reconciliation_widgets.js +++ b/addons/account/static/src/js/account_reconciliation_widgets.js @@ -16,6 +16,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'); @@ -71,8 +72,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"); @@ -365,6 +364,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({ @@ -1868,7 +1872,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"; @@ -2063,7 +2071,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/crm/wizard/base_partner_merge.py b/addons/crm/wizard/base_partner_merge.py index d2ddf32afcf..44320807797 100644 --- a/addons/crm/wizard/base_partner_merge.py +++ b/addons/crm/wizard/base_partner_merge.py @@ -306,7 +306,7 @@ class MergePartnerAutomatic(models.TransientModel): raise UserError(_("You cannot merge a contact with one of his parent.")) # check only admin can merge partners with different emails - if SUPERUSER_ID != self.env.uid and len(set(partner.email for partner in Partner.browse(partner_ids))) > 1: + if SUPERUSER_ID != self.env.uid and len(set(partner.email for partner in partner_ids)) > 1: raise UserError(_("All contacts must have the same email. Only the Administrator can merge contacts with different emails.")) # remove dst_partner from partners to merge diff --git a/addons/hr_expense/i18n/hr_expense.pot b/addons/hr_expense/i18n/hr_expense.pot index 78f13f7787e..edc3c84a67d 100644 --- a/addons/hr_expense/i18n/hr_expense.pot +++ b/addons/hr_expense/i18n/hr_expense.pot @@ -1156,7 +1156,7 @@ msgstr "" #. module: hr_expense #: selection:hr.expense,state:0 #: model:ir.ui.view,arch_db:hr_expense.view_hr_expense_filter -msgid "To Report" +msgid "To Submit" msgstr "" #. module: hr_expense diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index 9b4367f2992..84886443a37 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -35,7 +35,7 @@ class HrExpense(models.Model): payment_mode = fields.Selection([("own_account", "Employee (to reimburse)"), ("company_account", "Company")], default='own_account', states={'done': [('readonly', True)], 'post': [('readonly', True)]}, string="Payment By") attachment_number = fields.Integer(compute='_compute_attachment_number', string='Number of Attachments') state = fields.Selection([ - ('draft', 'To Report'), + ('draft', 'To Submit'), ('reported', 'Reported'), ('done', 'Posted'), ('refused', 'Refused') diff --git a/addons/hr_expense/views/hr_expense_views.xml b/addons/hr_expense/views/hr_expense_views.xml index 22073f88003..3c35430bf39 100644 --- a/addons/hr_expense/views/hr_expense_views.xml +++ b/addons/hr_expense/views/hr_expense_views.xml @@ -56,7 +56,7 @@ <field name="quantity" class="oe_inline"/> <field name="product_uom_id" widget="selection" class="oe_inline" groups="product.group_uom"/> </div> - <field name="tax_ids" domain="[('type_tax_use', '=', 'purchase')]" widget="many2many_tags" groups="account.group_no_one"/> + <field name="tax_ids" domain="[('type_tax_use', '=', 'purchase')]" widget="many2many_tags" groups="base.group_no_one"/> </group><group> <field name="reference"/> <field name="date"/> @@ -161,7 +161,7 @@ <field name="product_id"/> <field name="analytic_account_id" groups="analytic.group_analytic_accounting"/> <separator /> - <filter domain="[('state', '=', 'draft')]" string="To Report" name="to_report" help="New Expense"/> + <filter domain="[('state', '=', 'draft')]" string="To Submit" name="to_report" help="New Expense"/> <filter domain="[('state', '=', 'report')]" string="Reported" name="submitted" help="Confirmed Expenses"/> <filter domain="[('state', '=', 'refused')]" string="Refused" name="refused" help="Refused Expenses"/> <filter domain="[('state', '!=', 'cancel')]" string="Not Refused" name="uncancelled" help="Actual expense sheets, not the refused ones"/> diff --git a/addons/im_livechat/views/im_livechat_channel_templates.xml b/addons/im_livechat/views/im_livechat_channel_templates.xml index bbb4c9663b6..0e3a4b911c8 100644 --- a/addons/im_livechat/views/im_livechat_channel_templates.xml +++ b/addons/im_livechat/views/im_livechat_channel_templates.xml @@ -112,6 +112,7 @@ <script type="text/javascript" src="/web/static/lib/qweb/qweb2.js"></script> <!-- Odoo JS Framework --> <script type="text/javascript" src="/web/static/src/js/boot.js"></script> + <script type="text/javascript" src="/web/static/src/js/config.js"></script> <script type="text/javascript" src="/web/static/src/js/framework/class.js"></script> <script type="text/javascript" src="/web/static/src/js/framework/translation.js"></script> <script type="text/javascript" src="/web/static/src/js/framework/ajax.js"></script> @@ -121,7 +122,11 @@ <script type="text/javascript" src="/web/static/src/js/framework/registry.js"></script> <script type="text/javascript" src="/web/static/src/js/framework/session.js"></script> <script type="text/javascript" src="/web/static/src/js/framework/utils.js"></script> - <script type="text/javascript" src="/web/static/src/js/framework/core.js"></script> + <script type="text/javascript" src="/web/static/src/js/framework/bus.js"></script> + <script type="text/javascript" src="/web/static/src/js/framework/local_storage.js"></script> + <script type="text/javascript" src="/web/static/src/js/framework/qweb.js"></script> + <script type="text/javascript" src="/web/static/src/js/services/core.js"></script> + <script type="text/javascript" src="/web/static/src/js/services/session.js"></script> <!-- Bus, Mail, Livechat --> <script type="text/javascript" src="/bus/static/src/js/bus.js"></script> <script type="text/javascript" src="/mail/static/src/js/chat_window.js"></script> diff --git a/addons/project_issue/models/res_partner.py b/addons/project_issue/models/res_partner.py index 8bfe514987f..91dd0af3eec 100644 --- a/addons/project_issue/models/res_partner.py +++ b/addons/project_issue/models/res_partner.py @@ -11,6 +11,5 @@ class ResPartner(models.Model): def _compute_issue_count(self): Issue = self.env['project.issue'] - partners = {id: self.search([('id', 'child_of', self.ids)]).ids for id in self.ids} for partner in self: - partner.issue_count = Issue.search_count([('partner_id', 'in', partners[partner.id])]) + partner.issue_count = Issue.search_count([('partner_id', 'child_of', partner.commercial_partner_id.id)]) diff --git a/addons/project_issue/views/res_partner_views.xml b/addons/project_issue/views/res_partner_views.xml index aa2ac8a80b5..10aafb7d24f 100644 --- a/addons/project_issue/views/res_partner_views.xml +++ b/addons/project_issue/views/res_partner_views.xml @@ -10,7 +10,7 @@ <field name="arch" type="xml"> <div name="button_box" position="inside"> <button class="oe_stat_button" type="action" name="%(action_view_issues)d" - context="{'search_default_partner_id': active_id, 'default_partner_id': active_id}" + context="{'search_default_partner_id': commercial_partner_id, 'default_partner_id': active_id}" attrs="{'invisible': [('customer', '=', False)]}" icon="fa-bug"> <field name="issue_count" string="Issues" widget="statinfo"/> diff --git a/addons/stock/models/stock_pack_operation.py b/addons/stock/models/stock_pack_operation.py index a581d4028d3..01a24de273d 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 e2cdafe0ce1..7ae10fe6bfc 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 051bc2774a6..900e7f18309 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().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 07655717df5..7ebe9a72f22 100644 --- a/addons/survey/views/survey_views.xml +++ b/addons/survey/views/survey_views.xml @@ -237,6 +237,7 @@ <field name="model">survey.survey</field> <field name="arch" type="xml"> <search string="Survey"> + <field string="Title" name="title"/> <filter string="Archived" name="inactive" domain="[('active','=',False)]"/> </search> </field> diff --git a/addons/web/static/src/js/views/list_view.js b/addons/web/static/src/js/views/list_view.js index 22ca3e0b1ad..327edcbb052 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 4906ab345d0..3fd9bfca2e4 100644 --- a/addons/web_editor/static/src/js/snippets.editor.js +++ b/addons/web_editor/static/src/js/snippets.editor.js @@ -287,6 +287,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) { @@ -1052,6 +1055,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 2b9aaf15b32..d9e8c83a495 100644 --- a/addons/web_editor/static/src/xml/snippets.xml +++ b/addons/web_editor/static/src/xml/snippets.xml @@ -60,7 +60,7 @@ <!-- t-field options --> <t t-name="web_editor.many2one.button"> - <div class="dropdown"> + <div class="btn-group"> <a href="#" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" data-hover="dropdown" title="Search Contact"><i class="fa fa-search"></i></a> <ul class="dropdown-menu contact_menu" role="menu"> <li><a><input href="#" type="email" placeholder="Search"/></a></li> diff --git a/addons/web_kanban/static/src/js/kanban_view.js b/addons/web_kanban/static/src/js/kanban_view.js index 5de3c617758..b755c7559dd 100644 --- a/addons/web_kanban/static/src/js/kanban_view.js +++ b/addons/web_kanban/static/src/js/kanban_view.js @@ -331,6 +331,7 @@ var KanbanView = View.extend({ // Execute the given action self.do_action(self.on_create, { on_close: self.do_reload.bind(self), + additional_context: self.search_context, }); } else { // Open the form view diff --git a/addons/website/static/src/js/website.snippets.gallery.js b/addons/website/static/src/js/website.snippets.gallery.js index ed1293229b8..481ba06e6f3 100644 --- a/addons/website/static/src/js/website.snippets.gallery.js +++ b/addons/website/static/src/js/website.snippets.gallery.js @@ -354,18 +354,12 @@ options.registry.gallery_img = options.Class.extend({ editor.reapply(); },0); }, - remove: function (type) { - if (type !== "click") return; - - var self = this; + on_remove: function () { var $parent = snippet_editor.globalSelector.closest(this.$target.parent()); - this.buildingBlock.make_active(false); - this.$target.remove(); - setTimeout(function () { - self.buildingBlock.make_active($parent); - var gallery = $parent.data('snippet-editor').styles.gallery; - gallery.reapply(); - }, 0); + _.defer((function () { + this.buildingBlock.make_active($parent); + $parent.data('snippet-editor').styles.gallery.reapply(); + }).bind(this)); } }); diff --git a/addons/website/views/snippets.xml b/addons/website/views/snippets.xml index 10de2c6ae18..95ec8613786 100644 --- a/addons/website/views/snippets.xml +++ b/addons/website/views/snippets.xml @@ -775,7 +775,6 @@ <li data-position="last"><a href="#">Move to last</a></li> </ul> </li> - <li data-remove="remove"><a href="#"><i class="fa fa-trash-o"/>Remove from gallery</a></li> </div> <div data-js='carousel' diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index 7eef41bab37..03c00f09d87 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -128,7 +128,7 @@ class WebsiteSale(http.Controller): (variant id, [visible attribute ids], variant price, variant sale price) """ # product attributes with at least two choices - visible_attrs_ids = product.mapped('attribute_line_ids.attribute_id').filtered(lambda attr: len(attr.value_ids) > 1).ids + visible_attrs_ids = product.attribute_line_ids.filtered(lambda l: len(l.value_ids) > 1).mapped('attribute_id').ids to_currency = request.website.get_current_pricelist().currency_id attribute_value_ids = [] for variant in product.product_variant_ids: diff --git a/addons/website_sale/models/product.py b/addons/website_sale/models/product.py index c436350c809..17516246a2b 100644 --- a/addons/website_sale/models/product.py +++ b/addons/website_sale/models/product.py @@ -2,6 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo import api, fields, models, tools, _ import odoo.addons.decimal_precision as dp +from odoo.tools.translate import html_translate class ProductStyle(models.Model): @@ -108,7 +109,7 @@ class ProductTemplate(models.Model): domain=lambda self: ['&', ('model', '=', self._name), ('message_type', '=', 'comment')], string='Website Comments', ) - website_description = fields.Html('Description for the website', sanitize_attributes=False, translate=True) + website_description = fields.Html('Description for the website', sanitize_attributes=False, translate=html_translate) alternative_product_ids = fields.Many2many('product.template', 'product_alternative_rel', 'src_id', 'dest_id', string='Alternative Products', help='Suggest more expensive alternatives to ' 'your customers (upsell strategy). Those products show up on the product page.') diff --git a/odoo/addons/base/ir/ir_http.py b/odoo/addons/base/ir/ir_http.py index c63bd2f7226..6bca1edee58 100644 --- a/odoo/addons/base/ir/ir_http.py +++ b/odoo/addons/base/ir/ir_http.py @@ -324,7 +324,7 @@ class IrHttp(models.AbstractModel): # cache etag = hasattr(request, 'httprequest') and request.httprequest.headers.get('If-None-Match') - retag = hashlib.md5(last_update).hexdigest() + retag = '"%s"' % hashlib.md5(last_update).hexdigest() status = status or (304 if etag == retag else 200) headers.append(('ETag', retag)) headers.append(('Cache-Control', 'max-age=%s' % (STATIC_CACHE if unique else 0))) diff --git a/odoo/addons/base/res/res_partner_view.xml b/odoo/addons/base/res/res_partner_view.xml index f5d1e819ded..92d4c41db55 100644 --- a/odoo/addons/base/res/res_partner_view.xml +++ b/odoo/addons/base/res/res_partner_view.xml @@ -140,6 +140,7 @@ <field name="image" widget='image' class="oe_avatar" options='{"preview_image": "image_medium", "size": [90, 90]}'/> <div class="oe_title"> <field name="is_company" invisible="1"/> + <field name="commercial_partner_id" invisible="1"/> <field name="company_type" widget="radio" class="oe_edit_only" options="{'horizontal': true}"/> diff --git a/odoo/addons/test_new_api/models.py b/odoo/addons/test_new_api/models.py index 513fe06ca8c..5fc58ea976a 100644 --- a/odoo/addons/test_new_api/models.py +++ b/odoo/addons/test_new_api/models.py @@ -13,6 +13,7 @@ class Category(models.Model): name = fields.Char(required=True) color = fields.Integer('Color Index') parent = fields.Many2one('test_new_api.category') + root_categ = fields.Many2one(_name, compute='_compute_root_categ') display_name = fields.Char(compute='_compute_display_name', inverse='_inverse_display_name') dummy = fields.Char(store=False) discussions = fields.Many2many('test_new_api.discussion', 'test_new_api_discussion_category', @@ -26,6 +27,14 @@ class Category(models.Model): else: self.display_name = self.name + @api.depends('parent') + def _compute_root_categ(self): + for cat in self: + current = cat + while current.parent: + current = current.parent + cat.root_categ = current + @api.one def _inverse_display_name(self): names = self.display_name.split('/') diff --git a/odoo/addons/test_new_api/tests/test_onchange.py b/odoo/addons/test_new_api/tests/test_onchange.py index 16d122c712c..87fc8229b7c 100644 --- a/odoo/addons/test_new_api/tests/test_onchange.py +++ b/odoo/addons/test_new_api/tests/test_onchange.py @@ -75,6 +75,33 @@ class TestOnChange(common.TransactionCase): result = self.Message.onchange(values, 'body', field_onchange) self.assertNotIn('name', result['value']) + def test_onchange_many2one(self): + Category = self.env['test_new_api.category'] + + field_onchange = Category._onchange_spec() + self.assertEqual(field_onchange.get('parent'), '1') + + root = Category.create(dict(name='root')) + + values = { + 'name': 'test', + 'parent': root.id, + 'root_categ': False, + } + + self.env.invalidate_all() + result = Category.onchange(values, 'parent', field_onchange).get('value', {}) + self.assertIn('root_categ', result) + self.assertEqual(result['root_categ'], root.name_get()[0]) + + values.update(result) + values['parent'] = False + + self.env.invalidate_all() + result = Category.onchange(values, 'parent', field_onchange).get('value', {}) + self.assertIn('root_categ', result) + self.assertIs(result['root_categ'], False) + def test_onchange_one2many(self): """ test the effect of onchange() on one2many fields """ BODY = "What a beautiful day!" diff --git a/odoo/addons/test_new_api/views.xml b/odoo/addons/test_new_api/views.xml index b8c0f5bfa8b..0b053635319 100644 --- a/odoo/addons/test_new_api/views.xml +++ b/odoo/addons/test_new_api/views.xml @@ -205,6 +205,7 @@ <group> <field name="name"/> <field name="parent"/> + <field name="root_categ"/> <field name="dummy"/> </group> </sheet> diff --git a/odoo/fields.py b/odoo/fields.py index 4d3f43f0fa7..d6dfe499f28 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -1898,6 +1898,10 @@ class Many2one(_Relational): def convert_to_display_name(self, value, record): return ustr(value.display_name) + def convert_to_onchange(self, value, record, fnames=()): + if not value.id: + return False + return super(Many2one, self).convert_to_onchange(value, record, fnames) class UnionUpdate(SpecialValue): """ Placeholder for a value update; when this value is taken from the cache, @@ -1923,10 +1927,10 @@ class _RelationalMulti(_Relational): """ Update the cached value of ``self`` for ``records`` with ``value``. """ for record in records: if self in record._cache: - value = self.convert_to_cache(record[self.name] | value, record, validate=False) + val = self.convert_to_cache(record[self.name] | value, record, validate=False) else: - value = UnionUpdate(self, record, value) - record._cache[self] = value + val = UnionUpdate(self, record, value) + record._cache[self] = val def convert_to_cache(self, value, record, validate=True): # cache format: tuple(ids) diff --git a/odoo/modules/module.py b/odoo/modules/module.py index 66cbae460f0..1ff0d2d0ed1 100644 --- a/odoo/modules/module.py +++ b/odoo/modules/module.py @@ -156,7 +156,7 @@ def get_module_path(module, downloaded=False, display_warning=True): """ initialize_sys_path() for adp in ad_paths: - if os.path.exists(opj(adp, module)) or os.path.exists(opj(adp, '%s.zip' % module)): + if os.path.exists(opj(adp, module, MANIFEST)) or os.path.exists(opj(adp, '%s.zip' % module)): return opj(adp, module) if downloaded: diff --git a/odoo/tools/convert.py b/odoo/tools/convert.py index 089210d6f99..3c3eba3bec6 100644 --- a/odoo/tools/convert.py +++ b/odoo/tools/convert.py @@ -746,7 +746,8 @@ form: module.record_id""" % (xml_id,) record.append(Field(name, name='name')) record.append(Field(full_tpl_id, name='key')) record.append(Field("qweb", name='type')) - record.append(Field(el.get('priority', "16"), name='priority')) + if 'priority' in el.attrib: + record.append(Field(el.get('priority'), name='priority')) if 'inherit_id' in el.attrib: record.append(Field(name='inherit_id', ref=el.get('inherit_id'))) if 'website_id' in el.attrib: |
