Skip to content

Commit 5ba2bb7

Browse files
authored
Merge pull request #90 from OpenSPP/fix/alpha-to-beta-gis-report-programs-studio-api-v2
fix(spp_gis_report_programs,spp_studio_api_v2): alpha to beta promotion
2 parents ae00c8a + ec5830e commit 5ba2bb7

File tree

13 files changed

+1575
-174
lines changed

13 files changed

+1575
-174
lines changed

spp_gis_report_programs/README.rst

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,10 @@ Key Capabilities
4040
Key Models
4141
~~~~~~~~~~
4242

43-
+---------------------------+------------------------------------------+
44-
| Model | Extension |
45-
+===========================+==========================================+
46-
| ``spp.gis.report`` | Adds ``program_id`` field and filters |
47-
| | domain by program members |
48-
+---------------------------+------------------------------------------+
49-
| ``spp.gis.report.wizard`` | Adds ``program_id`` selection with |
50-
| | validation and code suffix |
51-
+---------------------------+------------------------------------------+
43+
- ``spp.gis.report`` — Adds ``program_id`` field and filters domain by
44+
program members
45+
- ``spp.gis.report.wizard`` — Adds ``program_id`` selection with
46+
validation and code suffix
5247

5348
Configuration
5449
~~~~~~~~~~~~~
@@ -67,18 +62,12 @@ UI Location
6762
Security
6863
~~~~~~~~
6964

70-
+------------------------------------------+----------------------------------+
71-
| Group | Access |
72-
+==========================================+==================================+
73-
| ``spp_programs.group_programs_officer`` | Read GIS reports, data, |
74-
| | templates, etc. |
75-
+------------------------------------------+----------------------------------+
76-
| ``spp_programs.group_programs_manager`` | Read/Write (no create/unlink) |
77-
| | all models |
78-
+------------------------------------------+----------------------------------+
79-
| ``spp_gis_report.group_gis_report_user`` | Implied for program managers via |
80-
| | XML |
81-
+------------------------------------------+----------------------------------+
65+
- ``spp_programs.group_programs_officer`` — Read GIS reports, data,
66+
templates, etc.
67+
- ``spp_programs.group_programs_manager`` — Read/Write (no
68+
create/unlink) all models
69+
- ``spp_gis_report.group_gis_report_user`` — Implied for program
70+
managers via XML
8271

8372
Models with access: ``spp.gis.report``, ``spp.gis.report.data``,
8473
``spp.gis.report.threshold``, ``spp.gis.report.template``,

spp_gis_report_programs/__manifest__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"views/gis_report_views.xml",
1717
"views/gis_report_wizard_views.xml",
1818
],
19+
"development_status": "Beta",
1920
"installable": True,
2021
"application": False,
2122
"auto_install": True,

spp_gis_report_programs/readme/DESCRIPTION.md

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ Auto-install glue module that adds program context filtering to GIS reports. Ext
1010

1111
### Key Models
1212

13-
| Model | Extension |
14-
| ----------------------- | -------------------------------------------------------------- |
15-
| `spp.gis.report` | Adds `program_id` field and filters domain by program members |
16-
| `spp.gis.report.wizard` | Adds `program_id` selection with validation and code suffix |
13+
- `spp.gis.report` — Adds `program_id` field and filters domain by program members
14+
- `spp.gis.report.wizard` — Adds `program_id` selection with validation and code suffix
1715

1816
### Configuration
1917

