Search code examples
pythonodooodoo-12odoo-13

Add extra fields to odoo many2many field


I am trying to create a fine allocation functionality in hr.employee.This module inherits from hr.employee. The process is:

  1. Click the Fine Allocation menu that opens a wizard. The wizard has a many2many relation field called employee_ids related to hr.employee. This should enable you to select batch employees and allocate fines together.

2.There being a field with name fine_amount, allocate individual fines for employees in each of the lines and then allocate fines by click of a button.

My question is, how do I extend this many2many field with the fine_amount field?

If I extend the hr.employee form with this field, it is not among the selected fields in the many2many lines.

My code looks like this

class FineAllocation(models.TransientModel):
_name = 'fine.allocation'
_description = 'Fine Allocation'

@api.model
def action_allocate_fine(self):
    pass

employee_ids = fields.Many2many('hr.employee','hr_employee_group_rel', 'deductions_id' 'Employees')
fine_amount = fields.Float(string='Fine Amount')

And this is for the wizard record view

<record id="employee_fine_allocation_form" model="ir.ui.view">
        <field name="name">Employee Fine Allocation Form</field>
        <field name="model">fine.allocation</field>
        <field name="arch" type="xml">
            <form string="Employee Fine Allocation">
                <group>
                    <span colspan="4" nolabel="1">This wizard will allocate fines for all selected employee(s) equalling the input fine amount.</span>
                </group>

                <group colspan="4">
                    <separator string="Employees" colspan="4" />
                    <newline />
                    <field name="employee_ids" nolabel="1" />
                </group>

                <footer>
                    <button name="action_allocate_fine" string="Allocate Fines" type="object" class="btn-primary" />
                    <button string="Cancel" class="btn-secondary" special="cancel" />
                </footer>
            </form>
        </field>
    </record>

Solution

  • By default, the many2many field shows the default view.

    When a view is requested by (model, type), the view matching the model and the type, with the lowest priority will be returned (it is the default view).

    To show the field in the many2many list you can extend the default view or define your view, try one of the followings:

    1. Extend the default tree view hr.view_employee_tree:

       <record id="view_employee_tree" model="ir.ui.view">
           <field name="name">hr.employee.tree</field>
           <field name="model">hr.employee</field>
           <field name="inherit_id" ref="hr.view_employee_tree"/>
           <field name="arch" type="xml">
               <tree>
                   <field name="fine_amount"/>
               </tree>
           </field>
       </record>
      
    2. Define a new tree view and force the many2many field to show it:

       <record id="view_employee_tree_allocate_fine" model="ir.ui.view">
       <field name="name">hr.employee.tree.allocate.fine</field>
       <field name="model">hr.employee</field>
       <field name="arch" type="xml">
           <tree string="Employees" decoration-bf="message_needaction==True">
               <field name="name"/>
               <field name="work_phone" class="o_force_ltr"/>
               <field name="work_email"/>
               <field name="company_id" groups="base.group_multi_company"/>
               <field name="department_id"/>
               <field name="job_id"/>
               <field name="parent_id"/>
               <field name="coach_id" invisible="1"/>
               <field name="message_needaction" invisible="1"/>
      
               <field name="fine_amount"/>
           </tree>
       </field>
       </record>
      

      To force the many2many field to show a specific view, pass tree_view_ref in context:

      <field name="employee_ids" nolabel="1" context="{'tree_view_ref': 'module_name.view_employee_tree_allocate_fine'}"/>
      
    3. Define the tree view in place:

       <field name="employee_ids">
           <tree editable="bottom">
               <field name="name"/>
               <field name="work_phone" class="o_force_ltr"/>
               <field name="work_email"/>
               <field name="company_id" groups="base.group_multi_company"/>
               <field name="department_id"/>
               <field name="job_id"/>
               <field name="parent_id"/>
               <field name="coach_id" invisible="1"/>
               <field name="message_needaction" invisible="1"/>
      
               <field name="fine_amount"/>
           </tree>
       </field>
      

    Edit:

    Set editable option to bottom when defining tree view in place.

    Edit2:

    With the following definition:

    employee_ids = fields.Many2many('hr.employee', 'hr_employee_rel', 'payslip_id', 'Employees')
    

    Odoo will try to create records and setting the value of column2 (It is not passed explicitly in the example above ) to Employees which will raise the following error:

    the “employees” column of the “hr_employee_rel” relationship does not exist
    LINE 2: ...   INSERT INTO hr_employee_rel (payslip_id, Employees)...
    

    employee_id is missing if you compare it to employee_ids in hr_payroll wizard model.

    In your question, you can see that there is no comma between deductions_id and Employees, Python will concatenate the two strings and you will end with column1 set to deductions_idEmployees. Odoo should raise the following error:

    psycopg2.errors.UndefinedColumn: ERROR: the column « deductions_idemployees » of the relation « hr_employee_group_rel » does not exist
    LINE 2: ... INSERT INTO hr_employee_group_rel (deductions ...
    

    You could avoid that error if you passed Employees as the string attribute, the second column (column2 attribute) should be generated automatically as it is an optional. You can try the following definition:

    employee_ids = fields.Many2many('hr.employee', 'hr_employee_fine_allocation_rel', 'payslip_id', string='Employees')
    

    The attributes relation, column1 and column2 are optional. If not given, names are automatically generated from model names.

    If you do not need to set them explicitly, the field declaration can be reduced to:

    employee_ids = fields.Many2many('hr.employee', string='Employees')