Browse Source

[IMP] base_custom_info: Make backend UI work

# Conflicts:
#	base_custom_info/i18n/es.po
pull/1115/head
Pedro M. Baeza 8 years ago
committed by Fanha Giang
parent
commit
be29d1b235
  1. 23
      base_custom_info/README.rst
  2. 4
      base_custom_info/__manifest__.py
  3. 2
      base_custom_info/demo/custom.info.category.csv
  4. 575
      base_custom_info/i18n/es.po
  5. 0
      base_custom_info/images/customizations-everywhere.jpg
  6. 0
      base_custom_info/images/templateception.jpg
  7. 122
      base_custom_info/models/custom_info.py
  8. 3
      base_custom_info/models/custom_info_option.py
  9. 14
      base_custom_info/models/custom_info_property.py
  10. 71
      base_custom_info/models/custom_info_template.py
  11. 169
      base_custom_info/models/custom_info_value.py
  12. 9
      base_custom_info/models/res_partner.py
  13. BIN
      base_custom_info/static/description/icon.png
  14. 60
      base_custom_info/static/description/icon.svg
  15. 25
      base_custom_info/tests/test_partner.py
  16. 83
      base_custom_info/views/custom_info_category_view.xml
  17. 113
      base_custom_info/views/custom_info_option_view.xml
  18. 169
      base_custom_info/views/custom_info_property_view.xml
  19. 124
      base_custom_info/views/custom_info_template_view.xml
  20. 185
      base_custom_info/views/custom_info_value_view.xml
  21. 72
      base_custom_info/views/menu.xml
  22. 52
      base_custom_info/views/res_partner_view.xml

23
base_custom_info/README.rst

@ -217,19 +217,7 @@ Known issues / Roadmap
====================== ======================
* Custom properties cannot be shared among templates. * Custom properties cannot be shared among templates.
* You get an error if you press *Save & New* when setting property values in
partner form.
* You have to press *reload custom information templates*, when the optimal
thing would be the reloading taking place whenever needed: when you change
the template, or when you choose an option that has an additional template.
However, `currently it is impossible for a x2many field to update itself
<https://github.com/odoo/odoo/issues/2693#issuecomment-56825399>`_, and it is
needed to skip some checks when you are saving a record after filling the
templates, which has to be done by `changing the context, something also not
possible currently at onchange time
<https://github.com/odoo/odoo/issues/7472>`_. So there are some technical
limitations that do not let us reach the ideal UX for this addon. So, in
short, press the button when you see it and be happy.
* Required attributes are for now only set in the UI, not in the ORM itself.
Bug Tracker Bug Tracker
=========== ===========
@ -245,10 +233,11 @@ Credits
Contributors Contributors
------------ ------------
* Rafael Blasco <rafabn@antiun.com>
* Carlos Dauden <carlos@incaser.es>
* Sergio Teruel <sergio@incaser.es>
* Jairo Llopis <yajo.sk8@gmail.com>
* Rafael Blasco <rafael.blasco@tecnativa.com>
* Carlos Dauden <carlos.dauden@tecnativa.com>
* Sergio Teruel <sergio.teruel@tecnativa.com>
* Jairo Llopis <jairo.llopis@tecnativa.com>
* Pedro M. Baeza <pedro.baeza@tecnativa.com>
Maintainer Maintainer
---------- ----------

4
base_custom_info/__manifest__.py

@ -38,9 +38,7 @@
"images/templates.png", "images/templates.png",
"images/values.png", "images/values.png",
], ],
'author': 'Antiun Ingeniería S.L., '
'Incaser Informatica S.L., '
'Tecnativa, '
'author': 'Tecnativa, '
'Odoo Community Association (OCA)', 'Odoo Community Association (OCA)',
'website': 'https://www.tecnativa.com', 'website': 'https://www.tecnativa.com',
'license': 'AGPL-3', 'license': 'AGPL-3',

2
base_custom_info/demo/custom.info.category.csv

@ -1,3 +1,3 @@
id,name,sequence id,name,sequence
cat_statics,Statics,50
cat_statics,Statistics,50
cat_gaming,Gaming,100 cat_gaming,Gaming,100

575
base_custom_info/i18n/es.po

