Setting a field 'invisible' conditionnaly only in form view

Hi, i’m customizing sale model to allow users to have also a reference field on sale_lines. I’m doing the same way as the field ‘delivery_date’ in purchase model.

class SaleLine(metaclass=PoolMeta):
    __name__ = 'sale.line'

    reference = fields.Function(fields.Char('Reference',
            states={
                'invisible': ((Eval('type') != 'line')
                    | Eval('reference_edit', False)),
                },
            depends=['type', 'reference_edit']),
        'on_change_with_reference')
    reference_edit = fields.Boolean(
        "Edit Reference",
        states={
            'invisible': Eval('type') != 'line',
            'readonly': Eval('sale_state').in_([
                    'done', 'cancelled']),
            },
        depends=['type', 'sale_state'],
        help="Check to edit the reference.")
    reference_store = fields.Char(
        "Reference",
        states={
            'invisible': ((Eval('type') != 'line')
                | ~Eval('reference_edit', False)),
            'readonly': Eval('sale_state').in_([
                    'done', 'cancelled']),
            },
        depends=['type', 'reference_edit', 'sale_state'])

    @classmethod
    def default_reference_edit(cls):
        return False

    @fields.depends('sale', '_parent_sale.reference', 'reference_edit',
        'reference_store')
    def on_change_with_reference(self, name=None):
        if self.reference_edit and self.reference_store:
            return self.reference_store
        if self.sale:
            return self.sale.reference

So users can have a reference by default which is sale_reference in function field ‘reference’, but they could also having a different reference on the sale_line by checking ‘reference_edit’ and setting a value in field ‘reference_store’

A switch is done in the view form to display the correct field (depending if ‘reference_edit’ is checked or not):

sale_line_form.xml

<?xml version="1.0"?>
<!-- The COPYRIGHT file at the top level of this repository contains the full 
     copyright notices and license terms. -->
<data>
    <xpath expr="/form/notebook/page[@id='general']/field[@name='shipping_date']" position="after">
        <label id="reference" string="Reference:"/>
        <group id="reference" col="-1">
            <field name="reference" xexpand="0"/>
            <field name="reference_store" xexpand="0"/>
            <field name="reference_edit" xexpand="0"/>
        </group>
    </xpath>
</data>

Until now everything is working well…
But i want also display that field ‘reference’ in the tree view…

sale_line_tree.xml

<?xml version="1.0"?>
<!-- The COPYRIGHT file at the top level of this repository contains the full 
     copyright notices and license terms. -->
<data>
    <xpath expr="/tree/field[@name='amount']" position="after">
        <field name="reference"/>
    </xpath>
</data>

My function field ‘reference’ can display the correct value depending if reference_edit is checked or not.
But as his state is ‘invisible’ when reference_edit is checked, the value of ‘reference’ field is not displayed in the tree view…

