12. Desarrollando con Odoo (7). Herencia prototípica. Separar vistas. Widget many2many. Pasar información a un formulario "hijo": Context, active_id, origin, default_field

1. Problema con las relaciones many2many 

La herencia prototípica es la herencia tradicional donde aparece una nueva tabla y las vistas no nos valen. Solamente se utiliza en Odoo para el caso que el modelo nuevo queramos desligarlo del todo del modelo padre. Normalmente utilizaremos aquella que solo utilizamos :_ inherit="padre" sin utilizar el _name o dejando a _name="padre")

Como vimos, para la herencia prototípica se utiliza (_name="hijo" y _inherit="padre")

Pero hay que tener cuidado pues los campos del padre de tipo Many2many no se pueden copiar pues la tabla puente que une las dos tablas de la relación tiene que ser nueva pues ahora utilizamos la tabla hijo en vez de la padre!!

Como es una tabla nueva, se tendrá que añadir al fichero de permisos "ir.model.access.csv


2. Separar vistas, menús  y modelos

Si creamos varios ficheros de vistas se debe:

  1. Añadir la información entre las etiquetas <odoo> y <data>
  2. Copiar las vistas tree y form , actions  para un mismo modelo en un mismo fichero.
  3. Todos los menús sem colocaran en un mismo fichero llamado menu.xml
  4. Se añade al elemento "data" del fichero __manifest__.py, pero el orden a incluir es primero "menu.xml", y luego por orden que aparecen en el menú preferiblemente
Veamos __manifest__.py

{
    'name': "ximoapp01",

    'summary': """
        Short (1 phrase/line) summary of the module's purpose, used as
        subtitle on modules listing or apps.openerp.com""",

    'description': """
        Long description of module's purpose
    """,

    'author': "My Company",
    'website': "https://www.yourcompany.com",

    # Categories can be used to filter modules in modules listing
    # Check https://github.com/odoo/odoo/blob/16.0/odoo/addons/base/data/ir_module_category_data.xml
    # for the full list
    'category': 'Uncategorized',
    'version': '0.1',

    # any module necessary for this one to work correctly
    'depends': ['base'],

    # always loaded
    'data': [
        'security/ir.model.access.csv',
        'views/project.xml',
        'views/history.xml',
        'views/task.xml',
        'views/sprint.xml',
        'views/technology.xml',
        'views/bug.xml',
        'views/improvement.xml',
        'views/developer.xml',
        'views/views.xml',
'views/templates.xml',
'views/menu.xml',
    ],
    # only loaded in demonstration mode
    'demo': [
        'demo/demo.xml',
    ],
}

Y veamos una vista como por ejemplo task.xml

<odoo>
  <data>
    <!-- 1.1 TREE explicit list view definition -->
    <record model="ir.ui.view" id="ximoapp01.task_list">
      <field name="name">ximoapp01 task list</field>
      <field name="model">ximoapp01.task</field>
      <field name="arch" type="xml">
        <tree>
          <field name="name"/>
          <field name="description"/>
          <field name="code"/>
          <field name="start_date"/>
          <field name="duration"/>
          <field name="end_date"/>
          <field name="is_paused"/>
          <field name="sprint"/>
          <field name="technologies"/>
          <field name="history"/>
          <field name="developers"/>
        </tree>
      </field>
    </record>

    <!-- 1.2 FORM explicit list view definition -->
    <record model="ir.ui.view" id="ximoapp01.task_form">
      <field name="name">ximoapp01 task form</field>
      <field name="model">ximoapp01.task</field>
      <field name="arch" type="xml">
        <form>
          <group>
            <field name="name"/>
            <field name="description"/>
            <field name="definition_date"/>
            <field name="code"/>
            <field name="start_date"/>
            <field name="duration"/>
            <field name="end_date"/>
            <field name="is_paused"/>
            <field name="sprint"/>
            <field name="technologies"/>
            <field name="history"/>
            <field
              name= "developers"
              domain="[('is_dev','=',True)]"
              context="{'form_view_ref':'ximoapp01.developer_form'}">
            </field>
          </group>
        </form>
      </field>
    </record>

    
    <!-- 2. actions opening views on models -->
    <record model="ir.actions.act_window" id="ximoapp01.action_task_window">
      <field name="name">ximoapp01 task window</field>
      <field name="res_model">ximoapp01.task</field>
      <field name="view_mode">tree,form</field>
    </record>

    <!-- 3. menu categories -->
    <menuitem name="Tasks" id="ximoapp01.menu_2_task_list" parent="ximoapp01.menu_2"
              action="ximoapp01.action_task_window"/>
    
  </data>
</odoo>

Y todos los menús se concentran en menu.xml

<odoo>
  <data>
    <!-- Top menu item -->

    <menuitem name="ximoapp01" id="ximoapp01.menu_root"/>

    <!-- menu categories -->

    <menuitem name="Projects &amp; Histories" id="ximoapp01.menu_1" parent="ximoapp01.menu_root"/>
    <menuitem name="Tasks &amp; Sprints"      id="ximoapp01.menu_2" parent="ximoapp01.menu_root"/>
    <menuitem name="Human resources"          id="ximoapp01.menu_3" parent="ximoapp01.menu_root"/>
    
<!--
    <menuitem name="Menu 2" id="ximoapp01.menu_2" parent="ximoapp01.menu_root"/>