@ -1,27 +1,61 @@
# Translation of Odoo Server. # Translation of Odoo Server.
# This file contains the translation of the following modules: # This file contains the translation of the following modules:
# * base_custom_info
# * base_custom_info
# #
# Translators:
# OCA Transbot <transbot@odoo-community.org>, 2016
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Odoo Server 9.0c\n" "Project-Id-Version: Odoo Server 9.0c\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-09-10 02:52+0000\n"
"PO-Revision-Date: 2016-09-10 02:52+0000\n"
"Last-Translator: OCA Transbot <transbot@odoo-community.org>, 2016\n"
"Language-Team: Spanish (https://www.transifex.com/oca/teams/23907/es/)\n"
"POT-Creation-Date: 2017-02-13 00:39+0000\n"
"PO-Revision-Date: 2017-02-13 00:39+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n" "Content-Transfer-Encoding: \n"
"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Plural-Forms: \n"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.model,name:base_custom_info.model_custom_info_model_link
msgid "A model that gets its ``ir.model`` computed"
msgstr ""
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_value_form
msgid "<strong>Warning!</strong>\n"
" You might see no changes in parent form until you save it."
msgstr "<strong>¡Aviso!</strong>\n"
" Puede no ver cambios en el formulario padre hasta que guarde."
#. module: base_custom_info
#: model:ir.model.fields,help:base_custom_info.field_base_config_settings_group_custom_info_partner
msgid "Add a tab in the partners form to edit custom information"
msgstr "Añade una pestaña en el formulario de empresas para editar su inf. personalizada."
#. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_option_template_id
msgid "Additional template"
msgstr "Plantilla adicional"
#. module: base_custom_info
#: model:ir.model.fields,help:base_custom_info.field_custom_info_option_template_id
msgid "Additional template to be applied to the owner if this option is chosen."
msgstr "Plantilla adicional a ser aplicada al propietario si esta opción se escoge."
#. module: base_custom_info
#: model:ir.ui.menu,name:base_custom_info.menu_advanced
msgid "Advanced"
msgstr "Avanzado"
#. module: base_custom_info
#: model:res.groups,name:base_custom_info.group_advanced
msgid "Advanced management"
msgstr "Gestión avanzada"
#. module: base_custom_info
#: model:ir.model.fields,help:base_custom_info.field_base_config_settings_group_custom_info_manager
msgid "Allow all employees to manage custom information"
msgstr "Permitir a todos los empleados gestionar inf. personalizada."
#. module: base_custom_info
#: model:custom.info.property,name:base_custom_info.prop_haters
msgid "Amount of people that hates him/her for being so smart"
msgstr "Cantidad de gente que lo odia por ser tan listo"
#. module: base_custom_info #. module: base_custom_info
#: sql_constraint:custom.info.value:0 #: sql_constraint:custom.info.value:0
@ -38,12 +72,58 @@ msgstr "Ya existe otra propiedad con ese nombre en esa plantilla."
msgid "Another template with that name exists for that model." msgid "Another template with that name exists for that model."
msgstr "Ya existe otra plantilla con ese nombre para ese modelo." msgstr "Ya existe otra plantilla con ese nombre para ese modelo."
#. module: base_custom_info
#: model:ir.model,name:base_custom_info.model_custom_info_option
msgid "Available options for a custom property"
msgstr "Opciones disponibles para una propiedad personalizada"
#. module: base_custom_info
#: model:custom.info.property,name:base_custom_info.prop_avg_note
msgid "Average note on all subjects"
msgstr "Nota media en todas las materias"
#. module: base_custom_info
#: model:ir.ui.menu,name:base_custom_info.menu_basic
msgid "Basic"
msgstr "Básico"
#. module: base_custom_info
#: model:res.groups,name:base_custom_info.group_basic
msgid "Basic management"
msgstr "Gestión básica"
#. module: base_custom_info
#: model:custom.info.option,name:base_custom_info.opt_cars
msgid "Cars"
msgstr "Coches"
#. module: base_custom_info
#: model:ir.actions.act_window,name:base_custom_info.custom_info_category_action
#: model:ir.ui.menu,name:base_custom_info.menu_category
msgid "Categories"
msgstr "Categorías"
#. module: base_custom_info
#: model:ir.model,name:base_custom_info.model_custom_info_category
msgid "Categorize custom info properties"
msgstr "Categorizar las propiedades de inf. personalizada"
#. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_category_id
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_category_id
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_property_search
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_value_search
msgid "Category"
msgstr "Categoría"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.actions.act_window,help:base_custom_info.custom_info_template_action #: model:ir.actions.act_window,help:base_custom_info.custom_info_template_action
msgid "Click to define a new custom info template." msgid "Click to define a new custom info template."
msgstr ""
msgstr "Pulse para definir una nueva plantilla de inf. personalizada."
#. module: base_custom_info #. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_category_create_uid
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_option_create_uid
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_create_uid #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_create_uid
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template_create_uid #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template_create_uid
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_create_uid #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_create_uid
@ -51,66 +131,115 @@ msgid "Created by"
msgstr "Creado por" msgstr "Creado por"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_category_create_date
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_option_create_date
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_create_date #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_create_date
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template_create_date #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template_create_date
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_create_date #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_create_date
msgid "Created on" msgid "Created on"
msgstr "Creado el"
msgstr "Creado en"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.ui.menu,name:base_custom_info.menu_base_custom_info #: model:ir.ui.menu,name:base_custom_info.menu_base_custom_info
msgid "Custom Info" msgid "Custom Info"
msgstr "Información personalizada"
msgstr "Inf. personalizada"
#. module: base_custom_info
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_category_tree
msgid "Custom Info Categories"
msgstr "Categorías de inf. personalizada"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.ui.view,arch_db:base_custom_info.base_custom_info_template_form
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_option_tree
msgid "Custom Info Options"
msgstr "Opciones de inf. personalizada"
#. module: base_custom_info
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_property_tree
msgid "Custom Info Properties"
msgstr "Propiedades de inf. personalizada"
#. module: base_custom_info
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_template_form
msgid "Custom Info Template" msgid "Custom Info Template"
msgstr "Plantilla de información personalizada"
msgstr "Plantilla de inf. personalizada"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.ui.view,arch_db:base_custom_info.base_custom_info_template_line_form
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_category_form
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_option_form
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_property_form
msgid "Custom Info Template Properties" msgid "Custom Info Template Properties"
msgstr "Propiedades de la plantilla de información personalizada"
msgstr "Propiedades de la plantilla de inf. personalizada"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.ui.view,arch_db:base_custom_info.base_custom_info_template_line_tree
#: model:ir.ui.view,arch_db:base_custom_info.base_custom_info_template_tree
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_template_tree
msgid "Custom Info Templates" msgid "Custom Info Templates"
msgstr "Plantillas de información personalizada"
msgstr "Plantillas de inf. personalizada"
#. module: base_custom_info
#: model:ir.module.category,name:base_custom_info.category
#: model:ir.ui.view,arch_db:base_custom_info.view_general_configuration
#: model:ir.ui.view,arch_db:base_custom_info.view_partner_form
msgid "Custom Information"
msgstr "Inf. personalizada"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_custom_info_template_id #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_custom_info_template_id
#: model:ir.model.fields,field_description:base_custom_info.field_res_partner_custom_info_template_id
msgid "Custom Information Template" msgid "Custom Information Template"
msgstr "Plantilla de información personalizada"
msgstr "Plantilla de inf. personalizada"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_custom_info_ids #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_custom_info_ids
#: model:ir.model.fields,field_description:base_custom_info.field_res_partner_custom_info_ids
msgid "Custom Properties" msgid "Custom Properties"
msgstr "Propiedades personalizadas" msgstr "Propiedades personalizadas"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.ui.view,arch_db:base_custom_info.base_custom_info_value_tree
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_value_tree
msgid "Custom Property Values" msgid "Custom Property Values"
msgstr "Valores de las propiedades personalizadas" msgstr "Valores de las propiedades personalizadas"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.model,name:base_custom_info.model_custom_info_property #: model:ir.model,name:base_custom_info.model_custom_info_property
msgid "Custom information property" msgid "Custom information property"
msgstr "Propiedad de información personalizada"
msgstr "Propiedad de inf. personalizada"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.model,name:base_custom_info.model_custom_info_template #: model:ir.model,name:base_custom_info.model_custom_info_template
msgid "Custom information template" msgid "Custom information template"
msgstr "Plantilla de información personalizada"
msgstr "Plantilla de inf. personalizada"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.model,name:base_custom_info.model_custom_info_value #: model:ir.model,name:base_custom_info.model_custom_info_value
msgid "Custom information value" msgid "Custom information value"
msgstr "Valor de información personalizada"
msgstr "Valor de inf. personalizada"
#. module: base_custom_info
#: selection:custom.info.property,field_type:0
msgid "Decimal number"
msgstr "Número decimal"
#. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_value_float
msgid "Decimal number value"
msgstr "Valor del número decimal"
#. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_default_value
msgid "Default value"
msgstr "Valor por defecto"
#. module: base_custom_info
#: code:addons/base_custom_info/models/custom_info_property.py:101
#, python-format
msgid "Default value %s cannot be converted to type %s."
msgstr "El valor por defecto %s no se puede convertir al tipo %s."
#. module: base_custom_info #. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_category_display_name
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_display_name #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_display_name
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_model_link_display_name
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_option_display_name
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_display_name #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_display_name
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template_display_name #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template_display_name
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_display_name #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_display_name
@ -118,29 +247,110 @@ msgid "Display Name"
msgstr "Nombre mostrado" msgstr "Nombre mostrado"
#. module: base_custom_info #. module: base_custom_info
#: model:res.groups,name:base_custom_info.group_partner
msgid "Display in partner form"
msgstr "Mostrar en el formulario de empresas"
#. module: base_custom_info
#: model:custom.info.property,name:base_custom_info.prop_smartypants
msgid "Does he/she believe he/she is the smartest person on earth?"
msgstr "¿Se cree la persona más lista de la Tierra?"
#. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_base_config_settings_group_custom_info_partner
msgid "Edit custom information in partners"
msgstr "Editar inf. personalizada en empresas"
#. module: base_custom_info
#: model:custom.info.property,name:base_custom_info.prop_fav_game
msgid "Favourite videogame"
msgstr "Videojuego favorito"
#. module: base_custom_info
#: model:custom.info.property,name:base_custom_info.prop_fav_genre
msgid "Favourite videogames genre"
msgstr "Género de videojuegos favorito"
#. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_field_name
msgid "Field name"
msgstr "Nombre del campo"
#. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_field_type
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_field_type
msgid "Field type"
msgstr "Tipo del campo"
#. module: base_custom_info
#: model:ir.model.fields,help:base_custom_info.field_custom_info_property_maximum
msgid "For numeric fields, it means the maximum possible value; for text fields, it means the maximum possible length. If it is smaller than the minimum, then this check is skipped"
msgstr "Para campos numéricos, significa el valor máximo permitido; para campos de texto, significa la longitud máxima permitida. Si es menor que el mínimo, entonces esta comprobación se omite."
#. module: base_custom_info
#: model:ir.model.fields,help:base_custom_info.field_custom_info_property_minimum
msgid "For numeric fields, it means the minimum possible value; for text fields, it means the minimum possible length. If it is bigger than the maximum, then this check is skipped"
msgstr "Para campos numéricos, significa el valor mínimo permitido; para campos de texto, significa la longitud mínima permitida. Si es mayor que el máximo, entonces esta comprobación se omite."
#. module: base_custom_info
#: model:custom.info.template,name:base_custom_info.tpl_gamer
msgid "Gamers"
msgstr "Jugadores"
#. module: base_custom_info
#: model:custom.info.category,name:base_custom_info.cat_gaming
msgid "Gaming"
msgstr "Juego"
#. module: base_custom_info
#: model:custom.info.option,name:base_custom_info.opt_graphical_adventure
msgid "Graphical adventure"
msgstr "Aventura gráfica"
#. module: base_custom_info
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_property_search
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_template_search
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_value_search
msgid "Group By"
msgstr "Agrupar por"
#. module: base_custom_info
#: model:custom.info.option,name:base_custom_info.opt_glasses
#: model:custom.info.property,default_value:base_custom_info.prop_weaknesses
msgid "Huge glasses"
msgstr "Gafas gigantes"
#. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_category_id
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_id #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_id
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_model_link_id
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_option_id
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_id #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_id
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template_id #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template_id
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_id #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_id
msgid "ID" msgid "ID"
msgstr "ID"
msgstr "ID (identificación)"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.ui.view,arch_db:base_custom_info.base_custom_info_template_form
msgid "Info Lines"
msgstr "Líneas de información"
#: code:addons/base_custom_info/models/custom_info_property.py:111
#, python-format
msgid "If you require a Yes/No field, you can only set Yes."
msgstr "Si requiere un campo Sí/No, sólo podrá escoger Sí."
#. module: base_custom_info
#: code:addons/base_custom_info/models/custom_info_property.py:114
#, python-format
msgid "If you require a numeric field, you cannot set it to zero."
msgstr "Si requiere un campo numérico, no podrá ponerlo a cero."
#. module: base_custom_info #. module: base_custom_info
#: model:ir.model,name:base_custom_info.model_custom_info #: model:ir.model,name:base_custom_info.model_custom_info
msgid "Inheritable abstract model to add custom info in any model" msgid "Inheritable abstract model to add custom info in any model"
msgstr ""
"Modelo abstracto que se puede heredar para añadir información personalizada "
"a cualquier modelo"
msgstr "Modelo abstracto que se puede heredar para añadir inf. personalizada a cualquier modelo"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info___last_update #: model:ir.model.fields,field_description:base_custom_info.field_custom_info___last_update
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_model_link___last_update
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_category___last_update
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_option___last_update
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property___last_update #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property___last_update
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template___last_update #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template___last_update
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value___last_update #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value___last_update
@ -148,30 +358,65 @@ msgid "Last Modified on"
msgstr "Última modificación en" msgstr "Última modificación en"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_category_write_uid
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_option_write_uid
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_write_uid #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_write_uid
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template_write_uid #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template_write_uid
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_write_uid #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_write_uid
msgid "Last Updated by" msgid "Last Updated by"
msgstr "Última actualización por"
msgstr "Última actualización de"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_category_write_date
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_option_write_date
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_write_date #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_write_date
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template_write_date #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template_write_date
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_write_date #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_write_date
msgid "Last Updated on" msgid "Last Updated on"
msgstr "Última actualización el"
msgstr "Última actualización en"
#. module: base_custom_info
#: code:addons/base_custom_info/models/custom_info_value.py:152
#, python-format
msgid "Length for %(prop)s is %(val)s, but it should be between %(min)d and %(max)d."
msgstr "La longitud de %(prop)s es %(val)s, pero debería estar entre %(min)d y %(max)d."
#. module: base_custom_info
#: model:custom.info.option,name:base_custom_info.opt_food
msgid "Loves junk food"
msgstr "Le encanta la comida basura"
#. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_base_config_settings_group_custom_info_manager
msgid "Manage custom information"
msgstr "Gestionar inf. personalizada"
#. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_maximum
msgid "Maximum"
msgstr "Máximo"
#. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_minimum
msgid "Minimum"
msgstr "Mínimo"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_model_link_model
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_model_link_model_id
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template_model
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template_model_id #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template_model_id
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_model
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_model_id
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_template_search
msgid "Model" msgid "Model"
msgstr "Modelo" msgstr "Modelo"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_model
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template_model
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_model
msgid "Model technical name"
msgstr "Nombre técnico del modelo"
#. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_category_name
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_option_name
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_name #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_name
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template_name #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template_name
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_name #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_name
@ -179,14 +424,69 @@ msgid "Name"
msgstr "Nombre" msgstr "Nombre"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.actions.act_window,name:base_custom_info.custom_info_template_line_action
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template_info_ids
#: model:ir.ui.menu,name:base_custom_info.menu_base_custom_info_template_line
#: model:custom.info.property,name:base_custom_info.prop_teacher
msgid "Name of his/her teacher"
msgstr "Nombre de su profesor"
#. module: base_custom_info
#: model:custom.info.option,name:base_custom_info.opt_videogames
msgid "Needs videogames"
msgstr "Necesita videojuegos"
#. module: base_custom_info
#: code:addons/base_custom_info/models/custom_info_value.py:123
#: code:addons/base_custom_info/models/custom_info_value.py:215
#, python-format
msgid "No"
msgstr "No"
#. module: base_custom_info
#: model:ir.actions.act_window,name:base_custom_info.custom_info_option_action
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_option_ids
#: model:ir.ui.menu,name:base_custom_info.menu_option
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_property_form
msgid "Options"
msgstr "Opciones"
#. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_owner_id
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_value_search
msgid "Owner"
msgstr "Propietario"
#. module: base_custom_info
#: model:ir.model,name:base_custom_info.model_res_partner
msgid "Partner"
msgstr "Empresa"
#. module: base_custom_info
#: model:custom.info.option,name:base_custom_info.opt_platforms
msgid "Platforms"
msgstr "Plataformas"
#. module: base_custom_info
#: model:ir.actions.act_window,name:base_custom_info.custom_info_property_action
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_category_property_ids
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_option_property_ids
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_template_property_ids
#: model:ir.ui.menu,name:base_custom_info.menu_property
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_template_form
msgid "Properties" msgid "Properties"
msgstr "Propiedades" msgstr "Propiedades"
#. module: base_custom_info
#: model:ir.model.fields,help:base_custom_info.field_custom_info_category_property_ids
msgid "Properties in this category."
msgstr "Propiedades en esta categoría."
#. module: base_custom_info
#: model:ir.model.fields,help:base_custom_info.field_custom_info_option_property_ids
msgid "Properties where this option is enabled."
msgstr "Propiedades en las que esta opción está disponible."
#. module: base_custom_info #. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_property_id #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_property_id
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_value_search
msgid "Property" msgid "Property"
msgstr "Propiedad" msgstr "Propiedad"
@ -195,13 +495,79 @@ msgstr "Propiedad"
msgid "Property Values" msgid "Property Values"
msgstr "Valor de la propiedad" msgstr "Valor de la propiedad"
#. module: base_custom_info
#: model:custom.info.option,name:base_custom_info.opt_rpg
msgid "RPG"
msgstr "RPG"
#. module: base_custom_info
#: model:ir.model.fields,help:base_custom_info.field_custom_info_value_owner_id
msgid "Record that owns this custom value."
msgstr "Registro que posee este valor personalizado."
#. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_required
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_required
msgid "Required"
msgstr "Requerido"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_res_id #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_res_id
msgid "Resource ID" msgid "Resource ID"
msgstr "ID del recurso"
msgstr "ID del Recurso"
#. module: base_custom_info
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_property_form
msgid "Select one of the existing options or create a new one clicking on 'Add an item'"
msgstr "Seleccione una de la opciones existentes o cree una nueva pulsando en 'Añadir un elemento'"
#. module: base_custom_info
#: selection:custom.info.property,field_type:0
msgid "Selection"
msgstr "Selección"
#. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_value_id
msgid "Selection value"
msgstr "Valor de selección"
#. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_category_sequence
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_category_sequence
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_sequence
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_category_sequence
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_property_sequence
msgid "Sequence"
msgstr "Secuencia"
#. module: base_custom_info
#: model:custom.info.option,name:base_custom_info.opt_shooter
msgid "Shooter"
msgstr "Shooter"
#. module: base_custom_info
#: model:custom.info.template,name:base_custom_info.tpl_smart
msgid "Smart partners"
msgstr "Gente lista"
#. module: base_custom_info
#: model:custom.info.category,name:base_custom_info.cat_statics
msgid "Statics"
msgstr "Statics"
#. module: base_custom_info
#: model:custom.info.option,name:base_custom_info.opt_strategy
msgid "Strategy"
msgstr "Estrategia"
#. module: base_custom_info
#: model:ir.model.fields,help:base_custom_info.field_custom_info_value_field_name
msgid "Technical name of the field where the value is stored."
msgstr "Nombre técnico del campo donde se guarda este valor"
#. module: base_custom_info #. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_template_id #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_template_id
#: model:ir.ui.view,arch_db:base_custom_info.custom_info_property_search
msgid "Template" msgid "Template"
msgstr "Plantilla" msgstr "Plantilla"
@ -211,20 +577,125 @@ msgstr "Plantilla"
msgid "Templates" msgid "Templates"
msgstr "Plantillas" msgstr "Plantillas"
#. module: base_custom_info
#: selection:custom.info.property,field_type:0
msgid "Text"
msgstr "Texto"
#. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_value_str
msgid "Text value"
msgstr "Valor de texto"
#. module: base_custom_info
#: model:res.groups,comment:base_custom_info.group_advanced
msgid "The user will be able to manage advanced custom information."
msgstr "El usuario tendrá acceso a una gestión avanzada de la inf. personalizada."
#. module: base_custom_info
#: model:res.groups,comment:base_custom_info.group_basic
msgid "The user will be able to manage basic custom information."
msgstr "El usuario tendrá acceso a una gestión básica de la inf. personalizada."
#. module: base_custom_info
#: model:ir.model.fields,help:base_custom_info.field_custom_info_property_field_type
#: model:ir.model.fields,help:base_custom_info.field_custom_info_value_field_type
msgid "Type of information that can be stored in the property."
msgstr "Tipo de información que se puede almacenar en esta propiedad."
#. module: base_custom_info #. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_value #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_value
msgid "Value" msgid "Value"
msgstr "Valor" msgstr "Valor"
#. module: base_custom_info
#: code:addons/base_custom_info/models/custom_info_value.py:158
#, python-format
msgid "Value for %(prop)s is %(val)s, but it should be between %(min)d and %(max)d."
msgstr "El valor de %(prop)s es %(val)s, pero debería estar entre %(min)d y %(max)d."
#. module: base_custom_info
#: code:addons/base_custom_info/models/custom_info_value.py:162
#, python-format
msgid "Value for %(prop)s is %(val)s, but it should be between %(min)f and %(max)f."
msgstr "El valor de %(prop)s es %(val)s, pero debería estar entre %(min)f y %(max)f."
#. module: base_custom_info
#: model:ir.model.fields,help:base_custom_info.field_custom_info_value_value
msgid "Value, always converted to/from the typed field."
msgstr "Valor, siempre convertido del/al campo tipado."
#. module: base_custom_info #. module: base_custom_info
#: model:ir.actions.act_window,name:base_custom_info.custom_info_value_action #: model:ir.actions.act_window,name:base_custom_info.custom_info_value_action
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_option_value_ids
#: model:ir.ui.menu,name:base_custom_info.menu_base_custom_info_value #: model:ir.ui.menu,name:base_custom_info.menu_base_custom_info_value
msgid "Values" msgid "Values"
msgstr "Valores" msgstr "Valores"
#. module: base_custom_info
#: model:ir.model.fields,help:base_custom_info.field_custom_info_option_value_ids
msgid "Values that have set this option."
msgstr "Valores que han escogido esta opción."
#. module: base_custom_info
#: model:custom.info.property,name:base_custom_info.prop_weaknesses
msgid "What weaknesses does he/she have?"
msgstr "¿Qué debilidades tiene?"
#. module: base_custom_info
#: model:ir.model.fields,help:base_custom_info.field_custom_info_property_option_ids
msgid "When the field type is 'selection', choose the available options here."
msgstr "Cuando el tipo de campo es 'selección', escoja las opciones disponibles aquí."
#. module: base_custom_info
#: selection:custom.info.property,field_type:0
msgid "Whole number"
msgstr "Número entero"
#. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_value_int
msgid "Whole number value"
msgstr "Valor del número entero"
#. module: base_custom_info
#: model:res.groups,comment:base_custom_info.group_partner
msgid "Will be able to edit custom information from partner's form."
msgstr "Podrá editar inf. personalizada en el formulario de empresa."
#. module: base_custom_info
#: model:ir.model.fields,help:base_custom_info.field_custom_info_property_default_value
msgid "Will be applied by default to all custom values of this property. This is a char field, so you have to enter some value that can be converted to the field type you choose."
msgstr "Se aplicará por defecto a todos los valores personalizados de esta propiedad. Este campo es de texto, así que tiene que introducir un valor que se pueda convertir al tipo de campo que ha escogido."
#. module: base_custom_info
#: code:addons/base_custom_info/models/custom_info_value.py:123
#, python-format
msgid "Yes"
msgstr "Sí"
#. module: base_custom_info
#: selection:custom.info.property,field_type:0
msgid "Yes/No"
msgstr "Sí/No"
#. module: base_custom_info
#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_value_bool
msgid "Yes/No value"
msgstr "Valor sí/no"
#. module: base_custom_info
#: code:addons/base_custom_info/models/custom_info_template.py:66
#, python-format
msgid "You cannot change the model because it is in use."
msgstr "No puede cambiar el modelo porque ya se está usando."
#. module: base_custom_info #. module: base_custom_info
#: model:ir.actions.act_window,help:base_custom_info.custom_info_template_action #: model:ir.actions.act_window,help:base_custom_info.custom_info_template_action
msgid ""
"You must define a custom info template for each\n"
" product properties group."
msgstr ""
msgid "You must define a custom info template for each properties group."
msgstr "Debe definir una plantilla de inf. personalizada por cada grupo de propiedades."
#. module: base_custom_info
#: model:ir.model,name:base_custom_info.model_base_config_settings
msgid "base.config.settings"
msgstr "base.config.settings"