So, my question:

  • Is it possible to have the state ‘invisible’ only in form view ?
  • Or should i create another function field ‘reference_display’ (with the same code as ‘reference’ but without state condition to display this field correctly in the tree view ?
  • Another idea ?

Thanks! :slight_smile:

We found the same problem when adding the delivery_date of purchase lines in list view and we solved it adding another field just for displaying as you suggest here:

For me this make snse because on the list view you want to add another field which is always visible while on the form view you should show/hide two diferent fields depending on the checkbox.

1 Like

I think the invisible state of reference should not be defined on the field but on the view (with ModelView.view_attributes) in such case because it depends on what is displayed.
The standard delivery_date should also be fixed to use view_attributes.

1 Like

Thanks for the tip @ced
I try this way (removing states on field reference and adding view_attributes) but this is not working (am i missing something ? - How to know if xm path is correctly found ?) :

    reference = fields.Function(fields.Char('Reference'),
        'on_change_with_reference')
    reference_edit = fields.Boolean(
        "Edit Reference",
        states={
            'invisible': Eval('type') != 'line',
            'readonly': Eval('sale_state').in_([
                    'done', 'cancelled']),
            },
        depends=['type', 'sale_state'],
        help="Check to edit the reference.")
    reference_store = fields.Char(
        "Reference",
        states={
            'invisible': ((Eval('type') != 'line')
                | ~Eval('reference_edit', False)),
            'readonly': Eval('sale_state').in_([
                    'done', 'cancelled']),
            },
        depends=['type', 'reference_edit', 'sale_state'])

    @classmethod
    def view_attributes(cls):
        return super().view_attributes() + [
            ('/form//field[@name="reference"]',
                'states', {
                    'invisible': ((Eval('type') != 'line')
                        | (Eval('reference_edit', False))),
                }),
            ]

    @classmethod
    def default_reference_edit(cls):
        return False

    @fields.depends('sale', '_parent_sale.reference', 'reference_edit',
        'reference_store')
    def on_change_with_reference(self, name=None):
        if self.reference_edit and self.reference_store:
            return self.reference_store
        if self.sale:
            return self.sale.reference

sale_line_form.xml

<?xml version="1.0"?>
<!-- The COPYRIGHT file at the top level of this repository contains the full 
     copyright notices and license terms. -->
<data>
    <xpath expr="/form/notebook/page[@id='general']/field[@name='shipping_date']" position="after">
        <label id="reference" string="Reference:"/>
        <group id="reference" col="-1">
            <field name="reference" xexpand="0"/>
            <field name="reference_store" xexpand="0"/>
            <field name="reference_edit" xexpand="0"/>
        </group>
    </xpath>
</data>

My bad the states attribute on the form view is not supported by the clients. This is probably because it will be ambiguous with states of the field.

Maybe another solution would be to add a setter to the Function field and a readonly states when the edit field is not checked (and of course for the proper state).

However, it seems used in other modules : modules/sale: 0ac079e22786 product.py

This is a page widget which is not linked to any field.

Ok, working this way :slight_smile: Thanks @ced and @pokoli !

     reference = fields.Function(fields.Char('Reference',
            states={
                'invisible': Eval('type') != 'line',
                'readonly': (Eval('sale_state').in_(['done', 'cancelled'])
                    | ~Eval('reference_edit')),
                },
            depends=['type', 'reference_edit', 'sale_state']),
        'on_change_with_reference', setter='set_reference_field')
    reference_edit = fields.Boolean(
        "Edit Reference",
        states={
            'invisible': Eval('type') != 'line',
            'readonly': Eval('sale_state').in_([
                    'done', 'cancelled']),
            },
        depends=['type', 'sale_state'],
        help="Check to edit the reference.")
    reference_store = fields.Char(
        "Reference",
        states={
            'readonly': Eval('sale_state').in_([
                    'done', 'cancelled']),
            },
        depends=['sale_state'])

    @classmethod
    def default_reference_edit(cls):
        return False

    @fields.depends('sale', '_parent_sale.reference', 'reference_edit',
        'reference_store')
    def on_change_with_reference(self, name=None):
        if self.reference_edit and self.reference_store:
            return self.reference_store
        if self.sale:
            return self.sale.reference

    @classmethod
    def set_reference_field(cls, lines, name, value):
        if not value:
            return
        cls.write(lines, {
                'reference_store': value,
                })

sale_line_form.xml

<?xml version="1.0"?>
<!-- The COPYRIGHT file at the top level of this repository contains the full 
     copyright notices and license terms. -->
<data>
    <xpath expr="/form/notebook/page[@id='general']/field[@name='shipping_date']" position="after">
        <label id="reference" string="Reference:"/>
        <group id="reference" col="-1">
            <field name="reference" xexpand="0"/>
            <field name="reference_edit" xexpand="0"/>
        </group>
    </xpath>
</data>
1 Like

I will be great to make the same design for existing standard modules.

2 Likes

issue10431 opened.

I think it could be simplified using a functional also for reference_edit field, don’t you? It’s value would be True when reference_store had some value, and False otherwise. It would have a setter that would copy the value from reference to reference_store when checked and reset it if unchecked.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.