Skip to content

Commit

Permalink
Merge pull request #26 from mkumar-02/17.0-develop
Browse files Browse the repository at this point in the history
ID Deduplication: Added group kind mapping with ID type.
  • Loading branch information
shibu-narayanan authored Jul 30, 2024
2 parents aede75b + 9b1e873 commit c6cab02
Show file tree
Hide file tree
Showing 14 changed files with 218 additions and 76 deletions.
10 changes: 10 additions & 0 deletions g2p_registry_id_deduplication/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# Part of OpenG2P Social Registry. See LICENSE file for full copyright and licensing details.

from . import models


def _uninstall_cleanup(env):
parameter_keys = [
"g2p_registry_id_deduplication.grp_deduplication_id_types_ids",
"g2p_registry_id_deduplication.ind_deduplication_id_types_ids",
]

for key in parameter_keys:
env["ir.config_parameter"].sudo().set_param(key, None)
3 changes: 3 additions & 0 deletions g2p_registry_id_deduplication/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
],
"external_dependencies": {},
"data": [
"security/ir.model.access.csv",
"views/deduplication_view.xml",
"views/group_kind_id_type_mapping_view.xml",
"views/group_view.xml",
"views/individual_view.xml",
"views/res_config_view.xml",
Expand All @@ -31,4 +33,5 @@
"application": True,
"installable": True,
"auto_install": False,
"uninstall_hook": "_uninstall_cleanup",
}
1 change: 1 addition & 0 deletions g2p_registry_id_deduplication/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Part of OpenG2P. See LICENSE file for full copyright and licensing details.

from . import group_kind_id_type_mapping
from . import registrant
from . import res_config_settings
23 changes: 23 additions & 0 deletions g2p_registry_id_deduplication/models/group_kind_id_type_mapping.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Part of OpenG2P. See LICENSE file for full copyright and licensing details.

import logging

from odoo import fields, models

_logger = logging.getLogger(__name__)


class G2PGroupKindDeduplication(models.TransientModel):
_name = "g2p.group.kind.deduplication.config"
_description = "Deduplication Mapping between Group Kind and ID Types"

kind_id = fields.Many2one("g2p.group.kind", string="Kind")
id_type_ids = fields.Many2many("g2p.id.type", string="ID Types")

_sql_constraints = [
(
"unique_group_kind",
"UNIQUE(kind_id)",
"The Kind must be unique.",
),
]
130 changes: 82 additions & 48 deletions g2p_registry_id_deduplication/models/registrant.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,71 +11,80 @@
class ResPartner(models.Model):
_inherit = "res.partner"

is_duplicate = fields.Boolean(default=False)
is_duplicated = fields.Boolean(default=False)

def deduplicate_registrants(self):
is_group = self._context.get("default_is_group")

ind_id_types = self.get_id_types(id_field="ind_deduplication_id_types_ids")

self.reset_duplicate_flag(is_group)

if not is_group:
ind_id_types = self.get_id_types_with_kind(
id_field="ind_deduplication_id_types_ids", is_group=is_group
)
ind_duplicate_ids = []
duplicates = self.get_duplicate_registrants(
is_group, ind_id_types, group_condition="group_kind.name IS NULL"
is_group, ind_id_types[False], group_condition="group_kind.name IS NULL"
)

for duplicate in duplicates:
duplicate_partner_ids_str = duplicate.get("partner_ids")
ind_duplicate_ids += duplicate_partner_ids_str.split(",")
ind_duplicate_ids += duplicate.get("partner_ids").split(",")

updated_ind_duplicate_ids = list(set(ind_duplicate_ids))
self.mark_registrant_as_duplicated(updated_ind_duplicate_ids)

message = f"{len(updated_ind_duplicate_ids)} individuals"

else:
grp_id_types = self.get_id_types(id_field="grp_deduplication_id_types_ids")
grp_id_types = self.get_id_types_with_kind(
id_field="grp_deduplication_id_types_ids", is_group=is_group
)
group_duplicate_ids = []
member_duplicate_ids = []
member_grp_duplicate_ids = []
member_individual_ids = []
grouped_kinds = self.get_grouped_kinds()
updated_grp_duplicate_ids = []
updated_member_grp_duplicate_ids = []
for kind in grouped_kinds:
group_kind_name = kind.get("kind")
group_ids_str = kind.get("group_ids")

group_duplicates = self.get_duplicate_registrants(
is_group,
grp_id_types,
group_condition=(
f"group_kind.name = '{group_kind_name}'"
if group_kind_name is not None
else "group_kind.name IS NULL"
),
)
group_duplicates = []
if group_kind_name in grp_id_types or (group_kind_name is None and "False" in grp_id_types):
group_duplicates = self.get_duplicate_registrants(
is_group,
grp_id_types["False" if group_kind_name is None else group_kind_name],
group_condition=(
f"group_kind.name = '{group_kind_name}'"
if group_kind_name is not None
else "group_kind.name IS NULL"
),
)

