Bundles multiple assessments per home visit; on completion groups them by funding workflow (x_fc_sale_type) into one draft sale order per workflow (March of Dimes / ADP / ODSP / WSIB / private / hardship / insurance). Adds the mobility scooter ADP device type, the power-mobility home-access rule, ADP multi-device combination guard, and the portal visit workspace. Verified on a westin-v19 clone (clean registry load + funding-grouping smoke test) then deployed to westin prod (fusion_portal 19.0.2.9.0). Prod's pre-existing orphaned tax links were preserved (Odoo skips existing FKs), pending a later audit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
738 lines
38 KiB
XML
738 lines
38 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!--
|
|
Copyright 2024-2026 Nexa Systems Inc.
|
|
License OPL-1 (Odoo Proprietary License v1.0)
|
|
Part of the Fusion Claim Assistant product family.
|
|
|
|
Accessibility Assessment Portal Templates
|
|
-->
|
|
<odoo>
|
|
<!-- ============================================================= -->
|
|
<!-- ASSESSMENT TYPE SELECTOR PAGE -->
|
|
<!-- ============================================================= -->
|
|
<template id="portal_accessibility_selector" name="Accessibility Assessment Selector">
|
|
<t t-call="portal.portal_layout">
|
|
<t t-set="breadcrumbs_searchbar" t-value="True"/>
|
|
<t t-set="no_breadcrumbs" t-value="True"/>
|
|
|
|
<div class="container py-4">
|
|
<!-- Custom Breadcrumb -->
|
|
<nav aria-label="breadcrumb" class="mb-4">
|
|
<ol class="breadcrumb">
|
|
<li class="breadcrumb-item"><a href="/my">Dashboard</a></li>
|
|
<li class="breadcrumb-item active">Accessibility Assessment</li>
|
|
</ol>
|
|
</nav>
|
|
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<h2 class="mb-4">
|
|
<i class="fa fa-wheelchair text-primary"></i>
|
|
Accessibility Assessment
|
|
</h2>
|
|
<p class="text-muted mb-4">
|
|
Select the type of accessibility assessment you want to perform.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<!-- Stair Lifts -->
|
|
<div class="col-md-6 col-lg-4 mb-4">
|
|
<a href="/my/accessibility/stairlift/straight" class="card h-100 shadow-sm fp-acc-card text-decoration-none" style="--fp-acc-accent: #0d6efd; --fp-acc-fg: #ffffff;">
|
|
<div class="fp-acc-thumb">
|
|
<img src="/fusion_portal/static/src/img/accessibility/straight-stair-lift.jpg" alt="Straight Stair Lift" loading="lazy"/>
|
|
</div>
|
|
<div class="card-body">
|
|
<h5 class="card-title fp-acc-title">Straight Stair Lift</h5>
|
|
<p class="card-text text-muted small">
|
|
Standard stair lift for straight staircases.
|
|
Includes track length calculation.
|
|
</p>
|
|
<span class="fp-acc-btn">
|
|
<i class="fa fa-plus-circle"></i> Start Assessment
|
|
</span>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
|
|
<div class="col-md-6 col-lg-4 mb-4">
|
|
<a href="/my/accessibility/stairlift/curved" class="card h-100 shadow-sm fp-acc-card text-decoration-none" style="--fp-acc-accent: #17a2b8; --fp-acc-fg: #ffffff;">
|
|
<div class="fp-acc-thumb">
|
|
<img src="/fusion_portal/static/src/img/accessibility/curved-stair-lift.jpg" alt="Curved Stair Lift" loading="lazy"/>
|
|
</div>
|
|
<div class="card-body">
|
|
<h5 class="card-title fp-acc-title">Curved Stair Lift</h5>
|
|
<p class="card-text text-muted small">
|
|
Custom curved stair lift with parking options.
|
|
Includes curve and step calculations.
|
|
</p>
|
|
<span class="fp-acc-btn">
|
|
<i class="fa fa-plus-circle"></i> Start Assessment
|
|
</span>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
|
|
<!-- VPL -->
|
|
<div class="col-md-6 col-lg-4 mb-4">
|
|
<a href="/my/accessibility/vpl" class="card h-100 shadow-sm fp-acc-card text-decoration-none" style="--fp-acc-accent: #198754; --fp-acc-fg: #ffffff;">
|
|
<div class="fp-acc-thumb">
|
|
<img src="/fusion_portal/static/src/img/accessibility/vertical-platform-lift.jpg" alt="Vertical Platform Lift" loading="lazy"/>
|
|
</div>
|
|
<div class="card-body">
|
|
<h5 class="card-title fp-acc-title">Vertical Platform Lift</h5>
|
|
<p class="card-text text-muted small">
|
|
VPL assessment with room dimensions,
|
|
power requirements, and certification.
|
|
</p>
|
|
<span class="fp-acc-btn">
|
|
<i class="fa fa-plus-circle"></i> Start Assessment
|
|
</span>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Ceiling Lift -->
|
|
<div class="col-md-6 col-lg-4 mb-4">
|
|
<a href="/my/accessibility/ceiling-lift" class="card h-100 shadow-sm fp-acc-card text-decoration-none" style="--fp-acc-accent: #ffc107; --fp-acc-fg: #212529;">
|
|
<div class="fp-acc-thumb">
|
|
<img src="/fusion_portal/static/src/img/accessibility/ceiling-lift.jpg" alt="Ceiling Lift" loading="lazy"/>
|
|
</div>
|
|
<div class="card-body">
|
|
<h5 class="card-title fp-acc-title">Ceiling Lift</h5>
|
|
<p class="card-text text-muted small">
|
|
Ceiling lift with track length, movement type,
|
|
and additional features.
|
|
</p>
|
|
<span class="fp-acc-btn">
|
|
<i class="fa fa-plus-circle"></i> Start Assessment
|
|
</span>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Custom Ramp -->
|
|
<div class="col-md-6 col-lg-4 mb-4">
|
|
<a href="/my/accessibility/ramp" class="card h-100 shadow-sm fp-acc-card text-decoration-none" style="--fp-acc-accent: #dc3545; --fp-acc-fg: #ffffff;">
|
|
<div class="fp-acc-thumb">
|
|
<img src="/fusion_portal/static/src/img/accessibility/custom-ramp.jpg" alt="Custom Ramp" loading="lazy"/>
|
|
</div>
|
|
<div class="card-body">
|
|
<h5 class="card-title fp-acc-title">Custom Ramp</h5>
|
|
<p class="card-text text-muted small">
|
|
Ramp with Ontario Building Code compliance.
|
|
Auto-calculates length and landings.
|
|
</p>
|
|
<span class="fp-acc-btn">
|
|
<i class="fa fa-plus-circle"></i> Start Assessment
|
|
</span>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Bathroom Modifications -->
|
|
<div class="col-md-6 col-lg-4 mb-4">
|
|
<a href="/my/accessibility/bathroom" class="card h-100 shadow-sm fp-acc-card text-decoration-none" style="--fp-acc-accent: #6c757d; --fp-acc-fg: #ffffff;">
|
|
<div class="fp-acc-thumb">
|
|
<img src="/fusion_portal/static/src/img/accessibility/bathroom-modification.jpg" alt="Bathroom Modification" loading="lazy"/>
|
|
</div>
|
|
<div class="card-body">
|
|
<h5 class="card-title fp-acc-title">Bathroom Modification</h5>
|
|
<p class="card-text text-muted small">
|
|
General bathroom modifications.
|
|
Free-form description with photos.
|
|
</p>
|
|
<span class="fp-acc-btn">
|
|
<i class="fa fa-plus-circle"></i> Start Assessment
|
|
</span>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Tub Cutout -->
|
|
<div class="col-md-6 col-lg-4 mb-4">
|
|
<a href="/my/accessibility/tub-cutout" class="card h-100 shadow-sm fp-acc-card text-decoration-none" style="--fp-acc-accent: #6c5ce7; --fp-acc-fg: #ffffff;">
|
|
<div class="fp-acc-thumb">
|
|
<img src="/fusion_portal/static/src/img/accessibility/tub-cutout.jpg" alt="Tub Cutout" loading="lazy"/>
|
|
</div>
|
|
<div class="card-body">
|
|
<h5 class="card-title fp-acc-title">Tub Cutout</h5>
|
|
<p class="card-text text-muted small">
|
|
Tub cutout assessment with internal and
|
|
external height measurements.
|
|
</p>
|
|
<span class="fp-acc-btn">
|
|
<i class="fa fa-plus-circle"></i> Start Assessment
|
|
</span>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- View All Assessments Link -->
|
|
<div class="row mt-4">
|
|
<div class="col-12 text-center">
|
|
<a href="/my/accessibility/list" class="btn btn-outline-primary">
|
|
<i class="fa fa-list"></i> View All Assessments
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<!-- ============================================================= -->
|
|
<!-- ASSESSMENT LIST PAGE -->
|
|
<!-- ============================================================= -->
|
|
<template id="portal_accessibility_list" name="Accessibility Assessment List">
|
|
<t t-call="portal.portal_layout">
|
|
<t t-set="breadcrumbs_searchbar" t-value="True"/>
|
|
<t t-set="no_breadcrumbs" t-value="True"/>
|
|
|
|
<div class="container py-4">
|
|
<!-- Custom Breadcrumb -->
|
|
<nav aria-label="breadcrumb" class="mb-4">
|
|
<ol class="breadcrumb">
|
|
<li class="breadcrumb-item"><a href="/my">Dashboard</a></li>
|
|
<li class="breadcrumb-item"><a href="/my/accessibility">Accessibility</a></li>
|
|
<li class="breadcrumb-item active">All Assessments</li>
|
|
</ol>
|
|
</nav>
|
|
|
|
<div class="row mb-4">
|
|
<div class="col-md-8">
|
|
<h2>
|
|
<i class="fa fa-list text-primary"></i>
|
|
My Accessibility Assessments
|
|
</h2>
|
|
</div>
|
|
<div class="col-md-4 text-right">
|
|
<a href="/my/accessibility" class="btn btn-primary">
|
|
<i class="fa fa-plus"></i> New Assessment
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<t t-if="assessments">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<thead class="thead-light">
|
|
<tr>
|
|
<th>Reference</th>
|
|
<th>Type</th>
|
|
<th>Client</th>
|
|
<th>Date</th>
|
|
<th>Status</th>
|
|
<th>Sale Order</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<t t-foreach="assessments" t-as="assessment">
|
|
<tr class="o_portal_my_doc_table">
|
|
<td>
|
|
<span t-field="assessment.reference"/>
|
|
</td>
|
|
<td>
|
|
<t t-if="assessment.assessment_type == 'stairlift_straight'">
|
|
<span class="badge badge-primary">Straight Stair Lift</span>
|
|
</t>
|
|
<t t-elif="assessment.assessment_type == 'stairlift_curved'">
|
|
<span class="badge badge-info">Curved Stair Lift</span>
|
|
</t>
|
|
<t t-elif="assessment.assessment_type == 'vpl'">
|
|
<span class="badge badge-success">VPL</span>
|
|
</t>
|
|
<t t-elif="assessment.assessment_type == 'ceiling_lift'">
|
|
<span class="badge badge-warning">Ceiling Lift</span>
|
|
</t>
|
|
<t t-elif="assessment.assessment_type == 'ramp'">
|
|
<span class="badge badge-danger">Ramp</span>
|
|
</t>
|
|
<t t-elif="assessment.assessment_type == 'bathroom'">
|
|
<span class="badge badge-secondary">Bathroom</span>
|
|
</t>
|
|
<t t-elif="assessment.assessment_type == 'tub_cutout'">
|
|
<span class="badge" style="background: #6c5ce7; color: white;">Tub Cutout</span>
|
|
</t>
|
|
</td>
|
|
<td><span t-field="assessment.client_name"/></td>
|
|
<td><span t-field="assessment.assessment_date"/></td>
|
|
<td>
|
|
<t t-if="assessment.state == 'draft'">
|
|
<span class="badge badge-secondary">Draft</span>
|
|
</t>
|
|
<t t-elif="assessment.state == 'completed'">
|
|
<span class="badge badge-success">Completed</span>
|
|
</t>
|
|
<t t-elif="assessment.state == 'cancelled'">
|
|
<span class="badge badge-danger">Cancelled</span>
|
|
</t>
|
|
</td>
|
|
<td>
|
|
<t t-if="assessment.sale_order_id">
|
|
<a t-attf-href="/my/sales/case/#{assessment.sale_order_id.id}">
|
|
<span t-field="assessment.sale_order_id.name"/>
|
|
</a>
|
|
</t>
|
|
<t t-else="">-</t>
|
|
</td>
|
|
</tr>
|
|
</t>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Pager -->
|
|
<div class="text-center">
|
|
<t t-call="portal.pager"/>
|
|
</div>
|
|
</t>
|
|
|
|
<t t-else="">
|
|
<div class="alert alert-info text-center">
|
|
<i class="fa fa-info-circle"></i>
|
|
No accessibility assessments found.
|
|
<a href="/my/accessibility" class="alert-link">Create your first assessment</a>.
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<!-- ============================================================= -->
|
|
<!-- SHARED FORM COMPONENTS -->
|
|
<!-- ============================================================= -->
|
|
|
|
<!-- Client Information Section (shared across all forms) -->
|
|
<template id="accessibility_client_info_section" name="Accessibility Client Info Section">
|
|
<div class="card mb-4">
|
|
<div class="card-header bg-primary text-white">
|
|
<h5 class="mb-0"><i class="fa fa-user"></i> Client Information</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Client Name <span class="text-danger">*</span></label>
|
|
<input type="text" name="client_name" class="form-control" required="required" placeholder="Full name"/>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Assessment Date</label>
|
|
<input type="date" name="assessment_date" class="form-control" t-att-value="today"/>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-8 mb-3">
|
|
<label class="form-label">Street Address</label>
|
|
<input type="text" name="client_address" id="client_address" class="form-control address-autocomplete"
|
|
placeholder="Start typing address..."/>
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label">Unit/Apt/Suite</label>
|
|
<input type="text" name="client_unit" id="client_unit" class="form-control"
|
|
placeholder="e.g., Unit 5, Apt 302"/>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label">City</label>
|
|
<input type="text" name="client_address_city" id="client_address_city" class="form-control" placeholder="City"/>
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label">Province</label>
|
|
<select name="client_address_province" id="client_address_province" class="form-select">
|
|
<option value="">-- Select --</option>
|
|
<option value="ON">Ontario</option>
|
|
<option value="QC">Quebec</option>
|
|
<option value="BC">British Columbia</option>
|
|
<option value="AB">Alberta</option>
|
|
<option value="MB">Manitoba</option>
|
|
<option value="SK">Saskatchewan</option>
|
|
<option value="NS">Nova Scotia</option>
|
|
<option value="NB">New Brunswick</option>
|
|
<option value="NL">Newfoundland and Labrador</option>
|
|
<option value="PE">Prince Edward Island</option>
|
|
<option value="NT">Northwest Territories</option>
|
|
<option value="YT">Yukon</option>
|
|
<option value="NU">Nunavut</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label">Postal Code</label>
|
|
<input type="text" name="client_address_postal" id="client_address_postal" class="form-control" placeholder="A1A 1A1"/>
|
|
</div>
|
|
</div>
|
|
<input type="hidden" name="client_address_street" id="client_address_street"/>
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Phone</label>
|
|
<input type="tel" name="client_phone" class="form-control" placeholder="(xxx) xxx-xxxx"/>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Email</label>
|
|
<input type="email" name="client_email" class="form-control" placeholder="email@example.com"/>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Funding Source <span class="text-danger">*</span></label>
|
|
<select name="funding_source" class="form-select" required="required">
|
|
<option value="direct_private" selected="selected">Private Pay (Direct)</option>
|
|
<option value="march_of_dimes">March of Dimes</option>
|
|
<option value="odsp">ODSP</option>
|
|
<option value="wsib">WSIB</option>
|
|
<option value="hardship">Hardship Funding</option>
|
|
<option value="insurance">Private Insurance</option>
|
|
<option value="other">Other</option>
|
|
</select>
|
|
<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>
|
|
|
|
<!-- Photo Upload Section (shared across all forms) -->
|
|
<template id="accessibility_photo_section" name="Accessibility Photo Section">
|
|
<div class="card mb-4">
|
|
<div class="card-header" style="background: linear-gradient(135deg, #5ba848 0%, #3a8fb7 60%, #2e7aad 100%); color: white;">
|
|
<h5 class="mb-0"><i class="fa fa-camera"></i> Photos</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<label class="form-label">Attach Photos</label>
|
|
<input type="file" id="photo_upload" class="form-control" accept="image/*" multiple="multiple"/>
|
|
<small class="text-muted">You can select multiple photos. Accepted formats: JPG, PNG, GIF, WEBP</small>
|
|
</div>
|
|
</div>
|
|
<div id="photo_preview" class="row mt-3">
|
|
<!-- Photo previews will appear here -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Notes Section (shared across all forms) -->
|
|
<template id="accessibility_notes_section" name="Accessibility Notes Section">
|
|
<div class="card mb-4">
|
|
<div class="card-header bg-light">
|
|
<h5 class="mb-0"><i class="fa fa-sticky-note"></i> General Notes</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<textarea name="notes" class="form-control" rows="3" placeholder="Any additional notes..."></textarea>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Form Submit Buttons -->
|
|
<template id="accessibility_submit_buttons" name="Accessibility Submit Buttons">
|
|
<div class="row mt-4">
|
|
<div class="col-12">
|
|
<div class="d-flex justify-content-between">
|
|
<a href="/my/accessibility" class="btn btn-outline-secondary">
|
|
<i class="fa fa-arrow-left"></i> Cancel
|
|
</a>
|
|
<div>
|
|
<button type="button" class="btn btn-outline-primary mr-2" onclick="saveAssessment(false)">
|
|
<i class="fa fa-save"></i> Save Draft
|
|
</button>
|
|
<button type="button" class="btn btn-success" onclick="saveAssessment(true)">
|
|
<i class="fa fa-check-circle"></i> Complete & Create Sale Order
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Google Maps + Form JavaScript -->
|
|
<template id="accessibility_form_scripts" name="Accessibility Form Scripts">
|
|
<!-- Google Maps Places API -->
|
|
<t t-if="google_maps_api_key">
|
|
<script t-attf-src="https://maps.googleapis.com/maps/api/js?key=#{google_maps_api_key}&libraries=places&callback=initAddressAutocomplete" async="async" defer="defer"></script>
|
|
</t>
|
|
|
|
<script type="text/javascript">
|
|
// Photo handling - Main photos
|
|
var photoDataArray = [];
|
|
|
|
// Additional photo arrays for curved stair lift
|
|
var topLandingPhotos = [];
|
|
var bottomLandingPhotos = [];
|
|
var assessmentVideoData = null;
|
|
var assessmentVideoFilename = null;
|
|
|
|
document.getElementById('photo_upload').addEventListener('change', function(e) {
|
|
var files = e.target.files;
|
|
var previewContainer = document.getElementById('photo_preview');
|
|
|
|
for (var i = 0; i < files.length; i++) {
|
|
var file = files[i];
|
|
var reader = new FileReader();
|
|
|
|
reader.onload = (function(idx) {
|
|
return function(e) {
|
|
photoDataArray.push(e.target.result);
|
|
|
|
var col = document.createElement('div');
|
|
col.className = 'col-6 col-md-3 mb-3';
|
|
col.innerHTML = '<div class="position-relative">' +
|
|
'<img src="' + e.target.result + '" class="img-fluid rounded" style="max-height: 150px; object-fit: cover; width: 100%;">' +
|
|
'<button type="button" class="btn btn-danger btn-sm position-absolute" style="top: 5px; right: 5px;" onclick="removePhoto(' + (photoDataArray.length - 1) + ', this)">' +
|
|
'<i class="fa fa-times"></i>' +
|
|
'</button>' +
|
|
'</div>';
|
|
previewContainer.appendChild(col);
|
|
};
|
|
})(i);
|
|
|
|
reader.readAsDataURL(file);
|
|
}
|
|
});
|
|
|
|
function removePhoto(index, btn) {
|
|
photoDataArray[index] = null;
|
|
btn.closest('.col-6').remove();
|
|
}
|
|
|
|
// Initialize landing photo handlers (for curved stair lift forms)
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Top landing photos
|
|
var topLandingInput = document.getElementById('top_landing_photos');
|
|
if (topLandingInput) {
|
|
topLandingInput.addEventListener('change', function(e) {
|
|
handleLandingPhotos(e.target.files, 'top_landing_preview', topLandingPhotos);
|
|
});
|
|
}
|
|
|
|
// Bottom landing photos
|
|
var bottomLandingInput = document.getElementById('bottom_landing_photos');
|
|
if (bottomLandingInput) {
|
|
bottomLandingInput.addEventListener('change', function(e) {
|
|
handleLandingPhotos(e.target.files, 'bottom_landing_preview', bottomLandingPhotos);
|
|
});
|
|
}
|
|
|
|
// Video upload
|
|
var videoInput = document.getElementById('assessment_video');
|
|
if (videoInput) {
|
|
videoInput.addEventListener('change', function(e) {
|
|
handleVideoUpload(e.target.files[0]);
|
|
});
|
|
}
|
|
});
|
|
|
|
function handleLandingPhotos(files, previewId, photoArray) {
|
|
var previewContainer = document.getElementById(previewId);
|
|
if (!previewContainer) return;
|
|
|
|
// Clear previous previews
|
|
previewContainer.innerHTML = '';
|
|
photoArray.length = 0;
|
|
|
|
for (var i = 0; i < files.length; i++) {
|
|
var file = files[i];
|
|
var reader = new FileReader();
|
|
|
|
reader.onload = (function(arr) {
|
|
return function(e) {
|
|
arr.push(e.target.result);
|
|
|
|
var col = document.createElement('div');
|
|
col.className = 'col-3 mb-2';
|
|
col.innerHTML = '<img src="' + e.target.result + '" class="img-thumbnail" style="max-height: 80px; object-fit: cover;"/>';
|
|
previewContainer.appendChild(col);
|
|
};
|
|
})(photoArray);
|
|
|
|
reader.readAsDataURL(file);
|
|
}
|
|
}
|
|
|
|
function handleVideoUpload(file) {
|
|
if (!file) return;
|
|
|
|
var videoPreview = document.getElementById('video_preview');
|
|
var videoPlayer = document.getElementById('video_player');
|
|
var compressStatus = document.getElementById('video_compress_status');
|
|
|
|
// Check file size (max 100MB)
|
|
var maxSize = 100 * 1024 * 1024;
|
|
if (file.size > maxSize) {
|
|
alert('Video file is too large. Maximum size is 100MB.');
|
|
document.getElementById('assessment_video').value = '';
|
|
return;
|
|
}
|
|
|
|
// Show preview
|
|
if (videoPlayer && videoPreview) {
|
|
videoPlayer.src = URL.createObjectURL(file);
|
|
videoPreview.style.display = 'block';
|
|
}
|
|
|
|
// Store video as base64
|
|
var reader = new FileReader();
|
|
reader.onload = function(e) {
|
|
assessmentVideoData = e.target.result;
|
|
assessmentVideoFilename = file.name;
|
|
console.log('Video loaded: ' + file.name + ' (' + (file.size / (1024 * 1024)).toFixed(2) + ' MB)');
|
|
};
|
|
reader.readAsDataURL(file);
|
|
}
|
|
|
|
// Address autocomplete
|
|
function initAddressAutocomplete() {
|
|
var addressInput = document.getElementById('client_address');
|
|
if (!addressInput) return;
|
|
|
|
var autocomplete = new google.maps.places.Autocomplete(addressInput, {
|
|
componentRestrictions: { country: 'ca' },
|
|
types: ['address']
|
|
});
|
|
|
|
autocomplete.addListener('place_changed', function() {
|
|
var place = autocomplete.getPlace();
|
|
if (!place.address_components) return;
|
|
|
|
var streetNumber = '';
|
|
var streetName = '';
|
|
var city = '';
|
|
var province = '';
|
|
var postalCode = '';
|
|
|
|
for (var i = 0; i < place.address_components.length; i++) {
|
|
var component = place.address_components[i];
|
|
var types = component.types;
|
|
|
|
if (types.includes('street_number')) {
|
|
streetNumber = component.long_name;
|
|
} else if (types.includes('route')) {
|
|
streetName = component.long_name;
|
|
} else if (types.includes('locality')) {
|
|
city = component.long_name;
|
|
} else if (types.includes('administrative_area_level_1')) {
|
|
province = component.short_name;
|
|
} else if (types.includes('postal_code')) {
|
|
postalCode = component.long_name;
|
|
}
|
|
}
|
|
|
|
// Update street address (hidden field stores original, visible shows formatted)
|
|
document.getElementById('client_address_street').value = (streetNumber + ' ' + streetName).trim();
|
|
|
|
// Update city field
|
|
var cityField = document.getElementById('client_address_city');
|
|
if (cityField) cityField.value = city;
|
|
|
|
// Update province select - match by code or name
|
|
var provinceSelect = document.getElementById('client_address_province');
|
|
if (provinceSelect) {
|
|
for (var j = 0; j < provinceSelect.options.length; j++) {
|
|
var optVal = provinceSelect.options[j].value.toUpperCase();
|
|
var optText = provinceSelect.options[j].text.toLowerCase();
|
|
if (optVal === province.toUpperCase() || optText === province.toLowerCase()) {
|
|
provinceSelect.selectedIndex = j;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update postal code field
|
|
var postalField = document.getElementById('client_address_postal');
|
|
if (postalField) postalField.value = postalCode;
|
|
});
|
|
}
|
|
|
|
// 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');
|
|
var formData = new FormData(form);
|
|
|
|
// Convert to JSON
|
|
var data = {};
|
|
formData.forEach(function(value, key) {
|
|
data[key] = value;
|
|
});
|
|
|
|
// Add main photos
|
|
data.photos = photoDataArray.filter(function(p) { return p !== null; });
|
|
|
|
// Add top landing photos (curved stair lift)
|
|
if (topLandingPhotos.length > 0) {
|
|
data.top_landing_photos = topLandingPhotos.filter(function(p) { return p !== null; });
|
|
}
|
|
|
|
// Add bottom landing photos (curved stair lift)
|
|
if (bottomLandingPhotos.length > 0) {
|
|
data.bottom_landing_photos = bottomLandingPhotos.filter(function(p) { return p !== null; });
|
|
}
|
|
|
|
// Add video (curved stair lift)
|
|
if (assessmentVideoData) {
|
|
data.assessment_video = assessmentVideoData;
|
|
data.assessment_video_filename = assessmentVideoFilename;
|
|
}
|
|
|
|
// Add sale order flag
|
|
data.create_sale_order = createSaleOrder;
|
|
|
|
// Show loading
|
|
var submitBtns = document.querySelectorAll('button[onclick*="saveAssessment"]');
|
|
submitBtns.forEach(function(btn) {
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<i class="fa fa-spinner fa-spin"></i> Saving...';
|
|
});
|
|
|
|
// Send request
|
|
fetch('/my/accessibility/save', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
jsonrpc: '2.0',
|
|
method: 'call',
|
|
params: data,
|
|
id: Math.floor(Math.random() * 1000000000)
|
|
})
|
|
})
|
|
.then(function(response) { return response.json(); })
|
|
.then(function(result) {
|
|
if (result.result && result.result.success) {
|
|
// Show success and redirect
|
|
alert(result.result.message);
|
|
window.location.href = result.result.redirect_url;
|
|
} else {
|
|
var errorMsg = result.result ? result.result.error : 'Unknown error';
|
|
alert('Error: ' + errorMsg);
|
|
submitBtns.forEach(function(btn) {
|
|
btn.disabled = false;
|
|
});
|
|
}
|
|
})
|
|
.catch(function(error) {
|
|
alert('Error saving assessment: ' + error);
|
|
submitBtns.forEach(function(btn) {
|
|
btn.disabled = false;
|
|
});
|
|
});
|
|
}
|
|
</script>
|
|
</template>
|
|
|
|
</odoo>
|