From f3f4a18099cf4c1e5f74d995cf4c6fda35f72807 Mon Sep 17 00:00:00 2001 From: Patrick Wilson <36892066+patrickrwilson@users.noreply.github.com> Date: Fri, 7 Oct 2022 09:31:57 -0400 Subject: [PATCH] [14.0][ADD] partner_usps_address_validation This module adds the ability for users to validate and correct addresses on contacts using the USPS address API. --- partner_usps_address_validation/README.rst | 80 +++++++++++ partner_usps_address_validation/__init__.py | 4 + .../__manifest__.py | 24 ++++ .../models/__init__.py | 4 + .../models/res_partner.py | 132 ++++++++++++++++++ .../models/usps_credential_configuration.py | 41 ++++++ .../readme/CONFIGURATION.rst | 2 + .../readme/CONTRIBUTORS.rst | 2 + .../readme/CREDITS.rst | 3 + .../readme/DESCRIPTION.rst | 3 + .../requirements.txt | 1 + .../security/ir.model.access.csv | 3 + .../static/description/icon.png | Bin 0 -> 9455 bytes .../views/config.xml | 45 ++++++ .../views/res_partner.xml | 29 ++++ .../wizard/__init__.py | 3 + .../wizard/usps_address_validation.py | 102 ++++++++++++++ .../wizard/usps_address_validation.xml | 51 +++++++ requirements.txt | 2 + .../addons/partner_usps_address_validation | 1 + .../partner_usps_address_validation/setup.py | 6 + 21 files changed, 538 insertions(+) create mode 100644 partner_usps_address_validation/README.rst create mode 100644 partner_usps_address_validation/__init__.py create mode 100644 partner_usps_address_validation/__manifest__.py create mode 100644 partner_usps_address_validation/models/__init__.py create mode 100644 partner_usps_address_validation/models/res_partner.py create mode 100644 partner_usps_address_validation/models/usps_credential_configuration.py create mode 100644 partner_usps_address_validation/readme/CONFIGURATION.rst create mode 100644 partner_usps_address_validation/readme/CONTRIBUTORS.rst create mode 100644 partner_usps_address_validation/readme/CREDITS.rst create mode 100644 partner_usps_address_validation/readme/DESCRIPTION.rst create mode 100644 partner_usps_address_validation/requirements.txt create mode 100644 partner_usps_address_validation/security/ir.model.access.csv create mode 100644 partner_usps_address_validation/static/description/icon.png create mode 100644 partner_usps_address_validation/views/config.xml create mode 100644 partner_usps_address_validation/views/res_partner.xml create mode 100644 partner_usps_address_validation/wizard/__init__.py create mode 100644 partner_usps_address_validation/wizard/usps_address_validation.py create mode 100644 partner_usps_address_validation/wizard/usps_address_validation.xml create mode 120000 setup/partner_usps_address_validation/odoo/addons/partner_usps_address_validation create mode 100644 setup/partner_usps_address_validation/setup.py diff --git a/partner_usps_address_validation/README.rst b/partner_usps_address_validation/README.rst new file mode 100644 index 00000000..b21858ce --- /dev/null +++ b/partner_usps_address_validation/README.rst @@ -0,0 +1,80 @@ +======================= +USPS Address Validation +======================= + + +.. |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 + +|badge1| |badge2| + +This module adds a tool to the Contacts page which validates the contact's address. Simply click the 'Validate' button, and the address of the contact will be compared to the USPS address database. The result will be a cleaned address, including the full 9 digit zipcode, in the exact format recognized by USPS + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +In the *General Settings* menu, enter your USPS API credentials under USPS Address Validation Settings + + +Usage +===== + +On a contact, enter as much of the address as possible to ensure an accurate match. Required are address, and either city/state or zipcode. Click the Validate button to bring up the wizard. User entered text appears on the left as the original address, and the right displays the USPS cleansed address, which may be edited as needed. Either accept or cancel the changes to return to the contact page. + + +Known issues / Roadmap +====================== + +* There are no knows issues at this time +* USPS will always return the address in all caps. CamelCase support is not planned for future releases. + +Bug Tracker +=========== + + +Credits +======= + +Authors +~~~~~~~ + +* ckolobow + +Contributors +~~~~~~~~~~~~ + +* Craig Kolobow + +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. + +.. |maintainer-ckolobow| image:: https://github.com/ckolobow.png?size=40px + :target: https://github.com/ckolobow + :alt: ckolobow + +Current `maintainer `__: + +|maintainer-ckolobow| + +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_usps_address_validation/__init__.py b/partner_usps_address_validation/__init__.py new file mode 100644 index 00000000..4cb4d0b1 --- /dev/null +++ b/partner_usps_address_validation/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2022 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from . import models +from . import wizard diff --git a/partner_usps_address_validation/__manifest__.py b/partner_usps_address_validation/__manifest__.py new file mode 100644 index 00000000..a4c799e3 --- /dev/null +++ b/partner_usps_address_validation/__manifest__.py @@ -0,0 +1,24 @@ +# Copyright 2022 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +{ + "name": "USPS Address Validation", + "category": "Contact", + "version": "14.0.1.0.0", + "summary": """Utilize the USPS open API for address validation""", + "depends": ["contacts"], + "external_dependencies": {"python": ["xmltodict", "requests"]}, + "data": [ + "security/ir.model.access.csv", + "wizard/usps_address_validation.xml", + "views/res_partner.xml", + "views/config.xml", + ], + "author": "Open Source Integrators, Odoo Community Association (OCA)", + "maintainer": ["ckolobow"], + "website": "https://github.com/OCA/l10n-usa", + "demo": [], + "installable": True, + "application": True, + "auto_install": False, + "license": "AGPL-3", +} diff --git a/partner_usps_address_validation/models/__init__.py b/partner_usps_address_validation/models/__init__.py new file mode 100644 index 00000000..fd5a36d9 --- /dev/null +++ b/partner_usps_address_validation/models/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2022 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from . import res_partner +from . import usps_credential_configuration diff --git a/partner_usps_address_validation/models/res_partner.py b/partner_usps_address_validation/models/res_partner.py new file mode 100644 index 00000000..26f56ce7 --- /dev/null +++ b/partner_usps_address_validation/models/res_partner.py @@ -0,0 +1,132 @@ +# Copyright 2022 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +import requests +import xmltodict + +from odoo import _, fields, models +from odoo.exceptions import ValidationError + + +class USPSAddressPartner(models.Model): + _inherit = "res.partner" + + usps_date_validation = fields.Date( + "Last Validation Date", + readonly=True, + copy=False, + help="The date the address was last validated by USPS and accepted", + ) + + def button_usps_address_validation(self): + view_ref = self.env.ref( + "partner_usps_address_validation.usps_address_validation_view_form" + ) + ctx = self.env.context.copy() + ctx.update({"active_ids": self.ids, "active_id": self.id}) + return { + "type": "ir.actions.act_window", + "name": "USPS Address Validation", + "binding_view_types": "form", + "view_mode": "form", + "view_id": view_ref.id, + "res_model": "usps.address.validation", + "nodestroy": True, + "res_id": False, + "target": "new", + "context": ctx, + } + + def cleanse_address(self, response_data): + response_data = response_data.get("AddressValidateResponse").get("Address") + if response_data.get("Address1") != "FALSE": + a1 = response_data.get("Address1") + else: + a1 = " " + if response_data.get("Address2") != "FALSE": + a2 = response_data.get("Address2") + else: + a2 = " " + if response_data.get("City") != "FALSE": + city = response_data.get("City") + else: + city = " " + if response_data.get("State") != "FALSE": + state = response_data.get("State") + else: + state = " " + if response_data.get("Zip5") != "FALSE": + z5 = response_data.get("Zip5") + else: + z5 = " " + if response_data.get("Zip4") != "FALSE": + z4 = response_data.get("Zip4") + else: + z4 = " " + if z4 != " " and z5 != " " and z4 and z5: + z = z5 + " - " + z4 + ok = "true" + elif z5: + z = z5 + ok = "false" + else: + raise ValidationError(response_data.get("Error").get("Description")) + if a2 != " " or a1 != " ": + am = "true" + else: + am = "false" + cleanse_address_res = { + "Address2": a1, + "Address1": a2, + "City": city, + "State": state, + "ZIPCode": z, + "AddressMatch": am, + "CityStateZipOK": ok, + } + return cleanse_address_res + + def usps_xml_request(self): + """ + prepare xml data for address validation api + """ + # Default address as of 6/22: "https://secure.shippingapis.com/ShippingAPI.dll" + try: + web = self.env["ir.config_parameter"].sudo().get_param("usps_api_url") + user_id = self.env["ir.config_parameter"].sudo().get_param("usps_username") + except Exception: + raise ValidationError( + _( + "Your credentials are not configured,\ + please ensure you have a username, password,\ + and API URL set under Contacts/Configuration/USPS\ + Credential Configuration" + ) + ) + address1 = str(self.street) + address2 = str(self.street2) + city = str(self.city) + state = str(self.state_id.code) + zipcode = str(self.zip) + request = ( + '%s+?API=Verify&XML=\ +
%s%s\ + %s%s\ + %s
' + % (web, user_id, address1, address2, city, state, zipcode) + ) + try: + response = requests.post(request) + response_data = xmltodict.parse(response.text) + except Exception as error: + raise ValidationError(error) + if response_data.get("Error"): + raise ValidationError(response_data.get("Error").get("Description")) + try: + cleanse_address_res = self.env["res.partner"].cleanse_address(response_data) + if cleanse_address_res: + return cleanse_address_res + else: + raise ValidationError(_(response_data)) + except Exception as error: + raise ValidationError(_(error)) diff --git a/partner_usps_address_validation/models/usps_credential_configuration.py b/partner_usps_address_validation/models/usps_credential_configuration.py new file mode 100644 index 00000000..915379c0 --- /dev/null +++ b/partner_usps_address_validation/models/usps_credential_configuration.py @@ -0,0 +1,41 @@ +# Copyright 2022 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + usps_api_url = fields.Char( + string="API URL", + default="https://secure.shippingapis.com/ShippingAPI.dll", + config_parameter="usps_api_url", + ) + usps_username = fields.Char(string="Username", config_parameter="usps_username") + usps_password = fields.Char(string="Password", config_parameter="usps_password") + + def get_values(self): + res = super(ResConfigSettings, self).get_values() + res.update( + usps_api_url=self.env["ir.config_parameter"] + .sudo() + .get_param("usps_api_url"), + usps_username=self.env["ir.config_parameter"] + .sudo() + .get_param("usps_username"), + usps_password=self.env["ir.config_parameter"] + .sudo() + .get_param("usps_password"), + ) + return res + + def set_values(self): + super(ResConfigSettings, self).set_values() + param = self.env["ir.config_parameter"].sudo() + usps_api_url = self.usps_api_url + usps_username = self.usps_username + usps_password = self.usps_password + param.set_param("usps_api_url", usps_api_url) + param.set_param("usps_username", usps_username) + param.set_param("usps_password", usps_password) diff --git a/partner_usps_address_validation/readme/CONFIGURATION.rst b/partner_usps_address_validation/readme/CONFIGURATION.rst new file mode 100644 index 00000000..8c830fd8 --- /dev/null +++ b/partner_usps_address_validation/readme/CONFIGURATION.rst @@ -0,0 +1,2 @@ +- Create a USPS account at https://www.usps.com/business/web-tools-apis/ and get API credentials. +- In the *General Settings* menu, enter your USPS API credentials under USPS Address Validation Settings diff --git a/partner_usps_address_validation/readme/CONTRIBUTORS.rst b/partner_usps_address_validation/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..2117d950 --- /dev/null +++ b/partner_usps_address_validation/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Craig Kolobow +* Patrick Wilson diff --git a/partner_usps_address_validation/readme/CREDITS.rst b/partner_usps_address_validation/readme/CREDITS.rst new file mode 100644 index 00000000..fa99fe6f --- /dev/null +++ b/partner_usps_address_validation/readme/CREDITS.rst @@ -0,0 +1,3 @@ +**Financial support** + +* Open Source Integrators diff --git a/partner_usps_address_validation/readme/DESCRIPTION.rst b/partner_usps_address_validation/readme/DESCRIPTION.rst new file mode 100644 index 00000000..4b8177e9 --- /dev/null +++ b/partner_usps_address_validation/readme/DESCRIPTION.rst @@ -0,0 +1,3 @@ +This module adds a tool to the Contacts page which validates the contact's address. +Simply click the 'Validate' button, and the address of the contact will be compared to the USPS address database. +The result will be a cleaned address, including the full 9 digit zipcode, in the exact format recognized by USPS. diff --git a/partner_usps_address_validation/requirements.txt b/partner_usps_address_validation/requirements.txt new file mode 100644 index 00000000..a0d4bb76 --- /dev/null +++ b/partner_usps_address_validation/requirements.txt @@ -0,0 +1 @@ +xmltodict diff --git a/partner_usps_address_validation/security/ir.model.access.csv b/partner_usps_address_validation/security/ir.model.access.csv new file mode 100644 index 00000000..3d9b657d --- /dev/null +++ b/partner_usps_address_validation/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_usps_address_validation,usps_address_validation,model_usps_address_validation,base.group_user,1,1,1,0 +access_usps_address_validation_manager,manager_usps_address_validation,model_usps_address_validation,base.group_user,1,1,1,1 diff --git a/partner_usps_address_validation/static/description/icon.png b/partner_usps_address_validation/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/partner_usps_address_validation/views/config.xml b/partner_usps_address_validation/views/config.xml new file mode 100644 index 00000000..c600947a --- /dev/null +++ b/partner_usps_address_validation/views/config.xml @@ -0,0 +1,45 @@ + + + + res.config.settings.view.form.inherit.base.setup + res.config.settings + + + + +
+
USPS Address Validation Settings
+
+
+ + + + + + +
+
+
+
+
+
diff --git a/partner_usps_address_validation/views/res_partner.xml b/partner_usps_address_validation/views/res_partner.xml new file mode 100644 index 00000000..ff070496 --- /dev/null +++ b/partner_usps_address_validation/views/res_partner.xml @@ -0,0 +1,29 @@ + + + + USPS Address Validation + res.partner + + + +