Files
Odoo-Modules/fusion_repairs/__manifest__.py
gsinghpal ef0c096e48 fix(fusion_repairs): Bundle 3 code-review fixes (H1-H5 + M1-M6 + L1)
HIGH
H1 X2 reminder flag was per-repair - multi-visit repairs missed reminders
  Moved x_fc_day_before_reminder_sent off repair.order onto
  fusion.technician.task so each scheduled visit is tracked separately.
  Cron now walks tasks directly with state-narrowed repair filter
  (confirmed/under_repair only, drops L1's draft inclusion).

H2 X4 NPS cron used write_date - moved on every chatter/invoice write
  Added x_fc_done_at Datetime on repair.order, stamped on the first
  transition to state=done via write() override. Cron filters on
  ('x_fc_done_at', '<=', cutoff) instead of write_date.

H3 X2 template's [:1] slice picked an arbitrary task, not tomorrow's
  Cron now passes the specific task via with_context(reminder_task_id=...).
  Template fetches that task by id; falls back to [:1] only for manual
  sends so chatter Send Email composer still works.

H4 NPS Google-Search fallback URL not URL-encoded - breaks on &/spaces
  Template now uses url_encode({'q': company_name}) so "Westin & Sons"
  produces a working URL instead of truncating at the ampersand.

H5 + L1 Loaner cron fired on drafts and used create_date instead of schedule_date
  Domain rewritten to: state in ('confirmed','under_repair'), exclude
  quote-only repairs, and EITHER schedule_date <= cutoff OR (schedule_date
  is False AND create_date <= cutoff). Added limit=200 ordered by
  create_date desc (M6).

MEDIUM
M1 Function-level datetime imports moved to module top
  date, datetime, timedelta imported once at the top of repair_order.py,
  removed from cron_send_day_before_reminders, cron_send_post_visit_nps,
  cron_offer_loaner_for_long_repairs.

M2 _notifications_enabled duplicated - promoted to single source
  repair_order._notifications_enabled now delegates to
  fusion.repair.intake.service._notifications_enabled() (with a fallback
  ICP read if the service AbstractModel isn't available).

M3 self.env.get('model') -> 'model' in self.env (Odoo standard idiom)
  Two call sites in repair_order.py converted.

M4 + M5 Bare 'except: continue' + missing logger - operational blindness
  Added import logging + _logger to repair_order.py. All three crons now
  log exceptions with _logger.exception(). Activity-type ref check now
  warns + returns early if the xml id is missing (instead of passing
  activity_type_id=False which raises). For X2 and X4 the flag is set
  regardless of send-success so we don't retry indefinitely on
  permanently-misconfigured partners.

M6 Loaner cron has limit=200 + order='create_date desc'
  Caps blast radius if 5000 stale draft repairs ever accumulate.

L1 X2 state filter tightened: was ('not in', ('done','cancel')), now
  ('in', ('confirmed','under_repair')) so drafts and quote-only don't
  email "your tech is coming tomorrow".

Verified - upgrade clean, no errors. Bumped to 19.0.1.3.1.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 00:07:41 -04:00

123 lines
4.4 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2024-2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
{
'name': 'Fusion Repairs',
'version': '19.0.1.3.1',
'category': 'Inventory/Repairs',
'summary': 'Guided medical equipment repair intake, dispatch, maintenance, and self-service portal',
'description': """
Fusion Repairs
==============
Comprehensive repairs and maintenance management for medical equipment retailers
and service providers (hospital beds, wheelchairs, stairlifts, porch lifts,
walkers, mattresses, rollators).
Phase 1 - MVP
-------------
- Three intake surfaces sharing one service layer:
* Backend wizard for CS reps on the phone
* Sales rep portal (/my/repair/new) for reps on the road
* Public client self-service portal (/repair) - voicemail ready
- Guided question templates per medical equipment category
- Phone-first partner lookup with duplicate-call detection
- Multi-equipment per call (one repair.order per unit)
- Photo / video capture during intake
- Third-party equipment support (equipment we didn't sell)
- Auto warranty detection from original sale order
- Office notification recipients + 4 follow-up activities
- repair.order extensions linked to fusion.technician.task
Phase 2-4 (roadmap)
-------------------
- AI self-check engine with strict medical safety guardrails
- Upsell engine and direct-buy parts/plans
- Repair warranty tracking (free re-do window)
- Visit report wizard with Poynt terminal payment
- Maintenance contracts with client self-booking
- Weekend safety on-call paging
- SMS notifications, compliance certificates, analytics
Copyright (C) 2024-2026 Nexa Systems Inc. All rights reserved.
""",
'author': 'Nexa Systems Inc.',
'website': 'https://www.nexasystems.ca',
'maintainer': 'Nexa Systems Inc.',
'support': 'support@nexasystems.ca',
'license': 'OPL-1',
'price': 0.00,
'currency': 'CAD',
'depends': [
'base',
'mail',
'portal',
'website',
'sale_management',
'stock',
'repair',
'maintenance',
'fusion_tasks',
'fusion_poynt',
'fusion_authorizer_portal',
],
'data': [
# Security
'security/security.xml',
'security/ir.model.access.csv',
# Data (must load before views that reference records)
'data/ir_sequence_data.xml',
'data/ir_config_parameter_data.xml',
'data/ir_cron_data.xml',
'data/mail_activity_type_data.xml',
'data/mail_template_data.xml',
'data/repair_product_category_data.xml',
'data/intake_template_data.xml',
'data/self_check_data.xml',
# Views
'views/repair_product_category_views.xml',
'views/intake_template_views.xml',
'views/service_catalog_views.xml',
'views/repair_warranty_views.xml',
'views/maintenance_contract_views.xml',
'views/repair_dashboard_views.xml',
'views/repair_order_views.xml',
'views/sale_order_views.xml',
'views/technician_task_views.xml',
'views/res_partner_views.xml',
'views/res_users_views.xml',
'views/res_config_settings_views.xml',
# Portal templates
'views/portal_sales_rep_templates.xml',
'views/portal_client_repair_templates.xml',
'views/portal_maintenance_templates.xml',
# Wizards
'wizard/repair_intake_wizard_views.xml',
'wizard/repair_visit_report_wizard_views.xml',
'wizard/qr_sticker_wizard_views.xml',
# Reports
'report/qr_sticker_report.xml',
# Menus (last, after all referenced actions exist)
'views/menus.xml',
],
'assets': {
'web.assets_backend': [
# Tokens MUST load first - dashboard.scss references its variables.
'fusion_repairs/static/src/scss/_fr_tokens.scss',
'fusion_repairs/static/src/scss/dashboard.scss',
'fusion_repairs/static/src/components/dashboard/dashboard.js',
'fusion_repairs/static/src/components/dashboard/dashboard.xml',
],
'web.assets_frontend': [
'fusion_repairs/static/src/scss/portal_repair_mobile.scss',
'fusion_repairs/static/src/scss/portal_client_repair.scss',
'fusion_repairs/static/src/js/portal_repair_intake.js',
],
},
'images': ['static/description/icon.png'],
'installable': True,
'application': True,
'auto_install': False,
}