From 46628774c5409c656993b291653f79a9330aa278 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Mon, 13 Feb 2017 02:07:14 +0100 Subject: [PATCH] [IMP] base_custom_info: Make backend UI work --- base_custom_info/README.rst | 23 +- base_custom_info/__openerp__.py | 4 +- .../demo/custom.info.category.csv | 2 +- base_custom_info/i18n/es.po | 251 ++++++++++-------- .../customizations-everywhere.jpg | Bin .../templateception.jpg | Bin base_custom_info/models/custom_info.py | 122 ++++----- base_custom_info/models/custom_info_option.py | 3 +- .../models/custom_info_property.py | 14 +- .../models/custom_info_template.py | 71 ++--- base_custom_info/models/custom_info_value.py | 169 ++++-------- base_custom_info/models/res_partner.py | 9 +- base_custom_info/static/description/icon.png | Bin 5289 -> 4075 bytes base_custom_info/static/description/icon.svg | 60 +++-- base_custom_info/tests/test_partner.py | 25 -- .../views/custom_info_category_view.xml | 83 +++--- .../views/custom_info_option_view.xml | 113 +++++--- .../views/custom_info_property_view.xml | 169 ++++++------ .../views/custom_info_template_view.xml | 124 ++++----- .../views/custom_info_value_view.xml | 185 +++++++------ base_custom_info/views/menu.xml | 72 ++--- base_custom_info/views/res_partner_view.xml | 52 ++-- 22 files changed, 763 insertions(+), 788 deletions(-) rename base_custom_info/{static/description => images}/customizations-everywhere.jpg (100%) rename base_custom_info/{static/description => images}/templateception.jpg (100%) diff --git a/base_custom_info/README.rst b/base_custom_info/README.rst index 8271d9f6e..2871256ed 100644 --- a/base_custom_info/README.rst +++ b/base_custom_info/README.rst @@ -217,19 +217,7 @@ Known issues / Roadmap ====================== * 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 - `_, 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 - `_. 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 =========== @@ -245,10 +233,11 @@ Credits Contributors ------------ -* Rafael Blasco -* Carlos Dauden -* Sergio Teruel -* Jairo Llopis +* Rafael Blasco +* Carlos Dauden +* Sergio Teruel +* Jairo Llopis +* Pedro M. Baeza Maintainer ---------- diff --git a/base_custom_info/__openerp__.py b/base_custom_info/__openerp__.py index 261fb15a6..64fb5957f 100644 --- a/base_custom_info/__openerp__.py +++ b/base_custom_info/__openerp__.py @@ -38,9 +38,7 @@ "images/templates.png", "images/values.png", ], - 'author': 'Antiun Ingeniería S.L., ' - 'Incaser Informatica S.L., ' - 'Tecnativa, ' + 'author': 'Tecnativa, ' 'Odoo Community Association (OCA)', 'website': 'https://www.tecnativa.com', 'license': 'LGPL-3', diff --git a/base_custom_info/demo/custom.info.category.csv b/base_custom_info/demo/custom.info.category.csv index 35104e4ef..4c64ccb09 100644 --- a/base_custom_info/demo/custom.info.category.csv +++ b/base_custom_info/demo/custom.info.category.csv @@ -1,3 +1,3 @@ id,name,sequence -cat_statics,Statics,50 +cat_statics,Statistics,50 cat_gaming,Gaming,100 diff --git a/base_custom_info/i18n/es.po b/base_custom_info/i18n/es.po index b5eb90896..3587f3576 100644 --- a/base_custom_info/i18n/es.po +++ b/base_custom_info/i18n/es.po @@ -1,41 +1,41 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: -# * base_custom_info +# * base_custom_info # -# Translators: -# OCA Transbot , 2016 -# Pedro M. Baeza , 2016 msgid "" msgstr "" "Project-Id-Version: Odoo Server 9.0c\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-09-09 11:16+0200\n" -"PO-Revision-Date: 2016-09-09 11:17+0200\n" -"Last-Translator: Jairo Llopis \n" -"Language-Team: Spanish (https://www.transifex.com/oca/teams/23907/es/)\n" -"Language: 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" "Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 1.8.8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" #. module: base_custom_info -#: model:ir.ui.view,arch_db:base_custom_info.base_custom_info_value_inner_form -msgid "" -"Warning!\n" -" You might see no changes in parent form until you save it." -msgstr "" -"¡Atención!\n" -" Puede que no vea los cambios en el formulario principal " -"hasta que lo guarde." +#: model:ir.ui.view,arch_db:base_custom_info.custom_info_value_form +msgid "Warning!\n" +" You might see no changes in parent form until you save it." +msgstr "¡Aviso!\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ñadir una pestaña en el formulario de empresas para editar su inf. " -"personalizada." +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 @@ -92,6 +92,11 @@ msgstr "Básico" 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 @@ -106,8 +111,8 @@ 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.base_custom_info_value_search #: 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" @@ -132,7 +137,7 @@ msgstr "Creado por" #: 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 msgid "Created on" -msgstr "Creado el" +msgstr "Creado en" #. module: base_custom_info #: model:ir.ui.menu,name:base_custom_info.menu_base_custom_info @@ -155,7 +160,7 @@ msgid "Custom Info Properties" msgstr "Propiedades de inf. personalizada" #. 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_template_form msgid "Custom Info Template" msgstr "Plantilla de inf. personalizada" @@ -167,7 +172,7 @@ msgid "Custom Info Template Properties" msgstr "Propiedades de la plantilla de inf. personalizada" #. module: base_custom_info -#: 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" msgstr "Plantillas de inf. personalizada" @@ -191,7 +196,7 @@ msgid "Custom Properties" msgstr "Propiedades personalizadas" #. 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" msgstr "Valores de las propiedades personalizadas" @@ -226,7 +231,7 @@ msgid "Default value" msgstr "Valor por defecto" #. module: base_custom_info -#: code:addons/base_custom_info/models/custom_info_property.py:99 +#: 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." @@ -249,13 +254,23 @@ 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?" +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" @@ -269,40 +284,39 @@ 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." +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." +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.property,default_value:base_custom_info.prop_weaknesses -msgid "Gafas gigantes" -msgstr "Gafas gigantes" +#: 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.base_custom_info_template_search -#: model:ir.ui.view,arch_db:base_custom_info.base_custom_info_value_search #: 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..." +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" @@ -314,16 +328,16 @@ msgstr "Gafas gigantes" #: 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 msgid "ID" -msgstr "ID" +msgstr "ID (identificación)" #. module: base_custom_info -#: code:addons/base_custom_info/models/custom_info_property.py:109 +#: 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:112 +#: 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." @@ -331,9 +345,7 @@ msgstr "Si requiere un campo numérico, no podrá ponerlo a cero." #. module: base_custom_info #: model:ir.model,name:base_custom_info.model_custom_info msgid "Inheritable abstract model to add custom info in any model" -msgstr "" -"Modelo abstracto que se puede heredar para añadir inf. personalizada a " -"cualquier modelo" +msgstr "Modelo abstracto que se puede heredar para añadir inf. personalizada a cualquier modelo" #. module: base_custom_info #: model:ir.model.fields,field_description:base_custom_info.field_custom_info___last_update @@ -343,7 +355,7 @@ msgstr "" #: 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 msgid "Last Modified on" -msgstr "Última modificación el" +msgstr "Última modificación en" #. module: base_custom_info #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_category_write_uid @@ -352,7 +364,7 @@ msgstr "Última modificación el" #: 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 msgid "Last Updated by" -msgstr "Última actualización por" +msgstr "Última actualización de" #. module: base_custom_info #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_category_write_date @@ -361,16 +373,13 @@ msgstr "Última actualización por" #: 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 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:199 +#: 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." +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 @@ -393,19 +402,17 @@ msgid "Minimum" msgstr "Mínimo" #. 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_template_model_id -#: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_model -#: model:ir.ui.view,arch_db:base_custom_info.base_custom_info_template_search +#: model:ir.ui.view,arch_db:base_custom_info.custom_info_template_search msgid "Model" msgstr "Modelo" #. module: base_custom_info -#: code:addons/base_custom_info/models/custom_info_template.py:52 -#, python-format -msgid "Model does not exist." -msgstr "El modelo no existe." +#: 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 @@ -427,8 +434,8 @@ msgid "Needs videogames" msgstr "Necesita videojuegos" #. module: base_custom_info -#: code:addons/base_custom_info/models/custom_info_value.py:161 -#: code:addons/base_custom_info/models/custom_info_value.py:256 +#: 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" @@ -437,26 +444,33 @@ msgstr "No" #: 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.base_custom_info_value_search +#: model:ir.ui.view,arch_db:base_custom_info.custom_info_value_search msgid "Owner" -msgstr "Dueño" +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" msgstr "Propiedades" @@ -472,21 +486,20 @@ msgstr "Propiedades en las que esta opción está disponible." #. module: base_custom_info #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_property_id -#: model:ir.ui.view,arch_db:base_custom_info.base_custom_info_value_search +#: model:ir.ui.view,arch_db:base_custom_info.custom_info_value_search msgid "Property" msgstr "Propiedad" -#. module: base_custom_info -#: code:addons/base_custom_info/models/custom_info_value.py:183 -#, python-format -msgid "Property %s is required." -msgstr "La propiedad %s es obligatoria" - #. module: base_custom_info #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_property_info_value_ids msgid "Property Values" 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." @@ -501,7 +514,12 @@ msgstr "Requerido" #. module: base_custom_info #: model:ir.model.fields,field_description:base_custom_info.field_custom_info_value_res_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 @@ -522,11 +540,26 @@ msgstr "Valor de selección" 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." @@ -557,14 +590,12 @@ 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." +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." +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 @@ -578,20 +609,16 @@ msgid "Value" msgstr "Valor" #. module: base_custom_info -#: code:addons/base_custom_info/models/custom_info_value.py:205 +#: 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." +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:209 +#: 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." +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 @@ -618,8 +645,7 @@ 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í." +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 @@ -638,17 +664,11 @@ 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." +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:161 +#: code:addons/base_custom_info/models/custom_info_value.py:123 #, python-format msgid "Yes" msgstr "Sí" @@ -664,7 +684,7 @@ msgid "Yes/No value" msgstr "Valor sí/no" #. module: base_custom_info -#: code:addons/base_custom_info/models/custom_info_template.py:59 +#: 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." @@ -672,11 +692,10 @@ msgstr "No puede cambiar el modelo porque ya se está usando." #. module: base_custom_info #: model:ir.actions.act_window,help:base_custom_info.custom_info_template_action 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." +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" + diff --git a/base_custom_info/static/description/customizations-everywhere.jpg b/base_custom_info/images/customizations-everywhere.jpg similarity index 100% rename from base_custom_info/static/description/customizations-everywhere.jpg rename to base_custom_info/images/customizations-everywhere.jpg diff --git a/base_custom_info/static/description/templateception.jpg b/base_custom_info/images/templateception.jpg similarity index 100% rename from base_custom_info/static/description/templateception.jpg rename to base_custom_info/images/templateception.jpg diff --git a/base_custom_info/models/custom_info.py b/base_custom_info/models/custom_info.py index 45c70e963..96617453a 100644 --- a/base_custom_info/models/custom_info.py +++ b/base_custom_info/models/custom_info.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- -# © 2015 Antiun Ingeniería S.L. - Sergio Teruel -# © 2015 Antiun Ingeniería S.L. - Carlos Dauden -# © 2016 Jairo Llopis +# Copyright 2015 Sergio Teruel +# Copyright 2015 Carlos Dauden +# Copyright 2016 Jairo Llopis +# Copyright 2017 Pedro M. Baeza # License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html from openerp import api, fields, models 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 :attr:`custom_info_template_id` field. @@ -21,54 +22,64 @@ class CustomInfo(models.AbstractModel): custom_info_template_id = fields.Many2one( comodel_name='custom.info.template', domain=lambda self: [("model", "=", self._name)], - string='Custom Information Template') + string='Custom Information Template', + ) 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)], - 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 - 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 def unlink(self): + """Remove linked custom info this way, as can't be handled + automatically. + """ info_values = self.mapped('custom_info_ids') res = super(CustomInfo, self).unlink() if res: info_values.unlink() 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.returns("custom.info.value") def get_custom_info_value(self, properties): @@ -84,38 +95,3 @@ class CustomInfo(models.AbstractModel): """Get all custom info templates involved in these owners.""" return (self.mapped("custom_info_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() diff --git a/base_custom_info/models/custom_info_option.py b/base_custom_info/models/custom_info_option.py index 6e20d94bd..a8ad2e95e 100644 --- a/base_custom_info/models/custom_info_option.py +++ b/base_custom_info/models/custom_info_option.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- -# © 2016 Jairo Llopis +# Copyright 2016 Jairo Llopis +# Copyright 2017 Pedro M. Baeza # License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html from openerp import api, fields, models diff --git a/base_custom_info/models/custom_info_property.py b/base_custom_info/models/custom_info_property.py index 67917cff3..a05b4d8a2 100644 --- a/base_custom_info/models/custom_info_property.py +++ b/base_custom_info/models/custom_info_property.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- -# © 2016 Jairo Llopis +# Copyright 2016 Jairo Llopis +# Copyright 2017 Pedro M. Baeza # License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html + from openerp import _, api, fields, models from openerp.exceptions import UserError, ValidationError @@ -28,13 +30,11 @@ class CustomInfoProperty(models.Model): readonly=True, ) 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( - related="template_id.model", - readonly=True, - auto_join=True, + related="template_id.model", readonly=True, auto_join=True, ) info_value_ids = fields.One2many( comodel_name="custom.info.value", diff --git a/base_custom_info/models/custom_info_template.py b/base_custom_info/models/custom_info_template.py index 16e814e0e..57ece627f 100644 --- a/base_custom_info/models/custom_info_template.py +++ b/base_custom_info/models/custom_info_template.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- -# © 2016 Jairo Llopis +# Copyright 2016 Jairo Llopis +# Copyright 2017 Pedro M. Baeza # License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html from openerp import _, api, fields, models @@ -13,57 +14,63 @@ class CustomInfoTemplate(models.Model): _order = "model_id, name" _sql_constraints = [ ("name_model", - "UNIQUE (name, model)", + "UNIQUE (name, model_id)", "Another template with that name exists for that model."), ] name = fields.Char(required=True, translate=True) 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( - '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( - '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.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.constrains("model") + @api.constrains("model_id") 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(): - 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( - _("You cannot change the model because it is in use.")) + _("You cannot change the model because it is in use.") + ) @api.multi def check_access_rule(self, operation): """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_rule(operation) return super(CustomInfoTemplate, self).check_access_rule(operation) diff --git a/base_custom_info/models/custom_info_value.py b/base_custom_info/models/custom_info_value.py index a7cd143a8..b09d4c993 100644 --- a/base_custom_info/models/custom_info_value.py +++ b/base_custom_info/models/custom_info_value.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- -# © 2016 Jairo Llopis +# Copyright 2016 Jairo Llopis +# Copyright 2017 Pedro M. Baeza # License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html from openerp import _, api, fields, models, SUPERUSER_ID from openerp.exceptions import ValidationError @@ -19,117 +20,63 @@ class CustomInfoValue(models.Model): ] 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( - 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.", ) res_id = fields.Integer( - "Resource ID", - required=True, - index=True, - store=True, + string="Resource ID", required=True, index=True, store=True, ondelete="cascade", ) 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, ) + property_sequence = fields.Integer( + related="property_id.sequence", store=True, index=True, readonly=True, + ) 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( - 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) - field_type = fields.Selection(related="property_id.field_type") + field_type = fields.Selection( + related="property_id.field_type", readonly=True, + ) field_name = fields.Char( compute="_compute_field_name", 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( - compute="_compute_value", - inverse="_inverse_value", + compute="_compute_value", inverse="_inverse_value", search="_search_value", 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( - 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 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) - @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 def _selection_owner_id(self): """You can choose among models linked to a template.""" @@ -154,15 +101,15 @@ class CustomInfoValue(models.Model): @api.depends("res_id", "model") def _compute_owner_id(self): """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 def _inverse_owner_id(self): """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.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.""" for s in self: 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": s.value = _("Yes") if s.value_bool else _("No") else: @@ -180,33 +127,19 @@ class CustomInfoValue(models.Model): @api.multi def _inverse_value(self): """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.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): """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 if minimum <= maximum: value = self[self.field_name] @@ -246,6 +179,12 @@ class CustomInfoValue(models.Model): if not record.value and 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 def _transform_value(self, value, format_, properties=None): """Transforms a text value to the expected format. @@ -266,10 +205,10 @@ class CustomInfoValue(models.Model): if not value: value = False 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": value = value.strip().lower() not in { "0", "false", "", "no", "off", _("No").lower()} diff --git a/base_custom_info/models/res_partner.py b/base_custom_info/models/res_partner.py index cc431bfbf..1f0a562b6 100644 --- a/base_custom_info/models/res_partner.py +++ b/base_custom_info/models/res_partner.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # Copyright 2016 Jairo Llopis +# Copyright 2017 Pedro M. Baeza # License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html from openerp import fields, models @@ -14,9 +15,5 @@ class ResPartner(models.Model): _name = "res.partner" _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}) diff --git a/base_custom_info/static/description/icon.png b/base_custom_info/static/description/icon.png index 896696e43b57a353305958c411a62b42430697d3..cc64c001a82d7f7128de843860f8f57b1f39aef6 100644 GIT binary patch delta 4054 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 literal 5289 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 r>88k + + + inkscape:export-xdpi="192" + inkscape:export-ydpi="192"> + id="metadata8"> image/svg+xml - + + id="defs6" /> + + + transform="matrix(0.3061173,0,0,0.3061173,-1.0360053,-1.0457906)" + style="fill:#ffffff;stroke:none"> + 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" /> diff --git a/base_custom_info/tests/test_partner.py b/base_custom_info/tests/test_partner.py index 9105586e7..21cfafb3a 100644 --- a/base_custom_info/tests/test_partner.py +++ b/base_custom_info/tests/test_partner.py @@ -162,28 +162,3 @@ class PartnerCase(TransactionCase): self.env.ref("base_custom_info.prop_avg_note")) with self.assertRaises(ValidationError): 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) diff --git a/base_custom_info/views/custom_info_category_view.xml b/base_custom_info/views/custom_info_category_view.xml index d9b5a7355..3c601d9bb 100644 --- a/base_custom_info/views/custom_info_category_view.xml +++ b/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). --> - - Custom Info Category Tree - custom.info.category - - - - - - - - + + custom.info.category + + + + + + + + - - Custom Info Category Form - custom.info.category - -
- - - - - - - -
-
-
+ + custom.info.category + +
+ + + + + + + +
+
+
- - Custom Info Category Search - custom.info.category - - - - - - - + + custom.info.category + + + + + + + - - Categories - ir.actions.act_window - custom.info.category - tree,form - form - + + Categories + ir.actions.act_window + custom.info.category + tree,form + form +
diff --git a/base_custom_info/views/custom_info_option_view.xml b/base_custom_info/views/custom_info_option_view.xml index 3ac65804c..ca204523f 100644 --- a/base_custom_info/views/custom_info_option_view.xml +++ b/base_custom_info/views/custom_info_option_view.xml @@ -1,54 +1,79 @@ - - Custom Info Option Tree - custom.info.option - - - - - - - - + + custom.info.option + + + + + + + + + - - Custom Info Option Form - custom.info.option - -
- - - - - - - - -
-
-
+ + custom.info.option + + primary + + + + + + + + + - - Custom Info Option Search - custom.info.option - - - - - - - + + custom.info.option + + +
+ + + + + + + +
+
+
- - Options - ir.actions.act_window - custom.info.option - tree,form - form - + + custom.info.option + + primary + + + + + + + + + + custom.info.option + + + + + + + + + + Options + ir.actions.act_window + custom.info.option + tree,form + form +
diff --git a/base_custom_info/views/custom_info_property_view.xml b/base_custom_info/views/custom_info_property_view.xml index 71a14f6a4..39d446f6d 100644 --- a/base_custom_info/views/custom_info_property_view.xml +++ b/base_custom_info/views/custom_info_property_view.xml @@ -1,84 +1,105 @@ - - Custom Info Property Tree - custom.info.property - - - - - - - - - - - - + + custom.info.property + + + + + + + + + + + + - - Custom Info Property Form - custom.info.property - -
- - - - - - - - - - - - - - - - -
-
-
+ + custom.info.property + + primary + + + + + + + + + custom.info.property + + +
+ + + + + + + + + + + + + + + +
+
+
- - Custom Info Property Search - custom.info.property - - - - - - - - - - - - - - - + + custom.info.property + + primary + + + + + + + + + custom.info.property + + + + + + + + + + + + + + + - - Properties - ir.actions.act_window - custom.info.property - tree,form - form - + + Properties + ir.actions.act_window + custom.info.property + tree,form + form +
diff --git a/base_custom_info/views/custom_info_template_view.xml b/base_custom_info/views/custom_info_template_view.xml index d8500fe96..4805471b4 100644 --- a/base_custom_info/views/custom_info_template_view.xml +++ b/base_custom_info/views/custom_info_template_view.xml @@ -1,72 +1,72 @@ - - Custom Info Template Tree - custom.info.template - - - - - - - - - + + custom.info.template + + + + + + + + + - - Custom Info Template Form - custom.info.template - -
- - - - - - - - - - -
-
-
+ + custom.info.template + +
+ + + + + + + + + + +
+
+
- - Custom Info Template Search - custom.info.template - - - - - - - - - - - + + custom.info.template + + + + + + + + + + + - - Templates - ir.actions.act_window - custom.info.template - tree,form - form - - - -

