diff --git a/partner_address_version/README.rst b/partner_address_version/README.rst new file mode 100644 index 000000000..496af0b0f --- /dev/null +++ b/partner_address_version/README.rst @@ -0,0 +1,81 @@ +======================= +Partner Address Version +======================= + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpartner--contact-lightgray.png?logo=github + :target: https://github.com/OCA/partner-contact/tree/12.0/partner_address_version + :alt: OCA/partner-contact +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/partner-contact-12-0/partner-contact-12-0-partner_address_version + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/134/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows you to manage versions for partner addresses. + +A list of fields are defined for versioning. These are immutable once set, and force the user to create a new partner +if they want to change one of these fields. + +This forces historical consistency. For example, the moment you confirm a +sale order, you might want to lock the address of that sale order instead of having it +change everytime that partner is modified (see e.g sale_partner_version). + +**Table of contents** + +.. contents:: + :local: + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Akretion + +Contributors +~~~~~~~~~~~~ + +* Benoît Guillot +* Kevin Khao + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/partner-contact `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/partner_address_version/__init__.py b/partner_address_version/__init__.py new file mode 100644 index 000000000..ebae2035b --- /dev/null +++ b/partner_address_version/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2018 Akretion - Benoît Guillot +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models +from . import wizards diff --git a/partner_address_version/__manifest__.py b/partner_address_version/__manifest__.py new file mode 100644 index 000000000..9347f73cb --- /dev/null +++ b/partner_address_version/__manifest__.py @@ -0,0 +1,14 @@ +# Copyright 2018 Akretion - Benoît Guillot +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Partner Address Version", + "version": "14.0.1.0.0", + "author": "Akretion, " "Odoo Community Association (OCA)", + "website": "https://github.com/OCA/partner-contact", + "category": "CRM", + "license": "AGPL-3", + "installable": True, + "depends": [ + "base", + ], +} diff --git a/partner_address_version/i18n/partner_address_version.pot b/partner_address_version/i18n/partner_address_version.pot new file mode 100644 index 000000000..bc41ef3de --- /dev/null +++ b/partner_address_version/i18n/partner_address_version.pot @@ -0,0 +1,38 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * partner_address_version +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: partner_address_version +#: model:ir.model.fields,field_description:partner_address_version.field_res_partner_date_version +#: model:ir.model.fields,field_description:partner_address_version.field_res_users_date_version +msgid "Date version" +msgstr "" + +#. module: partner_address_version +#: model:ir.model,name:partner_address_version.model_res_partner +msgid "Partner" +msgstr "" + +#. module: partner_address_version +#: model:ir.model.fields,field_description:partner_address_version.field_res_partner_version_hash +#: model:ir.model.fields,field_description:partner_address_version.field_res_users_version_hash +msgid "Version hash" +msgstr "" + +#. module: partner_address_version +#: code:addons/partner_address_version/models/res_partner.py:63 +#, python-format +msgid "You can't modify a versioned field %s on the versioned partner %s." +msgstr "" + diff --git a/partner_address_version/models/__init__.py b/partner_address_version/models/__init__.py new file mode 100644 index 000000000..6e8677380 --- /dev/null +++ b/partner_address_version/models/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2018 Akretion - Benoît Guillot +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import res_partner diff --git a/partner_address_version/models/res_partner.py b/partner_address_version/models/res_partner.py new file mode 100644 index 000000000..a077bb1ad --- /dev/null +++ b/partner_address_version/models/res_partner.py @@ -0,0 +1,113 @@ +# Copyright 2018 Akretion - Benoît Guillot +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import hashlib +from collections import OrderedDict + +from odoo import _, exceptions, fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + version_hash = fields.Char(readonly=True, copy=False) + date_version = fields.Datetime(string="Date version", readonly=True) + + def get_version_fields(self): + # deprecated uses _version_fields instead + return self._version_fields() + + def _version_fields(self): + return [ + "name", + "street", + "street2", + "zip", + "city", + "country_id", + "parent_id", + ] + + def get_version_hash(self): + # deprecated uses _version_hash instead + return self._version_hash() + + def _version_hash(self): + version_fields = self._version_fields() + version = OrderedDict() + for field in version_fields: + if field == "parent_id": + parent_id = self.parent_id.id if self.parent_id else self.id + version[field] = parent_id + elif self[field]: + version[field] = self[field] + version_hash = hashlib.md5(str(version).encode("utf-8")).hexdigest() + return version_hash + + def _version_impacted_tables(self): + """ + :return: + - list of tables to update in case of address versioning + """ + return [] + + def _version_exclude_keys(self): + """ + :return: + - dict: + key = table name + value = list of columns to ignore in case of address + versioning + """ + return {} + + def _version_need(self): + """ + This method is supposed to be overriden to determine when + an address versioning is needed or not + :return: True if versioning is required else False + """ + return False + + def _version_apply(self): + self.ensure_one() + if self._version_need(): + # the address is used, create a new version and + # update related tables + version_p = self._version_create() + partner_wizard = self.env[ + "base.partner.merge.automatic.wizard" + ].with_context(address_version=True) + partner_wizard._update_foreign_keys(self, version_p) + return False + + def write(self, vals): + version_fields = self._version_fields() + has_written_versioned_fields = any((f in version_fields) for f in vals.keys()) + for partner in self: + if ( + not partner.version_hash + and not vals.get("version_hash", False) + and has_written_versioned_fields + ): + partner._version_apply() + + if partner.version_hash and has_written_versioned_fields: + raise exceptions.UserError( + _( + "You can't modify a versioned field %s on the " + "versioned partner %s." + ) + % (version_fields, partner.name) + ) + return super(ResPartner, self).write(vals) + + def _version_create(self): + version_hash = self._version_hash() + default = { + "active": False, + "version_hash": version_hash, + "parent_id": self.parent_id.id if self.parent_id else self.id, + "date_version": fields.Datetime.now(), + } + return self.copy(default=default) diff --git a/partner_address_version/readme/CONTRIBUTORS.rst b/partner_address_version/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..a433be1a9 --- /dev/null +++ b/partner_address_version/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* Benoît Guillot +* Kevin Khao +* Cédric Pigeon diff --git a/partner_address_version/readme/DESCRIPTION.rst b/partner_address_version/readme/DESCRIPTION.rst new file mode 100644 index 000000000..e05a9400b --- /dev/null +++ b/partner_address_version/readme/DESCRIPTION.rst @@ -0,0 +1,8 @@ +This module allows you to manage versions for partner addresses. + +A list of fields are defined for versioning. These are immutable once set, and force the user to create a new partner +if they want to change one of these fields. + +This forces historical consistency. For example, the moment you confirm a +sale order, you might want to lock the address of that sale order instead of having it +change everytime that partner is modified (see e.g sale_partner_version). diff --git a/partner_address_version/static/description/icon.png b/partner_address_version/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/partner_address_version/static/description/icon.png differ diff --git a/partner_address_version/static/description/index.html b/partner_address_version/static/description/index.html new file mode 100644 index 000000000..3826b412e --- /dev/null +++ b/partner_address_version/static/description/index.html @@ -0,0 +1,425 @@ + + + + + + +Partner Address Version + + + +
+

