Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Reference ID for registrants #34

Merged
merged 7 commits into from
Aug 22, 2024
Merged
6 changes: 6 additions & 0 deletions g2p_social_registry/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
# Part of OpenG2P Registry. See LICENSE file for full copyright and licensing details.
from . import models


def post_init_hook(env):
partners = env["res.partner"].search([("is_registrant", "=", True), ("ref_id", "=", False)])
for partner in partners:
env["g2p.pending.reference_id"].create({"registrant_id": partner.id, "status": "failed"})
4 changes: 4 additions & 0 deletions g2p_social_registry/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@
"external_dependencies": {},
"data": [
"security/ir.model.access.csv",
"data/cron_job.xml",
"data/default_ref_id_config.xml",
"views/main_view.xml",
"views/region.xml",
"views/registrant_view.xml",
"views/res_config_settings_view.xml",
],
"assets": {},
"demo": [],
"images": [],
"application": True,
"installable": True,
"auto_install": False,
"post_init_hook": "post_init_hook",
}
13 changes: 13 additions & 0 deletions g2p_social_registry/data/cron_job.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<odoo>
<record id="ir_cron_retry_ref_id_job" model="ir.cron">
<field name="name">Reference ID Generation Cron Job</field>
<field name="model_id" ref="model_g2p_pending_reference_id" />
<field name="state">code</field>
<field name="code">model.retry_generate_ref_id()</field>
<field name="interval_number">1</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False" />
<field name="active" eval="False" />
</record>
</odoo>
9 changes: 9 additions & 0 deletions g2p_social_registry/data/default_ref_id_config.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Part of OpenG2P. See LICENSE file for full copyright and licensing details.
-->
<odoo>
<record id="unique_ref_id_default_config" model="g2p.reference_id.config">
<field name="name">Default Reference ID Config</field>
</record>
</odoo>
5 changes: 4 additions & 1 deletion g2p_social_registry/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# Part of OpenG2P Social Registry. See LICENSE file for full copyright and licensing details.
# Part of OpenG2P. See LICENSE file for full copyright and licensing details.

from . import region
from . import ref_id_config
from . import res_config_settings
from . import registrant
from . import pending_ref_id
36 changes: 36 additions & 0 deletions g2p_social_registry/models/pending_ref_id.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import logging

from odoo import api, fields, models

_logger = logging.getLogger(__name__)


class FallbackTable(models.Model):
_name = "g2p.pending.reference_id"

registrant_id = fields.Many2one("res.partner")
ref_id = fields.Char()
status = fields.Selection([("failed", "Failed"), ("success", "Success")], default="failed")

@api.model
def retry_generate_ref_id(self):
records = self.search([("status", "=", "failed")])
for record in records:
partner = record.registrant_id
if partner:
partner.generate_ref_id()
if partner.ref_id:
record.status = "success"
record.ref_id = partner.ref_id

def generate_ref_id_for_selected(self):
for rec in self.env.context.get("active_ids"):
partner = self.env["res.partner"].browse(rec)
registrant = self.env["g2p.pending.reference_id"].search([("registrant_id", "=", rec)], limit=1)

if not partner.ref_id:
partner.generate_ref_id()

if partner.ref_id and registrant:
registrant.status = "success"
registrant.ref_id = partner.ref_id
76 changes: 76 additions & 0 deletions g2p_social_registry/models/ref_id_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import logging
import os
from datetime import datetime

import requests
from jose import jwt

from odoo import _, api, fields, models
from odoo.exceptions import UserError

_logger = logging.getLogger(__name__)

ID_GENERATOR_API_BASE_URL = os.getenv(
"ID_GENERATOR_API_BASE_URL", "https://idgenerator.sandbox.net/v1/idgenerator/uin"
)
ID_GENERATOR_AUTH_URL = os.getenv(
"ID_GENERATOR_AUTH_URL",
"https://keycloak.openg2p.org/realms/master/protocol/openid-connect/token",
)
ID_GENERATOR_AUTH_CLIENT_ID = os.getenv("ID_GENERATOR_AUTH_CLIENT_ID", "")
ID_GENERATOR_AUTH_CLIENT_SECRET = os.getenv("ID_GENERATOR_AUTH_CLIENT_SECRET", "")
ID_GENERATOR_AUTH_GRANT_TYPE = os.getenv("ID_GENERATOR_AUTH_GRANT_TYPE", "client_credentials")


