1

I'm trying to hide the edit button in the bill of material form in Odoo, dependent on the state of a boolean.

I managed to remove the edit button permanent with the code below:

<xpath expr="//form[@string='Bill of Material']" position="attributes">
    <attribute name="edit">false</attribute>
</xpath>

Now I tried to make it conditional with a boolean like this:

<xpath expr="//form[@string='Bill of Material']" position="attributes">
    <attribute name="edit">true:realman==True;false:realman==False;</attribute>
</xpath>

This gives error:

SyntaxError: JSON.parse: unexpected non-whitespace character after JSON data at line 1 column 5 of the JSON data

When I looked up the javascript file, I've found this is the code to handle the edit attribute:

/**
 * Return whether the user can perform the action ('create', 'edit', 'delete') in this view.
 * An action is disabled by setting the corresponding attribute in the view's main element,
 * like: <form string="" create="false" edit="false" delete="false">
 */
is_action_enabled: function(action) {
    var attrs = this.fields_view.arch.attrs;
    return (action in attrs) ? JSON.parse(attrs[action]) : true;
},

I suppose I need to get false in that var attrs when the boolean realman in my form is False?

I've already tried to write it in curly brackets like the answer in this question: JSON.parse unexpected character error

That gave me errors too.

Why do I get this error and how can I fix this? Is this just a syntax error or are there more problems?

Community
  • 1
  • 1
RobbeM
  • 727
  • 7
  • 16
  • 36

3 Answers3

1

I successfully solved a similar issue by using field_view_get, but only if "realman" is passed into context

def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
    res = models.Model.fields_view_get(self, cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
    realman = context.get('realman', True)
    if not realman and view_type == 'form':
        doc = etree.XML(res['arch'])
        for t in doc.xpath("//form[@string='Bill of Material']"):
            t.attrib['edit'] = 'false'
        res['arch'] = etree.tostring(doc)
    return res

if realman is instead a field and you need to enable/disable the edit button, then I'm afraid it is just not possible. AFAIK.

pmaruszczyk
  • 2,157
  • 2
  • 24
  • 49
Alessandro Ruffolo
  • 1,575
  • 1
  • 16
  • 34
0

As Alessandro Ruffolo stated his solution doesn't work for buttons/actions depending on model's field. I have written solution in Javascript for that. It works in ODOO 10 (should in 9 also but I haven't tested it).

Example is checking on model's field "state". If it has value "noEditable" edit and delete buttons would hide. It isn't enough to overwrite is_action_enabled because ODOO is calling the method when there is no datarecord loaded yet. Therefore it's needed to check it again after methods do_show and reload.

// modify default form view for custom model my.custom.model
odoo.define('my.custom_model_form', function (require) {
    "use strict";

    var FormView = require('web.FormView');

    FormView.include({
        is_action_enabled: function(action) {
            if (this.model == "my.custom.model" && this.datarecord && this.datarecord.state == "noEditable" &&
                (action == 'delete' || action == 'edit')) {
                // don't allow edit nor delete
                return false;
            }
            // call default is_action_enabled method
            return this._super.apply(this, arguments);
        },
        deleteItem: null,
        deleteItemIdx: null,
        deleteItemShown: true,
        reinit_actions: function() {
            // apply for my custom model only
            if (this.model == "my.custom.model") {
                // hide/show edit button
                if (this.is_action_enabled('edit')) {
                    this.$buttons.find(".o_form_button_edit").show();
                } else {
                    this.$buttons.find(".o_form_button_edit").hide();
                }

                // find delete item in sidebar's items
                if (!this.deleteItem) {
                    // form view is adding it to "other"
                    if (this.sidebar && this.sidebar.items && this.sidebar.items.other) {
                        for (var i = 0; i < this.sidebar.items.other.length; i++) {
                            // on_button_delete is used as callback for delete button
                            // it's ugly way to find out which one is delete button, haven't found better way
                            if (this.sidebar.items.other[i].callback == this.on_button_delete) {
                                this.deleteItem = this.sidebar.items.other[i];
                                this.deleteItemIdx = i;
                                break;
                            }
                        }
                    }
                }
                // hide/show delete button
                if (this.is_action_enabled('delete')) {
                    if (!this.deleteItemShown) {
                        this.deleteItemShown = true;
                        // add delete item to sidebar's items
                        this.sidebar.items.other.splice(this.deleteItemIdx, 0, this.deleteItem);
                    }
                } else
                if (this.deleteItemShown) {
                    this.deleteItemShown = false;
                    // remove delete item from sidebar's items
                    this.sidebar.items.other.splice(this.deleteItemIdx, 1);
                }
            }
        },
        reload: function() {
            var self = this;
            // run reinit_actions after reload finish
            return this._super.apply(this, arguments).done(function() {
                 self.reinit_actions();
            });
        },
        do_show: function() {
            var self = this;
            // run reinit_actions after do_show finish
            return this._super.apply(this, arguments).done(function() {
                 self.reinit_actions();
            });
        }
    });

});
Community
  • 1
  • 1
matof
  • 119
  • 1
  • 8
0

I believe a solution with a computed field makes things simpler for these cases.

You can inherit the bill of material form and override it. Point the invisible condition to a computed field where you'll create your validation. Note that is necessary to add your computed field to the form, even though hiding it.

<xpath expr="//form[@string='Bill of Material']" position="attributes">
    <attribute name="attrs">
        {'invisible': [('show_bm_button', '=', False)]}
    </attribute>
    <field name="show_bm_button" invisible="1"/>
</xpath>

Then override the object in python and add the new computed field.

class ProductTemplate(models.Model):
    _inherit = 'product.template'

    show_bm_button = fields.Boolean(
            compute='_compute_show_bm_button', 
            readonly=False, store=False
        )

    # @api.depends('realman') #Just as a sample, if you have this field in this Model
    def _compute_show_bm_button(self):
        for record in self:
            realman = self._context.get('realman') or True
            if realman:
                record.show_bm_button = True
            else:
                record.show_bm_button = False
marins.md
  • 21
  • 3