feat(fusion_portal): Phase 3 - assessment visit workspace (accessibility path)
Adds the portal workspace: /my/visit/new starts a visit; /my/visit/<id> shows the add-as-you-go workspace (add buttons -> existing forms carrying ?visit_id, a deferred client+funding form, and a Complete button). Accessibility forms launched from a visit save as a DRAFT linked to it (JS carries visit_id into the form; the controller captures it and skips the per-assessment SO) - the VISIT completion then creates the grouped per-funding sale orders. NOT YET: express/ADP form visit-linking, email consolidation, polished tablet UI. Untested locally (Enterprise dep) - clone verification pending. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -388,6 +388,7 @@
|
||||
<small class="text-muted">Determines which sale order / funding workflow this case enters.</small>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="visit_id" id="acc_visit_id"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -647,6 +648,15 @@
|
||||
// Fallback if Google Maps not loaded
|
||||
window.initAddressAutocomplete = window.initAddressAutocomplete || function() {};
|
||||
|
||||
// Carry visit_id from the workspace launch (?visit_id=) into the form
|
||||
(function() {
|
||||
var _vid = new URLSearchParams(window.location.search).get('visit_id');
|
||||
if (_vid) {
|
||||
var f = document.getElementById('acc_visit_id');
|
||||
if (f) { f.value = _vid; }
|
||||
}
|
||||
})();
|
||||
|
||||
// Form submission
|
||||
function saveAssessment(createSaleOrder) {
|
||||
var form = document.getElementById('accessibility_form');
|
||||
|
||||
115
fusion_portal/views/portal_visit.xml
Normal file
115
fusion_portal/views/portal_visit.xml
Normal file
@@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<template id="portal_visit_workspace" name="Assessment Visit Workspace">
|
||||
<t t-call="portal.portal_layout">
|
||||
<t t-set="no_breadcrumbs" t-value="True"/>
|
||||
<div class="container py-4">
|
||||
<nav aria-label="breadcrumb" class="mb-3">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/my">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">Assessment Visit</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<t t-if="error">
|
||||
<div class="alert alert-danger"><i class="fa fa-exclamation-circle"/> <t t-esc="error"/></div>
|
||||
</t>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h3 class="mb-0"><i class="fa fa-clipboard text-primary"/> Visit <t t-esc="visit.name"/></h3>
|
||||
<span class="badge bg-secondary"><span t-field="visit.state"/></span>
|
||||
</div>
|
||||
|
||||
<t t-if="visit.state == 'done'">
|
||||
<div class="alert alert-success">
|
||||
<strong>Visit completed.</strong> Sale orders created:
|
||||
<ul class="mb-0">
|
||||
<t t-foreach="visit.sale_order_ids" t-as="so">
|
||||
<li><a t-attf-href="/my/sales/case/{{so.id}}"><t t-esc="so.name"/></a> — <span t-field="so.x_fc_sale_type"/></li>
|
||||
</t>
|
||||
</ul>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Assessments added this visit -->
|
||||
<div class="card border-0 shadow-sm mb-4">
|
||||
<div class="card-header bg-white"><strong>Assessments this visit</strong> (<t t-esc="visit.assessment_count"/>)</div>
|
||||
<div class="card-body">
|
||||
<p t-if="not visit.assessment_count" class="text-muted mb-0">
|
||||
Nothing added yet — use the buttons below to add what you're assessing.
|
||||
</p>
|
||||
<ul class="list-group" t-if="visit.assessment_count">
|
||||
<t t-foreach="visit.adp_assessment_ids" t-as="a">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<span><i class="fa fa-wheelchair text-primary"/> ADP — <span t-field="a.equipment_type"/></span>
|
||||
<span class="badge bg-light text-dark"><span t-field="a.state"/></span>
|
||||
</li>
|
||||
</t>
|
||||
<t t-foreach="visit.accessibility_assessment_ids" t-as="a">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<span><span t-field="a.assessment_type"/> — <span t-field="a.x_fc_funding_source"/></span>
|
||||
<span class="badge bg-light text-dark"><span t-field="a.state"/></span>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<t t-if="visit.state != 'done'">
|
||||
<!-- Add assessment -->
|
||||
<div class="card border-0 shadow-sm mb-4">
|
||||
<div class="card-header bg-white"><strong>+ Add assessment</strong></div>
|
||||
<div class="card-body d-flex flex-wrap gap-2">
|
||||
<a class="btn btn-outline-primary" t-attf-href="/my/assessment/express?visit_id={{visit.id}}">Wheelchair / ADP</a>
|
||||
<a class="btn btn-outline-primary" t-attf-href="/my/accessibility/stairlift/straight?visit_id={{visit.id}}">Straight Stair Lift</a>
|
||||
<a class="btn btn-outline-primary" t-attf-href="/my/accessibility/stairlift/curved?visit_id={{visit.id}}">Curved Stair Lift</a>
|
||||
<a class="btn btn-outline-primary" t-attf-href="/my/accessibility/vpl?visit_id={{visit.id}}">Platform / Porch Lift</a>
|
||||
<a class="btn btn-outline-primary" t-attf-href="/my/accessibility/ceiling-lift?visit_id={{visit.id}}">Ceiling Lift</a>
|
||||
<a class="btn btn-outline-primary" t-attf-href="/my/accessibility/ramp?visit_id={{visit.id}}">Custom Ramp</a>
|
||||
<a class="btn btn-outline-primary" t-attf-href="/my/accessibility/bathroom?visit_id={{visit.id}}">Bathroom Mod</a>
|
||||
<a class="btn btn-outline-primary" t-attf-href="/my/accessibility/tub-cutout?visit_id={{visit.id}}">Tub Cutout</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Client details (deferred) -->
|
||||
<div class="card border-0 shadow-sm mb-4">
|
||||
<div class="card-header bg-white"><strong>Client details</strong>
|
||||
<span class="text-muted small">— fill in after the therapist leaves</span></div>
|
||||
<div class="card-body">
|
||||
<form t-attf-action="/my/visit/{{visit.id}}/save" method="post">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3"><label class="form-label">Client Name</label>
|
||||
<input type="text" name="client_name" class="form-control" t-att-value="visit.client_name"/></div>
|
||||
<div class="col-md-6 mb-3"><label class="form-label">Phone</label>
|
||||
<input type="text" name="client_phone" class="form-control" t-att-value="visit.client_phone"/></div>
|
||||
<div class="col-md-6 mb-3"><label class="form-label">Email</label>
|
||||
<input type="email" name="client_email" class="form-control" t-att-value="visit.client_email"/></div>
|
||||
<div class="col-md-6 mb-3"><label class="form-label">Address</label>
|
||||
<input type="text" name="client_address" class="form-control" t-att-value="visit.client_address"/></div>
|
||||
</div>
|
||||
<div class="mb-3" t-if="visit.has_mod_items">
|
||||
<label class="form-label">Income under March of Dimes threshold?
|
||||
<span class="text-muted small">(MOD covers up to $15k/person, lifetime)</span></label>
|
||||
<select name="x_fc_income_under_mod_threshold" class="form-select">
|
||||
<option value="unknown" t-att-selected="visit.x_fc_income_under_mod_threshold == 'unknown'">Unknown</option>
|
||||
<option value="yes" t-att-selected="visit.x_fc_income_under_mod_threshold == 'yes'">Yes — under threshold (full $15k)</option>
|
||||
<option value="no" t-att-selected="visit.x_fc_income_under_mod_threshold == 'no'">No — over threshold (may be denied/partial)</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-secondary">Save client details</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Complete -->
|
||||
<form t-attf-action="/my/visit/{{visit.id}}/complete" method="post" t-if="visit.assessment_count">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
<button type="submit" class="btn btn-primary btn-lg">Complete visit & create sale orders →</button>
|
||||
<p class="text-muted small mt-2">Creates one sale order per funding workflow (ADP / March of Dimes / private / ...).</p>
|
||||
</form>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user