From 0ea6a3801dc742e7044a0af3acf19e7004261ef4 Mon Sep 17 00:00:00 2001 From: les-adhoc Date: Mon, 19 Oct 2026 17:41:01 -0300 Subject: [PATCH 1/5] [MIG] partner_state: Migration to 20.0 - Bump version to 20.0 and mark the module installable (odoo-module-migrator). - Convert ir.model.access.csv to ir.access.csv (upgrade_code 19.4-00-ir-access). - Search view: the "type_company" filter no longer exists in base.view_res_partner_filter; anchor the state filters on "filter_vat_set", which sits in the same group. --- partner_state/__manifest__.py | 6 +++--- partner_state/security/ir.access.csv | 4 ++++ partner_state/security/ir.model.access.csv | 4 ---- partner_state/views/partner_views.xml | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) create mode 100644 partner_state/security/ir.access.csv delete mode 100644 partner_state/security/ir.model.access.csv diff --git a/partner_state/__manifest__.py b/partner_state/__manifest__.py index 1edc59730..d638cd7a5 100644 --- a/partner_state/__manifest__.py +++ b/partner_state/__manifest__.py @@ -19,7 +19,7 @@ ############################################################################## { "name": "Partner State", - "version": "19.0.1.0.0", + "version": "20.0.1.0.0", "category": "Base", "author": "ADHOC SA, Odoo Community Association (OCA)", "website": "www.adhoc.com.ar", @@ -29,7 +29,7 @@ ], "data": [ "security/partner_state_security.xml", - "security/ir.model.access.csv", + "security/ir.access.csv", "data/res_company_data.xml", "data/partner_state_server_data.xml", "data/res_users_data.xml", @@ -38,5 +38,5 @@ "views/res_company_views.xml", ], "demo": ["demo/res.partner.state_field.csv"], - "installable": False, + "installable": True, } diff --git a/partner_state/security/ir.access.csv b/partner_state/security/ir.access.csv new file mode 100644 index 000000000..6f48eb9cc --- /dev/null +++ b/partner_state/security/ir.access.csv @@ -0,0 +1,4 @@ +id,name,model_id,group_id/id,operation,domain +access_res_partner_state_field_all,res_company_partner_state_field_all,res.partner.state_field,base.group_user,ru, +access_res_partner_state_field_manager,res_company_partner_state_field_manager,res.partner.state_field,base.group_erp_manager,crud, +access_ir_model_fields,ir_model_fields,ir.model.fields,base.group_user,r, diff --git a/partner_state/security/ir.model.access.csv b/partner_state/security/ir.model.access.csv deleted file mode 100644 index f01f601bc..000000000 --- a/partner_state/security/ir.model.access.csv +++ /dev/null @@ -1,4 +0,0 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_res_partner_state_field_all,res_company_partner_state_field_all,model_res_partner_state_field,base.group_user,1,1,0,0 -access_ir_model_fields,ir_model_fields,base.model_ir_model_fields,base.group_user,1,0,0,0 -access_res_partner_state_field_manager,res_company_partner_state_field_manager,model_res_partner_state_field,base.group_erp_manager,1,1,1,1 diff --git a/partner_state/views/partner_views.xml b/partner_state/views/partner_views.xml index 64ba9a368..f0325ac16 100644 --- a/partner_state/views/partner_views.xml +++ b/partner_state/views/partner_views.xml @@ -32,7 +32,7 @@ res.partner - + From 3c10f3e60a5e2c708cc3aa657a837b761b6c94f2 Mon Sep 17 00:00:00 2001 From: les-adhoc Date: Mon, 19 Oct 2026 17:41:01 -0300 Subject: [PATCH 2/5] [I18N] partner_state: sync README and translations to 20.0 --- partner_state/README.rst | 19 +++-- partner_state/i18n/es.po | 103 +++++++++++++++++++++++++-- partner_state/i18n/partner_state.pot | 102 ++++++++++++++++++++++++-- 3 files changed, 210 insertions(+), 14 deletions(-) diff --git a/partner_state/README.rst b/partner_state/README.rst index d14e438ee..6f5e6908c 100644 --- a/partner_state/README.rst +++ b/partner_state/README.rst @@ -14,12 +14,20 @@ Partner State ============= -Add three stats for partners:: 'Potential', 'Pending Approval' and 'Approved'. +Add three states for partners: 'Potential', 'Pending Approval' and 'Approved'. Some information is required so that a partner can be approved. That -information is configured on: Sales/Settings/Address Book/Partner State fields. +information is configured on: Contacts/Configuration/Partner State Fields. + +For each configured field you can also choose whether: + +* it is required to request approval, +* it can not be edited while the partner is pending or approved, +* changing it sends the partner back to 'Potential', +* its changes are logged in the chatter. You can also configure on company if you want to use partner state on that -company or not. +company or not. The state applies to commercial partners (companies and +individuals without a parent company). Installation ============ @@ -34,8 +42,8 @@ Configuration To configure this module, you need to: #. Set "Use partner state " in the company. -#. Set the field that you required to approve partner in Sale/Configuration/Contact: Partner State Fields. -#. Set the permition to approve to the user. +#. Set the field that you required to approve partner in Contacts/Configuration/Partner State Fields. +#. Set the permition to approve to the user (group "Approve Partners"). Usage ===== @@ -43,6 +51,7 @@ Usage To use this module, you need to: #. In the partner view clik the button to approve, or potencial to approve. +#. From the partner list, use the actions "Request Approval Partners" and "Approve Partners" to process several partners at once. .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas diff --git a/partner_state/i18n/es.po b/partner_state/i18n/es.po index 456f73483..5113ce513 100644 --- a/partner_state/i18n/es.po +++ b/partner_state/i18n/es.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 19.0+e\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-11-06 15:19+0000\n" -"PO-Revision-Date: 2025-12-11 18:10+0000\n" +"POT-Creation-Date: 2026-10-19 20:28+0000\n" +"PO-Revision-Date: 2026-10-19 20:28+0000\n" "Last-Translator: BAF \n" "Language-Team: Spanish \n" @@ -22,6 +22,99 @@ msgstr "" " ? 1 : 2);\n" "X-Generator: Weblate 5.10.4\n" +#. module: partner_state +#: model:ir.module.module,description:base.module_partner_state +msgid "" +".. |company| replace:: ADHOC SA\n" +"\n" +".. |company_logo| image:: https://raw.githubusercontent.com/ingadhoc/maintainer-tools/master/resources/adhoc-logo.png\n" +" :alt: ADHOC SA\n" +" :target: https://www.adhoc.com.ar\n" +"\n" +".. |icon| image:: https://raw.githubusercontent.com/ingadhoc/maintainer-tools/master/resources/adhoc-icon.png\n" +"\n" +".. image:: https://img.shields.io/badge/license-AGPL--3-blue.png\n" +" :target: https://www.gnu.org/licenses/agpl\n" +" :alt: License: AGPL-3\n" +"\n" +"=============\n" +"Partner State\n" +"=============\n" +"\n" +"Add three states for partners: 'Potential', 'Pending Approval' and 'Approved'.\n" +"Some information is required so that a partner can be approved. That\n" +"information is configured on: Contacts/Configuration/Partner State Fields.\n" +"\n" +"For each configured field you can also choose whether:\n" +"\n" +"* it is required to request approval,\n" +"* it can not be edited while the partner is pending or approved,\n" +"* changing it sends the partner back to 'Potential',\n" +"* its changes are logged in the chatter.\n" +"\n" +"You can also configure on company if you want to use partner state on that\n" +"company or not. The state applies to commercial partners (companies and\n" +"individuals without a parent company).\n" +"\n" +"Installation\n" +"============\n" +"\n" +"To install this module, you need to:\n" +"\n" +"#. Just install module.\n" +"\n" +"Configuration\n" +"=============\n" +"\n" +"To configure this module, you need to:\n" +"\n" +"#. Set \"Use partner state \" in the company.\n" +"#. Set the field that you required to approve partner in Contacts/Configuration/Partner State Fields.\n" +"#. Set the permition to approve to the user (group \"Approve Partners\").\n" +"\n" +"Usage\n" +"=====\n" +"\n" +"To use this module, you need to:\n" +"\n" +"#. In the partner view clik the button to approve, or potencial to approve.\n" +"#. From the partner list, use the actions \"Request Approval Partners\" and \"Approve Partners\" to process several partners at once.\n" +"\n" +"\n" +".. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas\n" +" :alt: Try me on Runbot\n" +" :target: http://runbot.adhoc.com.ar/\n" +"\n" +"Bug Tracker\n" +"===========\n" +"\n" +"Bugs are tracked on `GitHub Issues\n" +"`_. In case of trouble, please\n" +"check there if your issue has already been reported. If you spotted it first,\n" +"help us smashing it by providing a detailed and welcomed feedback.\n" +"\n" +"\n" +"Credits\n" +"=======\n" +"\n" +"Images\n" +"------\n" +"\n" +"* |company| |icon|\n" +"\n" +"Contributors\n" +"------------\n" +"\n" +"Maintainer\n" +"----------\n" +"\n" +"|company_logo|\n" +"\n" +"This module is maintained by the |company|.\n" +"\n" +"To contribute to this module, please visit https://www.adhoc.com.ar.\n" +msgstr "" + #. module: partner_state #: model:ir.model.fields,field_description:partner_state.field_res_partner_state_field__approval msgid "Approval?" @@ -56,8 +149,8 @@ msgstr "Cambios?" #. module: partner_state #: model:ir.model,name:partner_state.model_res_company -msgid "Companies" -msgstr "Compañías" +msgid "Company" +msgstr "Empresa" #. module: partner_state #: model:ir.model,name:partner_state.model_res_partner @@ -79,7 +172,7 @@ msgstr "Creado el" #: model:ir.model.fields,field_description:partner_state.field_res_partner__display_name #: model:ir.model.fields,field_description:partner_state.field_res_partner_state_field__display_name msgid "Display Name" -msgstr "Nombre para mostrar" +msgstr "Nombre en pantalla" #. module: partner_state #: model:ir.model.fields,help:partner_state.field_res_partner_state_field__block_edition diff --git a/partner_state/i18n/partner_state.pot b/partner_state/i18n/partner_state.pot index 4b8cf4680..bdc3f2d61 100644 --- a/partner_state/i18n/partner_state.pot +++ b/partner_state/i18n/partner_state.pot @@ -4,10 +4,10 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 19.0+e\n" +"Project-Id-Version: Odoo Server 20.0+e\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-11-25 18:35+0000\n" -"PO-Revision-Date: 2025-11-25 18:35+0000\n" +"POT-Creation-Date: 2026-10-19 20:28+0000\n" +"PO-Revision-Date: 2026-10-19 20:28+0000\n" "Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -15,6 +15,99 @@ msgstr "" "Content-Transfer-Encoding: \n" "Plural-Forms: \n" +#. module: partner_state +#: model:ir.module.module,description:base.module_partner_state +msgid "" +".. |company| replace:: ADHOC SA\n" +"\n" +".. |company_logo| image:: https://raw.githubusercontent.com/ingadhoc/maintainer-tools/master/resources/adhoc-logo.png\n" +" :alt: ADHOC SA\n" +" :target: https://www.adhoc.com.ar\n" +"\n" +".. |icon| image:: https://raw.githubusercontent.com/ingadhoc/maintainer-tools/master/resources/adhoc-icon.png\n" +"\n" +".. image:: https://img.shields.io/badge/license-AGPL--3-blue.png\n" +" :target: https://www.gnu.org/licenses/agpl\n" +" :alt: License: AGPL-3\n" +"\n" +"=============\n" +"Partner State\n" +"=============\n" +"\n" +"Add three states for partners: 'Potential', 'Pending Approval' and 'Approved'.\n" +"Some information is required so that a partner can be approved. That\n" +"information is configured on: Contacts/Configuration/Partner State Fields.\n" +"\n" +"For each configured field you can also choose whether:\n" +"\n" +"* it is required to request approval,\n" +"* it can not be edited while the partner is pending or approved,\n" +"* changing it sends the partner back to 'Potential',\n" +"* its changes are logged in the chatter.\n" +"\n" +"You can also configure on company if you want to use partner state on that\n" +"company or not. The state applies to commercial partners (companies and\n" +"individuals without a parent company).\n" +"\n" +"Installation\n" +"============\n" +"\n" +"To install this module, you need to:\n" +"\n" +"#. Just install module.\n" +"\n" +"Configuration\n" +"=============\n" +"\n" +"To configure this module, you need to:\n" +"\n" +"#. Set \"Use partner state \" in the company.\n" +"#. Set the field that you required to approve partner in Contacts/Configuration/Partner State Fields.\n" +"#. Set the permition to approve to the user (group \"Approve Partners\").\n" +"\n" +"Usage\n" +"=====\n" +"\n" +"To use this module, you need to:\n" +"\n" +"#. In the partner view clik the button to approve, or potencial to approve.\n" +"#. From the partner list, use the actions \"Request Approval Partners\" and \"Approve Partners\" to process several partners at once.\n" +"\n" +"\n" +".. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas\n" +" :alt: Try me on Runbot\n" +" :target: http://runbot.adhoc.com.ar/\n" +"\n" +"Bug Tracker\n" +"===========\n" +"\n" +"Bugs are tracked on `GitHub Issues\n" +"`_. In case of trouble, please\n" +"check there if your issue has already been reported. If you spotted it first,\n" +"help us smashing it by providing a detailed and welcomed feedback.\n" +"\n" +"\n" +"Credits\n" +"=======\n" +"\n" +"Images\n" +"------\n" +"\n" +"* |company| |icon|\n" +"\n" +"Contributors\n" +"------------\n" +"\n" +"Maintainer\n" +"----------\n" +"\n" +"|company_logo|\n" +"\n" +"This module is maintained by the |company|.\n" +"\n" +"To contribute to this module, please visit https://www.adhoc.com.ar.\n" +msgstr "" + #. module: partner_state #: model:ir.model.fields,field_description:partner_state.field_res_partner_state_field__approval msgid "Approval?" @@ -49,7 +142,7 @@ msgstr "" #. module: partner_state #: model:ir.model,name:partner_state.model_res_company -msgid "Companies" +msgid "Company" msgstr "" #. module: partner_state @@ -110,6 +203,7 @@ msgstr "" #. module: partner_state #: model:ir.model.fields,field_description:partner_state.field_res_partner__partner_state #: model:ir.model.fields,field_description:partner_state.field_res_users__partner_state +#: model:ir.module.module,shortdesc:base.module_partner_state #: model_terms:ir.ui.view,arch_db:partner_state.view_company_inherit_form #: model_terms:ir.ui.view,arch_db:partner_state.view_partner_state_search msgid "Partner State" From 647dc7f68dcdcb184a67719d508910df380c9ac8 Mon Sep 17 00:00:00 2001 From: les-adhoc Date: Mon, 19 Oct 2026 18:41:13 -0300 Subject: [PATCH 3/5] [FIX] partner_state: limit read on ir.model.fields to res.partner fields The module gave every internal user read on the whole ir.model.fields table. The core does not (in 20 only base.group_erp_manager has it), and in 20 that exposes fields such as ai_fields prompt_custom. partner_state only reads res.partner fields, so the access gets that domain. --- partner_state/security/ir.access.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/partner_state/security/ir.access.csv b/partner_state/security/ir.access.csv index 6f48eb9cc..ff72dd00e 100644 --- a/partner_state/security/ir.access.csv +++ b/partner_state/security/ir.access.csv @@ -1,4 +1,4 @@ id,name,model_id,group_id/id,operation,domain access_res_partner_state_field_all,res_company_partner_state_field_all,res.partner.state_field,base.group_user,ru, access_res_partner_state_field_manager,res_company_partner_state_field_manager,res.partner.state_field,base.group_erp_manager,crud, -access_ir_model_fields,ir_model_fields,ir.model.fields,base.group_user,r, +access_ir_model_fields,ir_model_fields,ir.model.fields,base.group_user,r,"[('model', '=', 'res.partner')]" From 3956f19affe5dab9dfca2136f095ec2507703812 Mon Sep 17 00:00:00 2001 From: Celina Devigili Date: Fri, 16 Oct 2026 15:16:39 +0000 Subject: [PATCH 4/5] [FIX] partner_state: keep standard fields in the chatter "Changes?" now only adds fields to the chatter. Fields that Odoo tracks by default are always tracked, with or without lines in Partner State Fields. Before, the _message_track override only logged the fields with "Changes?", so a database without lines logged no partner change. The "Changes?" column now shows a computed field: it is checked and read only for fields with native tracking, and it writes the stored value for the other fields. Existing lines show the right value without a migration. The README explains the options of Partner State Fields and lists the fields that Odoo tracks on contacts by default. In 19.0, also drop the unused initial_values and state_fields_to_track from write(). closes ingadhoc/partner#156 X-original-commit: ed6f85d5dbec209003732a1a3596e238ce5625d4 Signed-off-by: Matias Velazquez Signed-off-by: Luciano Esperlazza In 20.0, without mail_tracking the tracking is only rendered in the message body, so the test reads the field labels from there. The README also lists the related fields that 20 tracks with the name. Cherry-picked from ff015cb8 (19.0). --- partner_state/README.rst | 27 ++++-- partner_state/i18n/es.po | 47 +++++++--- partner_state/i18n/partner_state.pot | 47 +++++++--- partner_state/models/res_partner.py | 35 ++------ .../models/res_partner_state_field.py | 20 +++++ partner_state/tests/__init__.py | 1 + .../tests/test_partner_state_tracking.py | 87 +++++++++++++++++++ .../views/res_partner_state_field_views.xml | 4 +- 8 files changed, 207 insertions(+), 61 deletions(-) create mode 100644 partner_state/tests/test_partner_state_tracking.py diff --git a/partner_state/README.rst b/partner_state/README.rst index 6f5e6908c..d78d33056 100644 --- a/partner_state/README.rst +++ b/partner_state/README.rst @@ -16,14 +16,7 @@ Partner State Add three states for partners: 'Potential', 'Pending Approval' and 'Approved'. Some information is required so that a partner can be approved. That -information is configured on: Contacts/Configuration/Partner State Fields. - -For each configured field you can also choose whether: - -* it is required to request approval, -* it can not be edited while the partner is pending or approved, -* changing it sends the partner back to 'Potential', -* its changes are logged in the chatter. +information is configured on: Contacts > Configuration > Partner State Fields. You can also configure on company if you want to use partner state on that company or not. The state applies to commercial partners (companies and @@ -42,9 +35,25 @@ Configuration To configure this module, you need to: #. Set "Use partner state " in the company. -#. Set the field that you required to approve partner in Contacts/Configuration/Partner State Fields. +#. Set the field that you required to approve partner in Contacts > Configuration > Partner State Fields. #. Set the permition to approve to the user (group "Approve Partners"). +Each line of Partner State Fields has these options: + +* **Approval?**: the field is required to request the approval. +* **Changes?**: the changes of the field are logged in the chatter. It only + adds fields: the fields that Odoo already tracks are always logged, with or + without a line, and for them this option is checked and can not be unchecked. +* **Track?**: if the field changes on an approved or pending partner, the + partner goes back to Potential. Checking it also checks **Changes?**. +* **Block Edition**: the field can not be edited on an approved or pending + partner. + +The fields that Odoo tracks on contacts by default are Name, Email, Phone, +Related Company, Salesperson, Tax ID and Inlined Complete Address, plus Parent +name and Company Name Entity, that follow the name. Other installed modules can +add more fields (for example, ``base_ux`` adds Active). + Usage ===== diff --git a/partner_state/i18n/es.po b/partner_state/i18n/es.po index 5113ce513..0067239fb 100644 --- a/partner_state/i18n/es.po +++ b/partner_state/i18n/es.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 19.0+e\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-10-19 20:28+0000\n" -"PO-Revision-Date: 2026-10-19 20:28+0000\n" +"POT-Creation-Date: 2026-10-20 16:05+0000\n" +"PO-Revision-Date: 2026-10-20 16:05+0000\n" "Last-Translator: BAF \n" "Language-Team: Spanish \n" @@ -43,14 +43,7 @@ msgid "" "\n" "Add three states for partners: 'Potential', 'Pending Approval' and 'Approved'.\n" "Some information is required so that a partner can be approved. That\n" -"information is configured on: Contacts/Configuration/Partner State Fields.\n" -"\n" -"For each configured field you can also choose whether:\n" -"\n" -"* it is required to request approval,\n" -"* it can not be edited while the partner is pending or approved,\n" -"* changing it sends the partner back to 'Potential',\n" -"* its changes are logged in the chatter.\n" +"information is configured on: Contacts > Configuration > Partner State Fields.\n" "\n" "You can also configure on company if you want to use partner state on that\n" "company or not. The state applies to commercial partners (companies and\n" @@ -69,9 +62,25 @@ msgid "" "To configure this module, you need to:\n" "\n" "#. Set \"Use partner state \" in the company.\n" -"#. Set the field that you required to approve partner in Contacts/Configuration/Partner State Fields.\n" +"#. Set the field that you required to approve partner in Contacts > Configuration > Partner State Fields.\n" "#. Set the permition to approve to the user (group \"Approve Partners\").\n" "\n" +"Each line of Partner State Fields has these options:\n" +"\n" +"* **Approval?**: the field is required to request the approval.\n" +"* **Changes?**: the changes of the field are logged in the chatter. It only\n" +" adds fields: the fields that Odoo already tracks are always logged, with or\n" +" without a line, and for them this option is checked and can not be unchecked.\n" +"* **Track?**: if the field changes on an approved or pending partner, the\n" +" partner goes back to Potential. Checking it also checks **Changes?**.\n" +"* **Block Edition**: the field can not be edited on an approved or pending\n" +" partner.\n" +"\n" +"The fields that Odoo tracks on contacts by default are Name, Email, Phone,\n" +"Related Company, Salesperson, Tax ID and Inlined Complete Address, plus Parent\n" +"name and Company Name Entity, that follow the name. Other installed modules can\n" +"add more fields (for example, ``base_ux`` adds Active).\n" +"\n" "Usage\n" "=====\n" "\n" @@ -144,9 +153,15 @@ msgstr "Bloquear Edición" #. module: partner_state #: model:ir.model.fields,field_description:partner_state.field_res_partner_state_field__changes +#: model_terms:ir.ui.view,arch_db:partner_state.view_partner_state_tree_2 msgid "Changes?" msgstr "Cambios?" +#. module: partner_state +#: model:ir.model.fields,field_description:partner_state.field_res_partner_state_field__chatter_tracking +msgid "Chatter Tracking" +msgstr "Registro en el chatter" + #. module: partner_state #: model:ir.model,name:partner_state.model_res_company msgid "Company" @@ -185,6 +200,11 @@ msgstr "" msgid "Field" msgstr "Campo" +#. module: partner_state +#: model:ir.model.fields,help:partner_state.field_res_partner_state_field__chatter_tracking +msgid "Fields with native tracking are always tracked." +msgstr "Los campos con trazabilidad nativa se registran siempre." + #. module: partner_state #: model:ir.model.fields,field_description:partner_state.field_res_company__id #: model:ir.model.fields,field_description:partner_state.field_res_partner__id @@ -202,6 +222,11 @@ msgstr "Última actualización por" msgid "Last Updated on" msgstr "Última actualización en" +#. module: partner_state +#: model:ir.model.fields,field_description:partner_state.field_res_partner_state_field__native_tracking +msgid "Native Tracking" +msgstr "Trazabilidad nativa" + #. module: partner_state #. odoo-python #: code:addons/partner_state/models/res_partner.py:0 diff --git a/partner_state/i18n/partner_state.pot b/partner_state/i18n/partner_state.pot index bdc3f2d61..533df273a 100644 --- a/partner_state/i18n/partner_state.pot +++ b/partner_state/i18n/partner_state.pot @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 20.0+e\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-10-19 20:28+0000\n" -"PO-Revision-Date: 2026-10-19 20:28+0000\n" +"POT-Creation-Date: 2026-10-20 16:05+0000\n" +"PO-Revision-Date: 2026-10-20 16:05+0000\n" "Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -36,14 +36,7 @@ msgid "" "\n" "Add three states for partners: 'Potential', 'Pending Approval' and 'Approved'.\n" "Some information is required so that a partner can be approved. That\n" -"information is configured on: Contacts/Configuration/Partner State Fields.\n" -"\n" -"For each configured field you can also choose whether:\n" -"\n" -"* it is required to request approval,\n" -"* it can not be edited while the partner is pending or approved,\n" -"* changing it sends the partner back to 'Potential',\n" -"* its changes are logged in the chatter.\n" +"information is configured on: Contacts > Configuration > Partner State Fields.\n" "\n" "You can also configure on company if you want to use partner state on that\n" "company or not. The state applies to commercial partners (companies and\n" @@ -62,9 +55,25 @@ msgid "" "To configure this module, you need to:\n" "\n" "#. Set \"Use partner state \" in the company.\n" -"#. Set the field that you required to approve partner in Contacts/Configuration/Partner State Fields.\n" +"#. Set the field that you required to approve partner in Contacts > Configuration > Partner State Fields.\n" "#. Set the permition to approve to the user (group \"Approve Partners\").\n" "\n" +"Each line of Partner State Fields has these options:\n" +"\n" +"* **Approval?**: the field is required to request the approval.\n" +"* **Changes?**: the changes of the field are logged in the chatter. It only\n" +" adds fields: the fields that Odoo already tracks are always logged, with or\n" +" without a line, and for them this option is checked and can not be unchecked.\n" +"* **Track?**: if the field changes on an approved or pending partner, the\n" +" partner goes back to Potential. Checking it also checks **Changes?**.\n" +"* **Block Edition**: the field can not be edited on an approved or pending\n" +" partner.\n" +"\n" +"The fields that Odoo tracks on contacts by default are Name, Email, Phone,\n" +"Related Company, Salesperson, Tax ID and Inlined Complete Address, plus Parent\n" +"name and Company Name Entity, that follow the name. Other installed modules can\n" +"add more fields (for example, ``base_ux`` adds Active).\n" +"\n" "Usage\n" "=====\n" "\n" @@ -137,9 +146,15 @@ msgstr "" #. module: partner_state #: model:ir.model.fields,field_description:partner_state.field_res_partner_state_field__changes +#: model_terms:ir.ui.view,arch_db:partner_state.view_partner_state_tree_2 msgid "Changes?" msgstr "" +#. module: partner_state +#: model:ir.model.fields,field_description:partner_state.field_res_partner_state_field__chatter_tracking +msgid "Chatter Tracking" +msgstr "" + #. module: partner_state #: model:ir.model,name:partner_state.model_res_company msgid "Company" @@ -177,6 +192,11 @@ msgstr "" msgid "Field" msgstr "" +#. module: partner_state +#: model:ir.model.fields,help:partner_state.field_res_partner_state_field__chatter_tracking +msgid "Fields with native tracking are always tracked." +msgstr "" + #. module: partner_state #: model:ir.model.fields,field_description:partner_state.field_res_company__id #: model:ir.model.fields,field_description:partner_state.field_res_partner__id @@ -194,6 +214,11 @@ msgstr "" msgid "Last Updated on" msgstr "" +#. module: partner_state +#: model:ir.model.fields,field_description:partner_state.field_res_partner_state_field__native_tracking +msgid "Native Tracking" +msgstr "" + #. module: partner_state #. odoo-python #: code:addons/partner_state/models/res_partner.py:0 diff --git a/partner_state/models/res_partner.py b/partner_state/models/res_partner.py index f711f0816..e2055d27f 100644 --- a/partner_state/models/res_partner.py +++ b/partner_state/models/res_partner.py @@ -29,18 +29,6 @@ def _compute_partner_state_enable(self): partners.partner_state_enable = True def write(self, vals): - initial_values = {} - state_fields_to_track = [] - - for line in self.env["res.partner.state_field"].search([("track", "=", True)]): - field_name = line.field_id.name - state_fields_to_track.append(field_name) - if field_name in vals: - for partner in self: - if partner.id not in initial_values: - initial_values[partner.id] = {} - initial_values[partner.id][field_name] = getattr(partner, field_name) - ResPartnerStateField = self.env["res.partner.state_field"] for partner in self.filtered(lambda r: r.partner_state in ["approved", "pending"]): partner_block_fields = ResPartnerStateField.search([("block_edition", "=", True)]).mapped("field_id.name") @@ -105,20 +93,9 @@ def check_fields(self, field_type): return ret def _track_get_fields(self): - default_result = super()._track_get_fields() - - fields_to_exclude = set() - fields_to_include = set() - - for line in self.env["res.partner.state_field"].search([]): - field_name = line.field_id.name - if line.changes: - fields_to_include.add(field_name) - else: - fields_to_exclude.add(field_name) - - final_result = set(default_result) - fields_to_exclude - - final_result.update(fields_to_include) - - return final_result + tracked_fields = set(super()._track_get_fields()) + names = self.env["res.partner.state_field"].search([("changes", "=", True)]).mapped("field_id.name") + # fields_get() with an empty list returns every field + if names: + tracked_fields.update(self.fields_get(names, attributes=())) + return tracked_fields diff --git a/partner_state/models/res_partner_state_field.py b/partner_state/models/res_partner_state_field.py index c83b0ebc3..73944a60d 100644 --- a/partner_state/models/res_partner_state_field.py +++ b/partner_state/models/res_partner_state_field.py @@ -19,12 +19,32 @@ class ResPartnerStateField(models.Model): ) approval = fields.Boolean("Approval?", help="Required for Approval", default=True) changes = fields.Boolean("Changes?", help="Track changes of the partner in the chatter box") + native_tracking = fields.Boolean(compute="_compute_native_tracking") + chatter_tracking = fields.Boolean( + compute="_compute_chatter_tracking", + inverse="_inverse_chatter_tracking", + help="Fields with native tracking are always tracked.", + ) track = fields.Boolean("Track?", help="Track and, if change, go back to Potencial", default=True) block_edition = fields.Boolean( help="Do not allow to edit this field if the partner is approved", default=True, ) + @api.depends("field_id.tracking") + def _compute_native_tracking(self): + for line in self: + line.native_tracking = bool(line.field_id.tracking) + + @api.depends("changes", "native_tracking") + def _compute_chatter_tracking(self): + for line in self: + line.chatter_tracking = line.changes or line.native_tracking + + def _inverse_chatter_tracking(self): + for line in self.filtered(lambda x: not x.native_tracking): + line.changes = line.chatter_tracking + @api.onchange("track") def _compute_changes(self): if self.track: diff --git a/partner_state/tests/__init__.py b/partner_state/tests/__init__.py index f02936a48..d229259f0 100644 --- a/partner_state/tests/__init__.py +++ b/partner_state/tests/__init__.py @@ -4,3 +4,4 @@ ############################################################################## from . import test_partner_state +from . import test_partner_state_tracking diff --git a/partner_state/tests/test_partner_state_tracking.py b/partner_state/tests/test_partner_state_tracking.py new file mode 100644 index 000000000..0fe7fae0d --- /dev/null +++ b/partner_state/tests/test_partner_state_tracking.py @@ -0,0 +1,87 @@ +############################################################################## +# For copyright and license notices, see __manifest__.py file in module root +# directory +############################################################################## + +import re + +from odoo.tests.common import TransactionCase + + +class TestPartnerStateTracking(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.StateField = cls.env["res.partner.state_field"] + cls.StateField.search([]).unlink() + cls.env.company.partner_state_enable = True + cls.partner = cls.env["res.partner"].create({"name": "Tracking Partner", "email": "old@example.com"}) + # records created in the same transaction are not tracked until creation is finalized + cls._flush_tracking(cls.env) + + @staticmethod + def _flush_tracking(env): + env.flush_all() + env.cr.precommit.run() + + def _tracked_fields(self, vals): + """Write vals on the partner and return the field names logged in the chatter.""" + self.partner.message_ids.unlink() + self.partner.write(vals) + self._flush_tracking(self.env) + messages = self.partner.message_ids + if "tracking_value_ids" in messages._fields: + return set(messages.tracking_value_ids.field_id.mapped("name")) + # without mail_tracking, Odoo renders the tracking only in the body: "old → new (Label)" + labels = set(re.findall(r"\((.*?)\)", "".join(messages.mapped("body")))) + return {name for name in vals if self.partner._fields[name].string in labels} + + def _add_line(self, field_name, changes): + return self.StateField.create( + { + "field_id": self.env["ir.model.fields"]._get("res.partner", field_name).id, + "changes": changes, + "track": False, + "approval": False, + "block_edition": False, + } + ) + + def test_chatter_tracking_by_state_fields(self): + """ "Changes?" only adds fields to the chatter, it never removes standard ones. + + - Without lines, the standard tracked fields are logged. + - A line with "Changes?" logs a field that is not tracked by default, and an + approved partner does not go back to potential. + - A line without "Changes?" does not stop logging a standard tracked field. + """ + with self.subTest("without lines, a standard tracked field is logged"): + self.assertEqual(self._tracked_fields({"email": "new@example.com"}), {"email"}) + + with self.subTest("a line with changes logs a field not tracked by default and keeps the partner approved"): + self._add_line("ref", changes=True) + self.partner.partner_state_approved() + self.assertEqual(self._tracked_fields({"ref": "REF-1", "email": "other@example.com"}), {"ref", "email"}) + self.assertEqual(self.partner.partner_state, "approved") + + with self.subTest("a line without changes keeps logging a standard tracked field"): + self._add_line("email", changes=False) + self.assertEqual( + self._tracked_fields({"ref": "REF-2", "email": "last@example.com"}), + {"ref", "email"}, + ) + + def test_changes_column_for_standard_fields(self): + """The "Changes?" column is checked and fixed for fields already tracked by Odoo.""" + email_line = self._add_line("email", changes=False) + ref_line = self._add_line("ref", changes=False) + + with self.subTest("a standard field shows changes checked even if the line has it unchecked"): + self.assertTrue(email_line.native_tracking) + self.assertTrue(email_line.chatter_tracking) + + with self.subTest("another field shows and writes the stored changes value"): + self.assertFalse(ref_line.native_tracking) + self.assertFalse(ref_line.chatter_tracking) + ref_line.chatter_tracking = True + self.assertTrue(ref_line.changes) diff --git a/partner_state/views/res_partner_state_field_views.xml b/partner_state/views/res_partner_state_field_views.xml index 394274c8b..a2c817a05 100644 --- a/partner_state/views/res_partner_state_field_views.xml +++ b/partner_state/views/res_partner_state_field_views.xml @@ -7,7 +7,9 @@ - + + + From bc1c2fc90dececfa3bf752fa8943d5ccb36c163e Mon Sep 17 00:00:00 2001 From: les-adhoc Date: Thu, 22 Oct 2026 13:11:39 -0300 Subject: [PATCH 5/5] [FIX] partner_state: tracking test also sees fields logged outside the written values Without mail_tracking the test reads the logged labels from the message body. It now maps them against every field the model tracks, not only the written ones, so a field logged on top (a related field, or one that starts to be tracked natively) breaks the equality. --- partner_state/tests/test_partner_state_tracking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/partner_state/tests/test_partner_state_tracking.py b/partner_state/tests/test_partner_state_tracking.py index 0fe7fae0d..c961fd26e 100644 --- a/partner_state/tests/test_partner_state_tracking.py +++ b/partner_state/tests/test_partner_state_tracking.py @@ -34,7 +34,7 @@ def _tracked_fields(self, vals): return set(messages.tracking_value_ids.field_id.mapped("name")) # without mail_tracking, Odoo renders the tracking only in the body: "old → new (Label)" labels = set(re.findall(r"\((.*?)\)", "".join(messages.mapped("body")))) - return {name for name in vals if self.partner._fields[name].string in labels} + return {name for name in self.partner._track_get_fields() if self.partner._fields[name].string in labels} def _add_line(self, field_name, changes): return self.StateField.create(