Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading