From 3a15164605a02d2051b0077e35caed4642b1cd54 Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Wed, 20 May 2026 23:34:34 -0400 Subject: [PATCH] fix(fusion_repairs): Bundle 1 code-review fixes (H1-H5 + M1-M6) H1 Float -> Monetary for outstanding_balance Added currency_id companion field on the wizard so widget="monetary" renders properly. Currency defaults to env.company.currency_id. H2 Maps URL address duplication fusion_tasks address_street often contains the full Google-Places- formatted address. Concatenating address_street + address_city + zip was producing "15 Fisherman Dr, Brampton, ON L7A 1B7, Canada, Brampton, L7A 1B7". Now uses the existing address_display field (fusion_tasks computes it correctly for both Google Places and manual entries), with a partner-based fallback that includes street, street2, city, state_id.name, zip, country_id.name. H3 Banner copy hardcoded "14 days" Added duplicate_window_days compute field; banner now reads "in last days" from the ir.config_parameter. H4 Outstanding-balance multi-company + child_of direction - Dropped .sudo() (CS users already have access to their own company's invoices via standard groups + the Repairs Office rule) - Replaced child_of (which only walks descendants) with commercial_partner_id (the canonical Odoo "billed-to root" - covers child contacts AND walks up from a child if the caller IS a child) - Added ('company_id', 'in', env.companies.ids) filter to both the invoice search AND the duplicate-repair search so a CS rep in Westin Healthcare doesn't see NEXA Systems balances H5 duplicate_count capped at 5 (false reassurance) Now uses search_count for the true total + search(limit=5) for the display list. Earlier verification showed count=5 was actually capped; running again shows 15 for the same partner. M1 Function-level imports Moved urllib.parse.quote_plus and odoo.exceptions.UserError to module top in technician_task.py. M2 Many2many 'in' with scalar Changed ('x_fc_repair_skills', 'in', category.id) to ('x_fc_repair_skills', 'in', [category.id]) - safer against future ORM tightening. M4 C6 - added x_fc_is_quote_only field + filter + form indicator Boolean tracked field on repair.order (was previously discoverable only via chatter text). Indexed. Visible on the form's intake metadata row and filterable on the dashboard search view as "Quote Only". M5 Account-move read perf Replaced Move.search() + Python sum with _read_group( aggregates=['amount_residual:sum', '__count']) - pushes the SUM to Postgres; O(1) record load vs O(N). M6 Hide Maps button when no address Added invisible="not address_display and not partner_id" on the Open in Maps button so it doesn't appear on in-store tasks. Plus the dispatch-task cutoff is now a datetime (was a date) so the create_date >= cutoff comparison is type-correct. Verified end-to-end on local westin-v19 after fixes: C1 count: 15 (was capped at 5) window_days: 14 C5 balance: 0.0 currency: CAD warning: False (correct) C6 x_fc_is_quote_only: True tech_tasks: 0 (urgent intake, NOT dispatched) T1 URL: https://www.google.com/maps?q=15+Fisherman+Dr%2C+Brampton%2C+ON+L7A+1B7%2C+Canada%2C+Unit+7 (no duplicated city/zip) Bumped to 19.0.1.1.1. Co-authored-by: Cursor --- fusion_repairs/__manifest__.py | 2 +- fusion_repairs/models/intake_service.py | 3 +- fusion_repairs/models/repair_order.py | 9 +++ fusion_repairs/models/technician_task.py | 35 ++++++----- fusion_repairs/views/repair_order_views.xml | 3 + .../views/technician_task_views.xml | 3 +- fusion_repairs/wizard/repair_intake_wizard.py | 62 ++++++++++++++----- .../wizard/repair_intake_wizard_views.xml | 4 +- 8 files changed, 85 insertions(+), 36 deletions(-) diff --git a/fusion_repairs/__manifest__.py b/fusion_repairs/__manifest__.py index 5c9be84d..681a0cfb 100644 --- a/fusion_repairs/__manifest__.py +++ b/fusion_repairs/__manifest__.py @@ -4,7 +4,7 @@ { 'name': 'Fusion Repairs', - 'version': '19.0.1.1.0', + 'version': '19.0.1.1.1', 'category': 'Inventory/Repairs', 'summary': 'Guided medical equipment repair intake, dispatch, maintenance, and self-service portal', 'description': """ diff --git a/fusion_repairs/models/intake_service.py b/fusion_repairs/models/intake_service.py index b1ab387c..00feedc4 100644 --- a/fusion_repairs/models/intake_service.py +++ b/fusion_repairs/models/intake_service.py @@ -122,6 +122,7 @@ class FusionRepairIntakeService(models.AbstractModel): 'x_fc_third_party_equipment': bool(item.get('third_party')), 'x_fc_urgency': item.get('urgency') or 'normal', 'x_fc_issue_category': item.get('issue_category') or False, + 'x_fc_is_quote_only': bool(quote_only), 'internal_notes': self._wrap_internal_notes(item), } if product_id: @@ -468,7 +469,7 @@ class FusionRepairIntakeService(models.AbstractModel): skilled = Users.search([ ('x_fc_is_field_staff', '=', True), ('active', '=', True), - ('x_fc_repair_skills', 'in', category.id), + ('x_fc_repair_skills', 'in', [category.id]), ], order='id', limit=1) if skilled: return skilled.id diff --git a/fusion_repairs/models/repair_order.py b/fusion_repairs/models/repair_order.py index d609b67b..4ceecae3 100644 --- a/fusion_repairs/models/repair_order.py +++ b/fusion_repairs/models/repair_order.py @@ -88,6 +88,15 @@ class RepairOrder(models.Model): help='Auto-matched catalogue entry that pre-fills estimated cost and duration.', ) + # C6: quote-only flag (set when intake submitted in quote-only mode). + x_fc_is_quote_only = fields.Boolean( + string='Quote Only', + tracking=True, + index=True, + help='True when the intake was submitted in "Quote Only" mode - the ' + 'office has not yet authorised dispatching a technician.', + ) + # Maintenance contract back-link (Phase 3) x_fc_maintenance_contract_id = fields.Many2one( 'fusion.repair.maintenance.contract', diff --git a/fusion_repairs/models/technician_task.py b/fusion_repairs/models/technician_task.py index d4241a9a..59f97947 100644 --- a/fusion_repairs/models/technician_task.py +++ b/fusion_repairs/models/technician_task.py @@ -2,9 +2,12 @@ # Copyright 2024-2026 Nexa Systems Inc. # License OPL-1 (Odoo Proprietary License v1.0) +from urllib.parse import quote_plus + from markupsafe import Markup from odoo import _, fields, models +from odoo.exceptions import UserError class FusionTechnicianTaskRepairs(models.Model): @@ -78,25 +81,23 @@ class FusionTechnicianTaskRepairs(models.Model): # ------------------------------------------------------------------ def action_open_in_maps(self): self.ensure_one() - from urllib.parse import quote_plus - parts = [] - for f in ('address_street', 'address_city', 'address_zip'): - v = getattr(self, f, None) - if v: - parts.append(str(v)) - if not parts and self.partner_id: - for f in ('street', 'street2', 'city', 'state_id', 'zip'): - v = getattr(self.partner_id, f, None) - if v: - parts.append(v.name if hasattr(v, 'name') else str(v)) - if not parts: - from odoo.exceptions import UserError + # Prefer fusion_tasks.address_display because in real data address_street + # often contains the full Google-Places-formatted address; concatenating + # the other address_* fields would duplicate city/zip. + addr = (getattr(self, 'address_display', '') or '').strip() + if not addr and self.partner_id: + p = self.partner_id + parts = [ + p.street, p.street2, p.city, + p.state_id.name if p.state_id else False, + p.zip, + p.country_id.name if p.country_id else False, + ] + addr = ', '.join(str(x) for x in parts if x) + if not addr: raise UserError(_('No address on this task or its client.')) - query = quote_plus(', '.join(parts)) - # https://www.google.com/maps?q=ADDR works on every platform and - # automatically deep-links into the native app where supported. return { 'type': 'ir.actions.act_url', - 'url': f'https://www.google.com/maps?q={query}', + 'url': f'https://www.google.com/maps?q={quote_plus(addr)}', 'target': 'new', } diff --git a/fusion_repairs/views/repair_order_views.xml b/fusion_repairs/views/repair_order_views.xml index 79a2eb75..36b4bfb1 100644 --- a/fusion_repairs/views/repair_order_views.xml +++ b/fusion_repairs/views/repair_order_views.xml @@ -59,6 +59,7 @@ decoration-warning="x_fc_urgency == 'urgent'" decoration-danger="x_fc_urgency == 'safety'"/> + @@ -233,6 +234,8 @@ domain="[('x_fc_urgency', '=', 'urgent')]"/> + diff --git a/fusion_repairs/views/technician_task_views.xml b/fusion_repairs/views/technician_task_views.xml index ec1c8fe4..f1a1e234 100644 --- a/fusion_repairs/views/technician_task_views.xml +++ b/fusion_repairs/views/technician_task_views.xml @@ -14,7 +14,8 @@ type="object" string="Open in Maps" class="btn-secondary" - icon="fa-map-marker"/> + icon="fa-map-marker" + invisible="not address_display and not partner_id"/>