feat(fusion_portal): wire ADP/express into visit + portal entry tile + email consolidation

- express save captures visit_id; when visit-linked, action=submit saves the
  ADP assessment as a draft (signature + Page 11 PDF still captured) and returns
  to the visit instead of completing into a standalone SO, so the visit groups
  the ADP devices into one funding-routed order. Non-visit express flow unchanged.
- portal dashboard: featured 'Start a Visit' tile (sales reps) -> /my/visit/new.
- fix duplicate-authorizer email: _send_completion_notifications no longer
  re-emails the authorizer (already emailed with the full report by
  _send_assessment_completed_email); it now only notifies the client.
- visit grouped accessibility SOs now send one office completion email per SO.

Bump 19.0.2.10.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-06-02 08:39:37 -04:00
parent 20de9a6b69
commit ed91135a3f
6 changed files with 90 additions and 17 deletions

View File

@@ -458,6 +458,7 @@ class AssessmentPortal(CustomerPortal):
'current_page': 1,
'total_pages': 2,
'assessment': None,
'visit_id': kw.get('visit_id', ''),
'google_maps_api_key': google_maps_api_key,
}
@@ -516,6 +517,7 @@ class AssessmentPortal(CustomerPortal):
'partner': partner,
'user': user,
'assessment': assessment,
'visit_id': kw.get('visit_id') or (assessment.visit_id.id if assessment.visit_id else ''),
'authorizers': authorizers,
'authorizers_json': authorizers_json,
'clients': clients,
@@ -630,6 +632,30 @@ class AssessmentPortal(CustomerPortal):
except Exception as e:
_logger.error(f"Error saving Page 11 signature: {e}")
# ===== Visit-linked: defer SO creation to visit completion =====
# Started from a visit workspace: do NOT complete into a standalone
# sale order. Leave it as a draft linked to the visit so
# visit.action_complete_visit() groups the visit's ADP devices
# (combination-checked) into ONE ADP order. The Page 11 signature is
# already saved above; pre-generate its PDF so it is ready.
if assessment.visit_id and action == 'submit':
if assessment.signature_page_11 and assessment.consent_declaration_accepted:
try:
pdf_bytes = assessment.generate_template_pdf('Page 11')
if pdf_bytes:
import base64 as b64
assessment.write({
'signed_page_11_pdf': b64.b64encode(pdf_bytes),
'signed_page_11_pdf_filename': f'ADP_Page11_{assessment.reference}.pdf',
})
except Exception as pdf_e:
_logger.warning(f"Visit-linked Page 11 PDF generation failed (non-blocking): {pdf_e}")
_logger.info(
f"Express assessment {assessment.reference} saved to visit "
f"{assessment.visit_id.name} (completion deferred to visit)"
)
return request.redirect(f'/my/visit/{assessment.visit_id.id}')
# Handle navigation
if action == 'submit':
# If already completed, we just saved consent/signature above -- redirect with success
@@ -803,6 +829,13 @@ class AssessmentPortal(CustomerPortal):
def _build_express_assessment_vals(self, kw):
"""Build values dict from express form POST data"""
vals = {}
# Visit linkage (assessment started from a visit workspace)
if kw.get('visit_id'):
try:
vals['visit_id'] = int(kw.get('visit_id'))
except (ValueError, TypeError):
pass
# Equipment type
if kw.get('equipment_type'):