From 2011d84fe0235d790fdab538119296a0d3917148 Mon Sep 17 00:00:00 2001 From: joshua160miller Date: Fri, 18 Oct 2024 16:27:48 +0530 Subject: [PATCH] added unit tests and g2p_social_registry_model --- README.md | 1 + g2p_registry_id_deduplication/__manifest__.py | 1 + .../tests/__init__.py | 2 +- .../tests/test_registrant.py | 15 --- .../tests/test_res_config_settings.py | 71 +++++++++++ g2p_social_registry/tests/__init__.py | 4 + .../tests/test_pending_ref_id.py | 47 +++++++ .../tests/test_ref_id_config.py | 67 ++++++++++ .../tests/test_res_config_settings.py | 77 ++++++++++++ g2p_social_registry_model/README.md | 3 + g2p_social_registry_model/__init__.py | 3 + g2p_social_registry_model/__manifest__.py | 21 ++++ g2p_social_registry_model/models/__init__.py | 4 + .../models/odk_client.py | 49 ++++++++ .../models/res_partner.py | 117 ++++++++++++++++++ g2p_social_registry_model/pyproject.toml | 3 + .../static/description/icon.png | Bin 0 -> 3985 bytes .../views/group_view.xml | 41 ++++++ .../views/individual_view.xml | 18 +++ g2p_social_registry_theme/tests/__init__.py | 1 + .../tests/test_res_company.py | 28 +++++ .../tests/test_res_user.py | 63 ++++++++++ 22 files changed, 620 insertions(+), 16 deletions(-) create mode 100644 g2p_registry_id_deduplication/tests/test_res_config_settings.py create mode 100644 g2p_social_registry/tests/test_pending_ref_id.py create mode 100644 g2p_social_registry/tests/test_ref_id_config.py create mode 100644 g2p_social_registry/tests/test_res_config_settings.py create mode 100644 g2p_social_registry_model/README.md create mode 100644 g2p_social_registry_model/__init__.py create mode 100644 g2p_social_registry_model/__manifest__.py create mode 100644 g2p_social_registry_model/models/__init__.py create mode 100644 g2p_social_registry_model/models/odk_client.py create mode 100644 g2p_social_registry_model/models/res_partner.py create mode 100644 g2p_social_registry_model/pyproject.toml create mode 100644 g2p_social_registry_model/static/description/icon.png create mode 100644 g2p_social_registry_model/views/group_view.xml create mode 100644 g2p_social_registry_model/views/individual_view.xml create mode 100644 g2p_social_registry_theme/tests/__init__.py create mode 100644 g2p_social_registry_theme/tests/test_res_company.py create mode 100644 g2p_social_registry_theme/tests/test_res_user.py diff --git a/README.md b/README.md index 792be5f..2829e8a 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ addon | version | maintainers | summary [g2p_registry_g2p_connect_rest_api](g2p_registry_g2p_connect_rest_api/) | 17.0.0.0.0 | | OpenG2P Registry: G2P Connect REST API [g2p_registry_id_deduplication](g2p_registry_id_deduplication/) | 17.0.0.0.0 | | OpenG2P Registry ID Deduplication [g2p_social_registry](g2p_social_registry/) | 17.0.0.0.0 | | OpenG2P Social Registry +[g2p_social_registry_model](g2p_social_registry_model/) | 17.0.0.0.0 | | G2P Social Registry: Demo [g2p_social_registry_theme](g2p_social_registry_theme/) | 17.0.0.0.0 | | OpenG2P Social Registry: Theme [//]: # (end addons) diff --git a/g2p_registry_id_deduplication/__manifest__.py b/g2p_registry_id_deduplication/__manifest__.py index 9bde46b..a075c57 100644 --- a/g2p_registry_id_deduplication/__manifest__.py +++ b/g2p_registry_id_deduplication/__manifest__.py @@ -27,6 +27,7 @@ "/g2p_registry_id_deduplication/static/src/xml/duplicate_template.xml", ], }, + # 'test': ['tests/test_registry_config.py', 'tests/test_registrant.py'], "demo": [], "images": [], "application": True, diff --git a/g2p_registry_id_deduplication/tests/__init__.py b/g2p_registry_id_deduplication/tests/__init__.py index cb467d2..e1ea489 100644 --- a/g2p_registry_id_deduplication/tests/__init__.py +++ b/g2p_registry_id_deduplication/tests/__init__.py @@ -1,3 +1,3 @@ # Part of OpenG2P. See LICENSE file for full copyright and licensing details. -from . import test_registrant +from . import test_registrant, test_res_config_settings diff --git a/g2p_registry_id_deduplication/tests/test_registrant.py b/g2p_registry_id_deduplication/tests/test_registrant.py index d9af5c1..ba2fa2e 100644 --- a/g2p_registry_id_deduplication/tests/test_registrant.py +++ b/g2p_registry_id_deduplication/tests/test_registrant.py @@ -5,7 +5,6 @@ class TestResPartnerIDDeduplication(TransactionCase): def setUp(self): super().setUp() - self.partner_model = self.env["res.partner"] self.id_type_model = self.env["g2p.id.type"] self.reg_id_model = self.env["g2p.reg.id"] @@ -13,19 +12,13 @@ def setUp(self): self.dedup_grp_kind_config_model = self.env["g2p.group.kind.deduplication.config"] self.group_kind_model = self.env["g2p.group.kind"] self.config_parameter_model = self.env["ir.config_parameter"] - - # Create individual partners self.individual_1 = self.partner_model.create( {"name": "Individual 1", "is_registrant": True, "is_group": False} ) self.individual_2 = self.partner_model.create( {"name": "Individual 2", "is_registrant": True, "is_group": False} ) - - # Create group kind self.group_kind = self.group_kind_model.create({"name": "Test Kind"}) - - # Create group partners self.group_1 = self.partner_model.create( { "name": "Group 1", @@ -45,11 +38,9 @@ def setUp(self): } ) - # Create ID types self.id_type_1 = self.id_type_model.create({"name": "Household ID"}) self.id_type_2 = self.id_type_model.create({"name": "National ID"}) - # Create registration IDs for partners self.reg_id_1 = self.reg_id_model.create( { "partner_id": self.individual_1.id, @@ -87,7 +78,6 @@ def setUp(self): } ) - # Create group kind deduplication config self.grp_dedup_config = self.dedup_grp_kind_config_model.create( { "kind_id": self.group_kind.id, @@ -95,7 +85,6 @@ def setUp(self): } ) - # Set configuration parameters for deduplication self.config_parameter_model.set_param( "g2p_registry_id_deduplication.ind_deduplication_id_types_ids", f"[{self.id_type_2.id}]" ) @@ -105,7 +94,6 @@ def setUp(self): def test_deduplicate_registrants_individuals(self): self.partner_model.with_context(default_is_group=False).deduplicate_registrants() - self.assertTrue(self.individual_1.is_duplicated) self.assertTrue(self.individual_2.is_duplicated) self.assertFalse(self.group_1.is_duplicated) @@ -113,7 +101,6 @@ def test_deduplicate_registrants_individuals(self): def test_deduplicate_registrants_groups(self): self.partner_model.with_context(default_is_group=True).deduplicate_registrants() - self.assertFalse(self.individual_1.is_duplicated) self.assertFalse(self.individual_2.is_duplicated) self.assertTrue(self.group_1.is_duplicated) @@ -123,13 +110,11 @@ def test_deduplicate_registrants_missing_configuration(self): self.config_parameter_model.set_param( "g2p_registry_id_deduplication.ind_deduplication_id_types_ids", "[]" ) - with self.assertRaises(UserError, msg="Deduplication is not configured"): self.partner_model.with_context(default_is_group=False).deduplicate_registrants() def test_reset_duplicate_flag(self): self.partner_model.with_context(default_is_group=False).reset_duplicate_flag(False) - self.assertFalse(self.individual_1.is_duplicated) self.assertFalse(self.individual_2.is_duplicated) self.assertFalse(self.group_1.is_duplicated) diff --git a/g2p_registry_id_deduplication/tests/test_res_config_settings.py b/g2p_registry_id_deduplication/tests/test_res_config_settings.py new file mode 100644 index 0000000..ef9ade0 --- /dev/null +++ b/g2p_registry_id_deduplication/tests/test_res_config_settings.py @@ -0,0 +1,71 @@ +from odoo.tests.common import TransactionCase +from odoo.tools.safe_eval import safe_eval + + +class TestRegistryConfig(TransactionCase): + def setUp(self): + super().setUp() + self.registry_config_model = self.env["res.config.settings"] + self.id_type_model = self.env["g2p.id.type"] + self.grp_dedup_config_model = self.env["g2p.group.kind.deduplication.config"] + self.id_type_1 = self.id_type_model.create({"name": "National ID"}) + self.id_type_2 = self.id_type_model.create({"name": "Household ID"}) + self.grp_dedup_config = self.grp_dedup_config_model.create( + { + "kind_id": self.env["g2p.group.kind"].create({"name": "Test Group Kind"}).id, + "id_type_ids": [(6, 0, [self.id_type_1.id, self.id_type_2.id])], + } + ) + + def test_set_and_get_values(self): + config_settings = self.registry_config_model.create({}) + config_settings.grp_deduplication_id_types_ids = [(4, self.grp_dedup_config.id)] + config_settings.ind_deduplication_id_types_ids = [(4, self.id_type_1.id), (4, self.id_type_2.id)] + config_settings.set_values() + config_settings.get_values() + self.assertIn(self.grp_dedup_config.id, config_settings.grp_deduplication_id_types_ids.mapped("id")) + self.assertIn(self.id_type_1.id, config_settings.ind_deduplication_id_types_ids.mapped("id")) + self.assertIn(self.id_type_2.id, config_settings.ind_deduplication_id_types_ids.mapped("id")) + + def test_delete_id_type_updates_config(self): + config_settings = self.registry_config_model.create({}) + config_settings.ind_deduplication_id_types_ids = [(4, self.id_type_1.id), (4, self.id_type_2.id)] + config_settings.set_values() + self.id_type_1.unlink() + ir_config = self.env["ir.config_parameter"].sudo() + updated_id_types_param = ir_config.get_param( + "g2p_registry_id_deduplication.ind_deduplication_id_types_ids" + ) + updated_id_types_ids = safe_eval(updated_id_types_param) + self.assertNotIn(self.id_type_1.id, updated_id_types_ids) + self.assertIn(self.id_type_2.id, updated_id_types_ids) + + def test_set_values_empty_ids(self): + config_settings = self.registry_config_model.create({}) + config_settings.set_values() + ir_config = self.env["ir.config_parameter"].sudo() + ir_config.set_param("g2p_registry_id_deduplication.grp_deduplication_id_types_ids", "[]") + ir_config.set_param("g2p_registry_id_deduplication.ind_deduplication_id_types_ids", "[]") + grp_id_types_param = ir_config.get_param( + "g2p_registry_id_deduplication.grp_deduplication_id_types_ids" + ) + ind_id_types_param = ir_config.get_param( + "g2p_registry_id_deduplication.ind_deduplication_id_types_ids" + ) + self.assertEqual(grp_id_types_param, "[]") + self.assertEqual(ind_id_types_param, "[]") + + def test_unlink_id_type_with_no_references(self): + new_id_type = self.id_type_model.create({"name": "New ID Type"}) + self.assertTrue(new_id_type.exists(), "New ID Type should exist before unlinking.") + new_id_type.unlink() + self.assertFalse( + self.id_type_model.browse(new_id_type.id).exists(), + "New ID Type should not exist after unlinking.", + ) + + def test_unlink_id_type_with_references(self): + config_settings = self.registry_config_model.create({}) + config_settings.ind_deduplication_id_types_ids = [(4, self.id_type_1.id)] + config_settings.set_values() + self.id_type_1.unlink() diff --git a/g2p_social_registry/tests/__init__.py b/g2p_social_registry/tests/__init__.py index 548509e..33a66eb 100644 --- a/g2p_social_registry/tests/__init__.py +++ b/g2p_social_registry/tests/__init__.py @@ -1 +1,5 @@ # Part of OpenG2P Social Registry. See LICENSE file for full copyright and licensing details. + +from . import test_ref_id_config +from . import test_res_config_settings +from . import test_pending_ref_id diff --git a/g2p_social_registry/tests/test_pending_ref_id.py b/g2p_social_registry/tests/test_pending_ref_id.py new file mode 100644 index 0000000..a05f79a --- /dev/null +++ b/g2p_social_registry/tests/test_pending_ref_id.py @@ -0,0 +1,47 @@ +from unittest.mock import patch + +from odoo.tests import TransactionCase + + +class TestFallbackTable(TransactionCase): + def setUp(self): + super().setUp() + self.fallback_table = self.env["g2p.pending.reference_id"].create( + { + "registrant_id": self.env["res.partner"].create({"name": "Test Partner"}).id, + "ref_id": "", + "status": "failed", + } + ) + + @patch("odoo.addons.g2p_social_registry.models.registrant.ResPartner.generate_ref_id") + def test_retry_generate_ref_id_success(self, mock_generate_ref_id): + mock_generate_ref_id.return_value = None + self.fallback_table.registrant_id.ref_id = "some_ref_id" + self.fallback_table.retry_generate_ref_id() + self.assertEqual(self.fallback_table.status, "success") + self.assertEqual(self.fallback_table.ref_id, "some_ref_id") + + @patch("odoo.addons.g2p_social_registry.models.registrant.ResPartner.generate_ref_id") + def test_retry_generate_ref_id_failure(self, mock_generate_ref_id): + mock_generate_ref_id.return_value = False + self.fallback_table.retry_generate_ref_id() + self.assertEqual(self.fallback_table.status, "failed") + + @patch("odoo.addons.g2p_social_registry.models.registrant.ResPartner.generate_ref_id") + def test_generate_ref_id_for_selected_failure(self, mock_generate_ref_id): + mock_generate_ref_id.return_value = False + self.env.context = {"active_ids": [self.fallback_table.registrant_id.id]} + self.fallback_table.generate_ref_id_for_selected() + self.assertEqual(self.fallback_table.status, "failed") + + @patch("odoo.addons.g2p_social_registry.models.registrant.ResPartner.generate_ref_id") + @patch("odoo.addons.g2p_social_registry.models.pending_ref_id.FallbackTable.search") + def test_generate_ref_id_for_selected_no_registrant(self, mock_search, mock_generate_ref_id): + mock_search.return_value = False + mock_generate_ref_id.return_value = True + self.env.context = {"active_ids": [self.fallback_table.registrant_id.id]} + self.fallback_table.generate_ref_id_for_selected() + mock_search.assert_called_once_with( + [("registrant_id", "=", self.fallback_table.registrant_id.id)], limit=1 + ) diff --git a/g2p_social_registry/tests/test_ref_id_config.py b/g2p_social_registry/tests/test_ref_id_config.py new file mode 100644 index 0000000..4b192bb --- /dev/null +++ b/g2p_social_registry/tests/test_ref_id_config.py @@ -0,0 +1,67 @@ +from unittest.mock import MagicMock, patch + +import requests + +from odoo.tests import TransactionCase + + +class TestG2PReferenceIdconfig(TransactionCase): + def setUp(self): + super().setUp() + self.config = self.env["g2p.reference_id.config"].create( + { + "name": "Test Config", + "base_api_url": "https://idgenerator.sandbox.net/v1/idgenerator/uin", + "auth_url": "https://keycloak.openg2p.org/realms/master/protocol/openid-connect/token", + "auth_client_id": "client_id", + "auth_client_secret": "client_secret", + "auth_grant_type": "client_credentials", + "api_timeout": 10, + } + ) + + @patch("requests.post") + def test_get_access_token(self, mock_post): + mock_response = MagicMock() + mock_response.json.return_value = { + "access_token": ( + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ." + "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" + ) + } + mock_response.raise_for_status = lambda: None + mock_post.return_value = mock_response + access_token = self.config.get_access_token() + self.assertEqual( + access_token, + ( + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ." + "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" + ), + ) + + @patch("requests.post") + def test_get_access_token_error(self, mock_post): + mock_response = MagicMock() + mock_response.raise_for_status.side_effect = requests.exceptions.HTTPError() + mock_post.return_value = mock_response + with self.assertRaises(requests.exceptions.HTTPError): + self.config.get_access_token() + + @patch("requests.post") + def test_write_access_token(self, mock_post): + mock_response = MagicMock() + mock_response.json.return_value = { + "access_token": ( + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ." + "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" + ) + } + mock_response.raise_for_status = lambda: None + mock_post.return_value = mock_response + self.config.get_access_token() + self.assertIsNotNone(self.config.access_token) + self.assertIsNotNone(self.config.access_token_expiry) diff --git a/g2p_social_registry/tests/test_res_config_settings.py b/g2p_social_registry/tests/test_res_config_settings.py new file mode 100644 index 0000000..7aa929f --- /dev/null +++ b/g2p_social_registry/tests/test_res_config_settings.py @@ -0,0 +1,77 @@ +from unittest.mock import MagicMock, patch + +from odoo.tests import TransactionCase + + +class TestRegistryConfig(TransactionCase): + def setUp(self): + super().setUp() + self.config = self.env["g2p.reference_id.config"].create( + { + "name": "Test Config", + "base_api_url": "https://idgenerator.sandbox.net/v1/idgenerator/uin", + "auth_url": "https://keycloak.openg2p.org/realms/master/protocol/openid-connect/token", + "auth_client_id": "client_id", + "auth_client_secret": "client_secret", + "auth_grant_type": "client_credentials", + "api_timeout": 10, + } + ) + self.registry_config = self.env["res.config.settings"].create({}) + + @patch("requests.post") + def test_set_values(self, mock_post): + mock_response = MagicMock() + mock_response.json.return_value = {"access_token": "test_access_token"} + mock_response.raise_for_status = lambda: None + mock_post.return_value = mock_response + self.registry_config.id_generator_base_api_url = "https://idgenerator.sandbox.net/v1/idgenerator/uin" + self.registry_config.id_generator_auth_url = ( + "https://keycloak.openg2p.org/realms/master/protocol/openid-connect/token" + ) + self.registry_config.id_generator_auth_client_id = "new_client_id" + self.registry_config.id_generator_auth_client_secret = "new_client_secret" + self.registry_config.id_generator_auth_grant_type = "new_grant_type" + self.registry_config.id_generator_api_timeout = 20 + self.registry_config.set_values() + config = self.env["g2p.reference_id.config"].get_config() + self.assertEqual(config.base_api_url, "https://idgenerator.sandbox.net/v1/idgenerator/uin") + self.assertEqual( + config.auth_url, "https://keycloak.openg2p.org/realms/master/protocol/openid-connect/token" + ) + self.assertEqual(config.auth_client_id, "new_client_id") + self.assertEqual(config.auth_client_secret, "new_client_secret") + self.assertEqual(config.auth_grant_type, "new_grant_type") + self.assertEqual(config.api_timeout, 20) + + @patch("requests.post") + def test_get_values(self, mock_post): + mock_response = MagicMock() + mock_response.json.return_value = {"access_token": "test_access_token"} + mock_response.raise_for_status = lambda: None + mock_post.return_value = mock_response + self.registry_config.get_values() + config = self.env["g2p.reference_id.config"].get_config() + self.assertEqual(self.registry_config.id_generator_base_api_url, config.base_api_url) + self.assertEqual(self.registry_config.id_generator_auth_url, config.auth_url) + self.assertEqual(self.registry_config.id_generator_auth_client_id, config.auth_client_id) + self.assertEqual(self.registry_config.id_generator_auth_client_secret, config.auth_client_secret) + self.assertEqual(self.registry_config.id_generator_auth_grant_type, config.auth_grant_type) + self.assertEqual(self.registry_config.id_generator_api_timeout, config.api_timeout) + + @patch("requests.post") + def test_add_missing_ref_id_to_retry(self, mock_post): + mock_response = MagicMock() + mock_response.json.return_value = {"access_token": "test_access_token"} + mock_response.raise_for_status = lambda: None + mock_post.return_value = mock_response + partner_without_ref = self.env["res.partner"].create( + {"name": "Test Partner", "is_registrant": True, "ref_id": False} + ) + self.registry_config.add_missing_ref_id_to_retry() + pending_records = self.env["g2p.pending.reference_id"].search( + [("registrant_id", "=", partner_without_ref.id)] + ) + self.assertEqual(len(pending_records), 1) + self.assertEqual(pending_records.registrant_id.id, partner_without_ref.id) + self.assertEqual(pending_records.status, "failed") diff --git a/g2p_social_registry_model/README.md b/g2p_social_registry_model/README.md new file mode 100644 index 0000000..d936680 --- /dev/null +++ b/g2p_social_registry_model/README.md @@ -0,0 +1,3 @@ +# G2P Social Registry: Demo + +Refer to https://docs.openg2p.org. diff --git a/g2p_social_registry_model/__init__.py b/g2p_social_registry_model/__init__.py new file mode 100644 index 0000000..5d6d163 --- /dev/null +++ b/g2p_social_registry_model/__init__.py @@ -0,0 +1,3 @@ +# Part of OpenG2P. See LICENSE file for full copyright and licensing details. + +from . import models diff --git a/g2p_social_registry_model/__manifest__.py b/g2p_social_registry_model/__manifest__.py new file mode 100644 index 0000000..a3fe606 --- /dev/null +++ b/g2p_social_registry_model/__manifest__.py @@ -0,0 +1,21 @@ +# Part of OpenG2P. See LICENSE file for full copyright and licensing details. +{ + "name": "G2P Social Registry: Demo", + "category": "G2P", + "version": "17.0.0.0.0", + "sequence": 1, + "author": "OpenG2P", + "website": "https://openg2p.org", + "license": "LGPL-3", + "depends": ["g2p_social_registry"], + "data": [ + "views/group_view.xml", + "views/individual_view.xml", + ], + "assets": {}, + "demo": [], + "images": [], + "application": True, + "installable": True, + "auto_install": False, +} diff --git a/g2p_social_registry_model/models/__init__.py b/g2p_social_registry_model/models/__init__.py new file mode 100644 index 0000000..4d516cb --- /dev/null +++ b/g2p_social_registry_model/models/__init__.py @@ -0,0 +1,4 @@ +# Part of OpenG2P. See LICENSE file for full copyright and licensing details. + +from . import odk_client +from . import res_partner diff --git a/g2p_social_registry_model/models/odk_client.py b/g2p_social_registry_model/models/odk_client.py new file mode 100644 index 0000000..8637734 --- /dev/null +++ b/g2p_social_registry_model/models/odk_client.py @@ -0,0 +1,49 @@ +import odoo.addons.g2p_odk_importer.models.odk_client as base_odk_client + +original_individual_data = base_odk_client.ODKClient.get_individual_data + + +def patched_individual_data(self, record): + res = original_individual_data(self, record) + + if record.get("reg_ids"): + res.update( + { + "reg_ids": [ + ( + 0, + 0, + { + "id_type": self.env["g2p.id.type"] + .search([("name", "=", reg_id.get("id_type"))], limit=1) + .id, + "value": reg_id.get("value"), + "expiry_date": reg_id.get("expiry_date"), + "status": "valid", + }, + ) + for reg_id in record.get("reg_ids") + ], + "education_level": record.get("education_level", None), + "employment_status": record.get("employment_status", None), + "marital_status": record.get("marital_status", None), + } + ) + return res + + +# def patched_member_relationship(self, source_id, record): +# print(record) +# relation = self.env["g2p.relationship"].search( +# [("name", "=", record.get("household_member").get("relationship_with_household_head"))], limit=1 +# ) +# print('--- relation', relation) + +# if relation and source_id: +# return {"source": source_id, "relation": relation.id, "start_date": datetime.now()} + +# return None + + +base_odk_client.ODKClient.get_individual_data = patched_individual_data +# base_odk_client.ODKClient.get_member_relationship = patched_member_relationship diff --git a/g2p_social_registry_model/models/res_partner.py b/g2p_social_registry_model/models/res_partner.py new file mode 100644 index 0000000..f1a3683 --- /dev/null +++ b/g2p_social_registry_model/models/res_partner.py @@ -0,0 +1,117 @@ +# Part of OpenG2P. See LICENSE file for full copyright and licensing details. +import logging + +from odoo import fields, models + +_logger = logging.getLogger(__name__) + + +class G2PResPartnerInherited(models.Model): + _inherit = "res.partner" + + ####################################################### + ##### Social Status Information ##### + ####################################################### + + num_preg_lact_women = fields.Integer("Pregnant & Lactating Women") + num_malnourished_children = fields.Integer("Malnourished Children Under 5") + num_disabled = fields.Integer("No. of member with Disability") + type_of_disability = fields.Selection( + [ + ("visual_impairment", "Visual Impairment"), + ("hearing_impairment", "Hearing Impairment"), + ("physical_disability", "Physical Disability"), + ("cognitive_disability", "Cognitive Disability"), + ] + ) + caste_ethnic_group = fields.Selection( + [ + ("bantu", "Bantu"), + ("nilotic", "Nilotic"), + ("afro_asian", "Afro-Asiatic"), + ("khoisan", "Khoisan"), + ("pygmy", "Pygmy"), + ("other", "Other"), + ], + "Caste/Ethnic Group", + ) + belong_to_protected_groups = fields.Selection( + [("yes", "Yes"), ("no", "No")], "Belong to any protected groups?" + ) + other_vulnerable_status = fields.Selection( + [("yes", "Yes"), ("no", "No")], "Under any other vulnerable status?" + ) + + ####################################################### + ##### Household Details ##### + ####################################################### + + education_level = fields.Selection( + [ + ("primary", "Primary"), + ("secondary", "Secondary"), + ("higher_secondary", "Higher Secondary"), + ("bachelors", "Bachelors"), + ("masters", "Masters"), + ] + ) + employment_status = fields.Selection( + [ + ("employed_full", "Employed - Full Time"), + ("employed_part", "Employed - Part Time"), + ("self_employed", "Self-employed"), + ("unemployed", "Unemployed"), + ] + ) + marital_status = fields.Selection( + [("single", "Single"), ("married", "Married"), ("divorced", "divorced")] + ) + + ####################################################### + ##### Economic Status Information ##### + ####################################################### + + income_sources = fields.Selection( + [ + ("agriculture", "Agriculture"), + ("business", "Business"), + ("mining", "Mining"), + ("manufacturing", "Manufacturing"), + ("construction", "Construction"), + ], + "Sources of Income", + ) + annual_income = fields.Selection( + [("below_5000", "Below 5000"), ("5001_10000", "5001-10,000"), ("above_10000", "Above 10,000")], + ) + owns_two_wheeler = fields.Selection([("yes", "Yes"), ("no", "No")], "Owns a Two-Wheeler") + owns_three_wheeler = fields.Selection([("yes", "Yes"), ("no", "No")], "Owns a Three-Wheeler") + owns_four_wheeler = fields.Selection([("yes", "Yes"), ("no", "No")], "Owns a Four-Wheeler") + owns_cart = fields.Selection([("yes", "Yes"), ("no", "No")], "Owns a Cart") + land_ownership = fields.Selection([("yes", "Yes"), ("no", "No")]) + type_of_land_owned = fields.Selection( + [ + ("agricultural", "Agricultural Land"), + ("residential", "Residential Land"), + ("pastoral", "Pastoral Land"), + ("forest", "Forest Land"), + ("commercial", "Commercial Land"), + ("communal", "Communal Land"), + ("other", "Other"), + ] + ) + land_size = fields.Float() + owns_house = fields.Selection([("yes", "Yes"), ("no", "No")]) + owns_livestock = fields.Selection([("yes", "Yes"), ("no", "No")]) + + ####################################################### + ##### Housing Condition Information ##### + ####################################################### + + housing_type = fields.Selection( + [("permanent", "Permanent"), ("temporary", "Temporary")], "Type of Housing" + ) + house_condition = fields.Selection([("mud", "Mud"), ("cement", "Cement")]) + sanitation_condition = fields.Selection([("yes", "Yes"), ("no", "No")]) + water_access = fields.Selection([("yes", "Yes"), ("no", "No")]) + electricity_access = fields.Selection([("yes", "Yes"), ("no", "No")]) diff --git a/g2p_social_registry_model/pyproject.toml b/g2p_social_registry_model/pyproject.toml new file mode 100644 index 0000000..4231d0c --- /dev/null +++ b/g2p_social_registry_model/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/g2p_social_registry_model/static/description/icon.png b/g2p_social_registry_model/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5ecb429ea9ceb3863d46ea852dc9c9f78d4e901b GIT binary patch literal 3985 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 literal 0 HcmV?d00001 diff --git a/g2p_social_registry_model/views/group_view.xml b/g2p_social_registry_model/views/group_view.xml new file mode 100644 index 0000000..649be3c --- /dev/null +++ b/g2p_social_registry_model/views/group_view.xml @@ -0,0 +1,41 @@ + + + + + view_groups_sr_demo + res.partner + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/g2p_social_registry_model/views/individual_view.xml b/g2p_social_registry_model/views/individual_view.xml new file mode 100644 index 0000000..9badc76 --- /dev/null +++ b/g2p_social_registry_model/views/individual_view.xml @@ -0,0 +1,18 @@ + + + + + Individual Form + res.partner + + + + + + + + + + diff --git a/g2p_social_registry_theme/tests/__init__.py b/g2p_social_registry_theme/tests/__init__.py new file mode 100644 index 0000000..fce1767 --- /dev/null +++ b/g2p_social_registry_theme/tests/__init__.py @@ -0,0 +1 @@ +from . import test_res_user, test_res_company diff --git a/g2p_social_registry_theme/tests/test_res_company.py b/g2p_social_registry_theme/tests/test_res_company.py new file mode 100644 index 0000000..fc23fd8 --- /dev/null +++ b/g2p_social_registry_theme/tests/test_res_company.py @@ -0,0 +1,28 @@ +import base64 + +from odoo import tools +from odoo.modules.module import get_resource_path +from odoo.tests import TransactionCase + + +class TestResCompany(TransactionCase): + def setUp(self): + super().setUp() + self.company_model = self.env["res.company"] + + def test_get_g2p_favicon(self): + """Test fetching the G2P favicon.""" + company = self.company_model.create( + { + "name": "Test Company", + } + ) + favicon_base64 = company.get_g2p_favicon() + expected_img_path = get_resource_path( + "g2p_social_registry_theme", "static/src/img/favicon-white-background.png" + ) + with tools.file_open(expected_img_path, "rb") as f: + expected_favicon_base64 = base64.b64encode(f.read()) + self.assertEqual( + favicon_base64, expected_favicon_base64, "The fetched favicon does not match the expected one." + ) diff --git a/g2p_social_registry_theme/tests/test_res_user.py b/g2p_social_registry_theme/tests/test_res_user.py new file mode 100644 index 0000000..58cb5e7 --- /dev/null +++ b/g2p_social_registry_theme/tests/test_res_user.py @@ -0,0 +1,63 @@ +from unittest.mock import patch + +from odoo.exceptions import AccessDenied +from odoo.tests import TransactionCase + + +class TestResUser(TransactionCase): + def setUp(self): + """Set up test users.""" + super().setUp() + self.user_model = self.env["res.users"] + self.valid_user = self.user_model.create( + { + "name": "Valid User", + "login": "abbb6166@gmail.com", + "email": "abbb6166@gmail.com", + "password": "admin@123", + "active": True, + } + ) + self.valid_user.partner_id.is_registrant = False + user_group = self.env.ref("base.group_user") + self.valid_user.groups_id = [(4, user_group.id)] + self.invalid_user = self.user_model.create( + { + "name": "Invalid User", + "login": "invalid_user@example.com", + "email": "invalid_user@example.com", + "password": "admin@1234", + "active": True, + } + ) + self.registrant_user = self.user_model.create( + { + "name": "Registrant User", + "login": "registrant_user@example.com", + "email": "registrant_user@example.com", + "password": "admin@123", + "active": True, + } + ) + self.registrant_user.partner_id.is_registrant = True + + def test_reset_password_invalid_user(self): + """Test that an exception is raised when resetting password for an invalid user.""" + with self.assertRaises(Exception) as context: + self.invalid_user.reset_password("non_existent@example.com") + self.assertEqual( + str(context.exception), "Incorrect email. Please enter the registered email address." + ) + + @patch("odoo.addons.g2p_social_registry_theme.models.res_user.ResUser.reset_password") + def test_reset_password_valid_user(self, mock_action_reset_password): + """Test that the password reset is triggered for a valid user.""" + self.valid_user.reset_password("abbb6166@gmail.com") + mock_action_reset_password.assert_called_once() + + def test_login_access_denied_for_registrant(self): + """Test that a registrant user is denied access.""" + with self.assertRaises(AccessDenied): + self.registrant_user._login( + db="myTestDB", login="registrant_user@example.com", password="admin@123", user_agent_env=None + )