- Click to define a new custom info template. -

- You must define a custom info template for each properties group. -

- - + + Templates + ir.actions.act_window + custom.info.template + tree,form + form + + + +

+ Click to define a new custom info template. +

+

+ You must define a custom info template for each properties group. +

+
+
diff --git a/base_custom_info/views/custom_info_value_view.xml b/base_custom_info/views/custom_info_value_view.xml index 519721095..41355c7fa 100644 --- a/base_custom_info/views/custom_info_value_view.xml +++ b/base_custom_info/views/custom_info_value_view.xml @@ -1,93 +1,112 @@ - - Custom Info Value Tree - custom.info.value - - - - - - - - - + + custom.info.value + + + + + + + + + + - - Custom Info Value Form - custom.info.value - -
- - - - - - - - - - - - - - - -
- Warning! - You might see no changes in parent form until you save it. -
-
-
-
-
+ + custom.info.value + + primary + + + + bottom + + + + + + + - - Custom Info Value Search - custom.info.value - - - - - - - - - - - - - - - + + custom.info.value + +
+ + + + + + + + + + + + + + + +
+ Warning! + You might see no changes in parent form until you save it. +
+
+
+
+
+ + + custom.info.value + + + + + + + + + + + + + + + - - Values - ir.actions.act_window - custom.info.value - tree,form - form - + + Values + ir.actions.act_window + custom.info.value + tree,form + form +
diff --git a/base_custom_info/views/menu.xml b/base_custom_info/views/menu.xml index f69e82584..233a32947 100644 --- a/base_custom_info/views/menu.xml +++ b/base_custom_info/views/menu.xml @@ -1,41 +1,45 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/base_custom_info/views/res_partner_view.xml b/base_custom_info/views/res_partner_view.xml index f32978c82..4a8c17c9f 100644 --- a/base_custom_info/views/res_partner_view.xml +++ b/base_custom_info/views/res_partner_view.xml @@ -1,38 +1,32 @@ - - Custom info in partners form - res.partner - - - - - + + res.partner + + + + - - + + + + - -