0
base_custom_info/static/description/customizations-everywhere.jpg → base_custom_info/images/customizations-everywhere.jpg

Before

Width: 500  |  Height: 380  |  Size: 120 KiB

After

Width: 500  |  Height: 380  |  Size: 120 KiB

0
base_custom_info/static/description/templateception.jpg → base_custom_info/images/templateception.jpg

Before

Width: 318  |  Height: 240  |  Size: 57 KiB

After

Width: 318  |  Height: 240  |  Size: 57 KiB

122
base_custom_info/models/custom_info.py

@ -1,14 +1,15 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# © 2015 Antiun Ingeniería S.L. - Sergio Teruel
# © 2015 Antiun Ingeniería S.L. - Carlos Dauden
# © 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
# Copyright 2015 Sergio Teruel <sergio.teruel@tecnativa.com>
# Copyright 2015 Carlos Dauden <carlos.dauden@tecnativa.com>
# Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
# Copyright 2017 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html # License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html
from openerp import api, fields, models from openerp import api, fields, models
class CustomInfo(models.AbstractModel): class CustomInfo(models.AbstractModel):
"""Models that inherit this one will get custom information for free!
"""Models that inherit from this one will get custom information for free!
They will probably want to declare a default model in the context of the They will probably want to declare a default model in the context of the
:attr:`custom_info_template_id` field. :attr:`custom_info_template_id` field.
@ -21,54 +22,64 @@ class CustomInfo(models.AbstractModel):
custom_info_template_id = fields.Many2one( custom_info_template_id = fields.Many2one(
comodel_name='custom.info.template', comodel_name='custom.info.template',
domain=lambda self: [("model", "=", self._name)], domain=lambda self: [("model", "=", self._name)],
string='Custom Information Template')
string='Custom Information Template',
)
custom_info_ids = fields.One2many( custom_info_ids = fields.One2many(
comodel_name='custom.info.value',
inverse_name='res_id',
comodel_name='custom.info.value', inverse_name='res_id',
domain=lambda self: [("model", "=", self._name)], domain=lambda self: [("model", "=", self._name)],
context={"embed": True},
auto_join=True,
string='Custom Properties')
dirty_templates = fields.Boolean(
compute="_compute_dirty_templates",
auto_join=True, string='Custom Properties',
) )
# HACK https://github.com/odoo/odoo/pull/11042
@api.model
def create(self, vals):
res = super(CustomInfo, self).create(vals)
if not self.env.context.get("filling_templates"):
res.filtered(
"dirty_templates").action_custom_info_templates_fill()
return res
# HACK https://github.com/odoo/odoo/pull/11042
# HACK: Until https://github.com/odoo/odoo/pull/10557 is merged
# https://github.com/OCA/server-tools/pull/492#issuecomment-237594285
@api.multi @api.multi
def write(self, vals):
res = super(CustomInfo, self).write(vals)
if not self.env.context.get("filling_templates"):
self.filtered(
"dirty_templates").action_custom_info_templates_fill()
return res
def onchange(self, values, field_name, field_onchange): # pragma: no cover
x2many_field = 'custom_info_ids'
if x2many_field in field_onchange:
subfields = getattr(self, x2many_field)._fields.keys()
for subfield in subfields:
field_onchange.setdefault(
"{}.{}".format(x2many_field, subfield), u"",
)
return super(CustomInfo, self).onchange(
values, field_name, field_onchange,
)
@api.onchange('custom_info_template_id')
def _onchange_custom_info_template_id(self):
tmpls = self.all_custom_info_templates()
props_good = tmpls.mapped("property_ids")
props_enabled = self.mapped("custom_info_ids.property_id")
to_add = props_good - props_enabled
to_remove = props_enabled - props_good
values = self.custom_info_ids
values = values.filtered(lambda r: r.property_id not in to_remove)
for prop in to_add.sorted():
newvalue = self.custom_info_ids.new({
"property_id": prop.id,
"res_id": self.id,
"value": prop.default_value,
})
# HACK https://github.com/odoo/odoo/issues/13076
newvalue._inverse_value()
newvalue._compute_value()
values += newvalue
self.custom_info_ids = values
# Default values implied new templates? Then this is recursive
if self.all_custom_info_templates() != tmpls:
self._onchange_custom_info_template_id()
@api.multi @api.multi
def unlink(self): def unlink(self):
"""Remove linked custom info this way, as can't be handled
automatically.
"""
info_values = self.mapped('custom_info_ids') info_values = self.mapped('custom_info_ids')
res = super(CustomInfo, self).unlink() res = super(CustomInfo, self).unlink()
if res: if res:
info_values.unlink() info_values.unlink()
return res return res
@api.one
@api.depends("custom_info_template_id",
"custom_info_ids.value_id.template_id")
def _compute_dirty_templates(self):
"""Know if you need to reload the templates."""
expected_properties = self.all_custom_info_templates().mapped(
"property_ids")
actual_properties = self.mapped("custom_info_ids.property_id")
self.dirty_templates = expected_properties != actual_properties
@api.multi @api.multi
@api.returns("custom.info.value") @api.returns("custom.info.value")
def get_custom_info_value(self, properties): def get_custom_info_value(self, properties):
@ -84,38 +95,3 @@ class CustomInfo(models.AbstractModel):
"""Get all custom info templates involved in these owners.""" """Get all custom info templates involved in these owners."""
return (self.mapped("custom_info_template_id") | return (self.mapped("custom_info_template_id") |
self.mapped("custom_info_ids.value_id.template_id")) self.mapped("custom_info_ids.value_id.template_id"))
@api.multi
def action_custom_info_templates_fill(self):
"""Fill values with enabled custom info templates."""
recursive_owners = self
for owner in self.with_context(filling_templates=True):
values = owner.custom_info_ids
tpls = owner.all_custom_info_templates()
props_good = tpls.mapped("property_ids")
props_enabled = owner.mapped("custom_info_ids.property_id")
to_add = props_good - props_enabled
to_rm = props_enabled - props_good
# Remove remaining properties
# HACK https://github.com/odoo/odoo/pull/13480
values.filtered(lambda r: r.property_id in to_rm).unlink()
values = values.exists()
# Add new properties
for prop in to_add:
newvalue = values.new({
"property_id": prop.id,
"res_id": owner.id,
})
newvalue._onchange_property_set_default_value()
# HACK https://github.com/odoo/odoo/issues/13076
newvalue._inverse_value()
newvalue._compute_value()
values |= newvalue
owner.custom_info_ids = values
# Default values implied new templates? Then this is recursive
if owner.all_custom_info_templates() == tpls:
recursive_owners -= owner
# Changes happened under a different environment; update own
self.invalidate_cache()
if recursive_owners:
return recursive_owners.action_custom_info_templates_fill()