class FallbackTable(models.Model):
_name = "g2p.reference_id.config"
_description = "G2P Reference ID Configuration"

name = fields.Char()
base_api_url = fields.Char(default=ID_GENERATOR_API_BASE_URL)
auth_url = fields.Char(default=ID_GENERATOR_AUTH_URL)
auth_client_id = fields.Char(default=ID_GENERATOR_AUTH_CLIENT_ID)
auth_client_secret = fields.Char(default=ID_GENERATOR_AUTH_CLIENT_SECRET)
auth_grant_type = fields.Char(default=ID_GENERATOR_AUTH_GRANT_TYPE)
api_timeout = fields.Integer(default=10)

access_token = fields.Char()
access_token_expiry = fields.Datetime()

@api.model
def get_config(self):
return self.search([], limit=1)

def get_access_token(self):
self.ensure_one()

if self.access_token and self.access_token_expiry and self.access_token_expiry > datetime.utcnow():
return self.access_token

if not self.auth_url:
raise UserError(_("ID Generator Authentication URL is not set"))

data = {
"grant_type": self.auth_grant_type,
"client_id": self.auth_client_id,
"client_secret": self.auth_client_secret,
}

response = requests.post(self.auth_url, data=data, timeout=self.api_timeout)
_logger.debug("ID Generator Authentication API response: %s", response.text)
response.raise_for_status()

access_token = response.json().get("access_token", None)
token_exp = jwt.get_unverified_claims(access_token).get("exp")

self.write(
{
"access_token": access_token,
"access_token_expiry": datetime.fromtimestamp(token_exp)
if isinstance(token_exp, int)
else datetime.fromisoformat(token_exp)
if isinstance(token_exp, str)
else token_exp,
}
)
return access_token
2 changes: 1 addition & 1 deletion g2p_social_registry/models/region.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Part of OpenG2P Registry. See LICENSE file for full copyright and licensing details.
# Part of OpenG2P. See LICENSE file for full copyright and licensing details.

from odoo import fields, models

Expand Down
52 changes: 51 additions & 1 deletion g2p_social_registry/models/registrant.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,57 @@
from odoo import fields, models
import logging

import requests

from odoo import api, fields, models

_logger = logging.getLogger(__name__)


class ResPartner(models.Model):
_inherit = "res.partner"

region = fields.Many2one("g2p.region")
ref_id = fields.Char(string="Reference ID", index=True)

_sql_constraints = [
("ref_id_uniq", "UNIQUE(ref_id)", "ref_id is an unique identifier!"),
]

@api.model
def create(self, vals):
record = super().create(vals)
if vals.get("is_registrant"):
record.generate_ref_id()
return record

def generate_ref_id(self):
for rec in self:
try:
config = self.env["g2p.reference_id.config"].get_config()

access_token = config.get_access_token()
headers = {"Cookie": f"Authorization={access_token}"}

response = requests.get(config.base_api_url, headers=headers, timeout=config.api_timeout)
_logger.debug("ID Generator API response: %s", response.text)
response.raise_for_status()
res = response.json()

unique_id = res.get("response")["uin"]
rec.ref_id = rec.get_ref_id_prefix() + unique_id

except Exception as e:
_logger.error("Failed to generate ref_id for partner %s: %s", rec.id, str(e))

pending_ref_id_model = self.env["g2p.pending.reference_id"]

if not pending_ref_id_model.search([("registrant_id", "=", rec.id)]):
pending_ref_id_model.create({"registrant_id": rec.id, "status": "failed"})

def get_ref_id_prefix(self):
for rec in self:
if not rec.is_registrant:
return ""
if rec.is_group:
return "GRP-"
return "IND-"
72 changes: 72 additions & 0 deletions g2p_social_registry/models/res_config_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Part of OpenG2P. See LICENSE file for full copyright and licensing details.
import logging

from odoo import _, api, fields, models
from odoo.exceptions import UserError

_logger = logging.getLogger(__name__)