Partner Address Version

+ + +

Beta License: AGPL-3 OCA/partner-contact Translate me on Weblate Try me on Runbot

+

This module allows you to manage versions for partner addresses.

+

A list of fields are defined for versioning. These are immutable once set, and force the user to create a new partner +if they want to change one of these fields.

+

This forces historical consistency. For example, the moment you confirm a +sale order, you might want to lock the address of that sale order instead of having it +change everytime that partner is modified (see e.g sale_partner_version).

+

Table of contents

+ +
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Akretion
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/partner-contact project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/partner_address_version/tests/__init__.py b/partner_address_version/tests/__init__.py new file mode 100644 index 000000000..97dd0437c --- /dev/null +++ b/partner_address_version/tests/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2018 Akretion - Benoît Guillot +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import test_address_version diff --git a/partner_address_version/tests/test_address_version.py b/partner_address_version/tests/test_address_version.py new file mode 100644 index 000000000..84619c9c1 --- /dev/null +++ b/partner_address_version/tests/test_address_version.py @@ -0,0 +1,55 @@ +# Copyright 2018 Akretion - Benoît Guillot +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import hashlib +from collections import OrderedDict + +from odoo.exceptions import UserError +from odoo.tests import SavepointCase + + +class TestAddressVersion(SavepointCase): + @classmethod + def setUpClass(cls): + super(TestAddressVersion, cls).setUpClass() + cls.partner_vals = OrderedDict( + [ + ("name", u"Name"), + ("street", u"Street"), + ("street2", u"Street2"), + ("zip", u"Zip"), + ("city", u"City"), + ("country_id", cls.env.ref("base.fr")), + ] + ) + create_vals = cls.partner_vals.copy() + create_vals["country_id"] = cls.env.ref("base.fr").id + create_vals_2 = create_vals.copy() + cls.partner = cls.env["res.partner"].create(create_vals) + cls.partner_2 = cls.env["res.partner"].create(create_vals_2) + cls.partner_vals.update({"parent_id": cls.partner.id}) + + def test_hash(self): + test_hash = hashlib.md5(str(self.partner_vals).encode("utf-8")).hexdigest() + self.assertEqual(test_hash, self.partner._version_hash()) + + def test_create_version_partner(self): + new_partner = self.partner._version_create() + self.assertEqual(new_partner.active, False) + self.assertNotEqual(new_partner.id, self.partner.id) + self.assertEqual(new_partner.parent_id.id, self.partner.id) + + def test_write_versioned_partner(self): + new_partner = self.partner._version_create() + with self.assertRaises(UserError): + new_partner.street = "New street" + + def test_same_address_different_parent(self): + new_partner = self.partner._version_create() + new_partner_2 = self.partner_2._version_create() + for field in self.partner._version_fields(): + if field == "parent_id": + continue + self.assertEqual(new_partner[field], new_partner_2[field]) + self.assertNotEqual(new_partner.id, new_partner_2.id) + self.assertNotEqual(new_partner.version_hash, new_partner_2.version_hash) diff --git a/partner_address_version/wizards/__init__.py b/partner_address_version/wizards/__init__.py new file mode 100644 index 000000000..e3fc7010c --- /dev/null +++ b/partner_address_version/wizards/__init__.py @@ -0,0 +1 @@ +from . import base_partner_merge diff --git a/partner_address_version/wizards/base_partner_merge.py b/partner_address_version/wizards/base_partner_merge.py new file mode 100644 index 000000000..cb6890180 --- /dev/null +++ b/partner_address_version/wizards/base_partner_merge.py @@ -0,0 +1,23 @@ +# Copyright 2020 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import models + + +class MergePartnerAutomatic(models.TransientModel): + _inherit = "base.partner.merge.automatic.wizard" + + def _get_fk_on(self, table): + foreign_keys = super(MergePartnerAutomatic, self)._get_fk_on(table) + if table == "res_partner" and self.env.context.get("address_version"): + models = self.env["res.partner"]._version_impacted_tables() + limited_fk = [] + for fk in foreign_keys: + if fk[0] in models: + ignore_col_dict = self.env["res.partner"]._version_exclude_keys() + ignore_col = ignore_col_dict.get(fk[0], False) + if ignore_col and fk[1] in ignore_col: + continue + limited_fk.append(fk) + return limited_fk + return foreign_keys diff --git a/setup/partner_address_version/odoo/addons/partner_address_version b/setup/partner_address_version/odoo/addons/partner_address_version new file mode 120000 index 000000000..671837ddf --- /dev/null +++ b/setup/partner_address_version/odoo/addons/partner_address_version @@ -0,0 +1 @@ +../../../../partner_address_version \ No newline at end of file diff --git a/setup/partner_address_version/setup.py b/setup/partner_address_version/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/partner_address_version/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)