Skip to content
Open
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
4 changes: 3 additions & 1 deletion spp_base_common/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
{
"name": "OpenSPP Base (Common)",
"category": "OpenSPP/Core",
"version": "19.0.2.0.0",
"version": "19.0.2.0.1",
"sequence": 1,
"author": "OpenSPP.org",
"website": "https://github.com/OpenSPP/OpenSPP2",
Expand Down Expand Up @@ -38,6 +38,8 @@
"spp_base_common/static/src/xml/custom_list_create_template.xml",
"spp_base_common/static/src/js/filterable_radio_field.js",
"spp_base_common/static/src/xml/filterable_radio_field.xml",
"spp_base_common/static/src/xml/pager_hide_single.xml",
"spp_base_common/static/src/scss/pager_hide_single.scss",
],
"web._assets_primary_variables": [
"spp_base_common/static/src/scss/colors.scss",
Expand Down
10 changes: 10 additions & 0 deletions spp_base_common/static/src/scss/pager_hide_single.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// See OP#920 + spp_base_common/static/src/xml/pager_hide_single.xml.
// The control-panel right-side toolbar (refresh button, embedded
// actions, view switcher) had no vertical alignment of its own — it
// relied on the pager's h-100 nav to anchor the row's height.
// Once the pager is hidden, those siblings collapse to top-aligned.
// Set align-items: center explicitly so they stay vertically centred
// whether the pager is visible or not.
.o_control_panel_navigation {
align-items: center;
}
26 changes: 26 additions & 0 deletions spp_base_common/static/src/xml/pager_hide_single.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Hide the form-view "1/1" pager when there's only one record to navigate.

The standard web.Pager renders the counter + chevrons even when
`total === 1`, leaving a disabled "1/1 < >" widget that confuses
users. We drop the entire <nav> with `t-if` and pair the change
with an `align-items: center` rule on the parent
`o_control_panel_navigation` (see pager_hide_single.scss) so the
sibling refresh button stays vertically centred without the
pager's old h-100 anchor. `1/1+` (updateTotal set) stays visible
because the count may still grow.

See OP#920.
-->
<templates xml:space="preserve">
<t
t-name="spp_base_common.PagerHideSingle"
t-inherit="web.Pager"
t-inherit-mode="extension"
>
<xpath expr="//nav[hasclass('o_pager')]" position="attributes">
<attribute name="t-if">props.total &gt; 1 or props.updateTotal</attribute>
</xpath>
</t>
</templates>
14 changes: 14 additions & 0 deletions spp_change_request_v2/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,20 @@ Before declaring a new CR type complete:
Changelog
=========

19.0.2.0.4
~~~~~~~~~~

- fix(views): route post-submit CRs (pending / approved / applied /
rejected) through the stage review form when opened from the list,
matching the Edit Details → Upload Documents → Review & Submit
breadcrumb workflow used for fresh CRs (#920 round-2). Demo-generated
CRs in "Applied" state previously landed on the legacy main form view
from the list — now they open in ``spp_change_request_review_form``
like manually-created CRs. Adds the missing
``_action_open_review_form`` / ``_action_open_documents_form`` helpers
and wires ``action="action_open_stage_form" type="object"`` on the CR
list so row-click goes through the stage router.

19.0.2.0.3
~~~~~~~~~~

Expand Down
2 changes: 1 addition & 1 deletion spp_change_request_v2/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "OpenSPP Change Request V2",
"version": "19.0.2.0.3",
"version": "19.0.2.0.4",
"sequence": 50,
"category": "OpenSPP",
"summary": "Configuration-driven change request system with UX improvements, conflict detection and duplicate prevention",
Expand Down
71 changes: 53 additions & 18 deletions spp_change_request_v2/models/change_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,7 @@ def _on_submit(self):
action = "resubmitted" if old_state == "revision" else "submitted"
self._create_audit_event("submitted", old_state, "pending")
self._create_log(action)
self.stage = "review"

def _on_request_revision(self, notes):
super()._on_request_revision(notes)
Expand Down Expand Up @@ -1523,29 +1524,63 @@ def action_upload_document(self):
def action_open_stage_form(self):
"""Open the appropriate form view based on the current stage.

For draft/revision CRs: routes to the stage-specific form.
For other states: opens the main CR form (for validators/managers).
- **Draft / revision**: route by `stage` to the editable stage form
(details / documents / review).
- **Submitted+ (pending, approved, applied, rejected)**: always open
the review-stage form. That form already renders state-aware
headers (Approve/Reject for validators, Apply for managers,
Applied ribbon for completed, Start Over for rejected) and shows
the same Edit Details → Upload Documents → Review & Submit
breadcrumb. Without this, validators/managers (and demo-applied
CRs opened from the list) landed on the legacy main form view
which lacks the breadcrumb and the pager-hide treatment. See
OP#920 round-2.
"""
self.ensure_one()

if self.approval_state not in ("draft", "revision"):
return {
"type": "ir.actions.act_window",
"name": self.name,
"res_model": "spp.change.request",
"res_id": self.id,
"view_mode": "form",
"views": [[False, "form"]],
"target": "current",
}
if self.approval_state in ("draft", "revision"):
if self.stage == "documents":
return self._action_open_documents_form()
if self.stage == "review":
return self._action_open_review_form()
return self.action_open_detail()

# pending / approved / applied / rejected
return self._action_open_review_form()

if self.stage == "documents":
return self._action_open_documents_form()
if self.stage == "review":
return self._action_open_review_form()
def _action_open_review_form(self):
"""Open the CR in the Review & Submit stage form view."""
self.ensure_one()
view = self.env.ref(
"spp_change_request_v2.spp_change_request_review_form",
raise_if_not_found=False,
)
return {
"type": "ir.actions.act_window",
"name": self.name,
"res_model": "spp.change.request",
"res_id": self.id,
"view_mode": "form",
"views": [[view.id if view else False, "form"]],
"target": "current",
}

# Default: details stage
return self.action_open_detail()
def _action_open_documents_form(self):
"""Open the CR in the Upload Documents stage form view."""
self.ensure_one()
view = self.env.ref(
"spp_change_request_v2.spp_change_request_documents_form",
raise_if_not_found=False,
)
return {
"type": "ir.actions.act_window",
"name": self.name,
"res_model": "spp.change.request",
"res_id": self.id,
"view_mode": "form",
"views": [[view.id if view else False, "form"]],
"target": "current",
}
Comment on lines +1551 to +1583
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The methods _action_open_review_form and _action_open_documents_form are almost identical. Refactoring them into a single helper method reduces code duplication and improves maintainability. Additionally, using self._name instead of a hardcoded model string is more idiomatic and robust for inheritance, aligning with the practice of preferring the _name attribute to avoid synchronization edge cases.

    def _action_open_stage_view(self, view_xml_id):
        """Helper to open the CR in a specific stage form view."""
        self.ensure_one()
        view = self.env.ref(view_xml_id, raise_if_not_found=False)
        return {
            "type": "ir.actions.act_window",
            "name": self.name,
            "res_model": self._name,
            "res_id": self.id,
            "view_mode": "form",
            "views": [[view.id if view else False, "form"]],
            "target": "current",
        }

    def _action_open_review_form(self):
        """Open the CR in the Review & Submit stage form view."""
        return self._action_open_stage_view("spp_change_request_v2.spp_change_request_review_form")

    def _action_open_documents_form(self):
        """Open the CR in the Upload Documents stage form view."""
        return self._action_open_stage_view("spp_change_request_v2.spp_change_request_documents_form")
References
  1. To avoid synchronization edge cases with related fields, prefer accessing a record's model name via its _name attribute rather than from a related field on a parent record.


def action_goto_details(self):
"""Navigate to the details stage (replaces breadcrumb via client action)."""
Expand Down
4 changes: 4 additions & 0 deletions spp_change_request_v2/readme/HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
### 19.0.2.0.4

- fix(views): route post-submit CRs (pending / approved / applied / rejected) through the stage review form when opened from the list, matching the Edit Details → Upload Documents → Review & Submit breadcrumb workflow used for fresh CRs (#920 round-2). Demo-generated CRs in "Applied" state previously landed on the legacy main form view from the list — now they open in `spp_change_request_review_form` like manually-created CRs. Adds the missing `_action_open_review_form` / `_action_open_documents_form` helpers and wires `action="action_open_stage_form" type="object"` on the CR list so row-click goes through the stage router.

### 19.0.2.0.3

- fix: add HTML escaping to all computed Html fields with `sanitize=False` to prevent stored XSS (#50)
Expand Down
21 changes: 18 additions & 3 deletions spp_change_request_v2/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1339,26 +1339,41 @@ <h2>Changelog</h2>
</div>
</div>
<div class="section" id="section-1">
<h1>19.0.2.0.4</h1>
<ul class="simple">
<li>fix(views): route post-submit CRs (pending / approved / applied /
rejected) through the stage review form when opened from the list,
matching the Edit Details → Upload Documents → Review &amp; Submit
breadcrumb workflow used for fresh CRs (#920 round-2). Demo-generated
CRs in “Applied” state previously landed on the legacy main form view
from the list — now they open in <tt class="docutils literal">spp_change_request_review_form</tt>
like manually-created CRs. Adds the missing
<tt class="docutils literal">_action_open_review_form</tt> / <tt class="docutils literal">_action_open_documents_form</tt> helpers
and wires <tt class="docutils literal"><span class="pre">action=&quot;action_open_stage_form&quot;</span> <span class="pre">type=&quot;object&quot;</span></tt> on the CR
list so row-click goes through the stage router.</li>
</ul>
</div>
<div class="section" id="section-2">
<h1>19.0.2.0.3</h1>
<ul class="simple">
<li>fix: add HTML escaping to all computed Html fields with
<tt class="docutils literal">sanitize=False</tt> to prevent stored XSS (#50)</li>
</ul>
</div>
<div class="section" id="section-2">
<div class="section" id="section-3">
<h1>19.0.2.0.2</h1>
<ul class="simple">
<li>fix: fix batch approval wizard line deletion (#130)</li>
</ul>
</div>
<div class="section" id="section-3">
<div class="section" id="section-4">
<h1>19.0.2.0.1</h1>
<ul class="simple">
<li>fix: skip field types before getattr and isolate detail prefetch
(#129)</li>
</ul>
</div>
<div class="section" id="section-4">
<div class="section" id="section-5">
<h1>19.0.2.0.0</h1>
<ul class="simple">
<li>Initial migration to OpenSPP2</li>
Expand Down
6 changes: 4 additions & 2 deletions spp_change_request_v2/tests/test_stage_navigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,16 @@ def test_action_open_stage_form_draft_review(self):
cr.action_open_stage_form()

def test_action_open_stage_form_pending(self):
"""Pending CR opens main form (not stage form)."""
"""Pending CR opens the Review & Submit stage form so post-submit
states share the breadcrumb-based UI with draft / revision."""
cr = self._create_cr()
cr.approval_state = "pending"
result = cr.action_open_stage_form()
self.assertEqual(result["type"], "ir.actions.act_window")
self.assertEqual(result["res_model"], "spp.change.request")
self.assertEqual(result["res_id"], cr.id)
self.assertEqual(result["views"], [[False, "form"]])
review_view = self.env.ref("spp_change_request_v2.spp_change_request_review_form")
self.assertEqual(result["views"], [[review_view.id, "form"]])

def test_action_start_over_creates_new_cr(self):
"""action_start_over() creates a new CR with same type and registrant."""
Expand Down
2 changes: 2 additions & 0 deletions spp_change_request_v2/views/change_request_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
decoration-warning="display_state == 'revision'"
create="0"
sample="1"
action="action_open_stage_form"
type="object"
>
<field name="name" decoration-bf="1" />
<field name="request_type_id" />
Expand Down
Loading