# Group Duplicate
for duplicate in group_duplicates:
duplicate_partner_ids_str = duplicate.get("partner_ids")
group_duplicate_ids += duplicate_partner_ids_str.split(",")
group_duplicate_ids += duplicate.get("partner_ids").split(",")

updated_grp_duplicate_ids = list(set(group_duplicate_ids))
self.mark_registrant_as_duplicated(updated_grp_duplicate_ids)

# Group Member Duplicate
group_member_duplicates = self.get_duplicate_group_members(group_ids_str, ind_id_types)
for member_duplicate in group_member_duplicates:
duplicate_partner_ids_str = member_duplicate.get("partner_ids")
member_duplicate_ids += duplicate_partner_ids_str.split(",")

updated_member_duplicate_ids = list(set(member_duplicate_ids))
self.mark_registrant_as_duplicated(updated_member_duplicate_ids)

if len(updated_grp_duplicate_ids) > 0 and len(updated_member_duplicate_ids) > 0:
if group_kind_name in grp_id_types or (group_kind_name is None and "False" in grp_id_types):
group_member_duplicates = self.get_duplicate_group_members(
group_ids_str, grp_id_types["False" if group_kind_name is None else group_kind_name]
)
for member_duplicate in group_member_duplicates:
member_grp_duplicate_ids += member_duplicate.get("partner_ids").split(",")
member_individual_ids += member_duplicate.get("individual_ids").split(",")

updated_member_grp_duplicate_ids = list(set(member_grp_duplicate_ids))
updated_member_individual_ids = list(set(member_individual_ids))
self.mark_registrant_as_duplicated(updated_member_grp_duplicate_ids)

if len(updated_grp_duplicate_ids) > 0 and len(updated_member_individual_ids) > 0:
message = f"{len(updated_grp_duplicate_ids)} groups and \
{len(updated_member_duplicate_ids)} group members"
elif len(updated_member_duplicate_ids) > 0:
message = f"{len(updated_member_duplicate_ids)} group members"
{len(updated_member_individual_ids)} group members"
elif len(updated_member_individual_ids) > 0:
message = f"{len(updated_member_individual_ids)} group members"
else:
message = f"{len(updated_grp_duplicate_ids)} groups"

Expand All @@ -93,38 +102,63 @@ def deduplicate_registrants(self):
},
}

def get_id_types(self, id_field):
id_types = []
def get_id_types_with_kind(self, id_field, is_group):
id_types = {}

ir_config = self.env["ir.config_parameter"].sudo()

id_type_ids_str = ir_config.get_param(f"g2p_registry_id_deduplication.{id_field}", default=None)

if not id_type_ids_str:
raise UserError(_("Deduplication ID Types are not configured"))

id_type_ids = id_type_ids_str.strip("][").split(", ")
id_type_ids = id_type_ids if len(id_type_ids[0]) != 0 else []

if len(id_type_ids) < 1:
raise UserError(_("Deduplication is not configured"))

ind_id_type = []
for id_type in id_type_ids:
id_type_id = self.env["g2p.id.type"].sudo().search([("id", "=", id_type)], limit=1)
id_types.append(id_type_id.name)
if is_group and id_field == "grp_deduplication_id_types_ids":
kind_id_type_mapping = (
self.env["g2p.group.kind.deduplication.config"]
.sudo()
.search([("id", "=", id_type)], limit=1)
)
id_types_mapping = []
for rec in kind_id_type_mapping.id_type_ids:
id_types_mapping.append(rec.name)

if len(id_types_mapping) < 1:
raise UserError(_("No Configured ID Types found in the System"))

id_types.update(
{
f"{kind_id_type_mapping.kind_id.name}": tuple(id_types_mapping)
if len(id_types_mapping) != 1
else f"('{id_types_mapping[0]}')"
}
)

else:
id_type_name = self.env["g2p.id.type"].sudo().search([("id", "=", id_type)], limit=1)
ind_id_type.append(id_type_name.name)

if id_field == "ind_deduplication_id_types_ids":
id_types.update({False: tuple(ind_id_type) if len(ind_id_type) != 1 else f"('{ind_id_type[0]}')"})

if len(id_types) < 1:
raise UserError(_("No Configured ID Types found in the System"))

return tuple(id_types) if len(id_types) != 1 else f"('{id_types[0]}')"
return id_types

