Proper way of returning result to fields.Function(fields.Many2Many OR Many2One for queried objects

Hi folks,

we try to add an additional “codes” field to TaxLine as during our workflow this is additional information to us, which we would like to have access to.

What works:
we already queried successfully for the account.tax.code objects, but we are not able to show them in the view.

What does NOT work:
Returning account.tax.code object(s) as result to field “codes”

Error Messages like:
Traceback (most recent call last):
  File "/site-packages/trytond/wsgi.py", line 116, in dispatch_request
    return endpoint(request, **request.view_args)
  File "/site-packages/trytond/protocols/dispatcher.py", line 48, in rpc
    request, database_name, *request.rpc_params)
  File "/site-packages/trytond/wsgi.py", line 83, in auth_required
    return wrapped(*args, **kwargs)
  File "/site-packages/trytond/protocols/wrappers.py", line 131, in wrapper
    return func(request, pool, *args, **kwargs)
  File "/site-packages/trytond/protocols/dispatcher.py", line 181, in _dispatch
    result = rpc.result(meth(*c_args, **c_kwargs))
  File "/site-packages/trytond/model/modelsql.py", line 863, in read
    row[fname] = getter_result[row['id']]
IndexError: list index out of range

do arise.

Code:
xml structure update in ‘account/view/tax_line_tree.xml’:

Append xml view-structure with field name=“codes” in tax_line_tree.xml
<?xml version="1.0"?>

<tree editable="1">
    <field name="tax" expand="1"/>
    <field name="codes"/> <!-- added new field!!! ->
    <field name="amount"/>
    <field name="type"/>
    <field name="move_line"/>
</tree>

Source-Code tax.py:

  • add new field “codes” to class TaxLine in account/tax.py
  • plus add function “get_move_line_tax_codes”

class TaxLine(ModelSQL, ModelView):
    'Tax Line'
    __name__ = 'account.tax.line'
    currency_digits = fields.Function(fields.Integer('Currency Digits'),
        'on_change_with_currency_digits')
    amount = fields.Numeric('Amount', digits=(16, Eval('currency_digits', 2)),
        required=True, depends=['currency_digits'])
    type = fields.Selection([
            ('tax', "Tax"),
            ('base', "Base"),
            ], "Type", required=True)
    tax = fields.Many2One('account.tax', 'Tax', select=True,
        ondelete='RESTRICT', required=True,
        domain=[
            ('company', '=', Eval('company', -1)),
            ],
        depends=['company'])
    move_line = fields.Many2One('account.move.line', 'Move Line',
            required=True, select=True, ondelete='CASCADE')
    company = fields.Function(fields.Many2One('company.company', 'Company'),
        'on_change_with_company')
    
   #try with many2many
    codes = fields.Function(fields.Many2Many(
            'account.tax.code', None, None, 'Codes'),
        'get_move_line_tax_codes')

    #try with many2one
    #codes = fields.Function(fields.Many2One('account.tax.code', 'Codes'),
    #    'get_move_line_tax_codes')

    #function to query for tax.line related tax.codes
    @classmethod
    def get_move_line_tax_codes(cls, taxlines, name):
        pool = Pool()
        TaxCodeLine = pool.get('account.tax.code.line')
        taxcodeline = TaxCodeLine.__table__()
        taxline = cls.__table__()
        cursor = Transaction().connection.cursor()

        TaxCode = pool.get('account.tax.code')
        taxcode = TaxCode.__table__()

        filtered_tax_codes = defaultdict(list)
        i = 0
        for sub_ids in grouped_slice(taxlines):
            red_sql = reduce_ids(taxline.id, sub_ids)
            cursor.execute(*taxline.join(taxcodeline,
                condition=(taxline.tax == taxcodeline.tax)
                        & (taxline.type == taxcodeline.amount)).select(
                        taxcodeline.id, taxcodeline.code,
                        where=red_sql,
                        order_by=taxcodeline.id))

            for taxcodeline_id, taxcodeline_code in cursor.fetchall():
                filtered_tax_codes[taxcodeline_id] = taxcodeline_code
                i = i+1

        cursor = Transaction().connection.cursor()

        tax_codes = defaultdict(list)

        helper = []
        for value in filtered_tax_codes:
            helper.append(value)

        for sub_ids in grouped_slice(filtered_tax_codes):
            red_sql = reduce_ids(taxcodeline.id, sub_ids)
            cursor.execute(*taxcodeline.join(taxcode,
            condition = (taxcodeline.code == taxcode.id)).select(
                taxcode.id, taxcode.code, taxcode.name,
                where=red_sql,
                order_by=taxcodeline.id))

        taxes = []
        for taxcode_id, taxcode_code, taxcode_name in cursor:
            taxes.append(taxcode_id)

        i = 0
        
        #res = {}
        res = []
        for taxcode in TaxCode.browse(taxes):
            #res[taxcode.id] = set(), []
            #res[taxcode.id][i].append(taxcode)
            i = i+1
            res.append(taxcode)

        return res 