@@ -26,11 +24,9 @@ No configuration required. Auto-installs when both `spp_gis_report` and `spp_pro
2624

2725
### Security
2826

29-
| Group | Access |
30-
| ------------------------------------- | ----------------------------------------- |
31-
| `spp_programs.group_programs_officer` | Read GIS reports, data, templates, etc. |
32-
| `spp_programs.group_programs_manager` | Read/Write (no create/unlink) all models |
33-
| `spp_gis_report.group_gis_report_user` | Implied for program managers via XML |
27+
- `spp_programs.group_programs_officer` — Read GIS reports, data, templates, etc.
28+
- `spp_programs.group_programs_manager` — Read/Write (no create/unlink) all models
29+
- `spp_gis_report.group_gis_report_user` — Implied for program managers via XML
3430

3531
Models with access: `spp.gis.report`, `spp.gis.report.data`, `spp.gis.report.threshold`, `spp.gis.report.template`, `spp.gis.report.category`
3632

spp_gis_report_programs/static/description/index.html

Lines changed: 14 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -387,27 +387,12 @@ <h1>Key Capabilities</h1>
387387
</div>
388388
<div class="section" id="key-models">
389389
<h1>Key Models</h1>
390-
<table border="1" class="docutils">
391-
<colgroup>
392-
<col width="39%" />
393-
<col width="61%" />
394-
</colgroup>
395-
<thead valign="bottom">
396-
<tr><th class="head">Model</th>
397-
<th class="head">Extension</th>
398-
</tr>
399-
</thead>
400-
<tbody valign="top">
401-
<tr><td><tt class="docutils literal">spp.gis.report</tt></td>
402-
<td>Adds <tt class="docutils literal">program_id</tt> field and filters
403-
domain by program members</td>
404-
</tr>
405-
<tr><td><tt class="docutils literal">spp.gis.report.wizard</tt></td>
406-
<td>Adds <tt class="docutils literal">program_id</tt> selection with
407-
validation and code suffix</td>
408-
</tr>
409-
</tbody>
410-
</table>
390+
<ul class="simple">
391+
<li><tt class="docutils literal">spp.gis.report</tt> — Adds <tt class="docutils literal">program_id</tt> field and filters domain by
392+
program members</li>
393+
<li><tt class="docutils literal">spp.gis.report.wizard</tt> — Adds <tt class="docutils literal">program_id</tt> selection with
394+
validation and code suffix</li>
395+
</ul>
411396
</div>
412397
<div class="section" id="configuration">
413398
<h1>Configuration</h1>
@@ -425,31 +410,14 @@ <h1>UI Location</h1>
425410
</div>
426411
<div class="section" id="security">
427412
<h1>Security</h1>
428-
<table border="1" class="docutils">
429-
<colgroup>
430-
<col width="55%" />
431-
<col width="45%" />
432-
</colgroup>
433-
<thead valign="bottom">
434-
<tr><th class="head">Group</th>
435-
<th class="head">Access</th>
436-
</tr>
437-
</thead>
438-
<tbody valign="top">
439-
<tr><td><tt class="docutils literal">spp_programs.group_programs_officer</tt></td>
440-
<td>Read GIS reports, data,
441-
templates, etc.</td>
442-
</tr>
443-
<tr><td><tt class="docutils literal">spp_programs.group_programs_manager</tt></td>
444-
<td>Read/Write (no create/unlink)
445-
all models</td>
446-
</tr>
447-
<tr><td><tt class="docutils literal">spp_gis_report.group_gis_report_user</tt></td>
448-
<td>Implied for program managers via
449-
XML</td>
450-
</tr>
451-
</tbody>
452-
</table>
413+
<ul class="simple">
414+
<li><tt class="docutils literal">spp_programs.group_programs_officer</tt> — Read GIS reports, data,
415+
templates, etc.</li>
416+
<li><tt class="docutils literal">spp_programs.group_programs_manager</tt> — Read/Write (no
417+
create/unlink) all models</li>
418+
<li><tt class="docutils literal">spp_gis_report.group_gis_report_user</tt> — Implied for program
419+
managers via XML</li>
420+
</ul>
453421
<p>Models with access: <tt class="docutils literal">spp.gis.report</tt>, <tt class="docutils literal">spp.gis.report.data</tt>,
454422
<tt class="docutils literal">spp.gis.report.threshold</tt>, <tt class="docutils literal">spp.gis.report.template</tt>,
455423
<tt class="docutils literal">spp.gis.report.category</tt></p>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
2+
from . import test_gis_report_programs
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
2+
3+
4+
from odoo.exceptions import ValidationError
5+
from odoo.tests import tagged
6+
from odoo.tests.common import TransactionCase
7+
8+
9+
@tagged("post_install", "-at_install")
10+
class TestGISReportProgramsModel(TransactionCase):
11+
"""Test GIS Report extension for program context filtering."""
12+
13+
@classmethod
14+
def setUpClass(cls):
15+
super().setUpClass()
16+
cls.Report = cls.env["spp.gis.report"]
17+
cls.Program = cls.env["spp.program"]
18+
19+
cls.program = cls.Program.create(
20+
{
21+
"name": "Test Program for GIS",
22+
"target_type": "group",
23+
}
24+
)
25+
26+
# Get source model for res.partner
27+
cls.partner_model = cls.env["ir.model"].search([("model", "=", "res.partner")], limit=1)
28+
29+
def _make_report_vals(self, **overrides):
30+
"""Build minimal vals dict for a spp.gis.report record."""
31+
vals = {
32+
"name": "Test Report",
33+
"code": "TEST_RPT_001",
34+
"source_model_id": self.partner_model.id,
35+
"area_field_path": "area_id",
36+
"filter_mode": "domain",
37+
"aggregation_method": "count",
38+
"base_area_level": 2,
39+
"normalization_method": "raw",
40+
"threshold_mode": "auto_quartile",
41+
"refresh_mode": "manual",
42+
"geometry_type": "polygon",
43+
}
44+
vals.update(overrides)
45+
return vals
46+
47+
def test_program_id_field_exists(self):
48+
"""Test that program_id field is added to spp.gis.report."""
49+
field_info = self.Report.fields_get(["program_id"])
50+
self.assertIn("program_id", field_info)
51+
self.assertEqual(field_info["program_id"]["relation"], "spp.program")
52+
53+
def test_report_create_with_program(self):
54+
"""Test creating a report with program_id set."""
55+
report = self.Report.create(self._make_report_vals(program_id=self.program.id))
56+
self.assertEqual(report.program_id, self.program)
57+
58+
def test_report_create_without_program(self):
59+
"""Test creating a report without program_id."""
60+
report = self.Report.create(self._make_report_vals())
61+
self.assertFalse(report.program_id)
62+
63+
def test_apply_context_filters_with_program(self):
64+
"""Test _apply_context_filters adds program enrollment domain."""
65+
report = self.Report.new(
66+
{
67+
"program_id": self.program.id,
68+
"source_model": "res.partner",
69+
}
70+
)
71+
domain = [("is_registrant", "=", True)]
72+
result = report._apply_context_filters(domain)
73+
74+
# Should contain the original domain + program membership filter
75+
flat = str(result)
76+
self.assertIn("program_membership_ids.program_id", flat)
77+
78+
def test_apply_context_filters_without_program(self):
79+
"""Test _apply_context_filters is a no-op without program_id."""
80+
report = self.Report.new(
81+
{
82+
"source_model": "res.partner",
83+
}
84+
)
85+
domain = [("is_registrant", "=", True)]
86+
result = report._apply_context_filters(domain)
87+
self.assertEqual(result, domain)
88+
89+
def test_apply_context_filters_non_partner_model(self):
90+
"""Test _apply_context_filters skips filter for non-partner models."""
91+
report = self.Report.new(
92+
{
93+
"program_id": self.program.id,
94+
"source_model": "spp.area",
95+
}
96+
)
97+
domain = [("active", "=", True)]
98+
result = report._apply_context_filters(domain)
99+
# Should NOT add program filter since source_model is not res.partner
100+
self.assertEqual(result, domain)
101+
102+
103+
@tagged("post_install", "-at_install")
104+
class TestGISReportWizardPrograms(TransactionCase):
105+
"""Test GIS Report Wizard extension for program context."""
106+
107+
@classmethod
108+
def setUpClass(cls):
109+
super().setUpClass()
110+
cls.Wizard = cls.env["spp.gis.report.wizard"]
111+
cls.Program = cls.env["spp.program"]
112+
113+
cls.program = cls.Program.create(
114+
{
115+
"name": "Wizard Test Program",
116+
"target_type": "group",
117+
}
118+
)
119+
120+
def test_wizard_program_id_field(self):
121+
"""Test that program_id field exists on the wizard."""
122+
field_info = self.Wizard.fields_get(["program_id"])
123+
self.assertIn("program_id", field_info)
124+
self.assertEqual(field_info["program_id"]["relation"], "spp.program")
125+
126+
def test_validate_context_requirements_program_required(self):
127+
"""Test validation when template requires program but none set."""
128+
wizard = self.Wizard.new(
129+
{
130+
"template_requires_program": True,
131+
"program_id": False,
132+
}
133+
)
134+
with self.assertRaises(ValidationError):
135+
wizard._validate_context_requirements()
136+
137+
def test_validate_context_requirements_program_provided(self):
138+
"""Test validation passes when required program is provided."""
139+
wizard = self.Wizard.new(
140+
{
141+
"template_requires_program": True,
142+
"program_id": self.program.id,
143+
}
144+
)
145+
# Should not raise
146+
wizard._validate_context_requirements()
147+
148+
def test_validate_context_requirements_not_required(self):
149+
"""Test validation passes when program is not required."""
150+
wizard = self.Wizard.new(
151+
{
152+
"template_requires_program": False,
153+
"program_id": False,
154+
}
155+
)
156+
# Should not raise
157+
wizard._validate_context_requirements()
158+
159+
def test_get_context_filter_vals_with_program(self):
160+
"""Test _get_context_filter_vals includes program_id."""
161+
wizard = self.Wizard.new(
162+
{
163+
"program_id": self.program.id,
164+
}
165+
)
166+
vals = wizard._get_context_filter_vals()
167+
self.assertEqual(vals.get("program_id"), self.program.id)
168+
169+
def test_get_context_filter_vals_without_program(self):
170+
"""Test _get_context_filter_vals without program."""
171+
wizard = self.Wizard.new(
172+
{
173+
"program_id": False,
174+
}
175+
)
176+
vals = wizard._get_context_filter_vals()
177+
self.assertNotIn("program_id", vals)
178+
179+
def test_get_context_code_suffix_with_program(self):
180+
"""Test _get_context_code_suffix includes program ID."""
181+
wizard = self.Wizard.new(
182+
{
183+
"program_id": self.program.id,
184+
}
185+
)
186+
suffix = wizard._get_context_code_suffix()
187+
self.assertIn(str(self.program.id), suffix)
188+
189+
def test_get_context_code_suffix_without_program(self):
190+
"""Test _get_context_code_suffix returns empty without program."""
191+
wizard = self.Wizard.new(
192+
{
193+
"program_id": False,
194+
}
195+
)
196+
suffix = wizard._get_context_code_suffix()
197+
self.assertEqual(suffix, "")

0 commit comments

Comments
 (0)