From 9760cfa9780fd89caec0225473f2694bb0244faa Mon Sep 17 00:00:00 2001 From: emjay0921 Date: Wed, 17 Sep 2025 16:48:44 +0800 Subject: [PATCH 1/2] [ADD] phone validations --- spp_registry_base/__manifest__.py | 1 + spp_registry_base/models/__init__.py | 2 + spp_registry_base/models/phone_number.py | 48 ++++++++++++ .../models/phone_number_validation.py | 32 ++++++++ .../security/ir.model.access.csv | 3 + spp_registry_base/tests/__init__.py | 1 + .../tests/test_phone_number_validation.py | 69 +++++++++++++++++ .../views/phone_validation_view.xml | 74 +++++++++++++++++++ 8 files changed, 230 insertions(+) create mode 100644 spp_registry_base/models/phone_number.py create mode 100644 spp_registry_base/models/phone_number_validation.py create mode 100644 spp_registry_base/tests/test_phone_number_validation.py create mode 100644 spp_registry_base/views/phone_validation_view.xml diff --git a/spp_registry_base/__manifest__.py b/spp_registry_base/__manifest__.py index 57c84fdd7..b6cf4be8f 100644 --- a/spp_registry_base/__manifest__.py +++ b/spp_registry_base/__manifest__.py @@ -24,6 +24,7 @@ "views/groups_view.xml", "views/individuals_view.xml", "views/main_view.xml", + "views/phone_validation_view.xml", ], "assets": { "web.assets_backend": [ diff --git a/spp_registry_base/models/__init__.py b/spp_registry_base/models/__init__.py index 91fed54d4..5ae2fb221 100644 --- a/spp_registry_base/models/__init__.py +++ b/spp_registry_base/models/__init__.py @@ -1 +1,3 @@ from . import res_partner +from . import phone_number +from . import phone_number_validation diff --git a/spp_registry_base/models/phone_number.py b/spp_registry_base/models/phone_number.py new file mode 100644 index 000000000..619fb6af9 --- /dev/null +++ b/spp_registry_base/models/phone_number.py @@ -0,0 +1,48 @@ +import logging +import re + +from odoo import _, api, models +from odoo.exceptions import ValidationError + +_logger = logging.getLogger(__name__) + + +class G2PPhoneNumber(models.Model): + _inherit = "g2p.phone.number" + + def write(self, vals): + res = super().write(vals) + if "phone_no" in vals or "country_id" in vals: + self._onchange_phone_validation() + return res + + @api.model_create_multi + def create(self, vals): + record = super().create(vals) + record._onchange_phone_validation() + return record + + @api.onchange("phone_no", "country_id") + def _onchange_phone_validation(self): + phone_validation = self.env["spp.phone.validation"].search([("state", "=", "active")]) + if not self.phone_no: + return + + phone_no = self.phone_no + if phone_validation: + validated_success_count = 0 + error_msg = [] + for validation in phone_validation: + if validation.with_prefix: + pattern = r"^\+?" + re.escape(validation.prefix) + r"\d{" + str(validation.number_of_digits) + r"}$" + else: + pattern = r"^\d{" + str(validation.number_of_digits) + r"}$" + if re.match(pattern, phone_no): + validated_success_count += 1 + else: + error_msg.append(validation.name) + + if validated_success_count == 0: + message = "Phone number must match one of the following formats: " + ", ".join(error_msg) + raise ValidationError(_(message)) + return diff --git a/spp_registry_base/models/phone_number_validation.py b/spp_registry_base/models/phone_number_validation.py new file mode 100644 index 000000000..f7d6f8bef --- /dev/null +++ b/spp_registry_base/models/phone_number_validation.py @@ -0,0 +1,32 @@ +from odoo import api, fields, models + + +class SPPPhoneValidation(models.Model): + _name = "spp.phone.validation" + _description = "SPP Phone Validation" + + name = fields.Char(string="Sample Format", compute="_compute_name") + number_of_digits = fields.Integer(string="Number of Digits", required=True) + with_prefix = fields.Boolean(string="With Prefix") + prefix = fields.Char(string="Prefix") + state = fields.Selection( + [("active", "Active"), ("inactive", "Inactive")], + string="State", + default="active", + ) + + @api.depends("number_of_digits", "with_prefix", "prefix") + def _compute_name(self): + for record in self: + if record.with_prefix and record.prefix: + record.name = f"{record.prefix}{'X'*record.number_of_digits}" + else: + record.name = "X" * record.number_of_digits + + def activate_phone_validation(self): + for record in self: + record.state = "active" + + def deactivate_phone_validation(self): + for record in self: + record.state = "inactive" diff --git a/spp_registry_base/security/ir.model.access.csv b/spp_registry_base/security/ir.model.access.csv index 625a34573..da4ed57e0 100644 --- a/spp_registry_base/security/ir.model.access.csv +++ b/spp_registry_base/security/ir.model.access.csv @@ -10,6 +10,7 @@ id_type_read_registry_access,ID Type Read Access,g2p_registry_base.model_g2p_id_ group_kind_read_registry_access,Group Kind Read Access,g2p_registry_group.model_g2p_group_kind,spp_registry_base.read_registry,1,0,0,0 spp_read_res_model_access,Res Model Read Access,base.model_ir_model,spp_registry_base.read_registry,1,0,0,0 spp_read_res_model_fields_access,Res Model Fields Read Access,base.model_ir_model_fields,spp_registry_base.read_registry,1,0,0,0 +spp_phone_number_validation_read_access,Phone Number Validation Read Access,spp_registry_base.model_spp_phone_validation,spp_registry_base.read_registry,1,0,0,0 res_partner_write_registry_access,Registry Write Access,base.model_res_partner,spp_registry_base.write_registry,1,1,0,0 group_membership_write_registry_access,Group Membership Write Access,g2p_registry_membership.model_g2p_group_membership,spp_registry_base.write_registry,1,1,0,0 @@ -21,6 +22,7 @@ id_type_write_registry_access,ID Type Write Access,g2p_registry_base.model_g2p_i group_kind_write_registry_access,Group Kind Write Access,g2p_registry_group.model_g2p_group_kind,spp_registry_base.write_registry,1,1,0,0 spp_write_res_model_access,Res Model Write Access,base.model_ir_model,spp_registry_base.write_registry,1,1,0,0 spp_write_res_model_fields_access,Res Model Fields Write Access,base.model_ir_model_fields,spp_registry_base.write_registry,1,1,0,0 +spp_phone_number_validation_write_access,Phone Number Validation Write Access,spp_registry_base.model_spp_phone_validation,spp_registry_base.write_registry,1,1,0,0 res_partner_create_registry_access,Registry Create Access,base.model_res_partner,spp_registry_base.create_registry,1,0,1,0 group_membership_create_registry_access,Group Membership Create Access,g2p_registry_membership.model_g2p_group_membership,spp_registry_base.create_registry,1,1,1,0 @@ -32,3 +34,4 @@ id_type_create_registry_access,ID Type Create Access,g2p_registry_base.model_g2p group_kind_create_registry_access,Group Kind Create Access,g2p_registry_group.model_g2p_group_kind,spp_registry_base.create_registry,1,0,1,0 spp_create_res_model_access,Res Model Create Access,base.model_ir_model,spp_registry_base.create_registry,1,0,1,0 spp_create_res_model_fields_access,Res Model Fields Create Access,base.model_ir_model_fields,spp_registry_base.create_registry,1,0,1,0 +spp_phone_number_validation_create_access,Phone Number Validation Create Access,spp_registry_base.model_spp_phone_validation,spp_registry_base.create_registry,1,0,1,0 diff --git a/spp_registry_base/tests/__init__.py b/spp_registry_base/tests/__init__.py index d57d215f9..ced754a03 100644 --- a/spp_registry_base/tests/__init__.py +++ b/spp_registry_base/tests/__init__.py @@ -1 +1,2 @@ from . import test_res_partner +from . import test_phone_number_validation diff --git a/spp_registry_base/tests/test_phone_number_validation.py b/spp_registry_base/tests/test_phone_number_validation.py new file mode 100644 index 000000000..20f31c85a --- /dev/null +++ b/spp_registry_base/tests/test_phone_number_validation.py @@ -0,0 +1,69 @@ +from odoo.exceptions import ValidationError +from odoo.tests import TransactionCase + + +class TestPhoneValidation(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.partner_model = cls.env["res.partner"] + cls.phone_validation_model = cls.env["spp.phone.validation"] + cls.phone_model = cls.env["g2p.phone.number"] + cls.registrant = cls.partner_model.create( + { + "name": "Test Registrant", + "is_registrant": True, + } + ) + cls.phone_validation_1 = cls.phone_validation_model.create( + { + "number_of_digits": 10, + "with_prefix": True, + "prefix": "+63", + "state": "active", + } + ) + cls.phone_validation_2 = cls.phone_validation_model.create( + { + "number_of_digits": 11, + "with_prefix": False, + "state": "active", + } + ) + + def test_01_create_phone_with_invalid_number(self): + phone = self.phone_model.create( + { + "partner_id": self.registrant.id, + "phone_no": "+639123456789", + "country_id": self.env.ref("base.ph").id, + } + ) + + with self.assertRaises(ValidationError) as cm: + phone.phone_no = "12345" + phone._onchange_phone_validation() + + self.assertIn("Phone number must match one of the following formats", str(cm.exception)) + + def test_02_create_phone_with_valid_number_with_prefix(self): + phone = self.phone_model.create( + { + "partner_id": self.registrant.id, + "phone_no": "+639123456789", + "country_id": self.env.ref("base.ph").id, + } + ) + phone._onchange_phone_validation() + self.assertEqual(phone.phone_no, "+639123456789") + + def test_03_create_phone_with_valid_number_without_prefix(self): + phone = self.phone_model.create( + { + "partner_id": self.registrant.id, + "phone_no": "09123456789", + "country_id": self.env.ref("base.ph").id, + } + ) + phone._onchange_phone_validation() + self.assertEqual(phone.phone_no, "09123456789") diff --git a/spp_registry_base/views/phone_validation_view.xml b/spp_registry_base/views/phone_validation_view.xml new file mode 100644 index 000000000..edf7b1519 --- /dev/null +++ b/spp_registry_base/views/phone_validation_view.xml @@ -0,0 +1,74 @@ + + + + + view_phone_validation_tree + spp.phone.validation + 1 + + + + + + + +