Skip to content

Commit f7b7a95

Browse files
authored
Merge pull request #198 from OpenSPP/fix/941-program-config-ux-polish
feat(spp_programs): program config UX polish, payment zero-state, cancelable manager wizard (#941 #952 #953)
2 parents e8fd6e9 + d58d1de commit f7b7a95

31 files changed

Lines changed: 1041 additions & 65 deletions

spp_mis_demo_v2/models/mis_demo_generator.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,10 +1424,12 @@ def _ensure_program_managers(self, program):
14241424
if program[field]:
14251425
continue
14261426
for mgr_obj, def_mgr_obj in mapping.items():
1427-
# Create the concrete manager implementation and link via wrapper
1427+
# Create the concrete manager implementation and link via
1428+
# wrapper. Each concrete model's default_get() supplies a
1429+
# method-specific name; we don't pass "Default" anymore —
1430+
# see #941 round 2 / item 3.
14281431
def_mgr = self.env[def_mgr_obj].create(
14291432
{
1430-
"name": "Default",
14311433
"program_id": program.id,
14321434
}
14331435
)

spp_programs/__manifest__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"name": "OpenSPP Programs",
55
"summary": "Manage programs, cycles, beneficiary enrollment, entitlements (cash and in-kind), payments, and fund tracking for social protection.",
66
"category": "OpenSPP/Core",
7-
"version": "19.0.2.1.3",
7+
"version": "19.0.2.2.0",
88
"sequence": 1,
99
"author": "OpenSPP.org",
1010
"website": "https://github.com/OpenSPP/OpenSPP2",
@@ -112,6 +112,7 @@
112112
"wizard/create_program_wizard_compliance_views.xml",
113113
"wizard/create_program_wizard_cel_views.xml",
114114
"wizard/enrollment_wizard_views.xml",
115+
"wizard/exit_membership_wizard.xml",
115116
"wizard/prepare_entitlement_confirm_wizard.xml",
116117
],
117118
"assets": {
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
2+
"""Replace placeholder `name = "Default"` on default-manager records with
3+
their method-specific labels — see #941 round 2 / item 3.
4+
5+
Existing programs upgraded from a prior version still carry rows whose
6+
`name` literally reads "Default" (the value used by
7+
`SPPProgram.create_default_managers` before the cleanup). New rows are
8+
fine because each concrete model now seeds its own meaningful name via
9+
`default_get`. This migration backfills the historical rows so the Edit
10+
form shows e.g. "CEL Eligibility Criteria" instead of "Default".
11+
"""
12+
13+
import logging
14+
15+
from psycopg2 import sql
16+
17+
_logger = logging.getLogger(__name__)
18+
19+
20+
# (table_name, label) pairs. Tables are derived from the concrete-manager
21+
# `_name` (dots replaced by underscores).
22+
_DEFAULT_NAME_RENAMES = (
23+
("spp_program_membership_manager_default", "CEL Eligibility Criteria"),
24+
("spp_program_entitlement_manager_default", "Basic Cash"),
25+
("spp_program_entitlement_manager_cash", "Cash Entitlement"),
26+
("spp_program_entitlement_manager_inkind", "In-kind Entitlement"),
27+
("spp_cycle_manager_default", "Default Cycle Schedule"),
28+
("spp_compliance_manager_default", "CEL Compliance Criteria"),
29+
("spp_program_payment_manager_default", "Default Payment"),
30+
("spp_program_manager_default", "Default Program Manager"),
31+
("spp_deduplication_manager_default", "Default Deduplication"),
32+
)
33+
34+
35+
def migrate(cr, version):
36+
if not version:
37+
return
38+
for table, label in _DEFAULT_NAME_RENAMES:
39+
# Skip tables that don't exist (modules not installed in this DB).
40+
cr.execute(
41+
"SELECT 1 FROM information_schema.tables WHERE table_name = %s",
42+
(table,),
43+
)
44+
if not cr.fetchone():
45+
continue
46+
cr.execute(
47+
sql.SQL("UPDATE {tbl} SET name = %s WHERE name = 'Default'").format(
48+
tbl=sql.Identifier(table),
49+
),
50+
(label,),
51+
)
52+
if cr.rowcount:
53+
_logger.info(
54+
"Renamed %d %s rows from 'Default' to %r",
55+
cr.rowcount,
56+
table,
57+
label,
58+
)

spp_programs/models/cel/eligibility_cel.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,16 @@ class DefaultEligibilityManagerCEL(models.Model):
5656
help="True if the local expression differs from the source template",
5757
)
5858

