Files
Odoo-Modules/fusion_claims/models/res_company.py
gsinghpal 0fe8a71c05 fusion_claims: MOD workflow rework — two-assessment split, 3 submission paths, recovery actions (v19.0.8.0.3)
Reworks the March of Dimes workflow to match reality: the OT does their own
disability assessment and provides the VOD letter; our accessibility specialist
then visits to produce the proposal/drawings/quote; and the application can be
submitted by us (internal), the client, or the authorizer themselves. The old
workflow flattened all this into one assessment state with a dead-end
funding_denied and no document tracking.

Data model (13 new sale.order fields):
- 5 new document binaries + filenames: VOD letter, Application Form (filled),
  Notice of Assessment, Property Tax, Proposal Document
- x_fc_mod_submitted_by Selection (internal/client/authorizer)
- x_fc_mod_handoff_date, x_fc_mod_vod_requested_date
- x_fc_mod_accessibility_specialist_id (m2o res.partner — internal or external)
- x_fc_mod_previous_status_before_hold (for proper resume)
- x_fc_mod_funding_denial_reason (captured via wizard)

Settings (4 res.company fields + res_config_settings mirrors):
- x_fc_mod_application_form (blank) + filename
- x_fc_mod_vod_form (blank) + filename
- x_fc_mod_followup_assignee_mode (office_contact / sales_rep)
- x_fc_mod_followup_office_contact_id

res.partner: added 'accessibility_specialist' to x_fc_contact_type.

State machine:
- New state handoff_to_client between quote_submitted and awaiting_funding,
  used for paths B/C (client or authorizer submits themselves)
- Fixed action_mod_on_hold to save x_fc_mod_previous_status_before_hold
- Fixed action_mod_resume to restore previous status (was hardcoded to
  in_production, losing context for cases held earlier)

4 new wizards:
- mod_submission_path_wizard — chooses submitted_by, auto-fires VOD request
  email on first switch to 'internal'
- mod_funding_denied_wizard — captures denial category + reason
- mod_resubmit_wizard — revises + resubmits denied cases (with optional
  doc clearing)
- mod_submission_confirmed_wizard — records client/authorizer confirmed
  submission, advances to awaiting_funding

8 new action methods:
- action_mod_set_submission_path, action_mod_request_vod,
  action_mod_handoff_to_client (validates docs, fires handoff email),
  action_mod_confirmed_submission, action_mod_resubmit_from_denied,
  action_mod_cancel_from_denied, action_mod_reopen_cancelled
- action_mod_funding_denied now opens the denial wizard

3 new email methods + 2 existing fixes:
- _send_mod_vod_request_email — auto-attaches blank VOD form from company
  settings, sent to authorizer when we are handling submission
- _send_mod_handoff_email — two templates (client vs authorizer), attaches
  proposal + drawing + blank MOD Application Form
- _mod_company_attachment helper for building attachments from company Binary
- Fixed _send_mod_assessment_completed_email to include authorizer
- Fixed _send_mod_pod_submitted_email to include client

New cron:
- _cron_mod_handoff_followup (daily 09:00) — creates mail.activity for office
  to confirm MOD submission. Assignee via company setting (office contact or
  sales rep). Uses existing rolling-window cap (2/month per order).

Views:
- sale_order form: new status-bar buttons (set path, request VOD, handoff,
  confirm, resubmit, cancel, reopen), new document section in MOD Documents
  tab with submission-path tracking, denial details, hold history
- res_config_settings: new MOD blank forms upload + assignee config

Deployed to odoo-westin (westin-v19) and odoo-mobility (mobility). Pre-deploy
FK cleanup from earlier session means mobility updated cleanly without
workaround. HTTP 200 on both, cron verified active, all new fields present.
2026-04-09 07:34:17 -04:00