3
base_custom_info/models/custom_info_option.py

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# © 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
# Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
# Copyright 2017 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html # License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html
from openerp import api, fields, models from openerp import api, fields, models

14
base_custom_info/models/custom_info_property.py

@ -1,6 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# © 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
# Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
# Copyright 2017 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html # License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html
from openerp import _, api, fields, models from openerp import _, api, fields, models
from openerp.exceptions import UserError, ValidationError from openerp.exceptions import UserError, ValidationError
@ -28,13 +30,11 @@ class CustomInfoProperty(models.Model):
readonly=True, readonly=True,
) )
template_id = fields.Many2one( template_id = fields.Many2one(
comodel_name='custom.info.template',
string='Template',
required=True)
comodel_name='custom.info.template', string='Template',
required=True, ondelete="cascade",
)
model = fields.Char( model = fields.Char(
related="template_id.model",
readonly=True,
auto_join=True,
related="template_id.model", readonly=True, auto_join=True,
) )
info_value_ids = fields.One2many( info_value_ids = fields.One2many(
comodel_name="custom.info.value", comodel_name="custom.info.value",

71
base_custom_info/models/custom_info_template.py

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# © 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
# Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
# Copyright 2017 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html # License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html
from openerp import _, api, fields, models from openerp import _, api, fields, models
@ -13,57 +14,63 @@ class CustomInfoTemplate(models.Model):
_order = "model_id, name" _order = "model_id, name"
_sql_constraints = [ _sql_constraints = [
("name_model", ("name_model",
"UNIQUE (name, model)",
"UNIQUE (name, model_id)",
"Another template with that name exists for that model."), "Another template with that name exists for that model."),
] ]
name = fields.Char(required=True, translate=True) name = fields.Char(required=True, translate=True)
model = fields.Char( model = fields.Char(
index=True,
readonly=True,
required=True)
string="Model technical name", inverse="_inverse_model",
compute="_compute_model", search="_search_model"
)
model_id = fields.Many2one( model_id = fields.Many2one(
'ir.model',
'Model',
compute="_compute_model_id",
store=True,
ondelete="cascade",
comodel_name='ir.model', string='Model', ondelete="restrict",
required=True, auto_join=True,
) )
property_ids = fields.One2many( property_ids = fields.One2many(
'custom.info.property',
'template_id',
'Properties',
oldname="info_ids",
context={"embed": True},
comodel_name='custom.info.property', inverse_name='template_id',
string='Properties', oldname="info_ids",
) )
@api.multi @api.multi
@api.depends("model")
def _compute_model_id(self):
"""Get a related model from its name, for better UI."""
for s in self:
s.model_id = self.env["ir.model"].search([("model", "=", s.model)])
@api.depends("model_id")
def _compute_model(self):
for r in self:
r.model = r.model_id.model
@api.multi
def _inverse_model(self):
for r in self:
r.model_id = self.env["ir.model"].search([("model", "=", r.model)])
@api.model
def _search_model(self, operator, value):
models = self.env['ir.model'].search([('model', operator, value)])
return [('model_id', 'in', models.ids)]
@api.onchange('model')
def _onchange_model(self):
self._inverse_model()
@api.multi @api.multi
@api.constrains("model")
@api.constrains("model_id")
def _check_model(self): def _check_model(self):
"""Ensure model exists."""
for s in self:
if s.model not in self.env:
raise ValidationError(_("Model does not exist."))
# Avoid error when updating base module and a submodule extends a
# model that falls out of this one's dependency graph
"""Avoid error when updating base module and a submodule extends a
model that falls out of this one's dependency graph.
"""
for record in self:
with self.env.norecompute(): with self.env.norecompute():
oldmodels = set(s.mapped("property_ids.info_value_ids.model"))
if oldmodels and {s.model} != oldmodels:
oldmodels = record.mapped("property_ids.info_value_ids.model")
if oldmodels and record.model not in oldmodels:
raise ValidationError( raise ValidationError(
_("You cannot change the model because it is in use."))
_("You cannot change the model because it is in use.")
)
@api.multi @api.multi
def check_access_rule(self, operation): def check_access_rule(self, operation):
"""You access a template if you access its model.""" """You access a template if you access its model."""
for s in self:
model = self.env[s.model]
for record in self:
model = self.env[record.model_id.model or record.model]
model.check_access_rights(operation) model.check_access_rights(operation)
model.check_access_rule(operation) model.check_access_rule(operation)
return super(CustomInfoTemplate, self).check_access_rule(operation) return super(CustomInfoTemplate, self).check_access_rule(operation)

169
base_custom_info/models/custom_info_value.py

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# © 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
# Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
# Copyright 2017 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html # License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html
from openerp import _, api, fields, models, SUPERUSER_ID from openerp import _, api, fields, models, SUPERUSER_ID
from openerp.exceptions import ValidationError from openerp.exceptions import ValidationError
@ -19,117 +20,63 @@ class CustomInfoValue(models.Model):
] ]
model = fields.Char( model = fields.Char(
related="property_id.model",
index=True,
readonly=True,
auto_join=True,
store=True,
related="property_id.model", index=True, readonly=True,
auto_join=True, store=True,
) )
owner_id = fields.Reference( owner_id = fields.Reference(
selection="_selection_owner_id",
string="Owner",
compute="_compute_owner_id",
inverse="_inverse_owner_id",
selection="_selection_owner_id", string="Owner",
compute="_compute_owner_id", inverse="_inverse_owner_id",
help="Record that owns this custom value.", help="Record that owns this custom value.",
) )
res_id = fields.Integer( res_id = fields.Integer(
"Resource ID",
required=True,
index=True,
store=True,
string="Resource ID", required=True, index=True, store=True,
ondelete="cascade", ondelete="cascade",
) )
property_id = fields.Many2one( property_id = fields.Many2one(
comodel_name='custom.info.property',
required=True,
string='Property')
property_sequence = fields.Integer(
related="property_id.sequence",
store=True,
index=True,
comodel_name='custom.info.property', required=True, string='Property',
readonly=True, readonly=True,
) )
property_sequence = fields.Integer(
related="property_id.sequence", store=True, index=True, readonly=True,
)
category_sequence = fields.Integer( category_sequence = fields.Integer(
related="property_id.category_id.sequence",
store=True,
readonly=True,
related="property_id.category_id.sequence", store=True, readonly=True,
) )
category_id = fields.Many2one( category_id = fields.Many2one(
related="property_id.category_id",
store=True,
readonly=True,
related="property_id.category_id", store=True, readonly=True,
) )
name = fields.Char(related='property_id.name', readonly=True) name = fields.Char(related='property_id.name', readonly=True)
field_type = fields.Selection(related="property_id.field_type")
field_type = fields.Selection(
related="property_id.field_type", readonly=True,
)
field_name = fields.Char( field_name = fields.Char(
compute="_compute_field_name", compute="_compute_field_name",
help="Technical name of the field where the value is stored.", help="Technical name of the field where the value is stored.",
) )
required = fields.Boolean(related="property_id.required")
required = fields.Boolean(related="property_id.required", readonly=True)
value = fields.Char( value = fields.Char(
compute="_compute_value",
inverse="_inverse_value",
compute="_compute_value", inverse="_inverse_value",
search="_search_value", search="_search_value",
help="Value, always converted to/from the typed field.", help="Value, always converted to/from the typed field.",
) )
value_str = fields.Char(
string="Text value",
translate=True,
index=True,
)
value_int = fields.Integer(
string="Whole number value",
index=True,
)
value_float = fields.Float(
string="Decimal number value",
index=True,
)
value_bool = fields.Boolean(
string="Yes/No value",
index=True,
)
value_str = fields.Char(string="Text value", translate=True, index=True)
value_int = fields.Integer(string="Whole number value", index=True)
value_float = fields.Float(string="Decimal number value", index=True)
value_bool = fields.Boolean(string="Yes/No value", index=True)
value_id = fields.Many2one( value_id = fields.Many2one(
comodel_name="custom.info.option",
string="Selection value",
ondelete="cascade",
domain="[('property_ids', 'in', [property_id])]",
comodel_name="custom.info.option", string="Selection value",
ondelete="cascade", domain="[('property_ids', 'in', [property_id])]",
) )
@api.multi @api.multi
def check_access_rule(self, operation): def check_access_rule(self, operation):
"""You access a value if you access its property and owner record."""
if self.env.uid == SUPERUSER_ID:
return
for s in self:
s.property_id.check_access_rule(operation)
s.owner_id.check_access_rights(operation)
s.owner_id.check_access_rule(operation)
"""You access a value if you access its owner record."""
if self.env.uid != SUPERUSER_ID:
for record in self.filtered('owner_id'):
record.owner_id.check_access_rights(operation)
record.owner_id.check_access_rule(operation)
return super(CustomInfoValue, self).check_access_rule(operation) return super(CustomInfoValue, self).check_access_rule(operation)
@api.model
def create(self, vals):
"""Skip constrains in 1st lap. Update owner templates."""
# HACK https://github.com/odoo/odoo/pull/13439
if "value" in vals:
self.env.context.skip_required = True
result = super(CustomInfoValue, self).create(vals)
# HACK https://github.com/odoo/odoo/pull/11042
if not self.env.context.get("filling_templates"):
result.owner_id.exists().filtered("dirty_templates") \
.action_custom_info_templates_fill()
return result
# HACK https://github.com/odoo/odoo/pull/11042
@api.multi
def write(self, vals):
"""Update owner templates."""
result = super(CustomInfoValue, self).write(vals)
if not self.env.context.get("filling_templates"):
self.mapped("owner_id").exists().filtered("dirty_templates") \
.action_custom_info_templates_fill()
return result
@api.model @api.model
def _selection_owner_id(self): def _selection_owner_id(self):
"""You can choose among models linked to a template.""" """You can choose among models linked to a template."""
@ -154,15 +101,15 @@ class CustomInfoValue(models.Model):
@api.depends("res_id", "model") @api.depends("res_id", "model")
def _compute_owner_id(self): def _compute_owner_id(self):
"""Get the id from the linked record.""" """Get the id from the linked record."""
for s in self:
s.owner_id = "{},{}".format(s.model, s.res_id)
for record in self:
record.owner_id = "{},{}".format(record.model, record.res_id)
@api.multi @api.multi
def _inverse_owner_id(self): def _inverse_owner_id(self):
"""Store the owner according to the model and ID.""" """Store the owner according to the model and ID."""
for s in self:
s.model = s.owner_id._name
s.res_id = s.owner_id.id
for record in self.filtered('owner_id'):
record.model = record.owner_id._name
record.res_id = record.owner_id.id
@api.multi @api.multi
@api.depends("property_id.field_type", "field_name", "value_str", @api.depends("property_id.field_type", "field_name", "value_str",
@ -171,7 +118,7 @@ class CustomInfoValue(models.Model):
"""Get the value as a string, from the original field.""" """Get the value as a string, from the original field."""
for s in self: for s in self:
if s.field_type == "id": if s.field_type == "id":
s.value = ", ".join(s.value_id.mapped("display_name"))
s.value = s.value_id.display_name
elif s.field_type == "bool": elif s.field_type == "bool":
s.value = _("Yes") if s.value_bool else _("No") s.value = _("Yes") if s.value_bool else _("No")
else: else:
@ -180,33 +127,19 @@ class CustomInfoValue(models.Model):
@api.multi @api.multi
def _inverse_value(self): def _inverse_value(self):
"""Write the value correctly converted in the typed field.""" """Write the value correctly converted in the typed field."""
for s in self:
s[s.field_name] = self._transform_value(
s.value, s.field_type, s.property_id)
@api.one
@api.constrains("required", "field_name", "value_str", "value_int",
"value_float", "value_bool", "value_id")
def _check_required(self):
"""Ensure required fields are filled"""
# HACK https://github.com/odoo/odoo/pull/13439
try:
del self.env.context.skip_required
except AttributeError:
if (not self.env.context.get("filling_templates") and
self.required and not self[self.field_name]):
raise ValidationError(
_("Property %s is required.") %
self.property_id.display_name)
for record in self:
if (record.field_type == "id" and
record.value == record.value_id.display_name):
# Avoid another search that can return a different value
continue
record[record.field_name] = self._transform_value(
record.value, record.field_type, record.property_id,
)
@api.one @api.one
@api.constrains("property_id", "field_type", "field_name",
"value_str", "value_int", "value_float")
@api.constrains("property_id", "value_str", "value_int", "value_float")
def _check_min_max_limits(self): def _check_min_max_limits(self):
"""Ensure value falls inside the property's stablished limits.""" """Ensure value falls inside the property's stablished limits."""
# Skip constraint while filling the partner template
if self.env.context.get("filling_templates"):
return
minimum, maximum = self.property_id.minimum, self.property_id.maximum minimum, maximum = self.property_id.minimum, self.property_id.maximum
if minimum <= maximum: if minimum <= maximum:
value = self[self.field_name] value = self[self.field_name]
@ -246,6 +179,12 @@ class CustomInfoValue(models.Model):
if not record.value and record.property_id.default_value: if not record.value and record.property_id.default_value:
record.value = record.property_id.default_value record.value = record.property_id.default_value
@api.onchange('value')
def _onchange_value(self):
"""Inverse function is not launched after writing, so we need to
trigger it right now."""
self._inverse_value()
@api.model @api.model
def _transform_value(self, value, format_, properties=None): def _transform_value(self, value, format_, properties=None):
"""Transforms a text value to the expected format. """Transforms a text value to the expected format.
@ -266,10 +205,10 @@ class CustomInfoValue(models.Model):
if not value: if not value:
value = False value = False
elif format_ == "id" and properties: elif format_ == "id" and properties:
value = self.env["custom.info.option"].search(
[("property_ids", "in", properties.ids),
("name", "=ilike", value)])
value.ensure_one()
value = self.env["custom.info.option"].search([
("property_ids", "in", properties.ids),
("name", "ilike", u"%{}%".format(value)),
], limit=1)
elif format_ == "bool": elif format_ == "bool":
value = value.strip().lower() not in { value = value.strip().lower() not in {
"0", "false", "", "no", "off", _("No").lower()} "0", "false", "", "no", "off", _("No").lower()}

9
base_custom_info/models/res_partner.py

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com> # Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
# Copyright 2017 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html # License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html
from openerp import fields, models from openerp import fields, models
@ -14,9 +15,5 @@ class ResPartner(models.Model):
_name = "res.partner" _name = "res.partner"
_inherit = [_name, "custom.info"] _inherit = [_name, "custom.info"]
custom_info_template_id = fields.Many2one(
context={"default_model": _name},
)
custom_info_ids = fields.One2many(
context={"default_model": _name},
)
custom_info_template_id = fields.Many2one(context={"default_model": _name})
custom_info_ids = fields.One2many(context={"default_model": _name})

BIN
base_custom_info/static/description/icon.png

Before

Width: 200  |  Height: 200  |  Size: 5.2 KiB

After

Width: 128  |  Height: 128  |  Size: 4.0 KiB

60
base_custom_info/static/description/icon.svg

@ -1,4 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg <svg
xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#" xmlns:cc="http://creativecommons.org/ns#"
@ -7,60 +9,72 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="200"
height="200"
viewBox="0 0 200 200"
id="svg2" id="svg2"
version="1.1"
inkscape:version="0.91 r13725" inkscape:version="0.91 r13725"
width="60"
height="60"
viewBox="0 0 60 60"
sodipodi:docname="icon.svg" sodipodi:docname="icon.svg"
inkscape:export-filename="icon.png" inkscape:export-filename="icon.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
inkscape:export-xdpi="192"
inkscape:export-ydpi="192">
<metadata <metadata
id="metadata10">
id="metadata8">
<rdf:RDF> <rdf:RDF>
<cc:Work <cc:Work
rdf:about=""> rdf:about="">
<dc:format>image/svg+xml</dc:format> <dc:format>image/svg+xml</dc:format>
<dc:type <dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title />
</cc:Work> </cc:Work>
</rdf:RDF> </rdf:RDF>
</metadata> </metadata>
<defs <defs
id="defs8" />
id="defs6" />
<sodipodi:namedview <sodipodi:namedview
pagecolor="#9c0c65"
pagecolor="#ffffff"
bordercolor="#666666" bordercolor="#666666"
borderopacity="1" borderopacity="1"
objecttolerance="10" objecttolerance="10"
gridtolerance="10" gridtolerance="10"
guidetolerance="10" guidetolerance="10"
inkscape:pageopacity="1"
inkscape:pageopacity="0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="704"
id="namedview6"
inkscape:window-width="1855"
inkscape:window-height="1176"
id="namedview4"
showgrid="false" showgrid="false"
inkscape:snap-page="true"
inkscape:zoom="1.2291667"
inkscape:cx="88.427025"
inkscape:cy="64.892414"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:zoom="11.125147"
inkscape:cx="30.182701"
inkscape:cy="33.453159"
inkscape:window-x="65"
inkscape:window-y="24"
inkscape:window-maximized="1" inkscape:window-maximized="1"
inkscape:current-layer="svg2" /> inkscape:current-layer="svg2" />
<rect
style="opacity:1;fill:#9c0c65;fill-opacity:1"
id="rect4147"
width="60"
height="60"
x="0"
y="0" />
<path
style="opacity:1;fill:#000000;fill-opacity:0.39215687"
d="M 46.318359 7.140625 L 26.125 27.333984 C 22.675251 26.053376 17.543435 26.175054 10.058594 28.617188 L 0 38.675781 L 0 60 L 19.734375 60 L 30.587891 49.146484 C 31.777187 45.669439 32.902469 40.559366 32.345703 36.074219 L 53.798828 14.621094 L 46.318359 7.140625 z "
id="rect4171" />
<g <g
id="g4169" id="g4169"
transform="translate(-2.3856908,-0.417955)">
transform="matrix(0.3061173,0,0,0.3061173,-1.0360053,-1.0457906)"
style="fill:#ffffff;stroke:none">
<path <path
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
id="path4" id="path4"
d="m 63.145923,117.98015 9.436965,0 0,-9.43697 -9.436965,0 m 4.718483,61.34028 c -20.808512,0 -37.747871,-16.93935 -37.747871,-37.74786 0,-20.80852 16.939359,-37.747875 37.747871,-37.747875 20.808514,0 37.747864,16.939355 37.747864,37.747875 0,20.80851 -16.93935,37.74786 -37.747864,37.74786 m 0,-84.932702 A 47.18484,47.18484 0 0 0 20.679568,132.1356 47.18484,47.18484 0 0 0 67.864406,179.32044 47.18484,47.18484 0 0 0 115.04924,132.1356 47.18484,47.18484 0 0 0 67.864406,84.950758 m -4.718483,70.777262 9.436965,0 0,-28.31091 -9.436965,0 0,28.31091 z" />
d="m 63.145923,117.98015 9.436965,0 0,-9.43697 -9.436965,0 m 4.718483,61.34028 c -20.808512,0 -37.747871,-16.93935 -37.747871,-37.74786 0,-20.80852 16.939359,-37.747875 37.747871,-37.747875 20.808514,0 37.747864,16.939355 37.747864,37.747875 0,20.80851 -16.93935,37.74786 -37.747864,37.74786 m 0,-84.932702 A 47.18484,47.18484 0 0 0 20.679568,132.1356 47.18484,47.18484 0 0 0 67.864406,179.32044 47.18484,47.18484 0 0 0 115.04924,132.1356 47.18484,47.18484 0 0 0 67.864406,84.950758 m -4.718483,70.777262 9.436965,0 0,-28.31091 -9.436965,0 0,28.31091 z"
style="fill:#ffffff;stroke:none" />
<path <path
style="stroke:#9c0c65;stroke-width:4;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
style="fill:#ffffff;stroke:none;stroke-width:4;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4-3" id="path4-3"
d="m 161.99013,23.51547 c -2.44155,0 -4.8831,0.830128 -6.88517,2.8322 -14.0145,14.014501 -41.94585,41.945843 -41.94585,41.945843 l 7.32466,7.324652 -17.09086,17.090856 -9.766208,0 -9.766196,19.532409 9.766196,9.7662 19.532408,-9.7662 0,-9.76621 17.09086,-17.090851 7.32465,7.324652 c 0,0 27.93134,-27.931342 41.94585,-41.945843 3.02752,-4.443623 3.80881,-9.961528 0,-13.770347 L 168.8753,26.34767 c -2.00206,-2.002072 -4.44362,-2.8322 -6.88517,-2.8322 m 0,10.596331 9.7662,9.766203 -34.18171,34.181712 -9.7662,-9.766203 34.18171,-34.181712 z" d="m 161.99013,23.51547 c -2.44155,0 -4.8831,0.830128 -6.88517,2.8322 -14.0145,14.014501 -41.94585,41.945843 -41.94585,41.945843 l 7.32466,7.324652 -17.09086,17.090856 -9.766208,0 -9.766196,19.532409 9.766196,9.7662 19.532408,-9.7662 0,-9.76621 17.09086,-17.090851 7.32465,7.324652 c 0,0 27.93134,-27.931342 41.94585,-41.945843 3.02752,-4.443623 3.80881,-9.961528 0,-13.770347 L 168.8753,26.34767 c -2.00206,-2.002072 -4.44362,-2.8322 -6.88517,-2.8322 m 0,10.596331 9.7662,9.766203 -34.18171,34.181712 -9.7662,-9.766203 34.18171,-34.181712 z"
inkscape:connector-curvature="0" /> inkscape:connector-curvature="0" />

25
base_custom_info/tests/test_partner.py

@ -162,28 +162,3 @@ class PartnerCase(TransactionCase):
self.env.ref("base_custom_info.prop_avg_note")) self.env.ref("base_custom_info.prop_avg_note"))
with self.assertRaises(ValidationError): with self.assertRaises(ValidationError):
val.value = "11" val.value = "11"
def test_dirty_templates_setting_tpl(self):
"""If you set a template, it gets dirty."""
with self.env.do_in_onchange():
self.assertFalse(self.agrolait.dirty_templates)
self.agrolait.custom_info_template_id = self.tpl
self.assertTrue(self.agrolait.dirty_templates)
def test_dirty_templates_removing_tpl(self):
"""If you remove a template, it gets dirty."""
self.agrolait.custom_info_template_id = self.tpl
with self.env.do_in_onchange():
self.assertFalse(self.agrolait.dirty_templates)
self.agrolait.custom_info_template_id = False
self.assertTrue(self.agrolait.dirty_templates)
def test_dirty_templates_choosing_option(self):
"""If you choose an option with an extra template, it gets dirty."""
self.agrolait.custom_info_template_id = self.tpl
with self.env.do_in_onchange():
self.assertFalse(self.agrolait.dirty_templates)
val = self.agrolait.get_custom_info_value(
self.env.ref("base_custom_info.prop_weaknesses"))
val.value_id = self.env.ref("base_custom_info.opt_videogames")
self.assertTrue(self.agrolait.dirty_templates)