=> this leads to above error:

  File "/site-packages/trytond/model/modelsql.py", line 863, in read
    row[fname] = getter_result[row['id']]
IndexError: list index out of range

Please give advice on how to return the taxcode(s) object(s) correctly for many2many OR a many2one field solution.

System-Version: Trytond Version 5.8.x

Thanks in advance,
kind regards,
Raphael

You must return a list of ids instead of list of instances.

PS: you should not edit standard code but use the extension mechanism of Tryton otherwise your modification will be lost on future update.

This is how we finally solved the display of tax_codes in TaxLine … we had to switch to char field as otherwise with type account.tax.code the field would only render like " (0) " or " (1) "

class TaxLine(ModelSQL, ModelView):
    'Tax Line'
    __name__ = 'account.tax.line'
    currency_digits = fields.Function(fields.Integer('Currency Digits'),
        'on_change_with_currency_digits')
    amount = fields.Numeric('Amount', digits=(16, Eval('currency_digits', 2)),
        required=True, depends=['currency_digits'])
    type = fields.Selection([
            ('tax', "Tax"),
            ('base', "Base"),
            ], "Type", required=True)
    tax = fields.Many2One('account.tax', 'Tax', select=True,
        ondelete='RESTRICT', required=True,
        domain=[
            ('company', '=', Eval('company', -1)),
            ],
        depends=['company'])
    move_line = fields.Many2One('account.move.line', 'Move Line',
            required=True, select=True, ondelete='CASCADE')
    company = fields.Function(fields.Many2One('company.company', 'Company'),
        'on_change_with_company')
    codes = fields.Function(fields.Char('Tax_Code'),
               'get_move_line_tax_codes')

    @classmethod
    def get_move_line_tax_codes(cls, taxlines, name):
        pool = Pool()
        TaxCodeLine = pool.get('account.tax.code.line')
        taxcodeline = TaxCodeLine.__table__()
        taxline = cls.__table__()
        cursor = Transaction().connection.cursor()

        TaxCode = pool.get('account.tax.code')
        taxcode = TaxCode.__table__()

        filtered_tax_codes = defaultdict(list)
        tlid = None

        for sub_ids in grouped_slice(taxlines):
            red_sql = reduce_ids(taxline.id, sub_ids)
            cursor.execute(*taxline.join(taxcodeline,
                condition=(taxline.tax == taxcodeline.tax)
                        & (taxline.type == taxcodeline.amount)).select(
                        taxline.id, taxcodeline.id, taxcodeline.code,
                        where=red_sql,
                        order_by=taxcodeline.id))
            for taxline_id, taxcodeline_id, taxcodeline_code in cursor.fetchall():
                filtered_tax_codes[taxcodeline_id] = taxcodeline_code
                tlid = taxline_id

        cursor = Transaction().connection.cursor()
        tax_codes = defaultdict(list)

        helper = []
        for value in filtered_tax_codes:
            helper.append(value)

        for sub_ids in grouped_slice(filtered_tax_codes):
            red_sql = reduce_ids(taxcodeline.id, sub_ids)
            cursor.execute(*taxcodeline.join(taxcode,
            condition = (taxcodeline.code == taxcode.id)).select(
                taxcode.id, taxcode.code, taxcode.name,
                where=red_sql,
                order_by=taxcodeline.id))

        taxes = []
        for taxcode_id, taxcode_code, taxcode_name in cursor:
            taxes.append(taxcode_id)

        res = defaultdict(list)
        for taxcode in TaxCode.browse(taxes):
            taxdict = defaultdict(list)
            res[tlid] = str(taxcode.code)

        return res 

@ced Cédric thank you very much => Sure we implement the solution in our custom module to keep things nice and clean for future updates.

Kind regards,
Raphael