class RegistryConfig(models.TransientModel):
_inherit = "res.config.settings"

id_generator_base_api_url = fields.Char()
id_generator_auth_url = fields.Char()
id_generator_auth_client_id = fields.Char()
id_generator_auth_client_secret = fields.Char()
id_generator_auth_grant_type = fields.Char()
id_generator_api_timeout = fields.Integer()

def set_values(self):
res = super().set_values()
config = self.env["g2p.reference_id.config"].get_config()

config.write(
{
"base_api_url": self.id_generator_base_api_url,
"auth_url": self.id_generator_auth_url,
"auth_client_id": self.id_generator_auth_client_id,
"auth_client_secret": self.id_generator_auth_client_secret,
"auth_grant_type": self.id_generator_auth_grant_type,
"api_timeout": self.id_generator_api_timeout,
}
)

return res

@api.model
def get_values(self):
res = super().get_values()
config = self.env["g2p.reference_id.config"].get_config()

res.update(
{
"id_generator_base_api_url": config.base_api_url,
"id_generator_auth_url": config.auth_url,
"id_generator_auth_client_id": config.auth_client_id,
"id_generator_auth_client_secret": config.auth_client_secret,
"id_generator_auth_grant_type": config.auth_grant_type,
"id_generator_api_timeout": config.api_timeout,
}
)

return res

def add_missing_ref_id_to_retry(self):
query = """
SELECT id FROM res_partner
WHERE is_registrant = TRUE AND ref_id is Null
"""

pending_ref_id_model = self.env["g2p.pending.reference_id"]

try:
self._cr.execute(query) # pylint: disable=sql-injection

for rec in [record[0] for record in (self._cr.fetchall())]:
if not pending_ref_id_model.search([("registrant_id", "=", rec)], limit=1):
pending_ref_id_model.create({"registrant_id": rec, "status": "failed"})

except Exception as e:
_logger.error("Database Query Error: %s", e)
raise UserError(_("Database Query Error: %s") % e) from None
4 changes: 4 additions & 0 deletions g2p_social_registry/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
g2p_region_admin,Region Admin Access,g2p_social_registry.model_g2p_region,g2p_registry_base.group_g2p_admin,1,1,1,1
g2p_region_registrar,Region Registrar Access,g2p_social_registry.model_g2p_region,,1,0,0,0
g2p_pending_reference_id_admin,Pending Reference ID Admin Access,g2p_social_registry.model_g2p_pending_reference_id,g2p_registry_base.group_g2p_admin,1,1,1,1
g2p_pending_reference_id_registrar,Pending Reference ID Registrar Access,g2p_social_registry.model_g2p_pending_reference_id,,1,0,0,0
g2p_reference_id_config_admin,Reference ID Config Admin Access,g2p_social_registry.model_g2p_reference_id_config,g2p_registry_base.group_g2p_admin,1,1,1,1
g2p_reference_id_config_registrar,Reference ID Config Registrar Access,g2p_social_registry.model_g2p_reference_id_config,,1,0,0,0
15 changes: 14 additions & 1 deletion g2p_social_registry/views/registrant_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
<xpath expr="//field[@name='district']" position="after">
<field name="region" />
</xpath>
<!-- <xpath expr="//field[@name='registration_date']" position="after">
<field name="ref_id" readonly="1" />
</xpath> -->
</field>
</record>

Expand All @@ -19,9 +22,19 @@
<field name="inherit_id" ref="g2p_registry_group.view_groups_form" />
<field name="arch" type="xml">
<xpath expr="//page[@name='basic_info']/group/group" position="inside">

<field name="region" string="Region" />
</xpath>
<!-- <xpath expr="//field[@name='registration_date']" position="after">
<field name="ref_id" readonly="1" />
</xpath> -->
</field>
</record>

<record model="ir.actions.server" id="generate_ref_id">
<field name="name">Generate Reference ID</field>
<field name="model_id" ref="g2p_social_registry.model_g2p_pending_reference_id" />
<field name="binding_model_id" ref="base.model_res_partner" />
<field name="state">code</field>
<field name="code">action = model.generate_ref_id_for_selected()</field>
</record>
</odoo>
Loading
Loading