Skip to content

Commit

Permalink
Merge pull request #288 from vin0dkhichar/17.0-1.3
Browse files Browse the repository at this point in the history
Added Priority List
  • Loading branch information
shibu-narayanan authored Feb 4, 2025
2 parents 733558c + 360c5dc commit 62dfa75
Show file tree
Hide file tree
Showing 23 changed files with 617 additions and 1 deletion.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ addon | version | maintainers | summary
[g2p_program_registrant_info](g2p_program_registrant_info/) | 17.0.1.3.0 | | G2P Program: Registrant Info
[g2p_program_reimbursement](g2p_program_reimbursement/) | 17.0.1.3.0 | | OpenG2P Programs: Reimbursement
[g2p_programs](g2p_programs/) | 17.0.1.3.0 | | OpenG2P Programs
[g2p_programs_priority_list](g2p_programs_priority_list/) | 17.0.1.3.0 | | OpenG2P Programs Priority List
[g2p_proxy_means_test](g2p_proxy_means_test/) | 17.0.1.3.0 | | G2P: Proxy Means Test
[g2p_reimbursement_portal](g2p_reimbursement_portal/) | 17.0.0.0.0 | | G2P Reimbursement Portal
[g2p_reimbursement_portal](g2p_reimbursement_portal/) | 17.0.1.3.0 | | G2P Reimbursement Portal
[g2p_social_registry_importer](g2p_social_registry_importer/) | 17.0.1.3.0 | | Import records from Social Registry
[g2p_theme](g2p_theme/) | 17.0.1.3.0 | | OpenG2P Theme

Expand Down
3 changes: 3 additions & 0 deletions g2p_programs_priority_list/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# OpenG2P Programs Priority List

Refer to https://docs.openg2p.org.
2 changes: 2 additions & 0 deletions g2p_programs_priority_list/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import wizard
28 changes: 28 additions & 0 deletions g2p_programs_priority_list/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "OpenG2P Programs Priority List",
"category": "G2P/G2P",
"version": "17.0.1.3.0",
"sequence": 1,
"author": "OpenG2P",
"website": "https://openg2p.org",
"license": "LGPL-3",
"depends": ["g2p_programs"],
"data": [
"security/ir.model.access.csv",
"views/cycle_view.xml",
"views/program_manager_view.xml",
"views/cycle_manager_view.xml",
"views/cycle_membership_view.xml",
"wizard/create_cycle_wizard.xml",
],
"assets": {
"web.assets_backend": [
"/g2p_programs_priority_list/static/src/css/style.css",
],
},
"demo": [],
"images": [],
"application": True,
"installable": True,
"auto_install": False,
}
6 changes: 6 additions & 0 deletions g2p_programs_priority_list/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from . import cycle
from . import cycle_manager
from . import programs
from . import program_manager
from . import cycle_membership
from . import sorting_criteria
20 changes: 20 additions & 0 deletions g2p_programs_priority_list/models/cycle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from odoo import api, fields, models


class G2PCycleInherited(models.Model):
_inherit = "g2p.cycle"

inclusion_limit = fields.Integer(default=0)
eligibility_domain = fields.Text(string="Domain", default="[]")
is_not_disbursement = fields.Boolean(string="Not Disbursement", default=True)
sorting_criteria_ids = fields.One2many("g2p.sorting.criteria", "cycle_id", string="Sorting Order")

@api.model
def create(self, vals):
if "program_id" in vals:
program = self.env["g2p.program"].browse(vals["program_id"])
if program.program_managers.manager_ref_id:
vals[
"is_not_disbursement"
] = not program.program_managers.manager_ref_id.is_disbursement_through_priority_list
return super().create(vals)
152 changes: 152 additions & 0 deletions g2p_programs_priority_list/models/cycle_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import logging

from odoo import _, api, fields, models
from odoo.tools.safe_eval import safe_eval

_logger = logging.getLogger(__name__)


class DefaultCycleManagerInherited(models.Model):
_inherit = "g2p.cycle.manager.default"

eligibility_domain = fields.Text(string="Domain", default="[]")
inclusion_limit = fields.Integer(default=0)

sorting_criteria_ids = fields.One2many("g2p.sorting.criteria", "manager_id", string="Sorting Order")

@api.model
def create(self, vals):
if "program_id" in vals:
vals[
"eligibility_domain"
] = f"[('program_membership_ids.program_id', 'in', [{vals['program_id']}])]"
return super().create(vals)

def new_cycle(self, name, new_start_date, sequence):
cycle = super().new_cycle(name, new_start_date, sequence)

for rec in self:
is_disbursement = (
self.program_id.program_managers.manager_ref_id.is_disbursement_through_priority_list
)
if is_disbursement:
for sorting_criterion in self.sorting_criteria_ids:
self.env["g2p.sorting.criteria"].create(
{
"cycle_id": cycle.id,
"sequence": sorting_criterion.sequence,
"field_name": sorting_criterion.field_name.id,
"order": sorting_criterion.order,
}
)

cycle.write(
{"inclusion_limit": rec.inclusion_limit, "eligibility_domain": rec.eligibility_domain}
)
return cycle