83
base_custom_info/views/custom_info_category_view.xml

@ -3,51 +3,48 @@
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). --> License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -->
<odoo> <odoo>
<record id="custom_info_category_tree" model="ir.ui.view">
<field name="name">Custom Info Category Tree</field>
<field name="model">custom.info.category</field>
<field name="arch" type="xml">
<tree string="Custom Info Categories">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="property_ids"/>
</tree>
</field>
</record>
<record id="custom_info_category_tree" model="ir.ui.view">
<field name="model">custom.info.category</field>
<field name="arch" type="xml">
<tree string="Custom Info Categories">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="property_ids"/>
</tree>
</field>
</record>
<record id="custom_info_category_form" model="ir.ui.view">
<field name="name">Custom Info Category Form</field>
<field name="model">custom.info.category</field>
<field name="arch" type="xml">
<form string="Custom Info Template Properties">
<sheet>
<group>
<field name="name"/>
<field name="sequence"/>
<field name="property_ids"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="custom_info_category_form" model="ir.ui.view">
<field name="model">custom.info.category</field>
<field name="arch" type="xml">
<form string="Custom Info Template Properties">
<sheet>
<group>
<field name="name"/>
<field name="sequence"/>
<field name="property_ids"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="custom_info_category_search" model="ir.ui.view">
<field name="name">Custom Info Category Search</field>
<field name="model">custom.info.category</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="property_ids"/>
</search>
</field>
</record>
<record id="custom_info_category_search" model="ir.ui.view">
<field name="model">custom.info.category</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="property_ids"/>
</search>
</field>
</record>
<record id="custom_info_category_action" model="ir.actions.act_window">
<field name="name">Categories</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">custom.info.category</field>
<field name="view_mode">tree,form</field>
<field name="view_type">form</field>
</record>
<record id="custom_info_category_action" model="ir.actions.act_window">
<field name="name">Categories</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">custom.info.category</field>
<field name="view_mode">tree,form</field>
<field name="view_type">form</field>
</record>
</odoo> </odoo>

