Skip to content

Commit a9f126b

Browse files
authored
Merge pull request #105 from OpenSPP/feat/spp-cel-registry-search-stable-release
fix(spp_cel_registry_search)
2 parents 91a890a + 5c1cb59 commit a9f126b

13 files changed

Lines changed: 1774 additions & 41 deletions

File tree

spp_cel_domain/models/cel_executor.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ def compile_and_preview(
392392
model: str,
393393
expr: str,
394394
limit: int = 50,
395+
offset: int = 0,
395396
fields: list[str] | None = None,
396397
materialize_sql: bool = False,
397398
) -> dict[str, Any]:
@@ -465,15 +466,23 @@ def compile_and_preview(
465466
preview_ids = [] # Count-only mode
466467
elif limit == 0:
467468
# Use the default from the method signature (50)
468-
preview_recordset = self.env[model].search(final_domain, limit=50)
469+
preview_recordset = self.env[model].search(final_domain, limit=50, offset=offset)
469470
preview_ids = preview_recordset.ids
470471
else:
471-
preview_recordset = self.env[model].search(final_domain, limit=limit)
472+
preview_recordset = self.env[model].search(final_domain, limit=limit, offset=offset)
472473
preview_ids = preview_recordset.ids
473474

474475
# Read full record data if fields requested (for JSON-safe preview)
475476
if preview_ids and fields:
476-
preview_data = preview_recordset.read(fields)
477+
# Replace phone_number_ids with phone for reading, then enrich
478+
read_fields = [f for f in fields if f != "phone_number_ids"]
479+
preview_data = preview_recordset.read(read_fields)
480+
if "phone_number_ids" in fields:
481+
for rec_data in preview_data:
482+
rec_id = rec_data["id"]
483+
partner = preview_recordset.filtered(lambda r, rid=rec_id: r.id == rid)
484+
phones = partner.phone_number_ids.filtered(lambda p: not p.disabled).mapped("phone_no")
485+
rec_data["phone_numbers"] = phones
477486
# Enrich explanation with metrics info if any
478487
metrics_section = ""
479488
if metrics_info:

spp_cel_domain/models/cel_service.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,19 @@ class CELService(models.AbstractModel):
3232
_name = "spp.cel.service"
3333
_description = "CEL Expression Service"
3434

35+
@api.model
36+
def check_search_access(self):
37+
"""Check if the current user has CEL search access."""
38+
return self.env.user.has_group("spp_cel_registry_search.group_cel_search_user")
39+
3540
@api.model
3641
def compile_expression(
3742
self,
3843
expression,
3944
profile,
4045
base_domain=None,
4146
limit=0,
47+
offset=0,
4248
fields=None,
4349
materialize_sql=False,
4450
):
@@ -109,6 +115,7 @@ def compile_expression(
109115
root_model,
110116
expanded_expression,
111117
limit=limit,
118+
offset=offset,
112119
fields=fields,
113120
materialize_sql=materialize_sql,
114121
)

spp_cel_domain/tests/test_cel_service.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
22
"""Adversarial tests for CEL Service - designed to BREAK the service facade."""
33

4+
from odoo import fields
45
from odoo.tests import TransactionCase, tagged
56

67

@@ -521,3 +522,80 @@ def test_output_type_string(self):
521522
"""Output type 'string' should be accepted."""
522523
result = self.service.validate_formula_expression('"hello"', "registry_groups", output_type="string")
523524
self.assertTrue(result["valid"], f"Error: {result.get('error')}")
525+
526+
# --- Offset / Pagination Tests ---
527+
528+
def test_compile_expression_offset_returns_different_records(self):
529+
"""compile_expression with offset should return different records."""
530+
for i in range(5):
531+
self.env["res.partner"].create({"name": f"OffsetSvcTest{i}", "is_registrant": True, "is_group": False})
532+
result1 = self.service.compile_expression(
533+
'r.name == "OffsetSvcTest0" || r.name == "OffsetSvcTest1" || '
534+
'r.name == "OffsetSvcTest2" || r.name == "OffsetSvcTest3" || '
535+
'r.name == "OffsetSvcTest4"',
536+
"registry_individuals",
537+
limit=2,
538+
offset=0,
539+
fields=["id", "name"],
540+
)
541+
result2 = self.service.compile_expression(
542+
'r.name == "OffsetSvcTest0" || r.name == "OffsetSvcTest1" || '
543+
'r.name == "OffsetSvcTest2" || r.name == "OffsetSvcTest3" || '
544+
'r.name == "OffsetSvcTest4"',
545+
"registry_individuals",
546+
limit=2,
547+
offset=2,
548+
fields=["id", "name"],
549+
)
550+
self.assertTrue(result1["valid"], f"Error: {result1.get('error')}")
551+
self.assertTrue(result2["valid"], f"Error: {result2.get('error')}")
552+
ids1 = {r["id"] for r in result1.get("preview_records", [])}
553+
ids2 = {r["id"] for r in result2.get("preview_records", [])}
554+
self.assertFalse(ids1 & ids2, "Offset pages should not overlap")
555+
556+
def test_compile_expression_offset_beyond_results(self):
557+
"""compile_expression with offset beyond total should return empty."""
558+
result = self.service.compile_expression("true", "registry_individuals", limit=10, offset=999999, fields=["id"])
559+
self.assertTrue(result["valid"])
560+
self.assertEqual(len(result.get("preview_records", [])), 0)
561+
562+
def test_compile_expression_phone_number_enrichment(self):
563+
"""compile_expression with phone_number_ids field should enrich results."""
564+
partner = self.env["res.partner"].create({"name": "PhoneSvcTest99", "is_registrant": True, "is_group": False})
565+
if "spp.phone.number" in self.env:
566+
self.env["spp.phone.number"].create({"partner_id": partner.id, "phone_no": "+639171111111"})
567+
self.env["spp.phone.number"].create({"partner_id": partner.id, "phone_no": "+639172222222"})
568+
result = self.service.compile_expression(
569+
'r.name == "PhoneSvcTest99"',
570+
"registry_individuals",
571+
limit=10,
572+
fields=["id", "name", "phone_number_ids"],
573+
)
574+
self.assertTrue(result["valid"])
575+
records = result.get("preview_records", [])
576+
self.assertEqual(len(records), 1)
577+
if "spp.phone.number" in self.env:
578+
self.assertIn("phone_numbers", records[0])
579+
self.assertEqual(len(records[0]["phone_numbers"]), 2)
580+
581+
def test_compile_expression_phone_excludes_disabled(self):
582+
"""Phone enrichment should exclude disabled phone numbers."""
583+
partner = self.env["res.partner"].create(
584+
{"name": "PhoneDisabledTest99", "is_registrant": True, "is_group": False}
585+
)
586+
if "spp.phone.number" in self.env:
587+
self.env["spp.phone.number"].create({"partner_id": partner.id, "phone_no": "+639173333333"})
588+
disabled_phone = self.env["spp.phone.number"].create(
589+
{"partner_id": partner.id, "phone_no": "+639174444444"}
590+
)
591+
disabled_phone.disabled = fields.Datetime.now()
592+
result = self.service.compile_expression(
593+
'r.name == "PhoneDisabledTest99"',
594+
"registry_individuals",
595+
limit=10,
596+
fields=["id", "phone_number_ids"],
597+
)
598+
records = result.get("preview_records", [])
599+
if "spp.phone.number" in self.env and records:
600+
self.assertEqual(len(records[0]["phone_numbers"]), 1)
601+
self.assertEqual(records[0]["phone_numbers"], ["+639173333333"])

0 commit comments

Comments
 (0)