def add_beneficiaries(self, cycle, beneficiaries, state="draft"):
self.ensure_one()
self._ensure_can_edit_cycle(cycle)
_logger.debug("Adding beneficiaries to the cycle %s", cycle.name)
_logger.debug("Beneficiaries: %s", len(beneficiaries))

# Only add beneficiaries not added yet
existing_ids = cycle.cycle_membership_ids.mapped("partner_id.id")
_logger.debug("Existing IDs: %s", len(existing_ids))
beneficiaries = list(set(beneficiaries) - set(existing_ids))

is_disbursement = (
self.program_id.program_managers.manager_ref_id.is_disbursement_through_priority_list
)
if is_disbursement:
# Convert beneficiaries to recordset first
if beneficiaries:
if isinstance(beneficiaries, list):
ids = beneficiaries
else:
ids = beneficiaries.mapped("partner_id.id")

domain = safe_eval(cycle.eligibility_domain)

domain += [("id", "in", ids), ("disabled", "=", False)]
if self.program_id.target_type == "group":
domain += [("is_group", "=", True), ("is_registrant", "=", True)]
if self.program_id.target_type == "individual":
domain += [("is_group", "=", False), ("is_registrant", "=", True)]

remaining_limit = max(0, cycle.inclusion_limit - len(existing_ids))

sorted_criteria = cycle.sorting_criteria_ids.sorted("sequence")

order = []
for criterion in sorted_criteria:
field_name = criterion.field_name.name
reverse_flag = criterion.order == "desc"
order_direction = "desc" if reverse_flag else "asc"
order.append(f"{field_name} {order_direction}")

# Join the order list into a comma-separated string
order_str = ",".join(order)

# Query partners with sorting applied
sorted_beneficiaries = self.env["res.partner"].search(domain, order=order_str)

if len(sorted_beneficiaries) > remaining_limit:
beneficiaries = sorted_beneficiaries[:remaining_limit].ids
else:
beneficiaries = sorted_beneficiaries.ids

if len(beneficiaries) == 0:
message = _("No beneficiaries to import.")
kind = "warning"
sticky = False
elif len(beneficiaries) < self.MIN_ROW_JOB_QUEUE:
self._add_beneficiaries(cycle, beneficiaries, state, do_count=True)
message = _("%s beneficiaries imported.", len(beneficiaries))
kind = "success"
sticky = False
else:
self._add_beneficiaries_async(cycle, beneficiaries, state)
message = _("Import of %s beneficiaries started.", len(beneficiaries))
kind = "warning"
sticky = True

return {
"type": "ir.actions.client",
"tag": "display_notification",
"params": {
"title": _("Enrollment"),
"message": message,
"sticky": sticky,
"type": kind,
"next": {
"type": "ir.actions.act_window_close",
},
},
}

def _add_beneficiaries(self, cycle, beneficiaries, state="draft", do_count=False):
"""Add Beneficiaries with Rank
:param cycle: Recordset of cycle
:param beneficiaries: Recordset of beneficiaries
:param state: String state to be set to beneficiary
:return: None
"""
new_beneficiaries = []
for index, r in enumerate(beneficiaries):
new_beneficiaries.append(
[
0,
0,
{
"partner_id": r,
"enrollment_date": fields.Date.today(),
"state": state,
"rank": index + 1,
},
]
)
cycle.update({"cycle_membership_ids": new_beneficiaries})
cycle._compute_members_count()
7 changes: 7 additions & 0 deletions g2p_programs_priority_list/models/cycle_membership.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from odoo import fields, models


class G2PCycleMembershipInherited(models.Model):
_inherit = "g2p.cycle.membership"

rank = fields.Integer(index=True)
48 changes: 48 additions & 0 deletions g2p_programs_priority_list/models/program_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import logging
from datetime import datetime, timedelta

from odoo import fields, models

from odoo.addons.g2p_programs.models.programs import G2PProgram

_logger = logging.getLogger(__name__)


class DefaultProgramManagerInherited(models.Model):
_inherit = "g2p.program.manager.default"

is_disbursement_through_priority_list = fields.Boolean(
string="Disbursement Through Priority List", default=False
)

def new_cycle(self):
"""
Create the next cycle of the program.
If `is_disbursement_through_priority_list` is False, it copies enrolled beneficiaries.
"""
self.ensure_one()

for rec in self:
cycles = self.env["g2p.cycle"].search([("program_id", "=", rec.program_id.id)])
_logger.debug("Cycles found: %s", cycles)

cm = rec.program_id.get_manager(G2PProgram.MANAGER_CYCLE)

if not cycles:
_logger.debug("Creating first cycle with cycle manager: %s", cm)
new_cycle = cm.new_cycle("Cycle 1", datetime.now(), 1)
else:
last_cycle = rec.last_cycle()
new_sequence = last_cycle.sequence + 1
start_date = last_cycle.end_date + timedelta(days=1)
new_cycle = cm.new_cycle(f"Cycle {new_sequence}", start_date, new_sequence)