125 lines
5.0 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2024-2025 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Claim Assistant product family.
from odoo import models, fields
class ResCompany(models.Model):
_inherit = 'res.company'
# Store/Location Information
x_fc_store_address_1 = fields.Char(
string='Store Address Line 1',
help='First store/location address for reports (e.g., "Main Store - 123 Street, City, Province, Postal")',
)
x_fc_store_address_2 = fields.Char(
string='Store Address Line 2',
help='Second store/location address for reports (optional)',
)
x_fc_company_tagline = fields.Char(
string='Company Tagline',
help='Company tagline/slogan for reports (e.g., "Enhancing Accessibility, Improving Lives.")',
)
# Payment Information
x_fc_etransfer_email = fields.Char(
string='E-Transfer Email',
help='Email address for Interac e-Transfers',
)
x_fc_cheque_payable_to = fields.Char(
string='Cheque Payable To',
help='Name for cheque payments (defaults to company name if empty)',
)
x_fc_payment_terms_html = fields.Html(
string='Payment Terms',
help='Payment terms and conditions displayed on reports (supports HTML formatting)',
sanitize=True,
sanitize_overridable=True,
)
# Refund Policy
x_fc_include_refund_page = fields.Boolean(
string='Include Refund Policy Page',
default=True,
help='Include a separate refund policy page at the end of reports',
)
x_fc_refund_policy_html = fields.Html(
string='Refund Policy',
help='Full refund policy displayed on a separate page (supports HTML formatting)',
sanitize=True,
sanitize_overridable=True,
)
# Office Notification Recipients
x_fc_office_notification_ids = fields.Many2many(
'res.partner',
'fc_company_office_notification_partners_rel',
'company_id',
'partner_id',
string='Office Notification Recipients',
help='Contacts who will receive a copy (CC) of all automated ADP notifications',
)
# ==========================================================================
# MARCH OF DIMES SETTINGS (shared blank forms + follow-up assignment)
# Added 2026-04 MOD update. Latest revision of the MOD-issued blank forms
# lives here so every case can auto-attach them to outgoing emails.
# ==========================================================================
x_fc_mod_application_form = fields.Binary(
string='MOD Application Form (blank)',
attachment=True,
help='Blank March of Dimes Application Form (latest MOD revision). '
'Auto-attached to the handoff email sent to the client or authorizer '
'when they will be submitting the application themselves.',
)
x_fc_mod_application_form_filename = fields.Char(
string='MOD Application Form Filename',
)
x_fc_mod_vod_form = fields.Binary(
string='Verification of Disability Form (blank)',
attachment=True,
help='Blank March of Dimes Verification of Disability form. '
'Auto-attached to the VOD request email sent to the authorizer when '
'our office is handling the MOD submission internally.',
)
x_fc_mod_vod_form_filename = fields.Char(
string='VOD Form Filename',
)
x_fc_mod_followup_assignee_mode = fields.Selection(
selection=[
('office_contact', 'Designated Office Contact'),
('sales_rep', 'Order Sales Rep'),
],
string='MOD Follow-up Assignee',
default='office_contact',
help='Who gets the follow-up activity when a client or authorizer is '
'handling the MOD submission themselves. Office contact = one '
'designated person handles all follow-ups. Sales rep = the rep '
'on each order is responsible for their own follow-ups.',
)
x_fc_mod_followup_office_contact_id = fields.Many2one(
'res.users',
string='MOD Follow-up Office Contact',
help='The office user who receives MOD handoff follow-up activities '
'when assignee mode is set to Office Contact. Only used when '
'x_fc_mod_followup_assignee_mode == office_contact.',
)
def _get_cheque_payable_name(self):
"""Get the name for cheque payments, defaulting to company name."""
self.ensure_one()
return self.x_fc_cheque_payable_to or self.name
def _get_mod_followup_assignee(self, sale_order):
"""Return the res.users who should receive the MOD handoff follow-up
activity for the given order, based on this company's configuration.
Falls back to the order's sales rep when the office contact is missing.
"""
self.ensure_one()
if self.x_fc_mod_followup_assignee_mode == 'office_contact' and self.x_fc_mod_followup_office_contact_id:
return self.x_fc_mod_followup_office_contact_id
return sale_order.user_id or self.env.user