113
base_custom_info/views/custom_info_option_view.xml

@ -1,54 +1,79 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com> <!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
Copyright 2017 Pedro M. Baeza <pedro.baeza@tecnativa.com>
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). --> License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -->
<odoo> <odoo>
<record id="custom_info_option_tree" model="ir.ui.view">
<field name="name">Custom Info Option Tree</field>
<field name="model">custom.info.option</field>
<field name="arch" type="xml">
<tree string="Custom Info Options">
<field name="name"/>
<field name="template_id"/>
<field name="property_ids"/>
</tree>
</field>
</record>
<record id="custom_info_option_tree" model="ir.ui.view">
<field name="model">custom.info.option</field>
<field name="priority" eval="999"/>
<field name="arch" type="xml">
<tree string="Custom Info Options" editable="bottom">
<field name="name"/>
<!-- Hidden for now from backend UI -->
<field name="template_id" invisible="1"/>
</tree>
</field>
</record>
<record id="custom_info_option_form" model="ir.ui.view">
<field name="name">Custom Info Option Form</field>
<field name="model">custom.info.option</field>
<field name="arch" type="xml">
<form string="Custom Info Template Properties">
<sheet>
<group>
<field name="name"/>
<field name="template_id"/>
<field name="property_ids"/>
<field name="value_ids"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="custom_info_option_tree_full" model="ir.ui.view">
<field name="model">custom.info.option</field>
<field name="inherit_id" ref="custom_info_option_tree"/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<tree position="attributes">
<attribute name="editable"/>
</tree>
<field name="name" position="before">
<field name="property_ids" widget="many2many_tags"/>
</field>
</field>
</record>
<record id="custom_info_option_search" model="ir.ui.view">
<field name="name">Custom Info Option Search</field>
<field name="model">custom.info.option</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="property_ids"/>
</search>
</field>
</record>
<record id="custom_info_option_form" model="ir.ui.view">
<field name="model">custom.info.option</field>
<field name="priority" eval="999"/>
<field name="arch" type="xml">
<form string="Custom Info Template Properties">
<sheet>
<group>
<field name="name"/>
<!-- Hidden for now from backend UI -->
<field name="template_id" invisible="1"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="custom_info_option_action" model="ir.actions.act_window">
<field name="name">Options</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">custom.info.option</field>
<field name="view_mode">tree,form</field>
<field name="view_type">form</field>
</record>
<record id="custom_info_option_form_full" model="ir.ui.view">
<field name="model">custom.info.option</field>
<field name="inherit_id" ref="custom_info_option_form"/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<field name="template_id" position="after">
<field name="property_ids"/>
<field name="value_ids"/>
</field>
</field>
</record>
<record id="custom_info_option_search" model="ir.ui.view">
<field name="model">custom.info.option</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="property_ids"/>
</search>
</field>
</record>
<record id="custom_info_option_action" model="ir.actions.act_window">
<field name="name">Options</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">custom.info.option</field>
<field name="view_mode">tree,form</field>
<field name="view_type">form</field>
</record>
</odoo> </odoo>

