- Add portal gradient branding settings with 4 presets (Green/Teal, Blue/Purple, Sunset Orange, Dark Slate) and custom color picker - Live preview in settings, onchange updates colors reactively - Dynamic gradient applied across portal home, CSS, and card elements - Fix after photos visibility (conditional on resolved=yes) - Fix technician section gating on portal repair form - Move Create Sale Order button to form header for visibility - Fix portal home row width inconsistency (xpath target change) Co-authored-by: Cursor <cursoragent@cursor.com>
3703 lines
229 KiB
XML
3703 lines
229 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>
|
|
|
|
<!-- 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>
|
|
</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&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&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&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&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 & 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 & 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>
|
|
|
|
<!-- 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 & 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 ''}&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 ''}&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 ''}&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"/>
|
|
</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]}&search=#{search}&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 & 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 & 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 & 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 && 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 & 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 = '<i class="fa fa-check-circle text-success"></i>';
|
|
titleEl.className = 'pod-overlay-title text-success';
|
|
} else if (type === 'error') {
|
|
icon.innerHTML = '<i class="fa fa-exclamation-circle text-danger"></i>';
|
|
titleEl.className = 'pod-overlay-title text-danger';
|
|
} else {
|
|
icon.innerHTML = '<i class="fa fa-exclamation-triangle text-warning"></i>';
|
|
titleEl.className = 'pod-overlay-title text-warning';
|
|
}
|
|
titleEl.textContent = title;
|
|
msgEl.textContent = message;
|
|
|
|
actions.innerHTML = '';
|
|
if (type === 'success' && redirectUrl) {
|
|
actions.innerHTML = '<a href="' + redirectUrl + '" class="btn btn-success w-100 rounded-pill mb-2"><i class="fa fa-arrow-right me-1"></i>Continue</a>' +
|
|
'<p class="text-muted small mb-0">Redirecting in <span id="podCountdown">3</span>s...</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 <= 0) {
|
|
clearInterval(timer);
|
|
window.location.href = redirectUrl;
|
|
}
|
|
}, 1000);
|
|
} else {
|
|
actions.innerHTML = '<button class="btn btn-outline-secondary w-100 rounded-pill" onclick="closePodOverlay()">OK</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 && 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>
|
|
|
|
</odoo>
|