# Only copy beneficiaries if disbursement is NOT based on priority list
if not rec.is_disbursement_through_priority_list:
if new_cycle:
program_beneficiaries = rec.program_id.get_beneficiaries("enrolled").mapped(
"partner_id.id"
)
cm.add_beneficiaries(new_cycle, program_beneficiaries, "enrolled")

return new_cycle
76 changes: 76 additions & 0 deletions g2p_programs_priority_list/models/programs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import logging

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

_logger = logging.getLogger(__name__)


class G2PProgramInherit(models.Model):
_inherit = "g2p.program"

def create_new_cycle(self):
if self.beneficiaries_count <= 0:
raise UserError(
_("No enrolled registrants. Enroll registrants to program to create a new cycle.")
)

for rec in self:
message = None
kind = "success"
cycle_manager = rec.get_manager(self.MANAGER_CYCLE)
program_manager = rec.get_manager(self.MANAGER_PROGRAM)

if not cycle_manager:
raise UserError(_("No Cycle Manager defined."))
if not program_manager:
raise UserError(_("No Program Manager defined."))

_logger.debug("-" * 80)
_logger.debug("pm: %s", program_manager)

new_cycle = program_manager.new_cycle()
message = _("New cycle %s created.", new_cycle.name)

if new_cycle.is_not_disbursement:
return {
"type": "ir.actions.client",
"tag": "display_notification",
"params": {
"title": _("Cycle"),
"message": message,
"sticky": False,
"type": kind,
"next": {
"type": "ir.actions.act_window_close",
},
},
}
else:
wizard = self.env["cycle.creation.wizard"].create(
{
"cycle_id": new_cycle.id,
"name": new_cycle.name,
"program_id": new_cycle.program_id.id,
"eligibility_domain": new_cycle.eligibility_domain,
"inclusion_limit": new_cycle.inclusion_limit,
}
)

for criterion in new_cycle.sorting_criteria_ids:
self.env["cycle.creation.wizard.criteria"].create(
{
"wizard_id": wizard.id,
"field_name": criterion.field_name.id,
"order": criterion.order,
"sequence": criterion.sequence,
}
)
return {
"name": _("Update Priority Configuration"),
"type": "ir.actions.act_window",
"res_model": "cycle.creation.wizard",
"view_mode": "form",
"res_id": wizard.id,
"target": "new",
}
17 changes: 17 additions & 0 deletions g2p_programs_priority_list/models/sorting_criteria.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from odoo import fields, models


class SortingCriteria(models.Model):
_name = "g2p.sorting.criteria"
_description = "Sorting Criteria"

cycle_id = fields.Many2one("g2p.cycle", string="Cycle")
manager_id = fields.Many2one("g2p.cycle.manager.default", string="Cycle Manager")

sequence = fields.Integer()
field_name = fields.Many2one(
"ir.model.fields",
domain="[('model', '=', 'res.partner')]",
help="Select a field from res.partner for sorting",
)
order = fields.Selection([("asc", "Ascending"), ("desc", "Descending")], required=True)
3 changes: 3 additions & 0 deletions g2p_programs_priority_list/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
12 changes: 12 additions & 0 deletions g2p_programs_priority_list/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
g2p_cycle_creation_wizard_admin,Cycle Creation Wizard Admin Access,g2p_programs_priority_list.model_cycle_creation_wizard,g2p_registry_base.group_g2p_admin,1,1,1,1
g2p_cycle_creation_wizard_program_manager,Cycle Creation Wizard Program Manager Access,g2p_programs_priority_list.model_cycle_creation_wizard,g2p_programs.g2p_program_manager,1,1,1,1
g2p_cycle_creation_wizard_program_validator,Cycle Creation Wizard Program Validator Access,g2p_programs_priority_list.model_cycle_creation_wizard,g2p_programs.g2p_program_validator,1,1,1,0

g2p_sorting_criteria_admin,Sorting Criteria Admin Access,model_g2p_sorting_criteria,g2p_registry_base.group_g2p_admin,1,1,1,1
g2p_sorting_criteria_program_manager,Sorting Criteria Program Manager Access,model_g2p_sorting_criteria,g2p_programs.g2p_program_manager,1,1,1,1
g2p_sorting_criteria_program_validator,Sorting Criteria Program Validator Access,model_g2p_sorting_criteria,g2p_programs.g2p_program_validator,1,1,1,0

g2p_cycle_creation_wizard_criteria_admin,Cycle Creation Wizard Criteria Admin Access,g2p_programs_priority_list.model_cycle_creation_wizard_criteria,g2p_registry_base.group_g2p_admin,1,1,1,1
g2p_cycle_creation_wizard_criteria_program_manager,Cycle Creation Wizard Criteria Program Manager Access,g2p_programs_priority_list.model_cycle_creation_wizard_criteria,g2p_programs.g2p_program_manager,1,1,1,1
g2p_cycle_creation_wizard_criteria_program_validator,Cycle Creation Wizard Criteria Program Validator Access,g2p_programs_priority_list.model_cycle_creation_wizard_criteria,g2p_programs.g2p_program_validator,1,1,1,0
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 62dfa75

Please sign in to comment.