169
base_custom_info/views/custom_info_property_view.xml

@ -1,84 +1,105 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com> <!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
Copyright 2017 Pedro M. Baeza <pedro.baeza@tecnativa.com>
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). --> License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -->
<odoo> <odoo>
<record id="custom_info_property_tree" model="ir.ui.view">
<field name="name">Custom Info Property Tree</field>
<field name="model">custom.info.property</field>
<field name="arch" type="xml">
<tree string="Custom Info Properties">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="template_id" invisible="context.get('embed')"/>
<field name="field_type"/>
<field name="category_id"/>
<field name="required"/>
<field name="default_value"/>
</tree>
</field>
</record>
<record id="custom_info_property_tree" model="ir.ui.view">
<field name="model">custom.info.property</field>
<field name="priority" eval="999"/>
<field name="arch" type="xml">
<tree string="Custom Info Properties">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="field_type"/>
<field name="category_id"/>
<field name="required"/>
<field name="default_value"/>
</tree>
</field>
</record>
<record id="custom_info_property_form" model="ir.ui.view">
<field name="name">Custom Info Property Form</field>
<field name="model">custom.info.property</field>
<field name="arch" type="xml">
<form string="Custom Info Template Properties">
<sheet>
<group>
<field name="name"/>
<field name="template_id"
invisible="context.get('embed')"/>
<field name="field_type"/>
<field name="category_id"/>
<field name="required"/>
<field name="default_value"/>
<field name="minimum"
attrs="{'invisible': [('field_type', 'not in', ['str', 'int', 'float'])]}"/>
<field name="maximum"
attrs="{'invisible': [('field_type', 'not in', ['str', 'int', 'float'])]}"/>
</group>
<group>
<field name="info_value_ids"
invisible="context.get('embed')"/>
<field
name="option_ids"
attrs="{'invisible': [('field_type', '!=', 'id')]}"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="custom_info_property_tree_full" model="ir.ui.view">
<field name="model">custom.info.property</field>
<field name="inherit_id" ref="custom_info_property_tree"/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<field name="name" position="after">
<field name="template_id"/>
</field>
</field>
</record>
<record id="custom_info_property_form" model="ir.ui.view">
<field name="model">custom.info.property</field>
<field name="priority" eval="999"/>
<field name="arch" type="xml">
<form string="Custom Info Template Properties">
<sheet>
<group>
<field name="name"/>
<field name="field_type"/>
<field name="category_id"/>
<field name="required"/>
<field name="default_value"/>
<field name="minimum"
attrs="{'invisible': [('field_type', 'not in', ['str', 'int', 'float'])]}"
/>
<field name="maximum"
attrs="{'invisible': [('field_type', 'not in', ['str', 'int', 'float'])]}"
/>
</group>
<group string="Options" col="1" attrs="{'invisible': [('field_type', '!=', 'id')]}">
<label string="Select one of the existing options or create a new one clicking on 'Add an item'"/>
<field name="option_ids"
nolabel="1"
context="{'form_view_ref': 'base_custom_info.custom_info_option_form', 'tree_view_ref': 'base_custom_info.custom_info_option_tree'}"
/>
</group>
</sheet>
</form>
</field>
</record>
<record id="custom_info_property_search" model="ir.ui.view">
<field name="name">Custom Info Property Search</field>
<field name="model">custom.info.property</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="template_id"/>
<field name="field_type"/>
<field name="category_id"/>
<field name="required"/>
<field name="default_value"/>
<group expand="0" string="Group By">
<filter
string="Template"
context="{'group_by': 'template_id'}"/>
<filter
string="Category"
context="{'group_by': 'category_id'}"/>
</group>
</search>
</field>
</record>
<record id="custom_info_property_form_full" model="ir.ui.view">
<field name="model">custom.info.property</field>
<field name="inherit_id" ref="custom_info_property_form"/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<field name="name" position="after">
<field name="template_id"/>
</field>
</field>
</record>
<record id="custom_info_property_search" model="ir.ui.view">
<field name="model">custom.info.property</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="template_id"/>
<field name="field_type"/>
<field name="category_id"/>
<field name="required"/>
<field name="default_value"/>
<group expand="0" string="Group By">
<filter
string="Template"
context="{'group_by': 'template_id'}"/>
<filter
string="Category"
context="{'group_by': 'category_id'}"/>
</group>
</search>
</field>
</record>
<record id="custom_info_property_action" model="ir.actions.act_window">
<field name="name">Properties</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">custom.info.property</field>
<field name="view_mode">tree,form</field>
<field name="view_type">form</field>
</record>
<record id="custom_info_property_action" model="ir.actions.act_window">
<field name="name">Properties</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">custom.info.property</field>
<field name="view_mode">tree,form</field>
<field name="view_type">form</field>
</record>
</odoo> </odoo>

124
base_custom_info/views/custom_info_template_view.xml

@ -1,72 +1,72 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com> <!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
Copyright 2017 Pedro M. Baeza <pedro.baeza@tecnativa.com>
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). --> License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -->
<odoo> <odoo>
<record id="base_custom_info_template_tree" model="ir.ui.view">
<field name="name">Custom Info Template Tree</field>
<field name="model">custom.info.template</field>
<field name="arch" type="xml">
<tree string="Custom Info Templates">
<field name="name"/>
<field name="model"/>
<field name="model_id"/>
<field name="property_ids"/>
</tree>
</field>
</record>
<record id="custom_info_template_tree" model="ir.ui.view">
<field name="model">custom.info.template</field>
<field name="arch" type="xml">
<tree string="Custom Info Templates">
<field name="name"/>
<field name="model"/>
<field name="model_id"/>
<field name="property_ids"/>
</tree>
</field>
</record>
<record id="base_custom_info_template_form" model="ir.ui.view">
<field name="name">Custom Info Template Form</field>
<field name="model">custom.info.template</field>
<field name="arch" type="xml">
<form string="Custom Info Template">
<sheet>
<group>
<field name="name"/>
<field name="model"/>
<field name="model_id"/>
</group>
<group>
<field name="property_ids"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="custom_info_template_form" model="ir.ui.view">
<field name="model">custom.info.template</field>
<field name="arch" type="xml">
<form string="Custom Info Template">
<sheet>
<group>
<field name="name"/>
<field name="model"/>
<field name="model_id"/>
</group>
<group string="Properties">
<field name="property_ids"
nolabel="1"
context="{'form_view_ref': 'base_custom_info.custom_info_property_form', 'tree_view_ref': 'base_custom_info.custom_info_property_tree'}"
/>
</group>
</sheet>
</form>
</field>
</record>
<record id="base_custom_info_template_search" model="ir.ui.view">
<field name="name">Custom Info Template Search</field>
<field name="model">custom.info.template</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="model_id"/>
<field name="property_ids"/>
<group expand="0" string="Group By">
<filter
string="Model"
context="{'group_by': 'model_id'}"/>
</group>
</search>
</field>
</record>
<record id="custom_info_template_search" model="ir.ui.view">
<field name="model">custom.info.template</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="model_id"/>
<field name="property_ids"/>
<group expand="0" string="Group By">
<filter string="Model" context="{'group_by': 'model_id'}"/>
</group>
</search>
</field>
</record>
<record id="custom_info_template_action" model="ir.actions.act_window">
<field name="name">Templates</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">custom.info.template</field>
<field name="view_mode">tree,form</field>
<field name="view_type">form</field>
<field name="view_id" eval="False"/> <!-- Force empty -->
<field name="domain" eval="False"/> <!-- Force empty -->
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to define a new custom info template.
</p><p>
You must define a custom info template for each properties group.
</p>
</field>
</record>
<record id="custom_info_template_action" model="ir.actions.act_window">
<field name="name">Templates</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">custom.info.template</field>
<field name="view_mode">tree,form</field>
<field name="view_type">form</field>
<field name="view_id" eval="False"/> <!-- Force empty -->
<field name="domain" eval="False"/> <!-- Force empty -->
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to define a new custom info template.
</p>
<p>
You must define a custom info template for each properties group.
</p>
</field>
</record>
</odoo> </odoo>

