Files
Odoo-Modules/fusion_claims/wizard/mod_resubmit_wizard.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

88 lines
3.5 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2024-2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
"""
MOD Resubmit Wizard — lets the office revise an order that MOD has denied,
and kick it back into the workflow at `processing_drawings` so the specialist
can update drawings/proposal/quotation before it is re-submitted to MOD.
"""
from odoo import models, fields, api, _
from odoo.exceptions import UserError
from markupsafe import Markup
class ModResubmitWizard(models.TransientModel):
_name = 'fusion_claims.mod.resubmit.wizard'
_description = 'MOD - Resubmit After Denial'
sale_order_id = fields.Many2one('sale.order', required=True, readonly=True)
revision_notes = fields.Text(
string='Revision Notes',
required=True,
help='Describe what is being revised for the resubmission '
'(scope changes, updated pricing, additional documentation, etc).',
)
clear_old_documents = fields.Boolean(
string='Clear old drawings / proposal / quotation',
default=False,
help='Tick this if the new submission needs entirely new drawings and '
'proposal. The previous documents are preserved in chatter before '
'being cleared.',
)
@api.model
def default_get(self, fields_list):
res = super().default_get(fields_list)
if self.env.context.get('active_id'):
order = self.env['sale.order'].browse(self.env.context['active_id'])
res['sale_order_id'] = order.id
return res
def action_confirm(self):
self.ensure_one()
order = self.sale_order_id
if order.x_fc_mod_status != 'funding_denied':
raise UserError(_("Only denied orders can be resubmitted via this wizard."))
if self.clear_old_documents:
# Preserve the old docs in chatter before clearing them.
preserved = []
if order.x_fc_mod_drawing:
preserved.append('Drawing')
if order.x_fc_mod_proposal_doc:
preserved.append('Proposal')
if preserved:
order.message_post(
body=Markup(
f'<div class="alert alert-warning" role="alert">'
f'<strong>Previous documents cleared for resubmission:</strong> '
f'{", ".join(preserved)}<br/>'
f'See prior chatter for the originals.'
f'</div>'
),
message_type='notification',
subtype_xmlid='mail.mt_note',
)
order.with_context(skip_status_emails=True).write({
'x_fc_mod_drawing': False,
'x_fc_mod_drawing_filename': False,
'x_fc_mod_proposal_doc': False,
'x_fc_mod_proposal_doc_filename': False,
})
order.with_context(skip_status_emails=True).write({
'x_fc_mod_status': 'processing_drawings',
})
body = (
f'<div class="alert alert-info" role="alert">'
f'<strong><i class="fa fa-undo"></i> Resubmitting to March of Dimes after denial</strong>'
f'<p class="mb-0 mt-2"><strong>Revision notes:</strong> {self.revision_notes}</p>'
f'</div>'
)
order.message_post(
body=Markup(body),
message_type='notification',
subtype_xmlid='mail.mt_note',
)
return {'type': 'ir.actions.act_window_close'}