Function field inside form doesn't respect record rules

First, I’m upgrading an installation from 5.4 to version 6.4. Everything went smooth until access rights (which was kind of expected).

In this case we have a form where timesheet lines are shown. This is a Function field (one2many) which gets the timesheet lines based on the timesheet added (many2one from timesheet.work). This gives something like:

timesheet = fields.Many2One('timesheet.work', 'Timesheet')
timesheet_lines = fields.Function(
    fields.One2Many('timesheet.line', None, 'Timesheet Lines',
        domain=[('work', '=', Eval('timesheet'))],
        depends=['timesheet']), '_get_timesheet_lines')

def _get_timesheet_lines(self, name):
    if self.timesheet:
        return [t.id for t in self.timesheet.timesheet_lines]
    return []

The timesheet lines then are restricted to only show the timesheet lines of the user. For this a new group was created and a new Record Rule added to the group which filters the timesheet lines based on the employee. This is where things go wrong. We now get an UserError which says that the user is not allowed to read records with id ... ... etc. And the list of timesheet lines is empty.

We tracked the error back to version 6.0. From that version the error shows up. The strange thing is that when the user directly goes to Timesheet -> Lines via the menu, it still works. The user only gets it’s own timesheet lines.

It seems we clearly missing something here. In the release notes of version 6.0 there is a sentence saying:

“The record rules are now only applied if _check_access is set in the context. This improves the multi-company support.”

Adding a print statement to the _get_timesheet_lines indeed the _check_access is False. Change the _check_access to True didn’t help either.

def _get_timesheet_lines(self, name):
    with Transaction().set_context(_check_access=True):
        if self.timesheet:
            return [t.id for t in self.timesheet.timesheet_lines]
    return []

What are we doing wrong here?

Adding an extra domain to the list is not an option because a group higher up is allowed to see all the timesheet lines from all the users on that timesheet.

You are right this is what changed.

Because context applies only a new instances. You must make the search yourself to fill the function field.

PS:

domain is pointless on Function fields without setter.

You lost me here. Do I need to do a Pool().get('timesheet.line').search(...) to get the ids?

Correct, there is a setter but for clarity I removed it here.

Yes with the context _check_access set so the rule domain will be applied.

1 Like

I changed the function to

def _get_timesheet_lines(self, name):
    pool = Pool()
    Lines = pool.get('timesheet.line')
    with Transaction().set_context(_check_access=True):
        if self.timesheet:
            lines = Lines.search(['work', '=', self.timesheet])
            return [t.id for t in lines]
    return []

And now the error is gone.

For performances, I would suggest to convert the getter to a classmethod, to avoid looping searches if you need to load this field on multiple records at once

Thanks for the suggestion, but as the title says, this is inside a form. So no looping searches here. It’s even faster because there are less timesheet lines.

Another way would be to use a relate instead of a One2Many if there are a lot of timesheet lines. It also add the possibility to filter.

The user has also the possibility to add new timesheet lines from that One2Many which is working perfectly. Also the amount of timesheet lines are not that huge and most of the timesheets only contains two or three timesheet lines.

But what is a relate?

With a relate action it is also possible to add new record.

It is an action with the keyword form_relate and a domain that is using active_id.

Ah, that one. It then will open a new tab with the timesheet lines. In this case that’s not something the user wants because now they have everything in one overview, can compare and confirm everything if needed.

Users are often wrong about UX decision :wink:

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