59+
@api.model
60+
def default_get(self, fields_list):
61+
"""Default the manager name to its method-specific label so the
62+
record's actual `name` (visible on the Edit form) reads "CEL
63+
Eligibility Criteria" instead of the placeholder "Default"."""
64+
res = super().default_get(fields_list)
65+
if "name" in fields_list:
66+
res.setdefault("name", _("CEL Eligibility Criteria"))
67+
return res
68+
5969
# -------------------------------------------------------------------------
6070
# CEL Profile (computed for widget context)
6171
# -------------------------------------------------------------------------

spp_programs/models/cycle.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,27 @@ def _get_approval_definition(self):
610610
self.ensure_one()
611611
return self.cycle_approval_definition_id
612612

613+
def _notify_thread_by_email(self, message, recipients_data, **kwargs):
614+
"""Suppress outgoing email when the parent program has email
615+
notifications disabled. Chatter logging and in-app notifications are
616+
unaffected — only the email dispatch is short-circuited."""
617+
self.ensure_one()
618+
if self.program_id and not self.program_id._should_send_email_notifications():
619+
return
620+
return super()._notify_thread_by_email(message, recipients_data, **kwargs)
621+
622+
def _create_approval_activity(self, definition, review):
623+
"""Gate the approver-email path on the parent program's toggle.
624+
625+
spp.approval.mixin schedules a mail.activity for each approver on
626+
submit; the activity dispatch sends email through the assignee's
627+
notification preferences. Skip the scheduling entirely when the
628+
program has email notifications turned off."""
629+
self.ensure_one()
630+
if self.program_id and not self.program_id._should_send_email_notifications():
631+
return
632+
return super()._create_approval_activity(definition, review)
633+
613634
@api.onchange("start_date")
614635
def on_start_date_change(self):
615636
self.program_id.get_manager(constants.MANAGER_CYCLE).on_start_date_change(self)

spp_programs/models/entitlement.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def _generate_code(self):
8484

8585
is_cash_entitlement = fields.Boolean("Cash Entitlement", default=False)
8686
currency_id = fields.Many2one("res.currency", readonly=True, related="journal_id.currency_id")
87-
initial_amount = fields.Monetary(required=True, currency_field="currency_id")
87+
initial_amount = fields.Monetary(string="Amount", required=True, currency_field="currency_id")
8888
transfer_fee = fields.Monetary(currency_field="currency_id", default=0.0)
8989
balance = fields.Monetary(compute="_compute_balance") # in company currency
9090
# TODO: implement transactions against this entitlement
@@ -390,6 +390,29 @@ def _get_approval_definition(self):
390390
self.ensure_one()
391391
return self.entitlement_approval_definition_id
392392

393+
def _notify_thread_by_email(self, message, recipients_data, **kwargs):
394+
"""Suppress outgoing email when the parent program has email
395+
notifications disabled. Chatter logging and in-app notifications are
396+
unaffected — only the email dispatch is short-circuited."""
397+
self.ensure_one()
398+
program = self.cycle_id.program_id
399+
if program and not program._should_send_email_notifications():
400+
return
401+
return super()._notify_thread_by_email(message, recipients_data, **kwargs)
402+
403+
def _create_approval_activity(self, definition, review):
404+
"""Gate the approver-email path on the parent program's toggle.
405+
406+
spp.approval.mixin schedules a mail.activity for each approver on
407+
submit; the activity dispatch sends email through the assignee's
408+
notification preferences. Skip the scheduling entirely when the
409+
program has email notifications turned off."""
410+
self.ensure_one()
411+
program = self.cycle_id.program_id
412+
if program and not program._should_send_email_notifications():
413+
return
414+
return super()._create_approval_activity(definition, review)
415+
393416
def action_submit_for_approval(self):
394417
"""Submit entitlement for approval using standardized workflow."""
395418
for record in self:
@@ -885,6 +908,24 @@ def _resolve_approval_definition(self):
885908
self.ensure_one()
886909
return self._get_approval_definition()
887910

