From 26fe41e7d4e1fe06fbbd4fdea123c9116dbfcab6 Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Sun, 17 May 2026 12:39:26 -0400 Subject: [PATCH] fix(portal): sudo portal job queries so template traversal works for customers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Portal users have read access to fp.portal.job but NOT to fp.job. The new job-card macro traverses job.x_fc_job_id -> fp.job to surface part info, sale_order, ship-to address — that raised AccessError for real customers (admins were fine due to inherited groups). Adding .sudo() to the three Job queries in home(), portal_my_jobs(), and the certifications panel mirror lookup. Domain still filters to the customer's commercial partner tree, so sudo doesn't widen visibility — it just lets the template walk past the portal-job boundary to the privileged backend models. Same pattern is already used in the same file for sale.order, account.move, and stock.picking queries. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../controllers/portal.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/fusion_plating/fusion_plating_portal/controllers/portal.py b/fusion_plating/fusion_plating_portal/controllers/portal.py index 5e257c25..7048e855 100644 --- a/fusion_plating/fusion_plating_portal/controllers/portal.py +++ b/fusion_plating/fusion_plating_portal/controllers/portal.py @@ -392,7 +392,11 @@ class FpCustomerPortal(CustomerPortal): po_count = SO.search_count(po_domain) # Recent jobs (5) - Job = request.env['fusion.plating.portal.job'] + # sudo() so the rendered cards can traverse job.x_fc_job_id -> fp.job + # -> sale_order_id without hitting the portal user's ACL block on + # fp.job. Domain still filters to the customer's own commercial + # partner tree, so sudo doesn't widen visibility. + Job = request.env['fusion.plating.portal.job'].sudo() job_domain = [('partner_id', 'child_of', commercial.id)] recent_jobs = Job.search( job_domain, order='received_date desc, id desc', limit=5 @@ -778,7 +782,11 @@ class FpCustomerPortal(CustomerPortal): def portal_my_jobs(self, page=1, sortby=None, **kw): partner = request.env.user.partner_id commercial = partner.commercial_partner_id - Job = request.env['fusion.plating.portal.job'] + # sudo() so the rendered cards can traverse job.x_fc_job_id -> fp.job + # -> sale_order_id without hitting the portal user's ACL block on + # fp.job. Domain still filters to the customer's own commercial + # partner tree, so sudo doesn't widen visibility. + Job = request.env['fusion.plating.portal.job'].sudo() domain = [('partner_id', 'child_of', commercial.id)] searchbar_sortings = { @@ -1199,7 +1207,11 @@ class FpCustomerPortal(CustomerPortal): def portal_my_certifications(self, page=1, sortby=None, **kw): partner = request.env.user.partner_id commercial = partner.commercial_partner_id - Job = request.env['fusion.plating.portal.job'] + # sudo() so the rendered cards can traverse job.x_fc_job_id -> fp.job + # -> sale_order_id without hitting the portal user's ACL block on + # fp.job. Domain still filters to the customer's own commercial + # partner tree, so sudo doesn't widen visibility. + Job = request.env['fusion.plating.portal.job'].sudo() domain = [ ('partner_id', 'child_of', commercial.id), ('coc_attachment_id', '!=', False),