fix(portal): sidebar values + Purchase Order naming on /my/orders detail

1. Odoo's portal_order_page route calls _get_page_view_values which
   doesn't touch _prepare_portal_layout_values, so our sidebar
   context (fp_sidebar_items, fp_partner_display_name) was missing
   on every Odoo detail page (SO, invoice, delivery, quote). Override
   _get_page_view_values to setdefault our two keys into the values
   dict — non-clobbering, covers every detail route.

2. Rename "Sales Order(s)" / "Your Orders" to "Purchase Order(s)" on
   the customer portal so the wording matches the sidebar item and
   the customer's perspective (they purchase from us). Inherits in
   fp_sale_order_portal.xml replace the relevant text nodes in
   sale.portal_my_home_menu_sale / sale.portal_my_orders /
   sale.sale_order_portal_content.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-05-18 00:22:36 -04:00
parent ab7ff3eea5
commit 4e54ecc32f
2 changed files with 109 additions and 0 deletions

View File

@@ -196,6 +196,20 @@ class FpCustomerPortal(CustomerPortal):
values['fp_partner_display_name'] = commercial.name or partner.name
return values
def _get_page_view_values(self, document, access_token, values, session_history, no_breadcrumbs, **kwargs):
# Odoo's detail-page routes (sale order, invoice, delivery, etc.)
# funnel through this helper. It sets up chatter/pager but doesn't
# touch _prepare_portal_layout_values, so our sidebar context wouldn't
# otherwise reach those templates. Inject our keys conservatively via
# setdefault — never overwrite anything the page already set.
values = super()._get_page_view_values(
document, access_token, values, session_history, no_breadcrumbs, **kwargs,
)
layout = self._prepare_portal_layout_values()
values.setdefault('fp_sidebar_items', layout.get('fp_sidebar_items'))
values.setdefault('fp_partner_display_name', layout.get('fp_partner_display_name'))
return values
# ==========================================================================
# Customer-visible stage timeline (detail page)
# ==========================================================================

View File

@@ -95,4 +95,99 @@
</template>
<!-- ================================================================== -->
<!-- Breadcrumbs: rename "Sales Orders" → "Purchase Orders" on -->
<!-- confirmed-order pages, and "Sales Order SOXXXX" → "Purchase -->
<!-- Order SOXXXX" in the detail breadcrumb active item. -->
<!-- -->
<!-- The stock template (sale.portal_my_home_menu_sale) has two -->
<!-- adjacent <li> elements inside a t-elif chain: -->
<!-- • t-if → Quotations list crumb (state=sent/cancel) -->
<!-- • t-elif → Sales Orders list crumb (confirmed orders) -->
<!-- followed by a third <li t-if="sale_order"> that shows the -->
<!-- document name with a <span t-field="sale_order.type_name"/>. -->
<!-- -->
<!-- We replace only the confirmed-order <li t-elif> (sales-orders -->
<!-- list link) and the detail <li> (document name prefix). The -->
<!-- Quotations branch is left intact — our portal doesn't expose -->
<!-- /my/quotes from the sidebar, but we don't need to break it. -->
<!-- ================================================================== -->
<template id="fp_portal_breadcrumbs_purchase_order_rename"
inherit_id="sale.portal_my_home_menu_sale"
priority="30">
<!-- Replace the "Sales Orders" list-link breadcrumb item -->
<xpath expr="//li[@t-elif and contains(., 'Sales Orders')]" position="replace">
<li t-elif="page_name == 'order' or sale_order and sale_order.state not in ('sent', 'cancel')"
t-attf-class="breadcrumb-item #{'active ' if not sale_order else ''}">
<a t-if="sale_order" t-attf-href="/my/orders?{{ keep_query() }}">Purchase Orders</a>
<t t-else="">Purchase Orders</t>
</li>
</xpath>
<!-- Replace the detail breadcrumb item: "Sales Order SO-XXXXX" →
"Purchase Order SO-XXXXX". The original uses t-field="sale_order.type_name"
which returns "Sales Order" or "Quotation" at runtime. We hard-code
"Purchase Order" for the confirmed-order case (only this crumb fires
for state not in sent/cancel, so quotation pages are unaffected). -->
<xpath expr="//li[@t-if='sale_order' and hasclass('breadcrumb-item')]" position="replace">
<li t-if="sale_order" class="breadcrumb-item active">
<t t-if="sale_order.state in ('sent', 'cancel')">
<t t-out="sale_order.type_name"/>
</t>
<t t-else="">Purchase Order</t>
<t t-out="' ' + sale_order.name"/>
</li>
</xpath>
</template>
<!-- ================================================================== -->
<!-- /my/orders list page: rename "Your Orders" title and -->
<!-- "Sales Order #" column header -->
<!-- ================================================================== -->
<template id="fp_portal_my_orders_rename"
inherit_id="sale.portal_my_orders"
priority="30">
<!-- Rename the page title from "Your Orders" to "Purchase Orders" -->
<xpath expr="//t[@t-call='portal.portal_searchbar']" position="replace">
<t t-call="portal.portal_searchbar">
<t t-set="title">Purchase Orders</t>
</t>
</xpath>
<!-- Rename "Sales Order #" column header -->
<xpath expr="//span[@class='d-none d-md-inline'][contains(text(), 'Sales Order #')]" position="replace">
<span class="d-none d-md-inline">Purchase Order #</span>
</xpath>
</template>
<!-- ================================================================== -->
<!-- Detail page heading: "Sales Order - SO-XXXXX" → -->
<!-- "Purchase Order - SO-XXXXX" -->
<!-- -->
<!-- sale.sale_order_portal_content renders the <h2> with -->
<!-- <t t-out="sale_order.type_name"/> which evaluates to "Sales Order" -->
<!-- for confirmed orders and "Quotation" for draft/sent. We replace -->
<!-- the entire <h2> to hard-code "Purchase Order" for confirmed -->
<!-- orders and preserve "Quotation" for others. -->
<!-- ================================================================== -->
<template id="fp_sale_order_portal_content_rename"
inherit_id="sale.sale_order_portal_content"
priority="30">
<xpath expr="//div[@id='intro_row']/h2" position="replace">
<h2>
<t t-if="sale_order.state in ('sale', 'cancel', 'done')">Purchase Order</t>
<t t-else="" t-out="sale_order.type_name"/>
-
<em t-out="sale_order.name"/>
</h2>
</xpath>
</template>
</odoo>