def mark_registrant_as_duplicated(self, partner_ids):
for partner in partner_ids:
registrant = self.browse(int(partner))
if registrant:
registrant.update({"is_duplicate": True})
registrant.update({"is_duplicated": True})

def reset_duplicate_flag(self, is_group):
query = f"""
UPDATE res_partner
SET is_duplicate = FALSE
SET is_duplicated = FALSE
WHERE is_registrant = TRUE AND is_group = {is_group}
"""
_logger.debug("DB Query: %s" % query)
Expand Down Expand Up @@ -157,15 +191,15 @@ def get_grouped_kinds(self):
def get_duplicate_registrants(self, is_group, id_types, group_condition):
query = f"""
SELECT
reg_id.value AS id_value, STRING_AGG(partner.id::text, ',')
id_type.name AS id_name, reg_id.value AS id_value, STRING_AGG(partner.id::text, ',')
AS partner_ids
FROM res_partner AS partner
INNER JOIN g2p_reg_id AS reg_id ON reg_id.partner_id = partner.id
JOIN g2p_id_type AS id_type ON id_type.id = reg_id.id_type
LEFT JOIN g2p_group_kind AS group_kind ON group_kind.id = partner.kind
WHERE is_registrant = TRUE AND id_type.name IN {id_types} AND is_group = {is_group}
AND {group_condition}
GROUP BY reg_id.value
GROUP BY id_type.name, reg_id.value
HAVING COUNT(partner.id) > 1
"""

Expand All @@ -180,15 +214,15 @@ def get_duplicate_registrants(self, is_group, id_types, group_condition):
def get_duplicate_group_members(self, group_ids, id_types):
query = f"""
SELECT
reg_id.value AS id_value, STRING_AGG(group_member.group::text, ',')
AS partner_ids
id_type.name AS id_name, reg_id.value AS id_value, STRING_AGG(group_member.group::text, ',')
AS partner_ids, STRING_AGG(group_member.individual::text, ',') AS individual_ids
FROM res_partner AS partner
JOIN g2p_group_membership AS group_member ON partner.id = group_member.individual
INNER JOIN g2p_reg_id AS reg_id ON reg_id.partner_id = partner.id
JOIN g2p_id_type AS id_type ON id_type.id = reg_id.id_type
WHERE partner.is_registrant = TRUE AND id_type.name IN {id_types}
AND group_member.group IN ({group_ids})
GROUP BY reg_id.value
GROUP BY id_type.name, reg_id.value
HAVING COUNT(partner.id) > 1
"""

Expand Down
17 changes: 11 additions & 6 deletions g2p_registry_id_deduplication/models/res_config_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ class RegistryConfig(models.TransientModel):
_inherit = "res.config.settings"

grp_deduplication_id_types_ids = fields.Many2many(
"g2p.id.type",
"g2p.group.kind.deduplication.config",
)

ind_deduplication_id_types_ids = fields.Many2many("g2p.id.type", "g2p_registry_id_ind_deduplcation")
ind_deduplication_id_types_ids = fields.Many2many(
"g2p.id.type",
"g2p_registry_id_ind_deduplcation_rel",
)

def set_values(self):
res = super().set_values()
Expand All @@ -32,9 +35,11 @@ def get_values(self):
grp_id_types = ir_config.get_param("g2p_registry_id_deduplication.grp_deduplication_id_types_ids")
ind_id_types = ir_config.get_param("g2p_registry_id_deduplication.ind_deduplication_id_types_ids")
res.update(
grp_deduplication_id_types_ids=[(6, 0, safe_eval(grp_id_types))] if grp_id_types else False
)
res.update(
ind_deduplication_id_types_ids=[(6, 0, safe_eval(ind_id_types))] if ind_id_types else False
grp_deduplication_id_types_ids=[(6, 0, safe_eval.safe_eval(grp_id_types))]
if grp_id_types
else None,
ind_deduplication_id_types_ids=[(6, 0, safe_eval.safe_eval(ind_id_types))]
if ind_id_types
else None,
)
return res
3 changes: 3 additions & 0 deletions g2p_registry_id_deduplication/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
g2p_group_kind_deduplication_config_admin,Region Admin Access,g2p_registry_id_deduplication.model_g2p_group_kind_deduplication_config,g2p_registry_base.group_g2p_admin,1,1,1,1
g2p_group_kind_deduplication_config_registrar,Region Registrar Access,g2p_registry_id_deduplication.model_g2p_group_kind_deduplication_config,,1,0,0,0
2 changes: 1 addition & 1 deletion g2p_registry_id_deduplication/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Part of OpenG2P. See LICENSE file for full copyright and licensing details.

# from . import test_registrant
from . import test_registrant
Loading

0 comments on commit c6cab02

Please sign in to comment.