185
base_custom_info/views/custom_info_value_view.xml

@ -1,93 +1,112 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com> <!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
Copyright 2017 Pedro M. Baeza <pedro.baeza@tecnativa.com>
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). --> License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -->
<odoo> <odoo>
<record id="base_custom_info_value_tree" model="ir.ui.view">
<field name="name">Custom Info Value Tree</field>
<field name="model">custom.info.value</field>
<field name="arch" type="xml">
<tree string="Custom Property Values" create="0" delete="0">
<field name="owner_id" invisible="context.get('embed')"/>
<field name="category_id"/>
<field name="property_id"/>
<field name="value"/>
</tree>
</field>
</record>
<record id="custom_info_value_tree" model="ir.ui.view">
<field name="model">custom.info.value</field>
<field name="arch" type="xml">
<tree string="Custom Property Values" create="0" delete="0">
<field name="owner_id" invisible="context.get('embed')"/>
<field name="property_id"/>
<field name="category_id"/>
<field name="required" invisible="1"/>
<field name="value"
attrs="{'required': [('required', '=', True)]}"
/>
</tree>
</field>
</record>
<record id="base_custom_info_value_form" model="ir.ui.view">
<field name="name">Custom Info Value Form</field>
<field name="model">custom.info.value</field>
<field name="arch" type="xml">
<form>
<sheet>
<group name="metadata">
<field name="owner_id" invisible="context.get('embed')"/>
<field name="category_id"/>
<field name="property_id" readonly="context.get('embed')"/>
<field name="field_type" readonly="True"/>
<field name="required" readonly="True"/>
</group>
<group name="value">
<field
name="value_str"
attrs="{'invisible': [('field_type', '!=', 'str')], 'required': [('required', '=', True), ('field_type', '=', 'str')]}"/>
<field
name="value_int"
attrs="{'invisible': [('field_type', '!=', 'int')], 'required': [('required', '=', True), ('field_type', '=', 'int')]}"/>
<field
name="value_float"
attrs="{'invisible': [('field_type', '!=', 'float')], 'required': [('required', '=', True), ('field_type', '=', 'float')]}"/>
<field
name="value_bool"
attrs="{'invisible': [('field_type', '!=', 'bool')], 'required': [('required', '=', True), ('field_type', '=', 'bool')]}"/>
<field
name="value_id"
widget="selection"
attrs="{'invisible': [('field_type', '!=', 'id')], 'required': [('required', '=', True), ('field_type', '=', 'id')]}"/>
</group>
<div invisible="context.get('embed')"
class="alert alert-warning">
<strong>Warning!</strong>
You might see no changes in parent form until you save it.
</div>
</sheet>
</form>
</field>
</record>
<record id="custom_info_value_tree_editable" model="ir.ui.view">
<field name="model">custom.info.value</field>
<field name="priority" eval="999"/>
<field name="mode">primary</field>
<field name="inherit_id" ref="custom_info_value_tree"/>
<field name="arch" type="xml">
<tree position="attributes">
<attribute name="editable">bottom</attribute>
</tree>
<field name="value" position="after">
<field name="field_type" invisible="1"/>
<field name="value_id"
widget="selection"
attrs="{'invisible': [('field_type', '!=', 'id')], 'required': [('required', '=', True), ('field_type', '=', 'id')]}"
/>
</field>
</field>
</record>
<record id="base_custom_info_value_search" model="ir.ui.view">
<field name="name">Custom Info Value Search</field>
<field name="model">custom.info.value</field>
<field name="arch" type="xml">
<search>
<field name="model"/>
<field name="res_id"/>
<field name="category_id"/>
<field name="property_id"/>
<field name="value"/>
<group expand="0" string="Group By">
<filter
string="Owner"
context="{'group_by': ['model' , 'res_id']}"/>
<filter
string="Category"
context="{'group_by': 'category_id'}"/>
<filter
string="Property"
context="{'group_by': 'property_id'}"/>
</group>
</search>
</field>
</record>
<record id="custom_info_value_form" model="ir.ui.view">
<field name="model">custom.info.value</field>
<field name="arch" type="xml">
<form>
<sheet>
<group name="metadata">
<field name="owner_id" invisible="context.get('embed')"/>
<field name="property_id" readonly="context.get('embed')"/>
<field name="category_id"/>
<field name="field_type" readonly="True"/>
<field name="required" readonly="True"/>
</group>
<group name="value">
<field name="value_str"
attrs="{'invisible': [('field_type', '!=', 'str')], 'required': [('required', '=', True), ('field_type', '=', 'str')]}"
/>
<field name="value_int"
attrs="{'invisible': [('field_type', '!=', 'int')], 'required': [('required', '=', True), ('field_type', '=', 'int')]}"
/>
<field name="value_float"
attrs="{'invisible': [('field_type', '!=', 'float')], 'required': [('required', '=', True), ('field_type', '=', 'float')]}"
/>
<field name="value_bool"
attrs="{'invisible': [('field_type', '!=', 'bool')], 'required': [('required', '=', True), ('field_type', '=', 'bool')]}"
/>
<field name="value_id"
widget="selection"
attrs="{'invisible': [('field_type', '!=', 'id')], 'required': [('required', '=', True), ('field_type', '=', 'id')]}"
/>
</group>
<div class="alert alert-warning" invisible="not context.get('embed')">
<strong>Warning!</strong>
You might see no changes in parent form until you save it.
</div>
</sheet>
</form>
</field>
</record>
<record id="custom_info_value_search" model="ir.ui.view">
<field name="model">custom.info.value</field>
<field name="arch" type="xml">
<search>
<field name="model"/>
<field name="res_id"/>
<field name="category_id"/>
<field name="property_id"/>
<field name="value"/>
<group expand="0" string="Group By">
<filter
string="Owner"
context="{'group_by': ['model' , 'res_id']}"/>
<filter
string="Category"
context="{'group_by': 'category_id'}"/>
<filter
string="Property"
context="{'group_by': 'property_id'}"/>
</group>
</search>
</field>
</record>
<record id="custom_info_value_action" model="ir.actions.act_window">
<field name="name">Values</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">custom.info.value</field>
<field name="view_mode">tree,form</field>
<field name="view_type">form</field>
</record>
<record id="custom_info_value_action" model="ir.actions.act_window">
<field name="name">Values</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">custom.info.value</field>
<field name="view_mode">tree,form</field>
<field name="view_type">form</field>
</record>
</odoo> </odoo>

72
base_custom_info/views/menu.xml

@ -1,41 +1,45 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com> <!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
Copyright 2017 Pedro M. Baeza <pedro.baeza@tecnativa.com>
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). --> License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -->
<odoo> <odoo>
<!--Base menus -->
<menuitem id="menu_base_custom_info" name="Custom Info"
groups="base_custom_info.group_basic"/>
<menuitem
id="menu_basic"
name="Basic"
parent="menu_base_custom_info"/>
<menuitem
id="menu_advanced"
name="Advanced"
groups="base_custom_info.group_basic"
parent="menu_base_custom_info"/>
<menuitem id="menu_base_custom_info_template"
action="custom_info_template_action"
parent="menu_basic" sequence="10"/>
<menuitem id="menu_base_custom_info_value"
action="custom_info_value_action"
parent="menu_basic" sequence="20"/>
<menuitem id="menu_category"
action="custom_info_category_action"
parent="menu_advanced" sequence="10"/>
<menuitem id="menu_property"
action="custom_info_property_action"
parent="menu_advanced" sequence="20"/>
<menuitem id="menu_option"
action="custom_info_option_action"
parent="menu_advanced" sequence="30"/>
<!--Base menus -->
<menuitem id="menu_base_custom_info"
name="Custom Info"
groups="base_custom_info.group_basic"
web_icon="base_custom_info,static/description/icon.png"
/>
<menuitem
id="menu_basic"
name="Basic"
parent="menu_base_custom_info"/>
<menuitem
id="menu_advanced"
name="Advanced"
groups="base_custom_info.group_basic"
parent="menu_base_custom_info"/>
<menuitem id="menu_base_custom_info_template"
action="custom_info_template_action"
parent="menu_basic" sequence="10"/>
<menuitem id="menu_base_custom_info_value"
action="custom_info_value_action"
parent="menu_basic" sequence="20"/>
<menuitem id="menu_category"
action="custom_info_category_action"
parent="menu_advanced" sequence="10"/>
<menuitem id="menu_property"
action="custom_info_property_action"
parent="menu_advanced" sequence="20"/>
<menuitem id="menu_option"
action="custom_info_option_action"
parent="menu_advanced" sequence="30"/>
</odoo> </odoo>

52
base_custom_info/views/res_partner_view.xml

@ -1,38 +1,32 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com> <!-- Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
Copyright 2017 Pedro M. Baeza <pedro.baeza@tecnativa.com>
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). --> License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -->
<odoo> <odoo>
<record id="view_partner_form" model="ir.ui.view">
<field name="name">Custom info in partners form</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<xpath expr="//notebook[1]">
<page name="custom_info"
string="Custom Information"
groups="base_custom_info.group_partner">
<group>
<record id="view_partner_form" model="ir.ui.view">
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<xpath expr="//notebook[1]">
<page name="custom_info"
string="Custom Information"
groups="base_custom_info.group_partner">
<group> <group>
<field name="dirty_templates" invisible="True"/>
<field
name="custom_info_template_id"
options='{"no_quick_create": True}'/>
<group>
<field name="custom_info_template_id"
options='{"no_quick_create": True}'
/>
</group>
<field name="custom_info_ids"
colspan="4"
nolabel="1"
context="{'embed': True, 'tree_view_ref': 'base_custom_info.custom_info_value_tree_editable'}"
/>
</group> </group>
<group>
<button
string="Reload custom information templates"
type="object"
name="action_custom_info_templates_fill"
attrs="{'invisible': [('dirty_templates', '=', False)]}"/>
</group>
<group colspan="2">
<field name="custom_info_ids"/>
</group>
</group>
</page>
</xpath>
</field>
</record>
</page>
</xpath>
</field>
</record>
</odoo> </odoo>
Loading…
Cancel
Save