diff --git a/spp_change_request_v2/views/create_wizard_views.xml b/spp_change_request_v2/views/create_wizard_views.xml
index 3cf16a8..f52e477 100644
--- a/spp_change_request_v2/views/create_wizard_views.xml
+++ b/spp_change_request_v2/views/create_wizard_views.xml
@@ -30,25 +30,34 @@
-
+
-
+
+
+
-
+
+
-
+
-
-
+
+
+
+
+
+
+
+
""").format(reason)
+ # ══════════════════════════════════════════════════════════════════════════
+ # SEARCH ACTIONS
+ # ══════════════════════════════════════════════════════════════════════════
+
+ @api.onchange("_selected_partner_id")
+ def _onchange_selected_partner(self):
+ """Convert the bridge integer to a Many2one registrant_id."""
+ if self._selected_partner_id:
+ self.registrant_id = self.env["res.partner"].browse(
+ self._selected_partner_id
+ )
+
+ _SEARCH_PAGE_SIZE = 10
+
+ @api.onchange("search_text")
+ def _onchange_search_text(self):
+ """Reset page and run search when text changes."""
+ self.search_results_html = False
+ self.registrant_id = False
+ self._search_page = 0
+
+ if not self.search_text or len(self.search_text) < 2:
+ return
+
+ self._render_search_results()
+
+ @api.onchange("_search_page")
+ def _onchange_search_page(self):
+ """Re-render results when page changes."""
+ if self.search_text and len(self.search_text) >= 2:
+ self._render_search_results()
+
+ def _get_search_domain(self):
+ """Build the search domain based on search text and target type."""
+ domain = [("is_registrant", "=", True)]
+ if self.request_type_id and self.request_type_id.target_type:
+ target = self.request_type_id.target_type
+ if target == "individual":
+ domain.append(("is_group", "=", False))
+ elif target == "group":
+ domain.append(("is_group", "=", True))
+ return domain + [
+ "|",
+ ("name", "ilike", self.search_text),
+ ("reg_ids.value", "ilike", self.search_text),
+ ]
+
+ def _render_search_results(self):
+ """Search and render paginated HTML results."""
+ search_domain = self._get_search_domain()
+ total = self.env["res.partner"].search_count(search_domain)
+
+ if not total:
+ self.search_results_html = Markup(
+ "No registrants found.
"
+ )
+ return
+
+ page = self._search_page or 0
+ page_size = self._SEARCH_PAGE_SIZE
+ max_page = (total - 1) // page_size
+ page = min(page, max_page)
+
+ offset = page * page_size
+ partners = self.env["res.partner"].search(
+ search_domain, limit=page_size, offset=offset
+ )
+
+ rows = []
+ for p in partners:
+ # Build ALL IDs in "TypeName (value)" format, show max 2
+ id_parts = []
+ if p.reg_ids:
+ for rid in p.reg_ids:
+ if rid.value:
+ label = rid.id_type_as_str or "ID"
+ id_parts.append(f"{label} ({rid.value})")
+ if not id_parts:
+ id_html = Markup("")
+ id_title = ""
+ elif len(id_parts) <= 2:
+ id_html = escape(", ".join(id_parts))
+ id_title = ""
+ else:
+ visible = escape(", ".join(id_parts[:2]))
+ extra = len(id_parts) - 2
+ id_html = Markup(
+ '{} '
+ "+{} "
+ ).format(visible, extra)
+ id_title = ", ".join(id_parts)
+ ptype = (
+ ' Group'
+ if p.is_group
+ else ' Individual'
+ )
+ rows.append(
+ Markup(
+ ''
+ "| {} | "
+ '{} | '
+ "{} |
"
+ ).format(
+ p.id,
+ escape(p.name or ""),
+ escape(p.name or ""),
+ escape(id_title),
+ id_html,
+ Markup(ptype),
+ )
+ )
+
+ table = Markup(
+ '"
+ ).format(Markup("").join(rows))
+
+ # Pagination header
+ start = offset + 1
+ end = min(offset + page_size, total)
+ prev_cls = "text-muted" if page == 0 else "o_cr_page_prev"
+ next_cls = "text-muted" if page >= max_page else "o_cr_page_next"
+ pagination = Markup(
+ '"
+ ).format(start, end, total, prev_cls, page - 1, next_cls, page + 1)
+
+ self.search_results_html = pagination + table
+
+ def action_clear_registrant(self):
+ """Clear selected registrant, re-run search, and reopen wizard."""
+ self.ensure_one()
+ self._selected_partner_id = False
+ self.registrant_id = False
+ # Re-run search with existing search_text to repopulate results
+ self._onchange_search_text()
+ return {
+ "type": "ir.actions.act_window",
+ "name": _("New Change Request"),
+ "res_model": "spp.cr.create.wizard",
+ "res_id": self.id,
+ "view_mode": "form",
+ "target": "new",
+ }
+
# ══════════════════════════════════════════════════════════════════════════
# MAIN ACTIONS
# ══════════════════════════════════════════════════════════════════════════
@@ -277,7 +438,8 @@ def action_cancel(self):
@api.onchange("request_type_id")
def _onchange_request_type(self):
- """Clear registrant if it doesn't match the new target type."""
+ """Clear registrant and search if type changes."""
+ self.search_text = False
if self.request_type_id and self.registrant_id:
target = self.request_type_id.target_type
is_group = self.registrant_id.is_group