Files
Odoo-Modules/fusion_authorizer_portal/views/portal_templates.xml
Nexa Admin 431052920e feat: separate fusion field service and LTC into standalone modules, update core modules
- fusion_claims: separated field service logic, updated controllers/views
- fusion_tasks: updated task views and map integration
- fusion_authorizer_portal: added page 11 signing, schedule booking, migrations
- fusion_shipping: new standalone shipping module (Canada Post, FedEx, DHL, Purolator)
- fusion_ltc_management: new standalone LTC management module
2026-03-11 16:19:52 +00:00

4178 lines
259 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ==================== PORTAL HOME EXTENSION ==================== -->
<template id="portal_my_home_authorizer" inherit_id="portal.portal_my_home" priority="40">
<!-- Insert Fusion content before the default portal docs grid -->
<xpath expr="//div[hasclass('o_portal_docs')]" position="before">
<t t-if="request.env.user.partner_id.is_authorizer or request.env.user.partner_id.is_sales_rep_portal or request.env.user.partner_id.is_client_portal or request.env.user.partner_id.is_technician_portal">
<t t-set="fc_gradient" t-value="portal_gradient or 'linear-gradient(135deg, #5ba848 0%, #3a8fb7 60%, #2e7aad 100%)'"/>
<style>
:root { --fc-portal-gradient: <t t-out="fc_gradient"/>; }
</style>
<!-- Welcome Banner -->
<div class="row g-3 mb-4">
<div class="col-12">
<div class="border-0 shadow-sm p-4" t-attf-style="background: {{fc_gradient}}; border-radius: 12px;">
<div class="d-flex align-items-center text-white">
<div class="flex-grow-1">
<h4 class="mb-1" style="color: #fff; text-shadow: 0 1px 2px rgba(0,0,0,0.1);">
<i class="fa fa-hand-peace-o me-2"/>Welcome back, <t t-out="request.env.user.partner_id.name"/>!
</h4>
<p class="mb-0" style="color: rgba(255,255,255,0.9);">
<t t-if="request.env.user.partner_id.is_authorizer">
Manage your assigned cases and documents from your Authorizer Portal.
</t>
<t t-elif="request.env.user.partner_id.is_technician_portal">
View your assigned deliveries and collect proof of delivery signatures.
</t>
<t t-elif="request.env.user.partner_id.is_sales_rep_portal">
Track your cases and assessments from your Sales Portal.
</t>
<t t-else="">
View your funding claims and track your applications.
</t>
</p>
</div>
<t t-if="today">
<div class="text-end d-none d-md-block">
<div class="rounded-3 px-3 py-2" style="background: rgba(255,255,255,0.2);">
<small class="d-block" style="color: rgba(255,255,255,0.8);">Today</small>
<strong style="color: #fff;"><t t-out="today.strftime('%B %d, %Y')"/></strong>
</div>
</div>
</t>
</div>
</div>
</div>
</div>
<!-- Main Action Tiles - Professional Grid Layout -->
<div class="row g-3 mb-4">
<!-- 1. New Express Assessment (Sales Rep) - Featured tile -->
<t t-if="request.env.user.partner_id.is_sales_rep_portal">
<div class="col-md-6">
<a href="/my/assessment/express" class="card h-100 border-0 shadow-sm text-decoration-none portal-new-assessment-card" style="border-radius: 12px; min-height: 100px;">
<div class="card-body d-flex align-items-center p-4">
<div class="me-3">
<div class="icon-circle">
<i class="fa fa-plus-circle"/>
</div>
</div>
<div>
<h5 class="mb-1">New Express Assessment</h5>
<small>Start a new ADP equipment assessment</small>
</div>
</div>
</a>
</div>
</t>
<!-- 2. Accessibility Assessment (Sales Rep) -->
<t t-if="request.env.user.partner_id.is_sales_rep_portal">
<div class="col-md-6">
<a href="/my/accessibility" class="card h-100 border-0 shadow-sm text-decoration-none" style="border-radius: 12px; min-height: 100px;">
<div class="card-body d-flex align-items-center p-4">
<div class="me-3">
<div class="rounded-circle d-flex align-items-center justify-content-center" t-attf-style="width: 50px; height: 50px; background: {{fc_gradient}};">
<i class="fa fa-wheelchair fa-lg text-white"/>
</div>
</div>
<div>
<h5 class="mb-1 text-dark">Accessibility Assessment</h5>
<small class="text-muted">Stair lifts, VPL, ramps, and more</small>
</div>
</div>
</a>
</div>
</t>
<!-- 3. Sales Portal (Sales Rep) -->
<t t-if="request.env.user.partner_id.is_sales_rep_portal">
<div class="col-md-6">
<a href="/my/sales" class="card h-100 border-0 shadow-sm text-decoration-none" style="border-radius: 12px; min-height: 100px;">
<div class="card-body d-flex align-items-center p-4">
<div class="me-3">
<div class="rounded-circle d-flex align-items-center justify-content-center" t-attf-style="width: 50px; height: 50px; background: {{fc_gradient}};">
<i class="fa fa-briefcase fa-lg text-white"/>
</div>
</div>
<div>
<h5 class="mb-1 text-dark">Sales Portal</h5>
<small class="text-muted">View and manage your cases</small>
</div>
</div>
</a>
</div>
</t>
<!-- 4. Assessments -->
<t t-if="request.env.user.partner_id.is_authorizer or request.env.user.partner_id.is_sales_rep_portal">
<div class="col-md-6">
<a href="/my/assessments" class="card h-100 border-0 shadow-sm text-decoration-none" style="border-radius: 12px; min-height: 100px;">
<div class="card-body d-flex align-items-center p-4">
<div class="me-3">
<div class="rounded-circle d-flex align-items-center justify-content-center" t-attf-style="width: 50px; height: 50px; background: {{fc_gradient}};">
<i class="fa fa-clipboard fa-lg text-white"/>
</div>
</div>
<div>
<h5 class="mb-1 text-dark">Assessments</h5>
<small class="text-muted">View and complete assessments</small>
</div>
</div>
</a>
</div>
</t>
<!-- 5. Signature Requests (Documents to Sign) - Only show if Sign module is installed -->
<t t-if="sign_module_available and (request.env.user.partner_id.is_sales_rep_portal or request.env.user.partner_id.is_authorizer)">
<div class="col-md-6">
<a href="/my/signatures" class="card h-100 border-0 shadow-sm text-decoration-none position-relative" style="border-radius: 12px; min-height: 100px;">
<t t-if="sign_count">
<span class="position-absolute badge rounded-pill bg-danger" style="top: 10px; right: 10px; z-index: 1;"><t t-out="sign_count"/></span>
</t>
<div class="card-body d-flex align-items-center p-4">
<div class="me-3">
<div class="rounded-circle d-flex align-items-center justify-content-center" t-attf-style="width: 50px; height: 50px; background: {{fc_gradient}};">
<i class="fa fa-pencil-square-o fa-lg text-white"/>
</div>
</div>
<div>
<h5 class="mb-1 text-dark">Signature Requests</h5>
<small class="text-muted">
<t t-if="sign_count"><t t-out="sign_count"/> document(s) to sign</t>
<t t-else="">View and sign documents</t>
</small>
</div>
</div>
</a>
</div>
</t>
<!-- Authorizer Portal (Authorizers) -->
<t t-if="request.env.user.partner_id.is_authorizer">
<div class="col-md-6">
<a href="/my/authorizer" class="card h-100 border-0 shadow-sm text-decoration-none portal-authorizer-card" style="border-radius: 12px; min-height: 100px;">
<div class="card-body d-flex align-items-center p-4">
<div class="me-3">
<div class="icon-circle">
<i class="fa fa-user-md"/>
</div>
</div>
<div>
<h5 class="mb-1">Authorizer Portal</h5>
<small>View assigned cases and documents</small>
</div>
</div>
</a>
</div>
</t>
<!-- Technician Portal (Field Technicians) -->
<t t-if="request.env.user.partner_id.is_technician_portal">
<div class="col-md-6">
<a href="/my/technician" class="card h-100 border-0 shadow-sm text-decoration-none" style="border-radius: 12px; min-height: 100px;">
<div class="card-body d-flex align-items-center p-4">
<div class="me-3">
<div class="rounded-circle d-flex align-items-center justify-content-center" t-attf-style="width: 50px; height: 50px; background: {{fc_gradient}};">
<i class="fa fa-truck fa-lg text-white"/>
</div>
</div>
<div>
<h5 class="mb-1 text-dark">Technician Portal</h5>
<small class="text-muted">View deliveries and collect POD signatures</small>
</div>
</div>
</a>
</div>
</t>
<!-- My Schedule (All portal roles) -->
<div class="col-md-6">
<a href="/my/schedule" class="card h-100 border-0 shadow-sm text-decoration-none" style="border-radius: 12px; min-height: 100px;">
<div class="card-body d-flex align-items-center p-4">
<div class="me-3">
<div class="rounded-circle d-flex align-items-center justify-content-center" t-attf-style="width: 50px; height: 50px; background: {{fc_gradient}};">
<i class="fa fa-calendar-check-o fa-lg text-white"/>
</div>
</div>
<div>
<h5 class="mb-1 text-dark">My Schedule</h5>
<small class="text-muted">View and book appointments</small>
</div>
</div>
</a>
</div>
<!-- Clock In/Out -->
<t t-if="clock_enabled">
<div class="col-md-6">
<style>
@keyframes hcPulseGreen {
0% { box-shadow: 0 0 0 0 rgba(16,185,129,0.5); }
70% { box-shadow: 0 0 0 14px rgba(16,185,129,0); }
100% { box-shadow: 0 0 0 0 rgba(16,185,129,0); }
}
@keyframes hcPulseRed {
0% { box-shadow: 0 0 0 0 rgba(239,68,68,0.5); }
70% { box-shadow: 0 0 0 14px rgba(239,68,68,0); }
100% { box-shadow: 0 0 0 0 rgba(239,68,68,0); }
}
.hc-btn-ring {
width: 56px; height: 56px; border-radius: 50%; border: none;
display: flex; align-items: center; justify-content: center;
flex-shrink: 0; pointer-events: none;
}
.hc-btn-ring--in {
background: #10b981;
animation: hcPulseGreen 2s ease-in-out infinite;
}
.hc-btn-ring--out {
background: #ef4444;
animation: hcPulseRed 2s ease-in-out infinite;
}
.hc-btn-ring i { color: #fff; font-size: 1.4rem; }
.hc-btn-ring--in i { padding-left: 3px; }
.hc-timer-badge {
display: inline-block; font-family: monospace; font-size: 0.75rem; font-weight: 700;
color: #10b981; background: rgba(16,185,129,0.1); border-radius: 20px;
padding: 2px 10px; letter-spacing: 0.05em;
}
.hc-clock-link { text-decoration: none; }
.hc-clock-link:hover { text-decoration: none; }
.hc-clock-link:hover .card { box-shadow: 0 4px 16px rgba(0,0,0,0.12) !important; }
.hc-clock-link:active .hc-btn-ring { transform: scale(0.92); }
</style>
<a href="/my/clock" class="hc-clock-link"
id="homeClockCard"
t-att-data-checked-in="'true' if clock_checked_in else 'false'"
t-att-data-check-in-time="clock_check_in_time or ''">
<div class="card h-100 border-0 shadow-sm" style="border-radius: 12px; min-height: 100px;">
<div class="card-body d-flex align-items-center p-4">
<div class="me-3">
<div t-attf-class="hc-btn-ring #{clock_checked_in and 'hc-btn-ring--out' or 'hc-btn-ring--in'}">
<i t-attf-class="fa #{clock_checked_in and 'fa-stop' or 'fa-play'}"/>
</div>
</div>
<div style="min-width: 0;">
<h5 class="mb-0 text-dark" id="homeClockStatus">
<t t-if="clock_checked_in">Clocked In</t>
<t t-else="">Clock In</t>
</h5>
<div id="homeClockTimer">
<t t-if="clock_checked_in"><span class="hc-timer-badge">00:00:00</span></t>
<t t-else=""><small class="text-muted">Tap to start your shift</small></t>
</div>
</div>
</div>
</div>
</a>
</div>
</t>
<!-- Funding Claims (Clients/Authorizers) -->
<t t-if="request.env.user.partner_id.is_client_portal or request.env.user.partner_id.is_authorizer">
<div class="col-md-6">
<a href="/my/funding-claims" class="card h-100 border-0 shadow-sm text-decoration-none" style="border-radius: 12px; min-height: 100px;">
<div class="card-body d-flex align-items-center p-4">
<div class="me-3">
<div class="rounded-circle d-flex align-items-center justify-content-center" t-attf-style="width: 50px; height: 50px; background: {{fc_gradient}};">
<i class="fa fa-file-text-o fa-lg text-white"/>
</div>
</div>
<div>
<h5 class="mb-1 text-dark">Funding Claims</h5>
<small class="text-muted">View your funding claims</small>
</div>
</div>
</a>
</div>
</t>
</div>
<!-- ADP Posting Schedule Card -->
<div class="row g-3 mb-4" t-if="next_posting_date">
<div class="col-12">
<div class="card shadow-sm border-0 overflow-hidden" style="border-radius: 12px;">
<div class="card-header py-3" t-attf-style="background: {{fc_gradient}};">
<h5 class="mb-0 text-white"><i class="fa fa-calendar me-2"/>Upcoming ADP Posting Schedule</h5>
</div>
<div class="card-body" style="background: linear-gradient(180deg, #f8f9fa 0%, #ffffff 100%);">
<div class="row align-items-center">
<div class="col-md-4 text-center mb-3 mb-md-0">
<div class="rounded-3 p-4" t-attf-style="background: {{fc_gradient}};">
<small class="d-block" style="color: rgba(255,255,255,0.8);">Next ADP Posting</small>
<h2 class="mb-1 text-white"><t t-out="next_posting_display"/></h2>
<small style="color: rgba(255,255,255,0.8);"><t t-out="next_posting_weekday"/></small>
<div class="mt-2">
<span class="badge bg-white" style="color: #2e7aad;">
<t t-out="days_until_posting"/> days away
</span>
</div>
</div>
</div>
<div class="col-md-8">
<div class="row g-2">
<t t-foreach="posting_dates or []" t-as="pdate">
<div class="col-4 col-md-2">
<div t-attf-class="card text-center py-2 border #{pdate.get('is_next') and 'border-2' or ''}" t-attf-style="border-radius: 8px; #{pdate.get('is_next') and 'border-color: #3a8fb7 !important; background: linear-gradient(180deg, #f0f9f4 0%, #fff 100%);' or ''}">
<small class="text-muted"><t t-out="pdate.get('month', '')[:3]"/></small>
<h5 t-attf-class="mb-0" t-attf-style="#{pdate.get('is_next') and 'color: #2e7aad;' or 'color: #333;'}"><t t-out="pdate.get('day')"/></h5>
</div>
</div>
</t>
</div>
<div class="mt-3 small" style="color: #666;">
<i class="fa fa-info-circle me-1" style="color: #2e7aad;"/>
<strong>Submission Deadline:</strong> <t t-out="submission_deadline_display"/> by 6 PM
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Home Clock Timer (display only, links to /my/clock) -->
<script type="text/javascript">
(function() {
var card = document.getElementById('homeClockCard');
if (!card) return;
var isCheckedIn = card.dataset.checkedIn === 'true';
var checkInTime = card.dataset.checkInTime ? new Date(card.dataset.checkInTime + 'Z') : null;
if (!isCheckedIn || !checkInTime) return;
function pad(n) { return n &lt; 10 ? '0' + n : '' + n; }
var badge = document.querySelector('#homeClockTimer .hc-timer-badge');
if (!badge) return;
function tick() {
var diff = Math.max(0, Math.floor((new Date() - checkInTime) / 1000));
badge.textContent = pad(Math.floor(diff / 3600)) + ':' + pad(Math.floor((diff % 3600) / 60)) + ':' + pad(diff % 60);
}
tick();
setInterval(tick, 1000);
})();
</script>
</t>
</xpath>
</template>
<!-- ==================== AUTHORIZER DASHBOARD ==================== -->
<template id="portal_authorizer_dashboard" name="Authorizer Dashboard">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="False"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<div class="auth-dash">
<div class="container auth-dash-content">
<!-- Welcome Header -->
<div class="auth-dash-header">
<div class="auth-dash-header-inner">
<div>
<nav aria-label="breadcrumb" class="mb-2">
<ol class="breadcrumb mb-0" style="background: transparent; padding: 0;">
<li class="breadcrumb-item"><a href="/my/home" style="color: rgba(255,255,255,0.7);">Home</a></li>
<li class="breadcrumb-item active" style="color: rgba(255,255,255,0.9);" aria-current="page">Dashboard</li>
</ol>
</nav>
<h1 class="auth-dash-greeting">Welcome back, <t t-out="partner.name"/></h1>
<p class="auth-dash-subtitle"><t t-out="company.name"/> - Authorizer Portal</p>
</div>
</div>
</div>
<!-- Action Tiles -->
<div class="auth-dash-tiles">
<a href="/my/authorizer/cases" class="auth-tile auth-tile-cases">
<div class="auth-tile-icon">
<i class="fa fa-folder-open"/>
</div>
<div class="auth-tile-info">
<div class="auth-tile-title">My Cases</div>
<div class="auth-tile-desc">View and manage your assigned cases</div>
</div>
<div class="auth-tile-badge">
<span class="badge"><t t-out="total_cases"/></span>
</div>
<div class="auth-tile-arrow">
<i class="fa fa-chevron-right"/>
</div>
</a>
<a href="/my/assessments" class="auth-tile auth-tile-assessments">
<div class="auth-tile-icon">
<i class="fa fa-clipboard"/>
</div>
<div class="auth-tile-info">
<div class="auth-tile-title">Assessments</div>
<div class="auth-tile-desc">View completed assessments</div>
</div>
<div class="auth-tile-badge">
<span class="badge"><t t-out="assessment_count"/></span>
</div>
<div class="auth-tile-arrow">
<i class="fa fa-chevron-right"/>
</div>
</a>
<a href="/my/assessment/express" class="auth-tile auth-tile-new">
<div class="auth-tile-icon">
<i class="fa fa-plus-circle"/>
</div>
<div class="auth-tile-info">
<div class="auth-tile-title">New Assessment</div>
<div class="auth-tile-desc">Start a new client assessment</div>
</div>
<div class="auth-tile-arrow">
<i class="fa fa-chevron-right"/>
</div>
</a>
</div>
<!-- Needs Attention -->
<t t-if="needs_attention">
<div class="auth-dash-section">
<div class="auth-section-header auth-section-attention">
<i class="fa fa-exclamation-circle me-2"/>
Needs Your Attention
<span class="badge bg-danger ms-2"><t t-out="len(needs_attention)"/></span>
</div>
<div class="auth-case-list">
<t t-foreach="needs_attention" t-as="order">
<a t-attf-href="/my/authorizer/case/#{order.id}" class="auth-case-item auth-case-attention">
<div class="auth-case-main">
<div class="auth-case-client"><t t-out="order.partner_id.name"/></div>
<div class="auth-case-meta">
<span class="auth-case-ref"><t t-out="order.name"/></span>
<span class="auth-case-status badge-attention">
<t t-out="status_labels.get(order.x_fc_adp_application_status, '')"/>
</span>
</div>
</div>
<div class="auth-case-arrow">
<i class="fa fa-chevron-right"/>
</div>
</a>
</t>
</div>
</div>
</t>
<!-- Pending Assessments -->
<t t-if="pending_assessments">
<div class="auth-dash-section">
<div class="auth-section-header auth-section-pending">
<i class="fa fa-clock-o me-2"/>
Pending Assessments
</div>
<div class="auth-case-list">
<t t-foreach="pending_assessments" t-as="assessment">
<a t-attf-href="/my/assessment/#{assessment.id}" class="auth-case-item">
<div class="auth-case-main">
<div class="auth-case-client"><t t-out="assessment.client_name"/></div>
<div class="auth-case-meta">
<span class="auth-case-ref"><t t-out="assessment.reference"/></span>
<span class="auth-case-date">
<i class="fa fa-calendar me-1"/>
<t t-out="assessment.assessment_date" t-options='{"widget": "date"}'/>
</span>
</div>
</div>
<div class="auth-case-arrow">
<i class="fa fa-chevron-right"/>
</div>
</a>
</t>
</div>
</div>
</t>
<!-- Recent Cases -->
<t t-if="recent_cases">
<div class="auth-dash-section">
<div class="auth-section-header">
<i class="fa fa-history me-2"/>
Recent Cases
</div>
<div class="auth-case-list">
<t t-foreach="recent_cases" t-as="order">
<a t-attf-href="/my/authorizer/case/#{order.id}" class="auth-case-item">
<div class="auth-case-main">
<div class="auth-case-client"><t t-out="order.partner_id.name"/></div>
<div class="auth-case-meta">
<span class="auth-case-ref"><t t-out="order.name"/></span>
<t t-if="order.x_fc_sale_type">
<span class="auth-case-type">
<t t-out="sale_type_labels.get(order.x_fc_sale_type, order.x_fc_sale_type)"/>
</span>
</t>
<t t-if="order.x_fc_adp_application_status">
<span class="auth-case-status">
<t t-out="status_labels.get(order.x_fc_adp_application_status, '')"/>
</span>
</t>
</div>
</div>
<div class="auth-case-arrow">
<i class="fa fa-chevron-right"/>
</div>
</a>
</t>
</div>
</div>
</t>
<!-- Empty State -->
<t t-if="not recent_cases and not needs_attention">
<div class="auth-empty-state">
<i class="fa fa-inbox"/>
<h5>No cases yet</h5>
<p>Cases assigned to you will appear here.</p>
</div>
</t>
</div>
</div>
</t>
</template>
<!-- ==================== AUTHORIZER CASES LIST ==================== -->
<template id="portal_authorizer_cases" name="Authorizer Cases">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="False"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<!-- Breadcrumbs -->
<div class="container mt-3">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="/my/home">Home</a></li>
<li class="breadcrumb-item"><a href="/my/authorizer">Authorizer Dashboard</a></li>
<li class="breadcrumb-item active" aria-current="page">Cases</li>
</ol>
</nav>
</div>
<div class="container py-4">
<div class="row mb-4">
<div class="col-md-6">
<h2>
<i class="fa fa-folder-open text-primary me-2"/>
My Cases
</h2>
</div>
<div class="col-md-6">
<!-- Search Box -->
<form action="/my/authorizer/cases" method="get" class="d-flex">
<div class="input-group">
<input type="text" name="search" class="form-control"
id="portal-search-input"
placeholder="Search by client, reference, or claim number..."
t-att-value="search"/>
<button class="btn btn-primary" type="submit">
<i class="fa fa-search"/>
</button>
</div>
</form>
</div>
</div>
<!-- Search Results Container (for AJAX) -->
<div id="search-results-container">
<t t-if="cases">
<div class="table-responsive">
<table class="table table-hover">
<thead class="table-dark">
<tr>
<th>
<a t-attf-href="/my/authorizer/cases?sortby=name&amp;search=#{search}"
t-attf-class="text-white text-decoration-none #{'fw-bold' if sortby == 'name' else ''}">
Reference
</a>
</th>
<th>
<a t-attf-href="/my/authorizer/cases?sortby=client&amp;search=#{search}"
t-attf-class="text-white text-decoration-none #{'fw-bold' if sortby == 'client' else ''}">
Client
</a>
</th>
<th>
<a t-attf-href="/my/authorizer/cases?sortby=date&amp;search=#{search}"
t-attf-class="text-white text-decoration-none #{'fw-bold' if sortby == 'date' else ''}">
Date
</a>
</th>
<th>Claim #</th>
<th>
<a t-attf-href="/my/authorizer/cases?sortby=state&amp;search=#{search}"
t-attf-class="text-white text-decoration-none #{'fw-bold' if sortby == 'state' else ''}">
Status
</a>
</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="cases-table-body">
<t t-foreach="cases" t-as="order">
<tr>
<td><t t-out="order.name"/></td>
<td><t t-out="order.partner_id.name"/></td>
<td><t t-out="order.date_order" t-options='{"widget": "date"}'/></td>
<td>
<t t-if="order.x_fc_claim_number" t-out="order.x_fc_claim_number"/>
<t t-else="">-</t>
</td>
<td>
<span t-attf-class="badge #{'bg-secondary' if order.state == 'draft' else 'bg-primary' if order.state == 'sent' else 'bg-success' if order.state == 'sale' else 'bg-info'}">
<t t-out="order.state"/>
</span>
</td>
<td>
<a t-attf-href="/my/authorizer/case/#{order.id}" class="btn btn-sm btn-primary">
<i class="fa fa-eye me-1"/>View
</a>
</td>
</tr>
</t>
</tbody>
</table>
</div>
<!-- Pager -->
<t t-call="portal.pager"/>
</t>
<t t-else="">
<div class="text-center py-5">
<i class="fa fa-search fa-4x text-muted mb-3"/>
<h4 class="text-muted">No cases found</h4>
<p class="text-muted">
<t t-if="search">
No cases match your search "<t t-out="search"/>".
<a href="/my/authorizer/cases">Clear search</a>
</t>
<t t-else="">
No cases have been assigned to you yet.
</t>
</p>
</div>
</t>
</div>
</div>
</t>
</template>
<!-- ==================== AUTHORIZER CASE DETAIL ==================== -->
<template id="portal_authorizer_case_detail" name="Authorizer Case Detail">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="False"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<!-- Portal type configuration (can be overridden) -->
<t t-set="portal_type" t-value="portal_type or 'authorizer'"/>
<t t-set="portal_name" t-value="portal_name or 'Authorizer Portal'"/>
<t t-set="portal_dashboard_url" t-value="portal_dashboard_url or '/my/authorizer'"/>
<t t-set="portal_cases_url" t-value="portal_cases_url or '/my/authorizer/cases'"/>
<t t-set="portal_case_base_url" t-value="portal_case_base_url or '/my/authorizer/case'"/>
<!-- Breadcrumbs -->
<div class="container mt-3">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="/my/home">Home</a></li>
<li class="breadcrumb-item"><a t-att-href="portal_dashboard_url"><t t-out="portal_name"/></a></li>
<li class="breadcrumb-item"><a t-att-href="portal_cases_url">Cases</a></li>
<li class="breadcrumb-item active" aria-current="page"><t t-out="order.name"/></li>
</ol>
</nav>
</div>
<div class="container py-4">
<!-- Header -->
<div class="row mb-4">
<div class="col-12 col-md-8 mb-2 mb-md-0">
<h2>
<i class="fa fa-folder text-primary me-2"/>
<t t-out="order.name"/>
</h2>
<p class="text-muted mb-0">
Client: <strong><t t-out="order.partner_id.name"/></strong>
</p>
</div>
<div class="col-12 col-md-4 text-md-end">
<span t-attf-class="badge fs-6 #{'bg-secondary' if order.state == 'draft' else 'bg-primary' if order.state == 'sent' else 'bg-success'}">
<t t-out="order.state"/>
</span>
<a t-att-href="portal_cases_url" class="btn btn-outline-secondary ms-2 ms-md-0 mt-md-2">
<i class="fa fa-arrow-left me-1"/>Back
</a>
</div>
</div>
<div class="row">
<!-- Main Content -->
<div class="col-12 col-md-8 order-2 order-md-1">
<!-- Order Details Card -->
<div class="card mb-4">
<div class="card-header bg-dark text-white">
<i class="fa fa-info-circle me-2"/>Case Details
</div>
<div class="card-body">
<div class="row">
<div class="col-12 col-md-6">
<p><strong>Order Date:</strong> <t t-out="order.date_order" t-options='{"widget": "date"}'/></p>
<p><strong>Client:</strong> <t t-out="order.partner_id.name"/></p>
<t t-if="order.partner_id.street">
<p><strong>Address:</strong><br/>
<t t-out="order.partner_id.street"/><br/>
<t t-if="order.partner_id.city"><t t-out="order.partner_id.city"/>, </t>
<t t-if="order.partner_id.state_id"><t t-out="order.partner_id.state_id.name"/> </t>
<t t-out="order.partner_id.zip"/>
</p>
</t>
<t t-if="order.x_fc_reason_for_application">
<p><strong>Reason:</strong>
<t t-out="dict(order._fields['x_fc_reason_for_application'].selection).get(order.x_fc_reason_for_application, order.x_fc_reason_for_application)"/>
</p>
</t>
</div>
<div class="col-12 col-md-6">
<t t-if="order.x_fc_claim_number">
<p><strong>Claim Number:</strong> <t t-out="order.x_fc_claim_number"/></p>
</t>
<t t-if="order.x_fc_client_ref_1">
<p><strong>Client Ref 1:</strong> <t t-out="order.x_fc_client_ref_1"/></p>
</t>
<t t-if="order.x_fc_client_ref_2">
<p><strong>Client Ref 2:</strong> <t t-out="order.x_fc_client_ref_2"/></p>
</t>
<t t-if="order.user_id">
<p><strong>Sales Rep:</strong> <t t-out="order.user_id.name"/></p>
</t>
<t t-if="order.x_fc_adp_application_status">
<p><strong>Status:</strong>
<span t-attf-class="badge #{'bg-success' if order.x_fc_adp_application_status == 'case_closed' else 'bg-warning text-dark' if order.x_fc_adp_application_status in ['approved', 'billed'] else 'bg-info'}">
<t t-out="dict(order._fields['x_fc_adp_application_status'].selection).get(order.x_fc_adp_application_status, order.x_fc_adp_application_status)"/>
</span>
</p>
</t>
</div>
</div>
</div>
</div>
<!-- Important Dates Card -->
<div class="card mb-4">
<div class="card-header bg-info text-white">
<i class="fa fa-calendar me-2"/>Important Dates
</div>
<div class="card-body">
<div class="row">
<div class="col-12 col-md-6">
<t t-if="order.x_fc_assessment_start_date">
<p><strong>Assessment Start:</strong> <t t-out="order.x_fc_assessment_start_date" t-options='{"widget": "date"}'/></p>
</t>
<t t-if="order.x_fc_assessment_end_date">
<p><strong>Assessment End:</strong> <t t-out="order.x_fc_assessment_end_date" t-options='{"widget": "date"}'/></p>
</t>
<t t-if="order.x_fc_claim_submission_date">
<p><strong>Claim Submission:</strong> <t t-out="order.x_fc_claim_submission_date" t-options='{"widget": "date"}'/></p>
</t>
</div>
<div class="col-12 col-md-6">
<t t-if="order.x_fc_claim_authorization_date">
<p><strong>Authorization:</strong> <t t-out="order.x_fc_claim_authorization_date" t-options='{"widget": "date"}'/></p>
</t>
<t t-if="order.x_fc_claim_approval_date">
<p><strong>Approval:</strong> <t t-out="order.x_fc_claim_approval_date" t-options='{"widget": "date"}'/></p>
</t>
<t t-if="order.x_fc_adp_delivery_date">
<p><strong>ADP Delivery:</strong> <t t-out="order.x_fc_adp_delivery_date" t-options='{"widget": "date"}'/></p>
</t>
</div>
</div>
</div>
</div>
<!-- Products Card -->
<div class="card mb-4">
<div class="card-header bg-dark text-white">
<i class="fa fa-list me-2"/>Products/Devices
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead>
<tr>
<th>ADP Code</th>
<th>Description</th>
<th>Qty</th>
<th>Serial #</th>
</tr>
</thead>
<tbody>
<t t-foreach="order.order_line" t-as="line">
<t t-if="line.product_uom_qty >= 1">
<tr>
<td><t t-out="line.product_id.x_fc_adp_device_code or '-'"/></td>
<td><t t-out="line.product_id.name or line.name"/></td>
<td><t t-out="int(line.product_uom_qty)"/></td>
<td><t t-out="line.x_fc_serial_number or '-'"/></td>
</tr>
</t>
</t>
</tbody>
</table>
</div>
</div>
</div>
<!-- Comments Card -->
<div class="card mb-4">
<div class="card-header bg-dark text-white">
<i class="fa fa-comments me-2"/>Messages
</div>
<div class="card-body">
<!-- Add Comment Form -->
<form t-attf-action="#{portal_case_base_url}/#{order.id}/comment" method="post" class="mb-4">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<div class="mb-3">
<textarea name="comment" class="form-control" rows="3" placeholder="Add a message..." required="required"/>
</div>
<button type="submit" class="btn btn-primary">
<i class="fa fa-paper-plane me-1"/>Send Message
</button>
</form>
<!-- Messages List from Chatter -->
<t t-if="messages">
<hr/>
<t t-foreach="messages" t-as="msg">
<div class="border-start border-primary border-3 ps-3 mb-3">
<div class="d-flex justify-content-between">
<strong><t t-out="msg.author_id.name or 'System'"/></strong>
<small class="text-muted"><t t-out="msg.date" t-options='{"widget": "datetime"}'/></small>
</div>
<div class="mb-0 mt-1" t-out="msg.body"/>
</div>
</t>
</t>
<t t-else="">
<p class="text-muted text-center">No messages yet.</p>
</t>
</div>
</div>
<!-- Loaner Equipment Card -->
<div class="card mb-4">
<div class="card-header text-white" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
<i class="fa fa-wheelchair me-2"/>Loaner Equipment
</div>
<div class="card-body">
<!-- Active Loaners -->
<t t-set="active_loaners" t-value="order.x_fc_loaner_checkout_ids.filtered(lambda l: l.state in ('checked_out', 'overdue', 'rental_pending'))"/>
<t t-if="active_loaners">
<t t-foreach="active_loaners" t-as="loaner">
<div class="border rounded p-3 mb-3">
<div class="d-flex justify-content-between align-items-start mb-2">
<strong><t t-out="loaner.product_id.name"/></strong>
<span t-attf-class="badge #{
'bg-success' if loaner.state == 'checked_out' else
'bg-danger' if loaner.state == 'overdue' else
'bg-warning'}">
<t t-out="loaner.state"/>
</span>
</div>
<div class="small text-muted">
<div><i class="fa fa-barcode me-1"/>S/N: <t t-out="loaner.lot_id.name or 'N/A'"/></div>
<div><i class="fa fa-calendar me-1"/>Checked Out: <t t-out="loaner.checkout_date" t-options='{"widget": "date"}'/></div>
<div><i class="fa fa-clock-o me-1"/>Days Out: <t t-out="loaner.days_out"/></div>
<div><i class="fa fa-calendar-check-o me-1"/>Expected Return: <t t-out="loaner.expected_return_date" t-options='{"widget": "date"}'/></div>
</div>
<!-- Return Button -->
<button type="button" class="btn btn-sm btn-success w-100 mt-2 btn-loaner-return"
t-att-data-checkout-id="loaner.id"
t-att-data-product-name="loaner.product_id.name">
<i class="fa fa-undo me-1"/>Return Loaner
</button>
</div>
</t>
</t>
<!-- Past Loaners (collapsed) -->
<t t-set="past_loaners" t-value="order.x_fc_loaner_checkout_ids.filtered(lambda l: l.state == 'returned')"/>
<t t-if="past_loaners">
<div class="mb-2">
<a data-bs-toggle="collapse" href="#pastLoaners" class="text-muted small">
<i class="fa fa-history me-1"/><t t-out="len(past_loaners)"/> past loaner(s)
</a>
</div>
<div class="collapse" id="pastLoaners">
<t t-foreach="past_loaners" t-as="ploaner">
<div class="small text-muted border-start border-2 ps-2 mb-2">
<strong><t t-out="ploaner.product_id.name"/></strong>
<div><t t-out="ploaner.checkout_date" t-options='{"widget": "date"}'/> - <t t-out="ploaner.actual_return_date" t-options='{"widget": "date"}'/></div>
</div>
</t>
</div>
</t>
<!-- No active loaners: Show checkout button -->
<t t-if="not active_loaners">
<t t-if="request.env.user.partner_id.is_sales_rep_portal">
<button type="button" class="btn btn-outline-primary w-100" id="btn_checkout_loaner"
t-att-data-order-id="order.id"
t-att-data-client-id="order.partner_id.id">
<i class="fa fa-plus me-1"/>Checkout Loaner
</button>
</t>
<t t-elif="not past_loaners">
<p class="text-muted text-center mb-0 small">No loaner equipment assigned</p>
</t>
</t>
</div>
</div>
</div>
<!-- Sidebar -->
<div class="col-12 col-md-4 order-1 order-md-2 mb-4 mb-md-0">
<!-- Documents Card -->
<div class="card mb-4">
<div class="card-header bg-primary text-white">
<i class="fa fa-file-pdf-o me-2"/>Documents
</div>
<div class="card-body">
<!-- Upload Form -->
<form t-attf-action="#{portal_case_base_url}/#{order.id}/upload" method="post" enctype="multipart/form-data" class="mb-3">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<div class="mb-2">
<select name="document_type" class="form-select form-select-sm">
<option value="full_application">Full ADP Application</option>
<option value="pages_11_12">Pages 11 &amp; 12</option>
<option value="page_11">Page 11 Only</option>
<option value="page_12">Page 12 Only</option>
<option value="submitted_final">Final Application</option>
<option value="other">Other</option>
</select>
</div>
<div class="mb-2">
<input type="file" name="document_file" class="form-control form-control-sm" accept=".pdf,.png,.jpg,.jpeg,.xml" required="required"/>
</div>
<div class="mb-2">
<input type="text" name="revision_note" class="form-control form-control-sm" placeholder="Note (optional)"/>
</div>
<button type="submit" class="btn btn-sm btn-primary w-100">
<i class="fa fa-upload me-1"/>Upload
</button>
</form>
<hr/>
<!-- Documents List -->
<t t-if="documents">
<t t-set="doc_type_labels" t-value="{
'full_application': 'Original ADP Application',
'pages_11_12': 'Signed Pages 11 &amp; 12',
'page_11': 'Page 11',
'page_12': 'Page 12',
'submitted_final': 'Final Application',
'other': 'Other Document',
}"/>
<t t-foreach="documents" t-as="doc">
<div class="d-flex justify-content-between align-items-center mb-2 p-2 bg-light rounded">
<div>
<i t-attf-class="fa #{'fa-file-pdf-o text-danger' if doc.mimetype == 'application/pdf' else 'fa-file-image-o text-info' if 'image' in (doc.mimetype or '') else 'fa-file-code-o text-success' if 'xml' in (doc.filename or '') else 'fa-file text-muted'} me-1"/>
<small>
<t t-out="doc_type_labels.get(doc.document_type, doc.document_type)"/>
</small>
<br/>
<small class="text-muted">v<t t-out="doc.revision"/> - <t t-out="doc.upload_date" t-options='{"widget": "date"}'/></small>
</div>
<a t-attf-href="/my/authorizer/document/#{doc.id}/download?view=1"
target="_blank" class="btn btn-sm btn-outline-primary" title="View">
<i class="fa fa-eye"/>
</a>
</div>
</t>
</t>
<t t-else="">
<p class="text-muted text-center small">No documents uploaded yet.</p>
</t>
</div>
</div>
<!-- Attachments Card (from sale.order) -->
<div class="card mb-4">
<div class="card-header bg-secondary text-white">
<i class="fa fa-paperclip me-2"/>Other Attachments
</div>
<div class="card-body">
<!-- Original Application -->
<t t-if="order.x_fc_original_application">
<div class="d-flex justify-content-between align-items-center mb-2 p-2 bg-light rounded">
<div>
<i class="fa fa-file-pdf-o text-danger me-1"/>
<small>Original ADP Application</small>
</div>
<a t-attf-href="#{portal_case_base_url}/#{order.id}/attachment/original_application?view=1"
target="_blank" class="btn btn-sm btn-outline-primary" title="View">
<i class="fa fa-eye"/>
</a>
</div>
</t>
<!-- Final Submitted Application -->
<t t-if="order.x_fc_final_submitted_application">
<div class="d-flex justify-content-between align-items-center mb-2 p-2 bg-light rounded">
<div>
<i class="fa fa-file-pdf-o text-primary me-1"/>
<small>Final ADP Application</small>
</div>
<a t-attf-href="#{portal_case_base_url}/#{order.id}/attachment/final_application?view=1"
target="_blank" class="btn btn-sm btn-outline-primary" title="View">
<i class="fa fa-eye"/>
</a>
</div>
</t>
<!-- XML File -->
<t t-if="order.x_fc_xml_file">
<div class="d-flex justify-content-between align-items-center mb-2 p-2 bg-light rounded">
<div>
<i class="fa fa-file-code-o text-success me-1"/>
<small>XML File</small>
</div>
<a t-attf-href="#{portal_case_base_url}/#{order.id}/attachment/xml_file?view=1"
target="_blank" class="btn btn-sm btn-outline-success" title="View">
<i class="fa fa-eye"/>
</a>
</div>
</t>
<!-- Proof of Delivery -->
<t t-if="order.x_fc_proof_of_delivery">
<div class="d-flex justify-content-between align-items-center mb-2 p-2 bg-light rounded">
<div>
<i class="fa fa-truck text-info me-1"/>
<small>Proof of Delivery</small>
</div>
<a t-attf-href="#{portal_case_base_url}/#{order.id}/attachment/proof_of_delivery?view=1"
target="_blank" class="btn btn-sm btn-outline-info" title="View">
<i class="fa fa-eye"/>
</a>
</div>
</t>
<!-- Approval Photos - Thumbnail Gallery -->
<t t-if="order.x_fc_approval_photo_ids">
<div class="border-top pt-3 mt-3">
<small class="text-muted d-block mb-2">
<i class="fa fa-camera me-1"/>Approval Screenshots (<t t-out="len(order.x_fc_approval_photo_ids)"/>)
</small>
<div class="row g-2">
<t t-foreach="order.x_fc_approval_photo_ids" t-as="photo">
<div class="col-4 col-md-3 col-lg-2">
<a href="#" class="d-block"
data-bs-toggle="modal"
t-attf-data-bs-target="#photoModal#{photo.id}">
<img t-attf-src="#{portal_case_base_url}/#{order.id}/photo/#{photo.id}"
class="img-fluid rounded border"
style="height: 80px; width: 100%; object-fit: cover;"
alt="Approval Screenshot"/>
</a>
</div>
<!-- Modal for this photo -->
<div class="modal fade" t-attf-id="photoModal#{photo.id}" tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Approval Screenshot</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"/>
</div>
<div class="modal-body text-center">
<img t-attf-src="#{portal_case_base_url}/#{order.id}/photo/#{photo.id}"
class="img-fluid"
alt="Approval Screenshot"/>
</div>
</div>
</div>
</div>
</t>
</div>
</div>
</t>
<t t-if="not (
(order.x_fc_original_application) or
(order.x_fc_final_submitted_application) or
(order.x_fc_xml_file) or
(order.x_fc_proof_of_delivery) or
(order.x_fc_approval_photo_ids)
)">
<p class="text-muted text-center small mb-0">No additional attachments.</p>
</t>
</div>
</div>
<!-- POD Collection Card - visible for sales reps -->
<t t-if="request.env.user.partner_id.is_sales_rep_portal or order.user_id.id == request.env.user.id">
<div class="card mb-4">
<div t-attf-class="card-header text-white #{order.x_fc_pod_signature and 'bg-success' or 'bg-warning'}">
<i t-attf-class="fa #{order.x_fc_pod_signature and 'fa-check-circle' or 'fa-pencil-square-o'} me-2"/>
Proof of Delivery
</div>
<div class="card-body">
<t t-if="order.x_fc_pod_signature">
<div class="text-center mb-3">
<span class="badge bg-success fs-6">
<i class="fa fa-check me-1"/>Signature Collected
</span>
</div>
<p class="mb-1"><strong>Client Name:</strong> <t t-out="order.x_fc_pod_client_name or '-'"/></p>
<p class="mb-1" t-if="order.x_fc_pod_signature_date">
<strong>Signature Date:</strong>
<t t-out="order.x_fc_pod_signature_date" t-options="{'widget': 'date'}"/>
</p>
<p class="mb-0"><strong>Collected By:</strong>
<t t-out="order.x_fc_pod_signed_by_user_id.name if order.x_fc_pod_signed_by_user_id else '-'"/>
</p>
<hr/>
<a t-attf-href="/my/pod/#{order.id}" class="btn btn-outline-success btn-sm w-100">
<i class="fa fa-refresh me-1"/>Recollect Signature
</a>
</t>
<t t-else="">
<div class="text-center mb-3">
<i class="fa fa-pencil-square-o fa-3x text-warning mb-2"/>
<p class="text-muted">No signature collected yet</p>
</div>
<a t-attf-href="/my/pod/#{order.id}" class="btn btn-success w-100">
<i class="fa fa-pencil me-1"/>Collect POD Signature
</a>
</t>
</div>
</div>
</t>
</div>
</div>
</div>
</t>
</template>
<!-- ==================== SALES REP DASHBOARD ==================== -->
<template id="portal_sales_dashboard" name="Sales Rep Dashboard">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="False"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<!-- Breadcrumbs -->
<div class="container mt-3">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="/my/home">Home</a></li>
<li class="breadcrumb-item active" aria-current="page">Sales Dashboard</li>
</ol>
</nav>
</div>
<div class="container py-4">
<div class="row mb-4">
<div class="col-12">
<h2 class="mb-3">
<i class="fa fa-briefcase text-success me-2"/>
Sales Dashboard
</h2>
<p class="text-muted">Welcome back, <t t-out="partner.name"/>!</p>
</div>
</div>
<!-- Clock In/Out -->
<t t-if="clock_enabled">
<div class="tech-clock-card mb-3"
id="techClockCard"
t-att-data-checked-in="'true' if clock_checked_in else 'false'"
t-att-data-check-in-time="clock_check_in_time or ''">
<div class="d-flex align-items-center justify-content-between">
<div class="d-flex align-items-center gap-2">
<div class="tech-clock-dot" t-att-class="'tech-clock-dot--active' if clock_checked_in else ''"/>
<div>
<div class="tech-clock-status" id="clockStatusText">
<t t-if="clock_checked_in">Clocked In</t>
<t t-else="">Not Clocked In</t>
</div>
<div class="tech-clock-timer" id="clockTimer">00:00:00</div>
</div>
</div>
<button class="tech-clock-btn" id="clockActionBtn"
t-att-class="'tech-clock-btn--out' if clock_checked_in else 'tech-clock-btn--in'"
onclick="handleClockAction()">
<t t-if="clock_checked_in">
<i class="fa fa-stop-circle-o"/> Clock Out
</t>
<t t-else="">
<i class="fa fa-play-circle-o"/> Clock In
</t>
</button>
</div>
<div class="tech-clock-error" id="clockError" style="display:none;">
<i class="fa fa-exclamation-triangle"/>
<span id="clockErrorText"/>
</div>
</div>
</t>
<!-- Stats Cards - 2x2 on mobile, 4 columns on desktop -->
<div class="row mb-3 g-2">
<div class="col-6 col-md-3">
<div class="card bg-success bg-opacity-10 border-success h-100">
<div class="card-body text-center py-2 py-md-3">
<h3 class="card-title mb-0 text-success"><t t-out="total_count"/></h3>
<small class="card-text text-muted">Total Cases</small>
</div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="card bg-warning bg-opacity-10 border-warning h-100">
<div class="card-body text-center py-2 py-md-3">
<h3 class="card-title mb-0 text-warning"><t t-out="draft_count"/></h3>
<small class="card-text text-muted">Draft</small>
</div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="card bg-info bg-opacity-10 border-info h-100">
<div class="card-body text-center py-2 py-md-3">
<h3 class="card-title mb-0 text-info"><t t-out="len(pending_assessments) if pending_assessments else 0"/></h3>
<small class="card-text text-muted">Pending</small>
</div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="card bg-primary bg-opacity-10 border-primary h-100">
<div class="card-body text-center py-2 py-md-3">
<h3 class="card-title mb-0 text-primary"><t t-out="completed_assessments_count"/></h3>
<small class="card-text text-muted">Completed</small>
</div>
</div>
</div>
</div>
<!-- Quick Actions -->
<div class="row mb-4">
<div class="col-12">
<div class="card border-success">
<div class="card-header bg-success text-white">
<i class="fa fa-bolt me-2"/>Quick Actions
</div>
<div class="card-body">
<a href="/my/assessment/express" class="btn btn-success me-2 mb-2">
<i class="fa fa-plus me-1"/>New Assessment
</a>
<a href="/my/sales/cases" class="btn btn-primary me-2 mb-2">
<i class="fa fa-list me-1"/>View All Cases
</a>
<a href="/my/assessments" class="btn btn-info me-2 mb-2">
<i class="fa fa-clipboard me-1"/>All Assessments
</a>
</div>
</div>
</div>
</div>
<!-- Search and Filter Section -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header bg-light">
<i class="fa fa-search me-2"/>Search &amp; Filter Cases
</div>
<div class="card-body">
<form action="/my/sales" method="get" id="sales-search-form">
<div class="row g-2 g-md-3">
<!-- Search Input -->
<div class="col-12 col-md-4">
<label class="form-label small text-muted d-none d-md-block">Search</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-search"/></span>
<input type="text" name="search" class="form-control"
placeholder="Order #, client name..."
t-att-value="search or ''"/>
</div>
</div>
<!-- Sale Type Filter -->
<div class="col-6 col-md-3">
<label class="form-label small text-muted d-none d-md-block">Sale Type</label>
<select name="sale_type" class="form-select">
<option value="">All Types</option>
<option value="adp" t-att-selected="sale_type_filter == 'adp'">ADP</option>
<option value="adp_odsp" t-att-selected="sale_type_filter == 'adp_odsp'">ADP/ODSP</option>
<option value="odsp" t-att-selected="sale_type_filter == 'odsp'">ODSP</option>
<option value="wsib" t-att-selected="sale_type_filter == 'wsib'">WSIB</option>
<option value="direct_private" t-att-selected="sale_type_filter == 'direct_private'">Direct/Private</option>
<option value="insurance" t-att-selected="sale_type_filter == 'insurance'">Insurance</option>
<option value="march_of_dimes" t-att-selected="sale_type_filter == 'march_of_dimes'">March of Dimes</option>
<option value="rental" t-att-selected="sale_type_filter == 'rental'">Rentals</option>
<option value="other" t-att-selected="sale_type_filter == 'other'">Others</option>
</select>
</div>
<!-- Status Filter -->
<div class="col-6 col-md-3">
<label class="form-label small text-muted d-none d-md-block">Status</label>
<select name="status" class="form-select">
<option value="">All Status</option>
<option value="draft" t-att-selected="status_filter == 'draft'">Draft</option>
<option value="sent" t-att-selected="status_filter == 'sent'">Sent</option>
<option value="sale" t-att-selected="status_filter == 'sale'">Sale</option>
</select>
</div>
<!-- Search Button -->
<div class="col-12 col-md-2 d-flex align-items-end mt-2 mt-md-0">
<button type="submit" class="btn btn-success w-100">
<i class="fa fa-filter me-1"/>Filter
</button>
</div>
</div>
<!-- Active Filters Display -->
<t t-if="search or sale_type_filter or status_filter">
<div class="mt-3 pt-3 border-top">
<span class="text-muted small me-2">Active filters:</span>
<t t-if="search">
<span class="badge bg-primary me-1">
Search: "<t t-out="search"/>"
<a t-attf-href="/my/sales?sale_type=#{sale_type_filter or ''}&amp;status=#{status_filter or ''}" class="text-white ms-1">
<i class="fa fa-times"/>
</a>
</span>
</t>
<t t-if="sale_type_filter">
<span class="badge bg-info me-1">
Sale Type: <t t-out="sale_type_filter"/>
<a t-attf-href="/my/sales?search=#{search or ''}&amp;status=#{status_filter or ''}" class="text-white ms-1">
<i class="fa fa-times"/>
</a>
</span>
</t>
<t t-if="status_filter">
<span class="badge bg-secondary me-1">
Status: <t t-out="status_filter"/>
<a t-attf-href="/my/sales?search=#{search or ''}&amp;sale_type=#{sale_type_filter or ''}" class="text-white ms-1">
<i class="fa fa-times"/>
</a>
</span>
</t>
<a href="/my/sales" class="btn btn-sm btn-outline-secondary ms-2">
<i class="fa fa-times me-1"/>Clear All
</a>
</div>
</t>
</form>
</div>
</div>
</div>
</div>
<!-- Pending Assessments -->
<t t-if="pending_assessments">
<div class="row mb-4">
<div class="col-12">
<div class="card border-warning">
<div class="card-header bg-warning text-dark">
<i class="fa fa-clock-o me-2"/>Pending Assessments
</div>
<div class="card-body p-0">
<table class="table table-hover mb-0">
<thead>
<tr>
<th>Reference</th>
<th>Client</th>
<th>Date</th>
<th>Status</th>
<th></th>
</tr>
</thead>
<tbody>
<t t-foreach="pending_assessments" t-as="assessment">
<tr>
<td><t t-out="assessment.reference"/></td>
<td><t t-out="assessment.client_name"/></td>
<td><t t-out="assessment.assessment_date" t-options='{"widget": "date"}'/></td>
<td>
<span t-attf-class="badge #{'bg-secondary' if assessment.state == 'draft' else 'bg-warning'}">
<t t-out="assessment.state"/>
</span>
</td>
<td>
<a t-attf-href="/my/assessment/#{assessment.id}" class="btn btn-sm btn-outline-primary">
Continue
</a>
</td>
</tr>
</t>
</tbody>
</table>
</div>
</div>
</div>
</div>
</t>
<!-- Recent Cases / Filtered Results -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header bg-dark text-white d-flex justify-content-between align-items-center">
<span>
<i class="fa fa-folder-open me-2"/>
<t t-if="search or sale_type_filter or status_filter">
Search Results (<t t-out="len(recent_cases)"/> found)
</t>
<t t-else="">Recent Cases</t>
</span>
<a href="/my/sales/cases" class="btn btn-sm btn-light">View All</a>
</div>
<div class="card-body p-0">
<t t-if="recent_cases">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead>
<tr>
<th>Reference</th>
<th>Client</th>
<th class="d-none d-md-table-cell">Type</th>
<th class="d-none d-md-table-cell">Date</th>
<th class="d-none d-sm-table-cell">Status</th>
</tr>
</thead>
<tbody>
<t t-foreach="recent_cases" t-as="order">
<tr style="cursor: pointer;" onclick="window.location.href=this.dataset.href" t-att-data-href="'/my/sales/case/' + str(order.id)">
<td>
<strong><t t-out="order.name"/></strong>
<!-- Show type on mobile inline -->
<div class="d-md-none mt-1">
<span t-if="order.x_fc_sale_type == 'adp'" class="badge bg-primary badge-sm">ADP</span>
<span t-elif="order.x_fc_sale_type == 'adp_odsp'" class="badge bg-info badge-sm">ADP/ODSP</span>
<span t-elif="order.x_fc_sale_type in ('direct_private', 'direct_pri', 'direct')" class="badge bg-secondary badge-sm">Direct</span>
<span t-elif="order.x_fc_sale_type == 'regular'" class="badge bg-dark badge-sm">Regular</span>
<span t-elif="order.x_fc_sale_type" class="badge bg-light text-dark badge-sm"><t t-out="order.x_fc_sale_type"/></span>
</div>
</td>
<td>
<t t-out="order.partner_id.name"/>
<!-- Show status on mobile inline -->
<div class="d-sm-none mt-1">
<span t-attf-class="badge badge-sm #{'bg-secondary' if order.state == 'draft' else 'bg-info' if order.state == 'sent' else 'bg-success'}">
<t t-out="order.state"/>
</span>
</div>
</td>
<td class="d-none d-md-table-cell">
<span t-if="order.x_fc_sale_type == 'adp'" class="badge bg-primary">ADP</span>
<span t-elif="order.x_fc_sale_type == 'adp_odsp'" class="badge bg-info">ADP/ODSP</span>
<span t-elif="order.x_fc_sale_type == 'odsp'" class="badge bg-warning text-dark">ODSP</span>
<span t-elif="order.x_fc_sale_type == 'wsib'" class="badge bg-success">WSIB</span>
<span t-elif="order.x_fc_sale_type in ('direct_private', 'direct_pri', 'direct')" class="badge bg-secondary text-white">Direct/Private</span>
<span t-elif="order.x_fc_sale_type == 'insurance'" class="badge bg-info text-white">Insurance</span>
<span t-elif="order.x_fc_sale_type == 'march_of_dimes'" class="badge bg-danger">March of Dimes</span>
<span t-elif="order.x_fc_sale_type == 'muscular_dystrophy'" class="badge bg-danger">Muscular Dystrophy</span>
<span t-elif="order.x_fc_sale_type == 'other'" class="badge bg-secondary">Others</span>
<span t-elif="order.x_fc_sale_type == 'rental'" class="badge bg-warning text-dark">Rentals</span>
<span t-elif="order.x_fc_sale_type == 'regular'" class="badge bg-dark">Regular</span>
<span t-elif="order.x_fc_sale_type == 'sales'" class="badge bg-success">Sales</span>
<span t-else="" class="badge bg-light text-dark"><t t-out="order.x_fc_sale_type or 'N/A'"/></span>
</td>
<td class="d-none d-md-table-cell"><t t-out="order.date_order" t-options='{"widget": "date"}'/></td>
<td class="d-none d-sm-table-cell">
<span t-attf-class="badge #{'bg-secondary' if order.state == 'draft' else 'bg-info' if order.state == 'sent' else 'bg-success'}">
<t t-out="order.state"/>
</span>
</td>
</tr>
</t>
</tbody>
</table>
</div>
</t>
<t t-else="">
<div class="text-center py-4 text-muted">
<i class="fa fa-inbox fa-3x mb-3"/>
<p>No cases yet. Start a new assessment!</p>
</div>
</t>
</div>
</div>
</div>
</div>
</div>
<!-- Include loaner modals -->
<t t-call="fusion_authorizer_portal.loaner_checkout_modal"/>
<t t-call="fusion_authorizer_portal.loaner_return_modal"/>
<!-- Clock In/Out JS -->
<script type="text/javascript">
(function() {
var card = document.getElementById('techClockCard');
if (!card) return;
var isCheckedIn = card.dataset.checkedIn === 'true';
var checkInTime = card.dataset.checkInTime ? new Date(card.dataset.checkInTime + 'Z') : null;
var timerInterval = null;
function updateTimer() {
if (!checkInTime) return;
var diff = Math.max(0, Math.floor((new Date() - checkInTime) / 1000));
var h = Math.floor(diff / 3600);
var m = Math.floor((diff % 3600) / 60);
var s = diff % 60;
var pad = function(n) { return n &lt; 10 ? '0' + n : '' + n; };
document.getElementById('clockTimer').textContent = pad(h) + ':' + pad(m) + ':' + pad(s);
}
function startTimer() { stopTimer(); updateTimer(); timerInterval = setInterval(updateTimer, 1000); }
function stopTimer() { if (timerInterval) { clearInterval(timerInterval); timerInterval = null; } }
function applyState() {
var dot = card.querySelector('.tech-clock-dot');
var statusEl = document.getElementById('clockStatusText');
var btn = document.getElementById('clockActionBtn');
var timerEl = document.getElementById('clockTimer');
if (dot) dot.className = 'tech-clock-dot' + (isCheckedIn ? ' tech-clock-dot--active' : '');
if (statusEl) statusEl.textContent = isCheckedIn ? 'Clocked In' : 'Not Clocked In';
if (btn) {
btn.className = 'tech-clock-btn ' + (isCheckedIn ? 'tech-clock-btn--out' : 'tech-clock-btn--in');
btn.innerHTML = isCheckedIn
? '&lt;i class="fa fa-stop-circle-o">&lt;/i> Clock Out'
: '&lt;i class="fa fa-play-circle-o">&lt;/i> Clock In';
}
if (!isCheckedIn &amp;&amp; timerEl) timerEl.textContent = '00:00:00';
}
if (isCheckedIn &amp;&amp; checkInTime) startTimer();
window.handleClockAction = function() {
var btn = document.getElementById('clockActionBtn');
var errEl = document.getElementById('clockError');
var errText = document.getElementById('clockErrorText');
btn.disabled = true;
errEl.style.display = 'none';
function doClockAction(lat, lng, acc) {
fetch('/fusion_clock/clock_action', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({jsonrpc: '2.0', method: 'call', params: {
latitude: lat, longitude: lng, accuracy: acc, source: 'portal'
}})
})
.then(function(r) { return r.json(); })
.then(function(data) {
var result = data.result || {};
if (result.error) {
errText.textContent = result.error;
errEl.style.display = 'flex';
btn.disabled = false;
return;
}
if (result.action === 'clock_in') {
isCheckedIn = true;
checkInTime = new Date(result.check_in + 'Z');
startTimer();
} else {
isCheckedIn = false;
checkInTime = null;
stopTimer();
}
applyState();
btn.disabled = false;
})
.catch(function() {
errText.textContent = 'Network error. Please try again.';
errEl.style.display = 'flex';
btn.disabled = false;
});
}
if (window.fusionGetLocation) {
window.fusionGetLocation().then(function(coords) {
doClockAction(coords.latitude, coords.longitude, coords.accuracy);
}).catch(function() {
errText.textContent = 'Location access is required for clock in/out.';
errEl.style.display = 'flex';
btn.disabled = false;
});
} else if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(pos) {
doClockAction(pos.coords.latitude, pos.coords.longitude, pos.coords.accuracy);
}, function() {
errText.textContent = 'Location access is required for clock in/out.';
errEl.style.display = 'flex';
btn.disabled = false;
}, {enableHighAccuracy: true, timeout: 15000});
} else {
doClockAction(0, 0, 0);
}
};
})();
</script>
</t>
</template>
<!-- ==================== LOANER CHECKOUT MODAL (shared) ==================== -->
<template id="loaner_checkout_modal" name="Loaner Checkout Modal">
<div class="modal fade" id="loanerCheckoutModal" tabindex="-1" aria-labelledby="loanerCheckoutLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header text-white" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
<h5 class="modal-title" id="loanerCheckoutLabel">
<i class="fa fa-wheelchair me-2"/>Checkout Loaner Equipment
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"/>
</div>
<div class="modal-body">
<!-- Hidden fields for order/client context -->
<input type="hidden" id="modal_order_id"/>
<input type="hidden" id="modal_client_id"/>
<div class="row g-3">
<!-- Category -->
<div class="col-12 col-md-6">
<label class="form-label fw-bold">Category</label>
<select id="modal_category_id" class="form-select">
<option value="">All Categories</option>
</select>
</div>
<!-- Product -->
<div class="col-12 col-md-6">
<label class="form-label fw-bold">Product</label>
<select id="modal_product_id" class="form-select">
<option value="">-- Select Product --</option>
</select>
</div>
<!-- Serial -->
<div class="col-12 col-md-6">
<label class="form-label fw-bold">Serial Number</label>
<select id="modal_lot_id" class="form-select">
<option value="">-- Select Serial --</option>
</select>
</div>
<!-- Loan Days -->
<div class="col-12 col-md-6">
<label class="form-label fw-bold">Loan Duration (Days)</label>
<input type="number" id="modal_loan_days" class="form-control" value="7" min="1"/>
</div>
</div>
<!-- Quick Create Section -->
<hr class="my-3"/>
<div class="d-flex align-items-center mb-2">
<h6 class="mb-0 me-2">Quick Create New Product</h6>
<button type="button" class="btn btn-sm btn-outline-secondary" data-bs-toggle="collapse" data-bs-target="#modalQuickCreate">
<i class="fa fa-plus me-1"/>Expand
</button>
</div>
<div class="collapse" id="modalQuickCreate">
<div class="row g-3 mb-3">
<div class="col-12 col-md-4">
<label class="form-label fw-bold">Category</label>
<select id="modal_new_category_id" class="form-select">
<option value="">-- Select --</option>
</select>
</div>
<div class="col-12 col-md-4">
<label class="form-label fw-bold">Product Name</label>
<input type="text" id="modal_new_product_name" class="form-control" placeholder="e.g., Rollator Type 2"/>
</div>
<div class="col-12 col-md-4">
<label class="form-label fw-bold">Serial Number</label>
<input type="text" id="modal_new_serial" class="form-control" placeholder="e.g., SN-001"/>
</div>
</div>
<button type="button" id="modal_btn_create_product" class="btn btn-outline-primary btn-sm">
<i class="fa fa-plus me-1"/>Create Product
</button>
<div id="modal_create_result" class="mt-2" style="display: none;"/>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" id="modal_btn_checkout" class="btn text-white" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
<i class="fa fa-check me-1"/>Checkout Loaner
</button>
</div>
</div>
</div>
</div>
</template>
<!-- ==================== LOANER RETURN MODAL ==================== -->
<template id="loaner_return_modal" name="Loaner Return Modal">
<div class="modal fade" id="loanerReturnModal" tabindex="-1" aria-labelledby="loanerReturnLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header text-white" style="background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);">
<h5 class="modal-title" id="loanerReturnLabel">
<i class="fa fa-undo me-2"/>Return Loaner Equipment
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"/>
</div>
<div class="modal-body">
<input type="hidden" id="return_modal_checkout_id"/>
<div id="return_modal_product_name" class="fw-bold fs-5 mb-3"/>
<!-- Return Condition -->
<div class="mb-3">
<label class="form-label fw-bold">Return Condition</label>
<select id="return_modal_condition" class="form-select">
<option value="excellent">Excellent</option>
<option value="good" selected="selected">Good</option>
<option value="fair">Fair</option>
<option value="needs_repair">Needs Repair</option>
<option value="damaged">Damaged</option>
</select>
</div>
<!-- Return Location -->
<div class="mb-3">
<label class="form-label fw-bold">Return Location</label>
<select id="return_modal_location_id" class="form-select">
<option value="">-- Loading locations... --</option>
</select>
</div>
<!-- Notes -->
<div class="mb-3">
<label class="form-label fw-bold">Notes (Optional)</label>
<textarea id="return_modal_notes" class="form-control" rows="2" placeholder="Any notes about the return..."/>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" id="return_modal_btn_submit" class="btn text-white" style="background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);">
<i class="fa fa-check me-1"/>Confirm Return
</button>
</div>
</div>
</div>
</div>
</template>
<!-- ==================== SALES REP CASES LIST ==================== -->
<template id="portal_sales_cases" name="Sales Rep Cases">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="False"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<!-- Breadcrumbs -->
<div class="container mt-3">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="/my/home">Home</a></li>
<li class="breadcrumb-item"><a href="/my/sales">Sales Dashboard</a></li>
<li class="breadcrumb-item active" aria-current="page">Cases</li>
</ol>
</nav>
</div>
<div class="container py-4">
<div class="row mb-4">
<div class="col-md-6">
<h2>
<i class="fa fa-folder-open text-success me-2"/>
My Cases
</h2>
</div>
<div class="col-md-6">
<form action="/my/sales/cases" method="get" class="d-flex">
<div class="input-group">
<input type="text" name="search" class="form-control"
id="portal-search-input"
placeholder="Search by client, reference, or claim number..."
t-att-value="search"/>
<button class="btn btn-success" type="submit">
<i class="fa fa-search"/>
</button>
</div>
</form>
</div>
</div>
<t t-if="cases">
<!-- Sale Type Color/Label Mappings -->
<!-- Sale Type Color/Label Mappings removed - using inline conditionals -->
<div class="table-responsive">
<table class="table table-hover">
<thead class="table-success">
<tr>
<th>Reference</th>
<th>Client</th>
<th class="d-none d-md-table-cell">Type</th>
<th class="d-none d-md-table-cell">Date</th>
<th class="d-none d-lg-table-cell">Claim #</th>
<th class="d-none d-sm-table-cell">Status</th>
</tr>
</thead>
<tbody>
<t t-foreach="cases" t-as="order">
<tr style="cursor: pointer;" onclick="window.location.href=this.dataset.href" t-att-data-href="'/my/sales/case/' + str(order.id)">
<td>
<strong><t t-out="order.name"/></strong>
<!-- Mobile: show type inline -->
<div class="d-md-none mt-1">
<span t-if="order.x_fc_sale_type == 'adp'" class="badge bg-primary badge-sm">ADP</span>
<span t-elif="order.x_fc_sale_type == 'adp_odsp'" class="badge bg-info badge-sm">ADP/ODSP</span>
<span t-elif="order.x_fc_sale_type in ('direct_private', 'direct_pri', 'direct')" class="badge bg-secondary badge-sm">Direct</span>
<span t-elif="order.x_fc_sale_type == 'regular'" class="badge bg-dark badge-sm">Regular</span>
<span t-elif="order.x_fc_sale_type" class="badge bg-light text-dark badge-sm"><t t-out="order.x_fc_sale_type"/></span>
</div>
</td>
<td>
<t t-out="order.partner_id.name"/>
<!-- Mobile: show status inline -->
<div class="d-sm-none mt-1">
<span t-attf-class="badge badge-sm #{'bg-secondary' if order.state == 'draft' else 'bg-primary' if order.state == 'sent' else 'bg-success'}">
<t t-out="order.state"/>
</span>
</div>
</td>
<td class="d-none d-md-table-cell">
<span t-if="order.x_fc_sale_type == 'adp'" class="badge bg-primary">ADP</span>
<span t-elif="order.x_fc_sale_type == 'adp_odsp'" class="badge bg-info">ADP/ODSP</span>
<span t-elif="order.x_fc_sale_type == 'odsp'" class="badge bg-warning text-dark">ODSP</span>
<span t-elif="order.x_fc_sale_type == 'wsib'" class="badge bg-success">WSIB</span>
<span t-elif="order.x_fc_sale_type in ('direct_private', 'direct_pri', 'direct')" class="badge bg-secondary text-white">Direct/Private</span>
<span t-elif="order.x_fc_sale_type == 'insurance'" class="badge bg-info text-white">Insurance</span>
<span t-elif="order.x_fc_sale_type == 'march_of_dimes'" class="badge bg-danger">March of Dimes</span>
<span t-elif="order.x_fc_sale_type == 'muscular_dystrophy'" class="badge bg-danger">Muscular Dystrophy</span>
<span t-elif="order.x_fc_sale_type == 'other'" class="badge bg-secondary">Others</span>
<span t-elif="order.x_fc_sale_type == 'rental'" class="badge bg-warning text-dark">Rentals</span>
<span t-elif="order.x_fc_sale_type == 'regular'" class="badge bg-dark">Regular</span>
<span t-elif="order.x_fc_sale_type == 'sales'" class="badge bg-success">Sales</span>
<span t-else="" class="badge bg-light text-dark"><t t-out="order.x_fc_sale_type or 'N/A'"/></span>
</td>
<td class="d-none d-md-table-cell"><t t-out="order.date_order" t-options='{"widget": "date"}'/></td>
<td class="d-none d-lg-table-cell">
<t t-if="order.x_fc_claim_number" t-out="order.x_fc_claim_number"/>
<t t-else="">-</t>
</td>
<td class="d-none d-sm-table-cell">
<span t-attf-class="badge #{'bg-secondary' if order.state == 'draft' else 'bg-primary' if order.state == 'sent' else 'bg-success'}">
<t t-out="order.state"/>
</span>
</td>
</tr>
</t>
</tbody>
</table>
</div>
<t t-call="portal.pager"/>
</t>
<t t-else="">
<div class="text-center py-5">
<i class="fa fa-search fa-4x text-muted mb-3"/>
<h4 class="text-muted">No cases found</h4>
<p class="text-muted">
<t t-if="search">
No cases match your search.
<a href="/my/sales/cases">Clear search</a>
</t>
<t t-else="">
You don't have any cases yet.
<a href="/my/assessment/express">Start a new assessment</a>
</t>
</p>
</div>
</t>
</div>
</t>
</template>
<!-- ==================== SALES REP CASE DETAIL ==================== -->
<!-- Reuses authorizer case detail template with sales rep URLs -->
<template id="portal_sales_case_detail" name="Sales Rep Case Detail">
<t t-set="portal_type" t-value="'sales_rep'"/>
<t t-set="portal_name" t-value="'Sales Dashboard'"/>
<t t-set="portal_dashboard_url" t-value="'/my/sales'"/>
<t t-set="portal_cases_url" t-value="'/my/sales/cases'"/>
<t t-set="portal_case_base_url" t-value="'/my/sales/case'"/>
<t t-call="fusion_authorizer_portal.portal_authorizer_case_detail"/>
</template>
<!-- ==================== ASSESSMENTS LIST ==================== -->
<template id="portal_assessments" name="Assessments List">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="False"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<!-- Breadcrumbs -->
<div class="container mt-3">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="/my/home">Home</a></li>
<li class="breadcrumb-item active" aria-current="page">Assessments</li>
</ol>
</nav>
</div>
<div class="container py-4">
<div class="row mb-4">
<div class="col-md-4">
<h2>
<i class="fa fa-clipboard text-info me-2"/>
Assessments
</h2>
</div>
<div class="col-md-4">
<form action="/my/assessments" method="get" class="d-flex">
<input type="hidden" name="sortby" t-att-value="sortby"/>
<input type="hidden" name="state" t-att-value="state"/>
<div class="input-group">
<input type="text" name="search" class="form-control"
placeholder="Search..."
t-att-value="search"/>
<button class="btn btn-info" type="submit">
<i class="fa fa-search"/>
</button>
</div>
</form>
</div>
<div class="col-md-4 text-end">
<a href="/my/assessment/express" class="btn btn-success">
<i class="fa fa-plus me-1"/>New Assessment
</a>
</div>
</div>
<!-- State Filter -->
<div class="row mb-3">
<div class="col-12">
<div class="btn-group" role="group">
<t t-foreach="state_options" t-as="opt">
<a t-attf-href="/my/assessments?state=#{opt[0]}&amp;search=#{search}&amp;sortby=#{sortby}"
t-attf-class="btn btn-sm #{'btn-info' if state == opt[0] else 'btn-outline-info'}">
<t t-out="opt[1]"/>
</a>
</t>
</div>
</div>
</div>
<t t-if="assessments">
<div class="table-responsive">
<table class="table table-hover">
<thead class="table-info">
<tr>
<th>Reference</th>
<th>Client</th>
<th class="d-none d-md-table-cell">Date</th>
<th class="d-none d-lg-table-cell">Authorizer</th>
<th class="d-none d-sm-table-cell">Status</th>
</tr>
</thead>
<tbody>
<t t-foreach="assessments" t-as="asmt">
<tr style="cursor: pointer;" onclick="window.location.href=this.dataset.href" t-att-data-href="'/my/assessment/' + str(asmt.id)">
<td>
<strong><t t-out="asmt.reference"/></strong>
<!-- Mobile: show status inline -->
<div class="d-sm-none mt-1">
<span t-attf-class="badge badge-sm #{'bg-secondary' if asmt.state == 'draft' else 'bg-warning' if asmt.state == 'pending_signature' else 'bg-success' if asmt.state == 'completed' else 'bg-danger'}">
<t t-out="asmt.state"/>
</span>
<t t-if="asmt.signatures_complete">
<i class="fa fa-check-circle text-success ms-1"/>
</t>
<t t-else="">
<i class="fa fa-clock-o text-warning ms-1"/>
</t>
</div>
</td>
<td><t t-out="asmt.client_name"/></td>
<td class="d-none d-md-table-cell"><t t-out="asmt.assessment_date" t-options='{"widget": "date"}'/></td>
<td class="d-none d-lg-table-cell"><t t-out="asmt.authorizer_id.name or '-'"/></td>
<td class="d-none d-sm-table-cell">
<span t-attf-class="badge #{'bg-secondary' if asmt.state == 'draft' else 'bg-warning' if asmt.state == 'pending_signature' else 'bg-success' if asmt.state == 'completed' else 'bg-danger'}">
<t t-out="asmt.state"/>
</span>
<t t-if="asmt.signatures_complete">
<i class="fa fa-check-circle text-success ms-1"/>
</t>
<t t-else="">
<i class="fa fa-clock-o text-warning ms-1"/>
</t>
</td>
</tr>
</t>
</tbody>
</table>
</div>
<t t-call="portal.pager"/>
</t>
<t t-else="">
<div class="text-center py-5">
<i class="fa fa-clipboard fa-4x text-muted mb-3"/>
<h4 class="text-muted">No assessments found</h4>
<p class="text-muted">
<a href="/my/assessment/express" class="btn btn-success">
<i class="fa fa-plus me-1"/>Start a New Assessment
</a>
</p>
</div>
</t>
</div>
</t>
</template>
<!-- ==================== ASSESSMENT FORM ==================== -->
<template id="portal_assessment_form" name="Assessment Form">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="False"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<!-- Breadcrumbs -->
<div class="container mt-3">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="/my/home">Home</a></li>
<li class="breadcrumb-item"><a href="/my/assessments">Assessments</a></li>
<li class="breadcrumb-item active"><t t-out="assessment.reference or 'Assessment'"/></li>
</ol>
</nav>
</div>
<div class="container py-4" t-if="assessment">
<!-- ===== HEADER ===== -->
<div class="d-flex flex-wrap justify-content-between align-items-start mb-4">
<div>
<h2 class="mb-1">
<t t-out="assessment.reference"/>
<span t-attf-class="badge ms-2 #{
'bg-primary' if assessment.state == 'draft' else
'bg-warning text-dark' if assessment.state == 'pending_signature' else
'bg-success' if assessment.state == 'completed' else
'bg-danger'}" style="font-size: 0.5em; vertical-align: middle;">
<t t-if="assessment.state == 'draft'">Draft</t>
<t t-if="assessment.state == 'pending_signature'">Pending Signature</t>
<t t-if="assessment.state == 'completed'">Completed</t>
<t t-if="assessment.state == 'cancelled'">Cancelled</t>
</span>
</h2>
<div class="text-muted">
<span t-if="assessment.sales_rep_id">
<i class="fa fa-user me-1"/>By <strong><t t-out="assessment.sales_rep_id.name"/></strong>
</span>
<span t-if="assessment.assessment_date" class="ms-3">
<i class="fa fa-calendar me-1"/><t t-out="assessment.assessment_date" t-options='{"widget": "date"}'/>
</span>
<span t-if="assessment.sale_order_id" class="ms-3">
<i class="fa fa-folder-open me-1"/>Case:
<a t-attf-href="/my/sales/case/#{assessment.sale_order_id.id}" class="fw-bold">
<t t-out="assessment.sale_order_id.name"/>
</a>
</span>
</div>
</div>
<div class="mt-2 mt-md-0">
<a href="/my/assessments" class="btn btn-outline-secondary btn-sm me-1">
<i class="fa fa-arrow-left me-1"/>Back
</a>
<a t-attf-href="/my/assessment/express/#{assessment.id}" class="btn btn-primary btn-sm">
<i class="fa fa-pencil me-1"/>Edit
</a>
</div>
</div>
<div class="row">
<!-- ===== LEFT COLUMN ===== -->
<div class="col-12 col-lg-8 mb-4">
<!-- Client Information -->
<div class="card mb-3" style="border-radius: 12px; overflow: hidden;">
<div class="card-header text-white py-2" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
<i class="fa fa-user me-2"/>Client Information
</div>
<div class="card-body">
<div class="row">
<div class="col-12 col-md-6">
<div class="mb-2">
<small class="text-muted">Name</small>
<div class="fw-bold fs-5">
<t t-out="assessment.client_first_name or ''"/>
<t t-if="assessment.client_middle_name"> <t t-out="assessment.client_middle_name"/></t>
<t t-out="' ' + (assessment.client_last_name or '')"/>
</div>
</div>
<div class="mb-2" t-if="assessment.client_health_card">
<small class="text-muted">Health Card</small>
<div class="fw-bold">
<t t-out="assessment.client_health_card"/>
<t t-if="assessment.client_health_card_version">
<span class="text-muted ms-1">v<t t-out="assessment.client_health_card_version"/></span>
</t>
</div>
</div>
<div class="mb-2" t-if="assessment.client_dob">
<small class="text-muted">Date of Birth</small>
<div><t t-out="assessment.client_dob" t-options='{"widget": "date"}'/></div>
</div>
<div class="mb-2" t-if="assessment.client_type">
<small class="text-muted">Client Type</small>
<div>
<span class="badge bg-info">
<t t-if="assessment.client_type == 'reg'">REG (75/25)</t>
<t t-if="assessment.client_type == 'ods'">ODS (100/0)</t>
<t t-if="assessment.client_type == 'acs'">ACS (100/0)</t>
<t t-if="assessment.client_type == 'owp'">OWP (100/0)</t>
</span>
</div>
</div>
<div class="mb-2" t-if="assessment.client_weight">
<small class="text-muted">Weight</small>
<div><t t-out="int(assessment.client_weight)"/> lbs</div>
</div>
</div>
<div class="col-12 col-md-6">
<div class="mb-2" t-if="assessment.client_street or assessment.client_city">
<small class="text-muted">Address</small>
<div>
<t t-if="assessment.client_unit"><t t-out="assessment.client_unit"/> - </t>
<t t-out="assessment.client_street or ''"/><br/>
<t t-out="assessment.client_city or ''"/>
<t t-if="assessment.client_state">, <t t-out="assessment.client_state"/></t>
<t t-if="assessment.client_postal_code"> <t t-out="assessment.client_postal_code"/></t>
</div>
</div>
<div class="mb-2" t-if="assessment.client_phone">
<small class="text-muted">Phone</small>
<div><a t-attf-href="tel:#{assessment.client_phone}"><t t-out="assessment.client_phone"/></a></div>
</div>
<div class="mb-2" t-if="assessment.client_email">
<small class="text-muted">Email</small>
<div><a t-attf-href="mailto:#{assessment.client_email}"><t t-out="assessment.client_email"/></a></div>
</div>
</div>
</div>
</div>
</div>
<!-- Equipment and Measurements -->
<div class="card mb-3" style="border-radius: 12px; overflow: hidden;" t-if="assessment.equipment_type">
<div class="card-header text-white py-2" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);">
<i t-att-class="'fa me-2 ' + ('fa-blind' if assessment.equipment_type == 'rollator' else ('fa-wheelchair' if assessment.equipment_type == 'wheelchair' else 'fa-bolt'))"/>
Equipment &amp; Measurements
</div>
<div class="card-body">
<!-- Type Badges -->
<div class="mb-3">
<span class="badge fs-6 me-2" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
<t t-if="assessment.equipment_type == 'rollator'">Rollator</t>
<t t-if="assessment.equipment_type == 'wheelchair'">Wheelchair</t>
<t t-if="assessment.equipment_type == 'powerchair'">Powerchair</t>
</span>
<t t-if="assessment.equipment_type == 'rollator' and assessment.rollator_type">
<span class="badge bg-secondary fs-6">
<t t-if="assessment.rollator_type == 'type_1'">Type 1 - Two Wheel Walker</t>
<t t-if="assessment.rollator_type == 'type_2'">Type 2 - Rollator</t>
<t t-if="assessment.rollator_type == 'type_3'">Type 3 - Heavy Duty Rollator</t>
</span>
</t>
<t t-if="assessment.equipment_type == 'wheelchair' and assessment.wheelchair_type">
<span class="badge bg-secondary fs-6">
<t t-if="assessment.wheelchair_type == 'type_1'">Type 1 - Standard</t>
<t t-if="assessment.wheelchair_type == 'type_2'">Type 2 - Lightweight</t>
<t t-if="assessment.wheelchair_type == 'type_3'">Type 3 - Ultra Lightweight</t>
<t t-if="assessment.wheelchair_type == 'type_4'">Type 4 - Tilt</t>
<t t-if="assessment.wheelchair_type == 'type_5'">Type 5 - Dynamic Tilt</t>
</span>
</t>
<t t-if="assessment.equipment_type == 'powerchair' and assessment.powerchair_type">
<span class="badge bg-secondary fs-6">
<t t-if="assessment.powerchair_type == 'type_1'">Power Base Type 1</t>
<t t-if="assessment.powerchair_type == 'type_2'">Power Base Type 2</t>
<t t-if="assessment.powerchair_type == 'type_3'">Power Base Type 3</t>
</span>
</t>
</div>
<!-- Measurements Grid -->
<div class="row g-2 mb-3">
<t t-if="assessment.equipment_type == 'rollator'">
<div class="col-6 col-md-4" t-if="assessment.rollator_handle_height">
<div class="border rounded p-2 text-center">
<small class="text-muted d-block">Handle Height</small>
<strong class="fs-5"><t t-out="assessment.rollator_handle_height"/>"</strong>
</div>
</div>
<div class="col-6 col-md-4" t-if="assessment.rollator_seat_height">
<div class="border rounded p-2 text-center">
<small class="text-muted d-block">Seat Height</small>
<strong class="fs-5"><t t-out="assessment.rollator_seat_height"/>"</strong>
</div>
</div>
</t>
<t t-if="assessment.equipment_type in ['wheelchair', 'powerchair']">
<div class="col-6 col-md-4" t-if="assessment.seat_width">
<div class="border rounded p-2 text-center">
<small class="text-muted d-block">Seat Width</small>
<strong class="fs-5"><t t-out="assessment.seat_width"/>"</strong>
</div>
</div>
<div class="col-6 col-md-4" t-if="assessment.seat_depth">
<div class="border rounded p-2 text-center">
<small class="text-muted d-block">Seat Depth</small>
<strong class="fs-5"><t t-out="assessment.seat_depth"/>"</strong>
</div>
</div>
<div class="col-6 col-md-4" t-if="assessment.legrest_length">
<div class="border rounded p-2 text-center">
<small class="text-muted d-block">Legrest Length</small>
<strong class="fs-5"><t t-out="assessment.legrest_length"/>"</strong>
</div>
</div>
<div class="col-6 col-md-4" t-if="assessment.cane_height">
<div class="border rounded p-2 text-center">
<small class="text-muted d-block">Cane Height</small>
<strong class="fs-5"><t t-out="assessment.cane_height"/>"</strong>
</div>
</div>
<div class="col-6 col-md-4" t-if="assessment.back_height">
<div class="border rounded p-2 text-center">
<small class="text-muted d-block">Back Height</small>
<strong class="fs-5"><t t-out="assessment.back_height"/>"</strong>
</div>
</div>
<div class="col-6 col-md-4" t-if="assessment.client_weight">
<div class="border rounded p-2 text-center">
<small class="text-muted d-block">Client Weight</small>
<strong class="fs-5"><t t-out="int(assessment.client_weight)"/> lbs</strong>
</div>
</div>
</t>
</div>
<!-- Cushion and Backrest -->
<div class="row g-2" t-if="assessment.cushion_info or assessment.backrest_info">
<div class="col-12 col-md-6" t-if="assessment.cushion_info">
<div class="border rounded p-2">
<small class="text-muted">Cushion</small>
<div class="fw-bold"><t t-out="assessment.cushion_info"/></div>
</div>
</div>
<div class="col-12 col-md-6" t-if="assessment.backrest_info">
<div class="border rounded p-2">
<small class="text-muted">Backrest</small>
<div class="fw-bold"><t t-out="assessment.backrest_info"/></div>
</div>
</div>
</div>
</div>
</div>
<!-- Selected Options -->
<div class="card mb-3" style="border-radius: 12px; overflow: hidden;"
t-if="assessment.frame_options or assessment.wheel_options or assessment.legrest_options or assessment.additional_adp_options or assessment.rollator_addons or assessment.powerchair_options or assessment.specialty_controls or assessment.seatbelt_type">
<div class="card-header text-white py-2" style="background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);">
<i class="fa fa-check-square me-2"/>Selected Options &amp; Addons
</div>
<div class="card-body">
<t t-foreach="[
('Frame', 'fa-cube', assessment.frame_options, 'success'),
('Wheels', 'fa-circle-o', assessment.wheel_options, 'info'),
('Legrest', 'fa-arrows-v', assessment.legrest_options, 'warning'),
('ADP Options', 'fa-plus-circle', assessment.additional_adp_options, 'primary'),
('Rollator Addons', 'fa-plus', assessment.rollator_addons, 'secondary'),
('Powerchair', 'fa-bolt', assessment.powerchair_options, 'danger'),
('Specialty Controls', 'fa-gamepad', assessment.specialty_controls, 'dark'),
]" t-as="opt_group">
<div class="mb-3" t-if="opt_group[2]">
<small class="text-muted d-block mb-1">
<i t-attf-class="fa #{opt_group[1]} me-1"/><t t-out="opt_group[0]"/>
</small>
<t t-foreach="(opt_group[2] or '').split(',')" t-as="opt">
<span t-attf-class="badge border border-#{opt_group[3]} text-#{opt_group[3]} me-1 mb-1"
t-if="opt.strip()" t-out="opt.strip()"/>
</t>
</div>
</t>
<div t-if="assessment.seatbelt_type">
<small class="text-muted d-block mb-1"><i class="fa fa-lock me-1"/>Seat Belt</small>
<span class="badge bg-secondary">
<t t-if="assessment.seatbelt_type == 'standard'">Standard Belt</t>
<t t-if="assessment.seatbelt_type == 'padded'">Padded Belt (Modular)</t>
<t t-if="assessment.seatbelt_type == '4point'">4 Point Belt (Modular)</t>
<t t-if="assessment.seatbelt_type == 'chest_harness'">Chest Harness (Modular)</t>
<t t-if="assessment.seatbelt_type == 'additional_pads'">Additional Pads (Custom)</t>
</span>
</div>
</div>
</div>
<!-- Additional Notes -->
<div class="card mb-3" style="border-radius: 12px; overflow: hidden;" t-if="assessment.additional_customization">
<div class="card-header text-dark py-2" style="background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%);">
<i class="fa fa-sticky-note me-2"/>Additional Notes
</div>
<div class="card-body">
<t t-out="assessment.additional_customization"/>
</div>
</div>
</div>
<!-- ===== RIGHT COLUMN (Sidebar) ===== -->
<div class="col-12 col-lg-4">
<!-- Assessment Details -->
<div class="card mb-3" style="border-radius: 12px; overflow: hidden;">
<div class="card-header text-white py-2" style="background: linear-gradient(135deg, #a18cd1 0%, #fbc2eb 100%);">
<i class="fa fa-clipboard me-2"/>Assessment Details
</div>
<div class="card-body">
<div class="mb-2" t-if="assessment.authorizer_id">
<small class="text-muted">Authorizer / OT</small>
<div class="fw-bold"><t t-out="assessment.authorizer_id.name"/></div>
</div>
<div class="mb-2" t-if="assessment.assessment_start_date">
<small class="text-muted">Assessment Start</small>
<div><t t-out="assessment.assessment_start_date" t-options='{"widget": "date"}'/></div>
</div>
<div class="mb-2" t-if="assessment.assessment_end_date">
<small class="text-muted">Assessment End</small>
<div><t t-out="assessment.assessment_end_date" t-options='{"widget": "date"}'/></div>
</div>
<div class="mb-2" t-if="assessment.claim_authorization_date">
<small class="text-muted">Claim Authorization Date</small>
<div><t t-out="assessment.claim_authorization_date" t-options='{"widget": "date"}'/></div>
</div>
<div class="mb-2" t-if="assessment.assessment_location">
<small class="text-muted">Location</small>
<div>
<t t-if="assessment.assessment_location == 'home'">Home Visit</t>
<t t-if="assessment.assessment_location == 'clinic'">Clinic</t>
<t t-if="assessment.assessment_location == 'hospital'">Hospital</t>
<t t-if="assessment.assessment_location == 'ltc'">Long-Term Care</t>
<t t-if="assessment.assessment_location == 'other'">Other</t>
</div>
</div>
<div class="mb-2" t-if="assessment.reason_for_application">
<small class="text-muted">Reason for Application</small>
<div class="fw-bold"><t t-out="dict(assessment._fields['reason_for_application'].selection).get(assessment.reason_for_application, assessment.reason_for_application)"/></div>
</div>
<div class="mb-2" t-if="assessment.previous_funding_date">
<small class="text-muted">Previous Funding Date</small>
<div><t t-out="assessment.previous_funding_date" t-options='{"widget": "date"}'/></div>
</div>
</div>
</div>
<!-- Linked Case -->
<div class="card mb-3" style="border-radius: 12px; overflow: hidden;" t-if="assessment.sale_order_id">
<div class="card-header text-white py-2" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);">
<i class="fa fa-folder-open me-2"/>Linked Case
</div>
<div class="card-body text-center">
<a t-attf-href="/my/sales/case/#{assessment.sale_order_id.id}" class="btn btn-outline-primary w-100">
<i class="fa fa-external-link me-1"/>
<t t-out="assessment.sale_order_id.name"/>
</a>
<div class="mt-2 small text-muted">
<t t-out="assessment.sale_order_id.partner_id.name"/>
</div>
</div>
</div>
<!-- Assessment Photos -->
<div class="card mb-3" style="border-radius: 12px; overflow: hidden;" t-if="photos">
<div class="card-header text-white py-2" style="background: linear-gradient(135deg, #30cfd0 0%, #330867 100%);">
<i class="fa fa-camera me-2"/>Assessment Photos (<t t-out="len(photos)"/>)
</div>
<div class="card-body">
<div class="row g-2">
<t t-foreach="photos" t-as="photo">
<div class="col-6">
<a t-attf-href="/web/content/#{photo.id}?download=false" target="_blank">
<img t-attf-src="/web/image/#{photo.id}?width=200"
class="img-fluid rounded" style="width: 100%; height: 100px; object-fit: cover;"
t-att-alt="photo.name"/>
</a>
</div>
</t>
</div>
</div>
</div>
<!-- Loaner Equipment -->
<div class="card mb-3" style="border-radius: 12px; overflow: hidden;" t-if="assessment.sale_order_id">
<div class="card-header text-white py-2" style="background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);">
<i class="fa fa-wheelchair me-2"/>Loaner Equipment
</div>
<div class="card-body">
<t t-set="so_loaners" t-value="assessment.sale_order_id.x_fc_loaner_checkout_ids"/>
<t t-set="active_loaner" t-value="so_loaners.filtered(lambda l: l.state in ('checked_out', 'overdue', 'rental_pending'))"/>
<t t-if="active_loaner">
<t t-foreach="active_loaner" t-as="loaner">
<div class="border rounded p-2 mb-2">
<div class="fw-bold"><t t-out="loaner.product_id.name"/></div>
<div class="small text-muted">
S/N: <t t-out="loaner.lot_id.name or 'N/A'"/>
| Days: <t t-out="loaner.days_out"/>
</div>
<button type="button" class="btn btn-sm btn-success w-100 mt-1 btn-loaner-return"
t-att-data-checkout-id="loaner.id"
t-att-data-product-name="loaner.product_id.name">
<i class="fa fa-undo me-1"/>Return
</button>
</div>
</t>
</t>
<t t-if="not active_loaner">
<button type="button" class="btn btn-outline-primary btn-sm w-100" id="btn_checkout_loaner"
t-att-data-order-id="assessment.sale_order_id.id"
t-att-data-client-id="assessment.sale_order_id.partner_id.id">
<i class="fa fa-plus me-1"/>Checkout Loaner
</button>
</t>
<t t-set="returned_loaners" t-value="so_loaners.filtered(lambda l: l.state == 'returned')"/>
<t t-if="returned_loaners">
<div class="mt-2 small text-muted">
<t t-out="len(returned_loaners)"/> returned loaner(s)
</div>
</t>
</div>
</div>
<!-- Actions -->
<div class="card mb-3" style="border-radius: 12px; overflow: hidden;">
<div class="card-header text-white py-2" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
<i class="fa fa-cog me-2"/>Actions
</div>
<div class="card-body">
<a t-attf-href="/my/assessment/express/#{assessment.id}" class="btn btn-primary w-100 mb-2">
<i class="fa fa-pencil me-1"/>Edit Assessment
</a>
<t t-if="not is_readonly">
<form action="/my/assessment/save" method="post">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<input type="hidden" name="assessment_id" t-att-value="assessment.id"/>
<button type="submit" name="action" value="submit" class="btn btn-success w-100 mb-2">
<i class="fa fa-check me-1"/>Complete Assessment
</button>
</form>
</t>
</div>
</div>
</div>
</div>
</div>
<!-- Include loaner modals -->
<t t-call="fusion_authorizer_portal.loaner_checkout_modal"/>
<t t-call="fusion_authorizer_portal.loaner_return_modal"/>
</t>
</template>
<!-- ==================== SIGNATURE CAPTURE PAGE ==================== -->
<template id="portal_assessment_signatures" name="Assessment Signatures">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="False"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<!-- Breadcrumbs -->
<div class="container mt-3">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="/my/home">Home</a></li>
<li class="breadcrumb-item"><a href="/my/assessments">Assessments</a></li>
<li class="breadcrumb-item"><a t-attf-href="/my/assessment/#{assessment.id}"><t t-out="assessment.reference"/></a></li>
<li class="breadcrumb-item active" aria-current="page">Signatures</li>
</ol>
</nav>
</div>
<div class="container py-4">
<div class="row mb-4">
<div class="col-md-8">
<h2>
<i class="fa fa-pencil-square-o text-danger me-2"/>
Capture Signatures
</h2>
<p class="text-muted">
Assessment: <strong><t t-out="assessment.reference"/></strong> -
Client: <strong><t t-out="assessment.client_name"/></strong>
</p>
</div>
<div class="col-md-4 text-end">
<a t-attf-href="/my/assessment/#{assessment.id}" class="btn btn-outline-secondary">
<i class="fa fa-arrow-left me-1"/>Back to Assessment
</a>
</div>
</div>
<div class="row">
<!-- Page 11 Signature (Authorizer/OT) -->
<div class="col-md-6 mb-4">
<div class="card h-100">
<div class="card-header bg-primary text-white">
<i class="fa fa-user-md me-2"/>Page 11 - Authorizer/OT Signature
</div>
<div class="card-body">
<div class="mb-3">
<label class="form-label">Signer Name</label>
<input type="text" id="page11-signer-name" class="form-control"
t-att-value="assessment.signature_page_11_name or assessment.authorizer_id.name or ''"
placeholder="Enter signer name"/>
</div>
<div class="mb-3">
<label class="form-label">Signature</label>
<div class="border rounded p-2 bg-white" style="touch-action: none;">
<canvas id="signature-pad-11" width="400" height="200"
class="w-100" style="border: 1px dashed #ccc; cursor: crosshair;"></canvas>
</div>
</div>
<div class="d-flex justify-content-between">
<button type="button" class="btn btn-outline-secondary" onclick="clearSignature('11')">
<i class="fa fa-eraser me-1"/>Clear
</button>
<button type="button" class="btn btn-primary" onclick="saveSignature('page_11')">
<i class="fa fa-check me-1"/>Save Signature
</button>
</div>
<div id="page11-status" class="mt-2">
<t t-if="assessment.signature_page_11">
<span class="badge bg-success"><i class="fa fa-check me-1"/>Signature Saved</span>
</t>
</div>
</div>
</div>
</div>
<!-- Page 12 Signature (Client) -->
<div class="col-md-6 mb-4">
<div class="card h-100">
<div class="card-header bg-success text-white">
<i class="fa fa-user me-2"/>Page 12 - Client Signature
</div>
<div class="card-body">
<div class="mb-3">
<label class="form-label">Signer Name</label>
<input type="text" id="page12-signer-name" class="form-control"
t-att-value="assessment.signature_page_12_name or assessment.client_name or ''"
placeholder="Enter signer name"/>
</div>
<div class="mb-3">
<label class="form-label">Signature</label>
<div class="border rounded p-2 bg-white" style="touch-action: none;">
<canvas id="signature-pad-12" width="400" height="200"
class="w-100" style="border: 1px dashed #ccc; cursor: crosshair;"></canvas>
</div>
</div>
<div class="d-flex justify-content-between">
<button type="button" class="btn btn-outline-secondary" onclick="clearSignature('12')">
<i class="fa fa-eraser me-1"/>Clear
</button>
<button type="button" class="btn btn-success" onclick="saveSignature('page_12')">
<i class="fa fa-check me-1"/>Save Signature
</button>
</div>
<div id="page12-status" class="mt-2">
<t t-if="assessment.signature_page_12">
<span class="badge bg-success"><i class="fa fa-check me-1"/>Signature Saved</span>
</t>
</div>
</div>
</div>
</div>
</div>
<!-- Complete Button -->
<div class="row">
<div class="col-12 text-center">
<form t-attf-action="/my/assessment/#{assessment.id}/complete" method="post" class="d-inline">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<button type="submit" class="btn btn-lg btn-danger" id="complete-btn"
t-att-disabled="'' if assessment.signatures_complete else 'disabled'">
<i class="fa fa-check-circle me-2"/>Complete Assessment &amp; Create Sale Order
</button>
</form>
<p class="text-muted mt-2">
<t t-if="not assessment.signatures_complete">
<i class="fa fa-exclamation-triangle text-warning me-1"/>
Both signatures must be captured before completing the assessment.
</t>
<t t-else="">
<i class="fa fa-check-circle text-success me-1"/>
All signatures captured. Ready to complete!
</t>
</p>
</div>
</div>
</div>
<!-- Signature Pad Script -->
<script>
var assessmentId = <t t-out="assessment.id"/>;
var signaturePads = {};
document.addEventListener('DOMContentLoaded', function() {
initSignaturePad('11');
initSignaturePad('12');
});
function initSignaturePad(padId) {
var canvas = document.getElementById('signature-pad-' + padId);
if (!canvas) return;
var ctx = canvas.getContext('2d');
var isDrawing = false;
var lastX = 0;
var lastY = 0;
// Set canvas size
var rect = canvas.getBoundingClientRect();
canvas.width = rect.width;
canvas.height = 200;
// Style
ctx.strokeStyle = '#000';
ctx.lineWidth = 2;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
function getPos(e) {
var rect = canvas.getBoundingClientRect();
var x, y;
if (e.touches) {
x = e.touches[0].clientX - rect.left;
y = e.touches[0].clientY - rect.top;
} else {
x = e.clientX - rect.left;
y = e.clientY - rect.top;
}
return { x: x, y: y };
}
function startDrawing(e) {
e.preventDefault();
isDrawing = true;
var pos = getPos(e);
lastX = pos.x;
lastY = pos.y;
}
function draw(e) {
e.preventDefault();
if (!isDrawing) return;
var pos = getPos(e);
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(pos.x, pos.y);
ctx.stroke();
lastX = pos.x;
lastY = pos.y;
}
function stopDrawing(e) {
e.preventDefault();
isDrawing = false;
}
// Mouse events
canvas.addEventListener('mousedown', startDrawing);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', stopDrawing);
canvas.addEventListener('mouseout', stopDrawing);
// Touch events
canvas.addEventListener('touchstart', startDrawing);
canvas.addEventListener('touchmove', draw);
canvas.addEventListener('touchend', stopDrawing);
signaturePads[padId] = {
canvas: canvas,
ctx: ctx,
isEmpty: function() {
var blank = document.createElement('canvas');
blank.width = canvas.width;
blank.height = canvas.height;
return canvas.toDataURL() === blank.toDataURL();
},
clear: function() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
},
toDataURL: function() {
return canvas.toDataURL('image/png');
}
};
}
function clearSignature(padId) {
if (signaturePads[padId]) {
signaturePads[padId].clear();
}
}
function saveSignature(signatureType) {
var padId = signatureType === 'page_11' ? '11' : '12';
var pad = signaturePads[padId];
if (!pad || pad.isEmpty()) {
alert('Please draw a signature first.');
return;
}
var signerName = document.getElementById(signatureType.replace('_', '') + '-signer-name').value;
if (!signerName) {
alert('Please enter the signer name.');
return;
}
var signatureData = pad.toDataURL();
// Send to server
fetch('/my/assessment/' + assessmentId + '/save_signature', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
jsonrpc: '2.0',
method: 'call',
params: {
signature_type: signatureType,
signature_data: signatureData,
signer_name: signerName
}
})
})
.then(response => response.json())
.then(data => {
if (data.result &amp;&amp; data.result.success) {
var statusDiv = document.getElementById(signatureType.replace('_', '') + '-status');
statusDiv.innerHTML = '<span class="badge bg-success"><i class="fa fa-check me-1"></i>Signature Saved</span>';
if (data.result.signatures_complete) {
document.getElementById('complete-btn').disabled = false;
}
} else {
alert('Error saving signature: ' + (data.result.error || 'Unknown error'));
}
})
.catch(error => {
console.error('Error:', error);
alert('Error saving signature. Please try again.');
});
}
</script>
</t>
</template>
<!-- ==================== CLIENT FUNDING CLAIMS LIST ==================== -->
<template id="portal_client_claims" name="Funding Claims">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="False"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<!-- Breadcrumbs -->
<div class="container mt-3">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="/my/home">Home</a></li>
<li class="breadcrumb-item active" aria-current="page">My Funding Claims</li>
</ol>
</nav>
</div>
<div class="container py-4">
<div class="row mb-4">
<div class="col-md-8">
<h2>
<i class="fa fa-heartbeat text-danger me-2"/>
My Funding Claims
</h2>
<p class="text-muted">View the status and documents of your funding applications.</p>
</div>
<div class="col-md-4 text-end">
<div class="btn-group">
<t t-foreach="sortings" t-as="sort_key">
<a t-attf-href="/my/funding-claims?sortby=#{sort_key}"
t-attf-class="btn btn-sm #{'btn-primary' if sortby == sort_key else 'btn-outline-primary'}">
<t t-out="sortings[sort_key]['label']"/>
</a>
</t>
</div>
</div>
</div>
<t t-if="claims">
<div class="row">
<t t-foreach="claims" t-as="claim">
<div class="col-md-6 mb-4">
<div class="card h-100 shadow-sm">
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
<span>
<i class="fa fa-folder me-2"/>
<t t-out="claim.name"/>
</span>
<span t-attf-class="badge #{'bg-success' if claim.x_fc_adp_application_status == 'case_closed' else 'bg-warning text-dark' if claim.x_fc_adp_application_status in ['approved', 'billed'] else 'bg-info'}">
<t t-if="claim.x_fc_adp_application_status" t-out="dict(claim._fields['x_fc_adp_application_status'].selection).get(claim.x_fc_adp_application_status, claim.x_fc_adp_application_status)"/>
<t t-else="">Pending</t>
</span>
</div>
<div class="card-body">
<p class="mb-2">
<strong>Claim #:</strong>
<t t-if="claim.x_fc_claim_number" t-out="claim.x_fc_claim_number"/>
<t t-else=""><em class="text-muted">Not assigned</em></t>
</p>
<p class="mb-2">
<strong>Reason:</strong>
<t t-if="claim.x_fc_reason_for_application" t-out="dict(claim._fields['x_fc_reason_for_application'].selection).get(claim.x_fc_reason_for_application, '-')"/>
<t t-else="">-</t>
</p>
<p class="mb-2">
<strong>Date:</strong>
<t t-out="claim.date_order" t-options='{"widget": "date"}'/>
</p>
<t t-if="claim.x_fc_claim_approval_date">
<p class="mb-2">
<strong>Approval Date:</strong>
<t t-out="claim.x_fc_claim_approval_date" t-options='{"widget": "date"}'/>
</p>
</t>
</div>
<div class="card-footer bg-light">
<a t-attf-href="/my/funding-claims/#{claim.id}" class="btn btn-primary btn-sm">
<i class="fa fa-eye me-1"/>View Details
</a>
<t t-if="claim.x_fc_adp_application_status == 'case_closed'">
<span class="badge bg-success ms-2">
<i class="fa fa-file me-1"/>Documents Available
</span>
</t>
</div>
</div>
</div>
</t>
</div>
<!-- Pager -->
<t t-call="portal.pager"/>
</t>
<t t-else="">
<div class="text-center py-5">
<i class="fa fa-folder-open fa-4x text-muted mb-3"/>
<h4 class="text-muted">No funding claims found</h4>
<p class="text-muted">You don't have any funding claims on record.</p>
</div>
</t>
</div>
</t>
</template>
<!-- ==================== CLIENT FUNDING CLAIM DETAIL ==================== -->
<template id="portal_client_claim_detail" name="Funding Claim Detail">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="False"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<!-- Breadcrumbs -->
<div class="container mt-3">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="/my/home">Home</a></li>
<li class="breadcrumb-item"><a href="/my/funding-claims">My Funding Claims</a></li>
<li class="breadcrumb-item active" aria-current="page"><t t-out="order.name"/></li>
</ol>
</nav>
</div>
<div class="container py-4">
<!-- Header -->
<div class="row mb-4">
<div class="col-md-8">
<h2>
<i class="fa fa-folder text-primary me-2"/>
<t t-out="order.name"/>
</h2>
<t t-if="order.x_fc_claim_number">
<p class="lead mb-0">Claim #: <strong><t t-out="order.x_fc_claim_number"/></strong></p>
</t>
</div>
<div class="col-md-4 text-md-end">
<span t-attf-class="badge fs-6 #{'bg-success' if order.x_fc_adp_application_status == 'case_closed' else 'bg-warning text-dark' if order.x_fc_adp_application_status in ['approved', 'billed'] else 'bg-info'}">
<t t-if="order.x_fc_adp_application_status" t-out="dict(order._fields['x_fc_adp_application_status'].selection).get(order.x_fc_adp_application_status, order.x_fc_adp_application_status)"/>
<t t-else="">Pending</t>
</span>
<br/>
<a href="/my/funding-claims" class="btn btn-outline-secondary mt-2">
<i class="fa fa-arrow-left me-1"/>Back to Claims
</a>
</div>
</div>
<div class="row">
<!-- Main Content -->
<div class="col-md-8">
<!-- Claim Details Card -->
<div class="card mb-4">
<div class="card-header bg-primary text-white">
<i class="fa fa-info-circle me-2"/>Claim Information
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<p><strong>Status:</strong>
<t t-if="order.x_fc_adp_application_status" t-out="dict(order._fields['x_fc_adp_application_status'].selection).get(order.x_fc_adp_application_status, '-')"/>
<t t-else="">Pending</t>
</p>
<p><strong>Reason for Application:</strong>
<t t-if="order.x_fc_reason_for_application" t-out="dict(order._fields['x_fc_reason_for_application'].selection).get(order.x_fc_reason_for_application, '-')"/>
<t t-else="">-</t>
</p>
<p><strong>Order Date:</strong> <t t-out="order.date_order" t-options='{"widget": "date"}'/></p>
</div>
<div class="col-md-6">
<t t-if="order.x_fc_claim_number">
<p><strong>Claim Number:</strong> <t t-out="order.x_fc_claim_number"/></p>
</t>
<t t-if="order.x_fc_claim_approval_date">
<p><strong>Approval Date:</strong> <t t-out="order.x_fc_claim_approval_date" t-options='{"widget": "date"}'/></p>
</t>
<t t-if="order.x_fc_adp_delivery_date">
<p><strong>Delivery Date:</strong> <t t-out="order.x_fc_adp_delivery_date" t-options='{"widget": "date"}'/></p>
</t>
</div>
</div>
</div>
</div>
<!-- Dates Card -->
<div class="card mb-4">
<div class="card-header bg-info text-white">
<i class="fa fa-calendar me-2"/>Important Dates
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<t t-if="order.x_fc_assessment_start_date">
<p><strong>Assessment Start:</strong> <t t-out="order.x_fc_assessment_start_date" t-options='{"widget": "date"}'/></p>
</t>
<t t-if="order.x_fc_assessment_end_date">
<p><strong>Assessment End:</strong> <t t-out="order.x_fc_assessment_end_date" t-options='{"widget": "date"}'/></p>
</t>
<t t-if="order.x_fc_claim_submission_date">
<p><strong>Submission Date:</strong> <t t-out="order.x_fc_claim_submission_date" t-options='{"widget": "date"}'/></p>
</t>
</div>
<div class="col-md-6">
<t t-if="order.x_fc_claim_authorization_date">
<p><strong>Authorization Date:</strong> <t t-out="order.x_fc_claim_authorization_date" t-options='{"widget": "date"}'/></p>
</t>
<t t-if="order.x_fc_claim_approval_date">
<p><strong>Approval Date:</strong> <t t-out="order.x_fc_claim_approval_date" t-options='{"widget": "date"}'/></p>
</t>
<t t-if="order.x_fc_adp_delivery_date">
<p><strong>ADP Delivery Date:</strong> <t t-out="order.x_fc_adp_delivery_date" t-options='{"widget": "date"}'/></p>
</t>
</div>
</div>
</div>
</div>
<!-- Invoices Card -->
<t t-if="invoices">
<div class="card mb-4">
<div class="card-header bg-success text-white">
<i class="fa fa-file-text me-2"/>Invoices
</div>
<div class="card-body p-0">
<table class="table table-hover mb-0">
<thead>
<tr>
<th>Invoice</th>
<th>Date</th>
<th>Amount</th>
<th>Status</th>
<th></th>
</tr>
</thead>
<tbody>
<t t-foreach="invoices" t-as="inv">
<tr>
<td><t t-out="inv.name"/></td>
<td><t t-out="inv.invoice_date" t-options='{"widget": "date"}'/></td>
<td><t t-out="inv.amount_total" t-options='{"widget": "monetary", "display_currency": inv.currency_id}'/></td>
<td>
<span t-attf-class="badge #{'bg-success' if inv.payment_state == 'paid' else 'bg-warning text-dark'}">
<t t-out="inv.payment_state"/>
</span>
</td>
<td>
<a t-attf-href="/my/invoices/#{inv.id}" class="btn btn-sm btn-outline-primary">
<i class="fa fa-eye"/>
</a>
</td>
</tr>
</t>
</tbody>
</table>
</div>
</div>
</t>
</div>
<!-- Sidebar - Documents -->
<div class="col-md-4">
<div class="card mb-4">
<div class="card-header bg-dark text-white">
<i class="fa fa-file-pdf-o me-2"/>Documents
</div>
<div class="card-body">
<t t-if="is_case_closed">
<!-- Documents available after case closed -->
<t t-if="documents or order.x_fc_proof_of_delivery">
<t t-foreach="documents" t-as="doc">
<div class="d-flex justify-content-between align-items-center mb-3 p-2 bg-light rounded">
<div>
<i class="fa fa-file-pdf-o text-danger me-2"/>
<span>
<t t-if="doc.document_type == 'submitted_final'">Final Application</t>
<t t-elif="doc.document_type == 'pages_11_12'">Signed Pages 11 &amp; 12</t>
<t t-else="" t-out="doc.filename"/>
</span>
</div>
<a t-attf-href="/my/funding-claims/#{order.id}/document/#{doc.id}/download" class="btn btn-sm btn-primary">
<i class="fa fa-download"/>
</a>
</div>
</t>
<t t-if="order.x_fc_proof_of_delivery">
<div class="d-flex justify-content-between align-items-center mb-3 p-2 bg-light rounded">
<div>
<i class="fa fa-truck text-success me-2"/>
<span>Proof of Delivery</span>
</div>
<a t-attf-href="/my/funding-claims/#{order.id}/proof-of-delivery" class="btn btn-sm btn-success">
<i class="fa fa-download"/>
</a>
</div>
</t>
</t>
<t t-else="">
<p class="text-muted text-center">
<i class="fa fa-info-circle me-1"/>
No documents available.
</p>
</t>
</t>
<t t-else="">
<div class="alert alert-info mb-0">
<i class="fa fa-clock-o me-2"/>
<strong>Documents Pending</strong>
<p class="mb-0 small mt-2">
Documents will be available for download once your case is closed.
</p>
</div>
</t>
</div>
</div>
<!-- Need Help Card -->
<div class="card">
<div class="card-header bg-secondary text-white">
<i class="fa fa-question-circle me-2"/>Need Help?
</div>
<div class="card-body">
<p class="small text-muted mb-2">
If you have questions about your funding claim, please contact us.
</p>
<a href="/contactus" class="btn btn-outline-secondary btn-sm w-100">
<i class="fa fa-envelope me-1"/>Contact Us
</a>
</div>
</div>
</div>
</div>
</div>
</t>
</template>
<!-- ==================== TECHNICIAN PORTAL ==================== -->
<!-- NOTE: Technician portal templates have been moved to portal_technician_templates.xml -->
<!-- Legacy templates kept below for backward compatibility only -->
<template id="portal_technician_dashboard_legacy" name="Technician Dashboard (Legacy)">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="False"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<!-- Breadcrumbs -->
<div class="container mt-3">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="/my/home">Home</a></li>
<li class="breadcrumb-item active" aria-current="page">Technician Dashboard</li>
</ol>
</nav>
</div>
<div class="container py-4">
<div class="row mb-4">
<div class="col-12">
<h2 class="mb-3">
<i class="fa fa-truck text-primary me-2"/>
Technician Dashboard
</h2>
<p class="text-muted">Welcome back! Here are your assigned deliveries.</p>
</div>
</div>
<!-- Stats Cards -->
<div class="row mb-4">
<div class="col-md-3 col-sm-6 mb-3">
<div class="card bg-primary text-white h-100">
<div class="card-body text-center">
<h3 class="card-title mb-0"><t t-out="total_count"/></h3>
<p class="card-text small mb-0">Total Assigned</p>
</div>
</div>
</div>
<div class="col-md-3 col-sm-6 mb-3">
<div class="card bg-info text-white h-100">
<div class="card-body text-center">
<h3 class="card-title mb-0"><t t-out="ready_delivery_count"/></h3>
<p class="card-text small mb-0">Ready for Delivery</p>
</div>
</div>
</div>
<div class="col-md-3 col-sm-6 mb-3">
<div class="card bg-warning text-dark h-100">
<div class="card-body text-center">
<h3 class="card-title mb-0"><t t-out="pending_pod_count"/></h3>
<p class="card-text small mb-0">Pending POD</p>
</div>
</div>
</div>
<div class="col-md-3 col-sm-6 mb-3">
<div class="card bg-success text-white h-100">
<div class="card-body text-center">
<h3 class="card-title mb-0"><t t-out="completed_count"/></h3>
<p class="card-text small mb-0">POD Collected</p>
</div>
</div>
</div>
</div>
<!-- Pending POD Section -->
<div class="row mb-4" t-if="pending_pod">
<div class="col-12">
<div class="card border-warning">
<div class="card-header bg-warning text-dark">
<i class="fa fa-pencil-square-o me-2"/>Pending POD Signatures
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>Order</th>
<th>Client</th>
<th>Address</th>
<th>Scheduled</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<t t-foreach="pending_pod" t-as="order">
<tr>
<td>
<a t-attf-href="/my/technician/delivery/#{order.id}">
<t t-out="order.name"/>
</a>
</td>
<td><t t-out="order.partner_id.name"/></td>
<td>
<t t-if="order.partner_shipping_id">
<t t-out="order.partner_shipping_id.city"/>
</t>
</td>
<td>
<t t-if="order.x_fc_scheduled_delivery_datetime">
<t t-out="order.x_fc_scheduled_delivery_datetime" t-options="{'widget': 'datetime', 'format': 'MMM d, h:mm a'}"/>
</t>
<t t-else="">-</t>
</td>
<td>
<a t-attf-href="/my/pod/#{order.id}" class="btn btn-sm btn-success">
<i class="fa fa-pencil me-1"/>Collect POD
</a>
</td>
</tr>
</t>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Quick Actions -->
<div class="row mb-4">
<div class="col-12">
<a href="/my/technician/deliveries" class="btn btn-primary">
<i class="fa fa-list me-1"/>View All Deliveries
</a>
<a href="/my/technician/deliveries?filter_status=pending_pod" class="btn btn-outline-warning">
<i class="fa fa-pencil-square-o me-1"/>Pending POD Only
</a>
</div>
</div>
</div>
</t>
</template>
<template id="portal_technician_deliveries_legacy" name="Technician Deliveries List (Legacy)">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="False"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<!-- Breadcrumbs -->
<div class="container mt-3">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="/my/home">Home</a></li>
<li class="breadcrumb-item"><a href="/my/technician">Technician Dashboard</a></li>
<li class="breadcrumb-item active" aria-current="page">Deliveries</li>
</ol>
</nav>
</div>
<div class="container py-4">
<div class="row mb-4">
<div class="col-12">
<h2><i class="fa fa-truck me-2"/>My Deliveries</h2>
</div>
</div>
<!-- Filters -->
<div class="row mb-3">
<div class="col-md-6">
<form method="GET" action="/my/technician/deliveries">
<div class="input-group">
<input type="text" name="search" class="form-control"
placeholder="Search by client, order #, claim #..."
t-att-value="search"/>
<button type="submit" class="btn btn-primary">
<i class="fa fa-search"/>
</button>
</div>
<input type="hidden" name="filter_status" t-att-value="filter_status"/>
</form>
</div>
<div class="col-md-6 text-end">
<div class="btn-group">
<a t-attf-href="/my/technician/deliveries?filter_status=all"
t-attf-class="btn btn-outline-secondary #{'active' if filter_status == 'all' else ''}">All</a>
<a t-attf-href="/my/technician/deliveries?filter_status=ready_delivery"
t-attf-class="btn btn-outline-info #{'active' if filter_status == 'ready_delivery' else ''}">Ready</a>
<a t-attf-href="/my/technician/deliveries?filter_status=pending_pod"
t-attf-class="btn btn-outline-warning #{'active' if filter_status == 'pending_pod' else ''}">Pending POD</a>
<a t-attf-href="/my/technician/deliveries?filter_status=completed"
t-attf-class="btn btn-outline-success #{'active' if filter_status == 'completed' else ''}">Completed</a>
</div>
</div>
</div>
<!-- Deliveries Table -->
<div class="card">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>Order</th>
<th>Client</th>
<th>Delivery Address</th>
<th>Scheduled</th>
<th>POD Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<t t-foreach="deliveries" t-as="order">
<tr>
<td>
<a t-attf-href="/my/technician/delivery/#{order.id}">
<t t-out="order.name"/>
</a>
<t t-if="order.x_fc_claim_number">
<br/><small class="text-muted">Claim: <t t-out="order.x_fc_claim_number"/></small>
</t>
</td>
<td><t t-out="order.partner_id.name"/></td>
<td>
<t t-if="order.partner_shipping_id">
<t t-out="order.partner_shipping_id.street"/><br/>
<small><t t-out="order.partner_shipping_id.city"/>, <t t-out="order.partner_shipping_id.state_id.code or ''"/></small>
</t>
</td>
<td>
<t t-if="order.x_fc_scheduled_delivery_datetime">
<t t-out="order.x_fc_scheduled_delivery_datetime" t-options="{'widget': 'datetime', 'format': 'MMM d, h:mm a'}"/>
</t>
<t t-else="">-</t>
</td>
<td>
<t t-if="order.x_fc_pod_signature">
<span class="badge bg-success"><i class="fa fa-check me-1"/>Signed</span>
</t>
<t t-else="">
<span class="badge bg-warning text-dark"><i class="fa fa-clock-o me-1"/>Pending</span>
</t>
</td>
<td>
<t t-if="not order.x_fc_pod_signature">
<a t-attf-href="/my/pod/#{order.id}" class="btn btn-sm btn-success">
<i class="fa fa-pencil me-1"/>Collect POD
</a>
</t>
<t t-else="">
<a t-attf-href="/my/technician/delivery/#{order.id}" class="btn btn-sm btn-outline-primary">
<i class="fa fa-eye me-1"/>View
</a>
</t>
</td>
</tr>
</t>
<t t-if="not deliveries">
<tr>
<td colspan="6" class="text-center text-muted py-4">
<i class="fa fa-inbox fa-2x mb-2"/><br/>
No deliveries found
</td>
</tr>
</t>
</tbody>
</table>
</div>
</div>
</div>
<!-- Pager -->
<t t-if="pager">
<div class="d-flex justify-content-center mt-3">
<t t-call="portal.pager"/>
</div>
</t>
</div>
</t>
</template>
<template id="portal_technician_delivery_detail_legacy" name="Technician Delivery Detail (Legacy)">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="False"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<!-- Breadcrumbs -->
<div class="container mt-3">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="/my/home">Home</a></li>
<li class="breadcrumb-item"><a href="/my/technician">Technician Dashboard</a></li>
<li class="breadcrumb-item"><a href="/my/technician/deliveries">Deliveries</a></li>
<li class="breadcrumb-item active" aria-current="page"><t t-out="order.name"/></li>
</ol>
</nav>
</div>
<div class="container py-4">
<div class="row">
<div class="col-lg-8">
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fa fa-file-text-o me-2"/>Order Details
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<p><strong>Order:</strong> <t t-out="order.name"/></p>
<p><strong>Client:</strong> <t t-out="order.partner_id.name"/></p>
<p t-if="order.x_fc_claim_number">
<strong>Claim #:</strong> <t t-out="order.x_fc_claim_number"/>
</p>
</div>
<div class="col-md-6">
<p><strong>Status:</strong>
<span class="badge bg-info"><t t-out="order.x_fc_adp_application_status"/></span>
</p>
<p t-if="order.x_fc_scheduled_delivery_datetime">
<strong>Scheduled:</strong>
<t t-out="order.x_fc_scheduled_delivery_datetime" t-options="{'widget': 'datetime'}"/>
</p>
</div>
</div>
</div>
</div>
<!-- Delivery Address -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fa fa-map-marker me-2"/>Delivery Address
</h5>
</div>
<div class="card-body">
<t t-if="order.partner_shipping_id">
<p class="mb-1"><strong><t t-out="order.partner_shipping_id.name"/></strong></p>
<p class="mb-1"><t t-out="order.partner_shipping_id.street"/></p>
<p t-if="order.partner_shipping_id.street2" class="mb-1"><t t-out="order.partner_shipping_id.street2"/></p>
<p class="mb-1">
<t t-out="order.partner_shipping_id.city"/>,
<t t-out="order.partner_shipping_id.state_id.code or ''"/>
<t t-out="order.partner_shipping_id.zip"/>
</p>
<p t-if="order.partner_shipping_id.phone" class="mb-0">
<i class="fa fa-phone me-1"/><t t-out="order.partner_shipping_id.phone"/>
</p>
</t>
</div>
</div>
<!-- Order Lines -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fa fa-list me-2"/>Items to Deliver
</h5>
</div>
<div class="card-body p-0">
<table class="table mb-0">
<thead class="table-light">
<tr>
<th>Product</th>
<th>Quantity</th>
<th>Serial #</th>
</tr>
</thead>
<tbody>
<t t-foreach="order.order_line" t-as="line">
<t t-if="line.product_id and not line.display_type">
<tr>
<td><t t-out="line.product_id.name"/></td>
<td><t t-out="int(line.product_uom_qty)"/></td>
<td><t t-out="line.x_fc_serial_number or '-'"/></td>
</tr>
</t>
</t>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-lg-4">
<!-- POD Status Card -->
<div class="card mb-4">
<div t-attf-class="card-header #{order.x_fc_pod_signature and 'bg-success text-white' or 'bg-warning'}">
<h5 class="mb-0">
<i t-attf-class="fa #{order.x_fc_pod_signature and 'fa-check-circle' or 'fa-clock-o'} me-2"/>
Proof of Delivery
</h5>
</div>
<div class="card-body">
<t t-if="order.x_fc_pod_signature">
<p class="text-success"><i class="fa fa-check me-1"/>Signature Collected</p>
<p><strong>Client Name:</strong> <t t-out="order.x_fc_pod_client_name"/></p>
<p t-if="order.x_fc_pod_signature_date">
<strong>Date:</strong> <t t-out="order.x_fc_pod_signature_date" t-options="{'widget': 'date'}"/>
</p>
<p><strong>Collected By:</strong> <t t-out="order.x_fc_pod_signed_by_user_id.name"/></p>
</t>
<t t-else="">
<p class="text-warning">Signature not yet collected</p>
<a t-attf-href="/my/pod/#{order.id}" class="btn btn-success w-100">
<i class="fa fa-pencil me-1"/>Collect POD Signature
</a>
</t>
</div>
</div>
</div>
</div>
</div>
</t>
</template>
<!-- ==================== POD SIGNATURE PAGE ==================== -->
<template id="portal_pod_signature" name="POD Signature Capture">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="False"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<!-- Breadcrumbs -->
<div class="container mt-3">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="/my/home">Home</a></li>
<t t-if="user_role == 'technician'">
<li class="breadcrumb-item"><a href="/my/technician">Technician Dashboard</a></li>
<li class="breadcrumb-item"><a href="/my/technician/deliveries">Deliveries</a></li>
<li class="breadcrumb-item"><a t-attf-href="/my/technician/delivery/#{order.id}"><t t-out="order.name"/></a></li>
</t>
<t t-else="">
<li class="breadcrumb-item"><a href="/my/sales">Sales Dashboard</a></li>
<li class="breadcrumb-item"><a href="/my/sales/cases">Cases</a></li>
<li class="breadcrumb-item"><a t-attf-href="/my/sales/case/#{order.id}"><t t-out="order.name"/></a></li>
</t>
<li class="breadcrumb-item active" aria-current="page">Collect POD Signature</li>
</ol>
</nav>
</div>
<div class="container py-4">
<div class="row mb-4">
<div class="col-12">
<h2>
<i class="fa fa-pencil-square-o me-2"/>
Proof of Delivery - <t t-out="order.name"/>
</h2>
</div>
</div>
<div class="row">
<div class="col-lg-7">
<!-- Order Summary -->
<div class="card mb-4">
<div class="card-header bg-primary text-white">
<h5 class="mb-0"><i class="fa fa-truck me-2"/>Delivery Summary</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<p><strong>Client:</strong> <t t-out="order.partner_id.name"/></p>
<p><strong>Order:</strong> <t t-out="order.name"/></p>
<p t-if="order.x_fc_claim_number">
<strong>Claim #:</strong> <t t-out="order.x_fc_claim_number"/>
</p>
</div>
<div class="col-md-6">
<p><strong>Delivery Address:</strong></p>
<t t-if="delivery_address">
<p class="mb-0 text-muted">
<t t-out="delivery_address.street"/><br/>
<t t-out="delivery_address.city"/>,
<t t-out="delivery_address.state_id.code or ''"/>
<t t-out="delivery_address.zip"/>
</p>
</t>
</div>
</div>
<hr/>
<h6>Items Delivered:</h6>
<t t-set="product_lines" t-value="order.order_line.sudo().filtered(lambda l: l.product_id and not l.display_type)"/>
<t t-if="product_lines">
<table class="table table-bordered table-sm mb-0">
<thead class="table-light">
<tr>
<th>Item</th>
<th class="text-center" style="width:60px;">Qty</th>
</tr>
</thead>
<tbody>
<t t-foreach="product_lines" t-as="line">
<tr>
<td>
<t t-out="line.product_id.name"/>
<t t-if="line.product_id.x_fc_adp_device_code">
<small class="text-muted"> (<t t-out="line.product_id.x_fc_adp_device_code"/>)</small>
</t>
</td>
<td class="text-center"><t t-out="int(line.product_uom_qty)"/></td>
</tr>
</t>
</tbody>
</table>
</t>
<t t-else="">
<p class="text-muted mb-0"><i class="fa fa-info-circle me-1"/>No order lines found for this case.</p>
</t>
</div>
</div>
</div>
<div class="col-lg-5">
<!-- Signature Capture Form -->
<div class="card" id="pod-signature-section">
<div class="card-header bg-success text-white">
<h5 class="mb-0"><i class="fa fa-pencil me-2"/>Client Signature</h5>
</div>
<div class="card-body">
<t t-if="has_existing_signature">
<div class="alert alert-warning">
<i class="fa fa-exclamation-triangle me-2"/>
A signature has already been collected for this order.
Submitting a new signature will replace the existing one.
</div>
</t>
<form id="podSignatureForm">
<div class="mb-3">
<label for="client_name" class="form-label">
Client Name <span class="text-danger">*</span>
</label>
<input type="text" class="form-control" id="client_name"
name="client_name" required=""
t-att-value="order.x_fc_pod_client_name or order.partner_id.name"
placeholder="Enter the client's full name"/>
</div>
<div class="mb-3">
<label for="signature_date" class="form-label">
Signature Date
</label>
<input type="date" class="form-control" id="signature_date"
name="signature_date"/>
<script>document.getElementById('signature_date').value = new Date().toISOString().slice(0,10);</script>
</div>
<div class="mb-3">
<label class="form-label">
Signature <span class="text-danger">*</span>
</label>
<div class="border rounded p-2 bg-white">
<canvas id="signature-canvas"
style="width: 100%; height: 200px; border: 1px dashed #ccc; border-radius: 4px; touch-action: none;">
</canvas>
</div>
<div class="d-flex justify-content-between mt-2">
<small class="text-muted">Draw signature above</small>
<button type="button" class="btn btn-sm btn-outline-secondary"
onclick="clearSignature()">
<i class="fa fa-eraser me-1"/>Clear
</button>
</div>
</div>
<div class="d-grid gap-2">
<button type="button" class="btn btn-success btn-lg"
onclick="submitPODSignature()">
<i class="fa fa-check me-2"/>Submit Signature
</button>
<a t-attf-href="#{user_role == 'technician' and '/my/technician/delivery/' + str(order.id) or '/my/sales/case/' + str(order.id)}"
class="btn btn-outline-secondary">
Cancel
</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- POD Status Overlay -->
<style>
.pod-overlay {
display: none;
position: fixed;
top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.65);
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
z-index: 9999;
align-items: center;
justify-content: center;
}
.pod-overlay.show { display: flex; }
.pod-overlay-card {
background: #fff;
border-radius: 20px;
padding: 2.5rem 2rem;
max-width: 420px;
width: 90%;
text-align: center;
animation: podSlideUp 0.3s ease;
box-shadow: 0 8px 32px rgba(0,0,0,0.2);
}
.pod-overlay-icon { font-size: 3.5rem; margin-bottom: 1rem; }
.pod-overlay-title { font-weight: 700; margin-bottom: 0.5rem; }
.pod-overlay-msg { color: #6c757d; margin-bottom: 1.5rem; }
@keyframes podSlideUp {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
@media (prefers-color-scheme: dark) {
.pod-overlay-card { background: #1e1e2e; color: #e0e0e0; }
.pod-overlay-msg { color: #adb5bd; }
}
</style>
<div id="podStatusOverlay" class="pod-overlay">
<div class="pod-overlay-card">
<div class="pod-overlay-icon" id="podOverlayIcon"></div>
<h4 class="pod-overlay-title" id="podOverlayTitle"></h4>
<p class="pod-overlay-msg" id="podOverlayMsg"></p>
<div id="podOverlayActions"></div>
</div>
</div>
<!-- Signature Pad JavaScript -->
<script type="text/javascript">
let signaturePad = null;
let canvas = null;
let ctx = null;
let isDrawing = false;
function showPodOverlay(type, title, message, redirectUrl) {
var overlay = document.getElementById('podStatusOverlay');
var icon = document.getElementById('podOverlayIcon');
var titleEl = document.getElementById('podOverlayTitle');
var msgEl = document.getElementById('podOverlayMsg');
var actions = document.getElementById('podOverlayActions');
if (type === 'success') {
icon.innerHTML = '&lt;i class="fa fa-check-circle text-success">&lt;/i>';
titleEl.className = 'pod-overlay-title text-success';
} else if (type === 'error') {
icon.innerHTML = '&lt;i class="fa fa-exclamation-circle text-danger">&lt;/i>';
titleEl.className = 'pod-overlay-title text-danger';
} else {
icon.innerHTML = '&lt;i class="fa fa-exclamation-triangle text-warning">&lt;/i>';
titleEl.className = 'pod-overlay-title text-warning';
}
titleEl.textContent = title;
msgEl.textContent = message;
actions.innerHTML = '';
if (type === 'success' &amp;&amp; redirectUrl) {
actions.innerHTML = '&lt;a href="' + redirectUrl + '" class="btn btn-success w-100 rounded-pill mb-2">&lt;i class="fa fa-arrow-right me-1">&lt;/i>Continue&lt;/a>' +
'&lt;p class="text-muted small mb-0">Redirecting in &lt;span id="podCountdown">3&lt;/span>s...&lt;/p>';
overlay.classList.add('show');
var sec = 3;
var timer = setInterval(function() {
sec--;
var cd = document.getElementById('podCountdown');
if (cd) cd.textContent = sec;
if (sec &lt;= 0) {
clearInterval(timer);
window.location.href = redirectUrl;
}
}, 1000);
} else {
actions.innerHTML = '&lt;button class="btn btn-outline-secondary w-100 rounded-pill" onclick="closePodOverlay()">OK&lt;/button>';
overlay.classList.add('show');
}
}
function closePodOverlay() {
document.getElementById('podStatusOverlay').classList.remove('show');
}
document.addEventListener('DOMContentLoaded', function() {
initSignaturePad();
// Auto-scroll to signature section so user doesn't have to scroll manually
var sigSection = document.getElementById('pod-signature-section');
if (sigSection) {
setTimeout(function() {
sigSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
}, 300);
}
});
function initSignaturePad() {
canvas = document.getElementById('signature-canvas');
if (!canvas) return;
ctx = canvas.getContext('2d');
// Set canvas size
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * 2;
canvas.height = rect.height * 2;
ctx.scale(2, 2);
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.lineWidth = 2;
ctx.strokeStyle = '#000';
// Mouse events
canvas.addEventListener('mousedown', startDrawing);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', stopDrawing);
canvas.addEventListener('mouseout', stopDrawing);
// Touch events
canvas.addEventListener('touchstart', handleTouchStart, {passive: false});
canvas.addEventListener('touchmove', handleTouchMove, {passive: false});
canvas.addEventListener('touchend', stopDrawing);
}
function getPos(e) {
const rect = canvas.getBoundingClientRect();
if (e.touches) {
return {
x: e.touches[0].clientX - rect.left,
y: e.touches[0].clientY - rect.top
};
}
return {
x: e.clientX - rect.left,
y: e.clientY - rect.top
};
}
function startDrawing(e) {
isDrawing = true;
const pos = getPos(e);
ctx.beginPath();
ctx.moveTo(pos.x, pos.y);
}
function draw(e) {
if (!isDrawing) return;
const pos = getPos(e);
ctx.lineTo(pos.x, pos.y);
ctx.stroke();
}
function stopDrawing() {
isDrawing = false;
}
function handleTouchStart(e) {
e.preventDefault();
startDrawing(e);
}
function handleTouchMove(e) {
e.preventDefault();
draw(e);
}
function clearSignature() {
if (ctx) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
}
function isCanvasEmpty() {
const blank = document.createElement('canvas');
blank.width = canvas.width;
blank.height = canvas.height;
return canvas.toDataURL() === blank.toDataURL();
}
function submitPODSignature() {
const clientName = document.getElementById('client_name').value.trim();
const signatureDate = document.getElementById('signature_date').value;
if (!clientName) {
showPodOverlay('warning', 'Missing Information', 'Please enter the client name.');
return;
}
if (isCanvasEmpty()) {
showPodOverlay('warning', 'Missing Signature', 'Please draw a signature before submitting.');
return;
}
const signatureData = canvas.toDataURL('image/png');
// Show loading state
const btn = document.querySelector('button[onclick="submitPODSignature()"]');
const originalText = btn.innerHTML;
btn.innerHTML = '<i class="fa fa-spinner fa-spin me-2"></i>Saving...';
btn.disabled = true;
// Submit via AJAX
fetch('<t t-out="'/my/pod/' + str(order.id) + '/sign'"/>', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
jsonrpc: '2.0',
method: 'call',
params: {
client_name: clientName,
signature_data: signatureData,
signature_date: signatureDate || null
},
id: Math.floor(Math.random() * 1000000)
})
})
.then(response => response.json())
.then(data => {
if (data.result &amp;&amp; data.result.success) {
showPodOverlay('success', 'Signature Saved!', 'Proof of Delivery has been recorded successfully.', data.result.redirect_url);
} else {
showPodOverlay('error', 'Error', data.result?.error || 'An unknown error occurred.');
btn.innerHTML = originalText;
btn.disabled = false;
}
})
.catch(error => {
console.error('Error:', error);
showPodOverlay('error', 'Connection Error', 'An error occurred. Please check your connection and try again.');
btn.innerHTML = originalText;
btn.disabled = false;
});
}
</script>
</t>
</template>
<!-- ============================================================ -->
<!-- TASK-LEVEL POD SIGNATURE (works for shadow + regular tasks) -->
<!-- ============================================================ -->
<template id="portal_task_pod_signature" name="Task POD Signature Capture">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="False"/>
<t t-set="no_breadcrumbs" t-value="True"/>
<div class="container mt-3">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="/my/home">Home</a></li>
<li class="breadcrumb-item"><a href="/my/technician">Dashboard</a></li>
<li class="breadcrumb-item"><a href="/my/technician/tasks">Tasks</a></li>
<li class="breadcrumb-item"><a t-attf-href="/my/technician/task/#{task.id}"><t t-out="task.name"/></a></li>
<li class="breadcrumb-item active" aria-current="page">Collect POD Signature</li>
</ol>
</nav>
</div>
<div class="container py-4">
<div class="row mb-4">
<div class="col-12">
<h2>
<i class="fa fa-pencil-square-o me-2"/>
Proof of Delivery - <t t-out="task.name"/>
</h2>
</div>
</div>
<div class="row">
<div class="col-lg-7">
<div class="card mb-4">
<div class="card-header bg-primary text-white">
<h5 class="mb-0"><i class="fa fa-truck me-2"/>Delivery Summary</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<p><strong>Client:</strong> <t t-out="task.client_display_name or 'N/A'"/></p>
<p><strong>Task:</strong> <t t-out="task.name"/></p>
<p><strong>Type:</strong>
<t t-out="dict(task._fields['task_type'].selection).get(task.task_type, '')"/>
</p>
</div>
<div class="col-md-6">
<p><strong>Delivery Address:</strong></p>
<p class="mb-0 text-muted">
<t t-out="task.address_display or 'No address'"/>
</p>
<t t-if="task.scheduled_date">
<p class="mt-2"><strong>Scheduled:</strong>
<t t-out="task.scheduled_date" t-options="{'widget': 'date'}"/>
<t t-out="task.time_start_display"/> - <t t-out="task.time_end_display"/>
</p>
</t>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-5">
<div class="card" id="task-pod-signature-section">
<div class="card-header bg-success text-white">
<h5 class="mb-0"><i class="fa fa-pencil me-2"/>Client Signature</h5>
</div>
<div class="card-body">
<t t-if="has_existing_signature">
<div class="alert alert-warning">
<i class="fa fa-exclamation-triangle me-2"/>
A signature has already been collected. Submitting a new one will replace it.
</div>
</t>
<form id="taskPodSignatureForm">
<div class="mb-3">
<label for="task_client_name" class="form-label">
Client Name <span class="text-danger">*</span>
</label>
<input type="text" class="form-control" id="task_client_name"
name="client_name" required=""
t-att-value="task.pod_client_name or task.client_display_name or ''"
placeholder="Enter the client's full name"/>
</div>
<div class="mb-3">
<label for="task_signature_date" class="form-label">Signature Date</label>
<input type="date" class="form-control" id="task_signature_date" name="signature_date"/>
<script>document.getElementById('task_signature_date').value = new Date().toISOString().slice(0,10);</script>
</div>
<div class="mb-3">
<label class="form-label">Signature <span class="text-danger">*</span></label>
<div class="border rounded p-2 bg-white">
<canvas id="task-signature-canvas"
style="width:100%;height:200px;border:1px dashed #ccc;border-radius:4px;touch-action:none;">
</canvas>
</div>
<div class="d-flex justify-content-between mt-2">
<small class="text-muted">Draw signature above</small>
<button type="button" class="btn btn-sm btn-outline-secondary"
onclick="clearTaskSignature()">
<i class="fa fa-eraser me-1"/>Clear
</button>
</div>
</div>
<div class="d-grid gap-2">
<button type="button" class="btn btn-success btn-lg"
onclick="submitTaskPODSignature()">
<i class="fa fa-check me-2"/>Submit Signature
</button>
<a t-attf-href="/my/technician/task/#{task.id}" class="btn btn-outline-secondary">Cancel</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<style>
.tpod-overlay { display:none; position:fixed; top:0; left:0; width:100%; height:100%;
background:rgba(0,0,0,0.65); backdrop-filter:blur(4px); -webkit-backdrop-filter:blur(4px);
z-index:9999; align-items:center; justify-content:center; }
.tpod-overlay.show { display:flex; }
.tpod-overlay-card { background:#fff; border-radius:20px; padding:2.5rem 2rem;
max-width:420px; width:90%; text-align:center; animation:tpodSlideUp 0.3s ease;
box-shadow:0 8px 32px rgba(0,0,0,0.2); }
.tpod-overlay-icon { font-size:3.5rem; margin-bottom:1rem; }
@keyframes tpodSlideUp { from { opacity:0; transform:translateY(30px); } to { opacity:1; transform:translateY(0); } }
</style>
<div id="taskPodOverlay" class="tpod-overlay">
<div class="tpod-overlay-card">
<div class="tpod-overlay-icon" id="tpodIcon"></div>
<h4 id="tpodTitle" style="font-weight:700;"></h4>
<p id="tpodMsg" style="color:#6c757d;margin-bottom:1.5rem;"></p>
<div id="tpodActions"></div>
</div>
</div>
<script type="text/javascript">
var tCanvas, tCtx, tIsDrawing = false;
function showTaskPodOverlay(type, title, msg, url) {
var ov = document.getElementById('taskPodOverlay');
document.getElementById('tpodIcon').innerHTML = type === 'success'
? '&lt;i class="fa fa-check-circle text-success">&lt;/i>'
: '&lt;i class="fa fa-exclamation-circle text-danger">&lt;/i>';
document.getElementById('tpodTitle').textContent = title;
document.getElementById('tpodTitle').className = type === 'success' ? 'text-success' : 'text-danger';
document.getElementById('tpodMsg').textContent = msg;
var acts = document.getElementById('tpodActions');
if (type === 'success' &amp;&amp; url) {
acts.innerHTML = '&lt;a href="' + url + '" class="btn btn-success w-100 rounded-pill mb-2">Continue&lt;/a>' +
'&lt;p class="text-muted small mb-0">Redirecting in &lt;span id="tpodCD">3&lt;/span>s...&lt;/p>';
ov.classList.add('show');
var s = 3, t = setInterval(function() { s--; var c = document.getElementById('tpodCD');
if (c) c.textContent = s; if (s &lt;= 0) { clearInterval(t); window.location.href = url; } }, 1000);
} else {
acts.innerHTML = '&lt;button class="btn btn-outline-secondary w-100 rounded-pill" onclick="document.getElementById(\'taskPodOverlay\').classList.remove(\'show\')">OK&lt;/button>';
ov.classList.add('show');
}
}
document.addEventListener('DOMContentLoaded', function() {
tCanvas = document.getElementById('task-signature-canvas');
if (!tCanvas) return;
tCtx = tCanvas.getContext('2d');
var r = tCanvas.getBoundingClientRect();
tCanvas.width = r.width * 2; tCanvas.height = r.height * 2;
tCtx.scale(2, 2); tCtx.lineCap = 'round'; tCtx.lineJoin = 'round';
tCtx.lineWidth = 2; tCtx.strokeStyle = '#000';
tCanvas.addEventListener('mousedown', tStart);
tCanvas.addEventListener('mousemove', tDraw);
tCanvas.addEventListener('mouseup', tStop);
tCanvas.addEventListener('mouseout', tStop);
tCanvas.addEventListener('touchstart', function(e) { e.preventDefault(); tStart(e); }, {passive:false});
tCanvas.addEventListener('touchmove', function(e) { e.preventDefault(); tDraw(e); }, {passive:false});
tCanvas.addEventListener('touchend', tStop);
var sec = document.getElementById('task-pod-signature-section');
if (sec) setTimeout(function() { sec.scrollIntoView({behavior:'smooth', block:'start'}); }, 300);
});
function tPos(e) {
var r = tCanvas.getBoundingClientRect();
if (e.touches) return { x: e.touches[0].clientX - r.left, y: e.touches[0].clientY - r.top };
return { x: e.clientX - r.left, y: e.clientY - r.top };
}
function tStart(e) { tIsDrawing = true; var p = tPos(e); tCtx.beginPath(); tCtx.moveTo(p.x, p.y); }
function tDraw(e) { if (!tIsDrawing) return; var p = tPos(e); tCtx.lineTo(p.x, p.y); tCtx.stroke(); }
function tStop() { tIsDrawing = false; }
function clearTaskSignature() { if (tCtx) tCtx.clearRect(0, 0, tCanvas.width, tCanvas.height); }
function submitTaskPODSignature() {
var name = document.getElementById('task_client_name').value.trim();
var sigDate = document.getElementById('task_signature_date').value;
if (!name) { showTaskPodOverlay('error', 'Missing Information', 'Please enter the client name.'); return; }
var blank = document.createElement('canvas');
blank.width = tCanvas.width; blank.height = tCanvas.height;
if (tCanvas.toDataURL() === blank.toDataURL()) {
showTaskPodOverlay('error', 'Missing Signature', 'Please draw a signature before submitting.'); return;
}
var sigData = tCanvas.toDataURL('image/png');
var btn = document.querySelector('button[onclick="submitTaskPODSignature()"]');
var orig = btn.innerHTML; btn.innerHTML = '&lt;i class="fa fa-spinner fa-spin me-2">&lt;/i>Saving...'; btn.disabled = true;
fetch('<t t-out="'/my/technician/task/' + str(task.id) + '/pod/sign'"/>', {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ jsonrpc:'2.0', method:'call',
params: { client_name: name, signature_data: sigData, signature_date: sigDate || null },
id: Math.floor(Math.random()*1000000) })
}).then(function(r) { return r.json(); }).then(function(d) {
if (d.result &amp;&amp; d.result.success) {
showTaskPodOverlay('success', 'Signature Saved!', 'Proof of Delivery recorded.', d.result.redirect_url);
} else {
showTaskPodOverlay('error', 'Error', d.result?.error || 'Unknown error');
btn.innerHTML = orig; btn.disabled = false;
}
}).catch(function() {
showTaskPodOverlay('error', 'Connection Error', 'Please check your connection.');
btn.innerHTML = orig; btn.disabled = false;
});
}
</script>
</t>
</template>
</odoo>