3

I have added a new state to a Selection field, using the parameter selection_add:

state = fields.Selection(
    selection_add=[
        ('draft_ok', 'Validated Quotation'),
    ],
)

Now I want to show it in the XML view, where currently state is shown this way:

<field name="state" widget="statusbar" statusbar_visible="draft,sent,sale"/>

If I inherit from that view, to add the new state:

<xpath expr="//header/field[@name='state']" position="attributes">
    <attribute name="statusbar_visible">draft,draft_ok,sent,sale</attribute>
</xpath>

The new state is shown at the end of the status bar. I want to show it between draft and sent states.

The only way I know to do that is redefining the states in Python:

state = fields.Selection(
    selection=[
        ('draft', 'Quotation'),
        ('draft_ok', 'Validated Quotation'),
        ('sent', 'Quotation Sent'),
        ('sale', 'Sales Order'),
        ('done', 'Locked'),
        ('cancel', 'Cancelled'),
    ],
)

But this solution is not very consistent, because if other module also adds a state to this field and my module code is executed after it, I would destroy the state added by this other module.

So I am looking for other way to show the statusbar with a customised order. Any ideas?

forvas
  • 8,705
  • 7
  • 46
  • 127
  • 3
    I think there is no other solution so far (provided by the framework). That's why i use methods returning the selection in my modules. It allows others to override/extend the selection the way they want. – CZoellner May 22 '19 at 09:11
  • @CZoellner That is a really good solution, from now I will apply it on in my modules too. That should be a must-do rule for Odoo programmers. If everyone did that... – forvas May 22 '19 at 09:45
  • I think it is something of updating the value of this field on somewhere in database – khelili miliana May 22 '19 at 10:15
  • Nice approach @CZoellner – Charif DZ May 22 '19 at 10:47

2 Answers2

3

From the code responsible of this witch located in fields.Selection class There is no way to do it without using special tricks:

# frame code
def _setup_attrs(self, model, name):
    super(Selection, self)._setup_attrs(model, name)
    # determine selection (applying 'selection_add' extensions)
    for field in reversed(resolve_mro(model, name, self._can_setup_from)):
        # We cannot use field.selection or field.selection_add here
        # because those attributes are overridden by ``_setup_attrs``.
        if 'selection' in field.args:
            self.selection = field.args['selection']
        if 'selection_add' in field.args:
            # use an OrderedDict to update existing values
            selection_add = field.args['selection_add']
            self.selection = OrderedDict(self.selection + selection_add).items()

Like for example monkey patching I tried normal inheritance It didn't work I think It needs a lot of work.

This what I tried and it worked just fine in Odoo 9. I created a new key is selection_add_after witch is a dictionary

    1. key is the value of selection that you want to add item after it

    2. value is the list of selection items that you want to add
    def _setup_attrs(self, model, name):
        super(fields.Selection, self)._setup_attrs(model, name)
        # determine selection (applying 'selection_add' extensions)
        for field in reversed(fields.resolve_mro(model, name, self._can_setup_from)):
            # We cannot use field.selection or field.selection_add here
            # because those attributes are overridden by ``_setup_attrs``.
            if 'selection' in field.args:
                self.selection = field.args['selection']
            if 'selection_add' in field.args:
                # use an OrderedDict to update existing values
                selection_add = field.args['selection_add']
                self.selection = OrderedDict(self.selection + selection_add).items()
            if 'selection_add_after' in field.args:
                selection_add_atfer = field.args['selection_add_after']
                new_selection = []
                for item in self.selection:
                    new_selection.append(item) # add the element firs
                    items_to_add = selection_add_atfer.get(item[0], [])
                    for item_to_add in items_to_add:  # then add the element if there is
                        new_selection.append(item_to_add)
                # I don't know why they used OrderdedDict ???!! do you have any idea why?!!
                self.selection = OrderedDict(new_selection).items()

    # mucky patch the method in selection field
    fields.Selection._setup_attrs = _setup_attrs

Make sure you patch before defining the field

    # add element after draft 
    state = fields.Selection(selection_add_after={'draft': [('hello', 'Hello')]})

    # add element after draft and other emelent after confirmed 
    state = fields.Selection(selection_add_after={'draft': [('hello', 'Hello')], 'confirmed': [('test','Test')]})

You can add new key like removing or anything you want.

But monkey patching Framework method is also a bad idea because if there is any updates in the _setup_attrs is always removed by this.

EDIT

For Odoo 11, this is the code:

def _setup_attrs(self, model, name):
    super(fields.Selection, self)._setup_attrs(model, name)
    # determine selection (applying 'selection_add' extensions)
    for field in reversed(fields.resolve_mro(model, name, self._can_setup_from)):
        # We cannot use field.selection or field.selection_add here
        # because those attributes are overridden by ``_setup_attrs``.
        if 'selection' in field.args:
            self.selection = field.args['selection']
        if 'selection_add' in field.args:
            # use an OrderedDict to update existing values
            selection_add = field.args['selection_add']
            self.selection = list(OrderedDict(self.selection + selection_add).items())
        if 'selection_add_after' in field.args:
            selection_add_atfer = field.args['selection_add_after']
            new_selection = []
            for item in self.selection:
                new_selection.append(item) # add the element firs
                items_to_add = selection_add_atfer.get(item[0], [])
                for item_to_add in items_to_add:  # then add the element if there is
                    new_selection.append(item_to_add)
            # I don't know why they used OrderdedDict ???!! do you have any idea why?!!
            self.selection = list(OrderedDict(new_selection).items())

fields.Selection._setup_attrs = _setup_attrs
Charif DZ
  • 13,500
  • 3
  • 13
  • 36
  • 1
    Wow, your code worked. Just updated the answer with the version 11 code, since needs a little modification. I guess it is less probable that a 3rd party module modifies the `_setup_attrs` method of `fields.Selection` than the `state` field of one of the standard modules, so this will be a bit better solution. Thank you a lot for the info! – forvas May 22 '19 at 12:25
  • 1
    Do you have any idea why the use list(orderedDict) ?!! – Charif DZ May 22 '19 at 13:03
  • hahaha I do not know since the source variable is a list and it respects the order, doesn't it? – forvas May 22 '19 at 13:13
0

try this way :

state = fields.Selection(
    selection_add=[
         ('draft_ok', 'Validated Quotation'),(sent,)
    ],
)
wllbll
  • 411
  • 3
  • 10