-->
    <!-- actions -->

    <menuitem name="Projects" id="ximoapp01.menu_1_project_list"            parent="ximoapp01.menu_1"
              action="ximoapp01.action_project_window"/>
    <menuitem name="Histories" id="ximoapp01.menu_1_history_list"           parent="ximoapp01.menu_1"
              action="ximoapp01.action_history_window"/> 
    

    <menuitem name="Tasks" id="ximoapp01.menu_2_task_list"              parent="ximoapp01.menu_2"
              action="ximoapp01.action_task_window"/>
    <menuitem name="Sprints" id="ximoapp01.menu_2_sprint_list"          parent="ximoapp01.menu_2"
              action="ximoapp01.action_sprint_window"/> 

    <menuitem name="Technologies" id="ximoapp01.menu_2_technology_list" parent="ximoapp01.menu_2"
              action="ximoapp01.action_technology_window"/>

    <menuitem name="Developers" id="ximoapp01.menu_3_developer_list"    parent="ximoapp01.menu_3"
              action="ximoapp01.action_developer_window"/>          
<!--
    <menuitem name="Server to list" id="ximoapp01" parent="ximoapp01.menu_2"
              action="ximoapp01.action_server"/>
  </data>
</odoo>

3. Widget many2many

Veamos el widget many2many aplicado al campo developer de la vista task

<field
   name= "developers"
   domain="[('is_dev','=',True)]"
   context="{'form_view_ref':'ximoapp01.developer_form'}"
   widget= "many2many_tags">
</field>


Y aparece un widget parecido a este


4. Pasar información al formulario

Ya vimos que el context permite pasar información. Si vamos a un campo many2many le indicamos el context que coja el valor de un campo al abrir su formulario correspondiente. Veamos los pasos a realizar

1. Para la vista de developers.xml, en el campo taskscreamos una variable llamada "current_developer"(podria ser otro nombre cualquiera), que le asignamos el valor "active_id" que es el valor del id de la tabla del form del que partimos (o sea el id del developer).

<field name="tasks"
       context="{'current_developer':active_id}">
</field>

2. En el modelo de task , creamos una función antes de la definición del campo developers que se llame _get_default_developer, que obtenga el developer y retorne su id.

3. A continuación en dicho modelo, le decimos que el campo developer tiene un default que apunta a dicha función

    def _get_default_developer(self):
        developer= self.browse(self._context.get('current_developer'))
        if developer:
            return [developer.id]
        else:
            return []
    
    developers = fields.Many2many(comodel_name="res.partner", 
                                  relation="partner_task", 
                                  column1="task_id", 
                                  column2="partner_id", 
                                  default=_get_default_developer, 
                                  help="Desarrolladores")

5. Errores producidos

5.1 Propiedad origin para ids no disponibles

Dentro de proyecto ir a historia y crear una tarea y dice puede asignar el proyecto, pero no se produce dicho error al ir a historia directamente del menu. 

Es decir "Proyecto-Historia-Nueva Tarea falla pero "Historia-Nueva Tarea" no falla

El problema viene de que dentro del modelo task tenemos esta funcion que utiliza el campo "record.history.project.id" que puede que no contenga un valor aún y por tanto el project.id y todavía no está disponible por no estar almacenado en BD

@api.depends('history')
def _get_sprint(self):
  for record in self:
    sprints = self.env['ximoapp01.sprint'].search([('project.id', '=', record.history.project.id)])
    found = False
    for sprint in sprints:
      if not found:
        if isinstance(sprint.end_date, datetime.datetime):
          if sprint.end_date > datetime.datetime.now():
            record.sprint = sprint.id
            found = True
    if not found:
      record.sprint = False

Para solucionar el problema si "record.history.project.id" no está ya generado, este valor se obtendrá de la propiedad "origin" --> "record.history.project.id.origin"

@api.depends('history')
def _get_sprint(self):
  for record in self:
    #sprints = self.env['ximoapp01.sprint'].search([('project.id', '=', record.history.project.id)])
    if isinstance(record.history.project.id, models.NewId):
      id_project= int (record.history.id.origin)
    else:
      id_project= record.history.id
    sprints = self.env['ximoapp01.sprint'].search([('project.id', '=', id_project)])
    found = False
    for sprint in sprints:
      if not found:
        if isinstance(sprint.end_date, datetime.datetime):
          if sprint.end_date > datetime.datetime.now():
            record.sprint = sprint.id
            found = True
    if not found:
      record.sprint = False


5.2 Widget many2many creación de res.partner con is_dev=Fase 

En el modelo task, al crear un desarrollador con el widget many2many, se crea con is_dev=False, con lo que ya no se puede buscar en el form de desarroladores. Para ello en el campo developer de la vista debemos añadir al context 'default_is_dev' : True

<field
   name= "developers"
   domain="[('is_dev','=',True)]"
   context="{'form_view_ref':'ximoapp01.developer_form' , 'default_is_dev':True }"
   widget= "many2many_tags">
</field>





Comentarios

Entradas populares de este blog

20. Desarrollando con Odoo (15). Permisos y grupos. Crear usuarios de la aplicación. Restringir permisos a usuarios

2. El Modo desarrollador

8. Desarrollando con Odoo (6). Herencia de clase en modelos. Herencia de vistas. Operaciones numeradas en Odoo ??