911+
def _notify_thread_by_email(self, message, recipients_data, **kwargs):
912+
"""Suppress outgoing email when the parent program has email
913+
notifications disabled. Chatter logging and in-app notifications are
914+
unaffected — only the email dispatch is short-circuited."""
915+
self.ensure_one()
916+
program = self.cycle_id.program_id
917+
if program and not program._should_send_email_notifications():
918+
return
919+
return super()._notify_thread_by_email(message, recipients_data, **kwargs)
920+
921+
def _create_approval_activity(self, definition, review):
922+
"""Gate the approver-email path on the parent program's toggle."""
923+
self.ensure_one()
924+
program = self.cycle_id.program_id
925+
if program and not program._should_send_email_notifications():
926+
return
927+
return super()._create_approval_activity(definition, review)
928+
888929
def action_submit_for_approval(self):
889930
"""Submit in-kind entitlement for approval using standardized workflow."""
890931
for record in self:

spp_programs/models/managers/compliance_manager.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,14 @@ class DefaultComplianceManager(models.Model):
9797
_inherit = ["spp.compliance.manager.base", "spp.manager.source.mixin"]
9898
_description = "Default Compliance Manager"
9999

100+
@api.model
101+
def default_get(self, fields_list):
102+
"""Default the manager name to its method-specific label."""
103+
res = super().default_get(fields_list)
104+
if "name" in fields_list:
105+
res.setdefault("name", _("CEL Compliance Criteria"))
106+
return res
107+
100108
# Mode selection
101109
compliance_cel_mode = fields.Selection(
102110
selection=[

spp_programs/models/managers/cycle_manager_base.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,14 @@ class DefaultCycleManager(models.Model):
407407
]
408408
_description = "Default Cycle Manager"
409409

410+
@api.model
411+
def default_get(self, fields_list):
412+
"""Default the manager name to its method-specific label."""
413+
res = super().default_get(fields_list)
414+
if "name" in fields_list:
415+
res.setdefault("name", _("Default Cycle Schedule"))
416+
return res
417+
410418
cycle_duration = fields.Integer(default=1, required=True, string="Recurrence")
411419
approver_group_id = fields.Many2one(
412420
comodel_name="res.groups",

spp_programs/models/managers/deduplication_manager.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import logging
55
from datetime import date
66

7-
from odoo import Command, api, fields, models
7+
from odoo import Command, _, api, fields, models
88

99
_logger = logging.getLogger(__name__)
1010

@@ -53,6 +53,14 @@ class DefaultDeduplication(models.Model):
5353
_capability_individual = True
5454
_capability_group = True
5555

56+
@api.model
57+
def default_get(self, fields_list):
58+
"""Default the manager name to its method-specific label."""
59+
res = super().default_get(fields_list)
60+
if "name" in fields_list:
61+
res.setdefault("name", _("Default Deduplication"))
62+
return res
63+
5664
def deduplicate_beneficiaries(self, states):
5765
for rec in self:
5866
duplicate_beneficiaries = []

spp_programs/models/managers/entitlement_manager_base.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,14 @@ class DefaultCashEntitlementManager(models.Model):
329329
# Set to True so that the UI will display the payment management components
330330
IS_CASH_ENTITLEMENT = True
331331

332+
@api.model
333+
def default_get(self, fields_list):
334+
"""Default the manager name to its method-specific label."""
335+
res = super().default_get(fields_list)
336+
if "name" in fields_list:
337+
res.setdefault("name", _("Basic Cash"))
338+
return res
339+
332340
amount_per_cycle = fields.Monetary(
333341
currency_field="currency_id",
334342
aggregator="sum",

0 commit comments

Comments
 (0)