updates
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': 'Fusion Authorizer & Sales Portal',
|
||||
'version': '19.0.2.0.9',
|
||||
'version': '19.0.2.2.0',
|
||||
'category': 'Sales/Portal',
|
||||
'summary': 'Portal for Authorizers (OTs) and Sales Reps with Assessment Forms',
|
||||
'description': """
|
||||
@@ -66,6 +66,7 @@ This module provides external portal access for:
|
||||
'views/res_partner_views.xml',
|
||||
'views/sale_order_views.xml',
|
||||
'views/assessment_views.xml',
|
||||
'views/loaner_checkout_views.xml',
|
||||
'views/pdf_template_views.xml',
|
||||
# Portal Templates
|
||||
'views/portal_templates.xml',
|
||||
@@ -75,6 +76,7 @@ This module provides external portal access for:
|
||||
'views/portal_accessibility_forms.xml',
|
||||
'views/portal_technician_templates.xml',
|
||||
'views/portal_book_assessment.xml',
|
||||
'views/portal_repair_form.xml',
|
||||
],
|
||||
'assets': {
|
||||
'web.assets_backend': [
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
|
||||
from . import portal_main
|
||||
from . import portal_assessment
|
||||
from . import pdf_editor
|
||||
from . import pdf_editor
|
||||
from . import portal_repair
|
||||
182
fusion_authorizer_portal/controllers/portal_repair.py
Normal file
182
fusion_authorizer_portal/controllers/portal_repair.py
Normal file
@@ -0,0 +1,182 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2024-2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
|
||||
from odoo import http, _, fields
|
||||
from odoo.http import request
|
||||
import base64
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LTCRepairPortal(http.Controller):
|
||||
|
||||
def _is_password_required(self):
|
||||
password = request.env['ir.config_parameter'].sudo().get_param(
|
||||
'fusion_claims.ltc_form_password', ''
|
||||
)
|
||||
return bool(password and password.strip())
|
||||
|
||||
def _process_photos(self, file_list, repair):
|
||||
attachment_ids = []
|
||||
for photo in file_list:
|
||||
if photo and photo.filename:
|
||||
data = photo.read()
|
||||
if data:
|
||||
attachment = request.env['ir.attachment'].sudo().create({
|
||||
'name': photo.filename,
|
||||
'datas': base64.b64encode(data),
|
||||
'res_model': 'fusion.ltc.repair',
|
||||
'res_id': repair.id,
|
||||
})
|
||||
attachment_ids.append(attachment.id)
|
||||
return attachment_ids
|
||||
|
||||
def _is_authenticated(self):
|
||||
if not request.env.user._is_public():
|
||||
return True
|
||||
if not self._is_password_required():
|
||||
return True
|
||||
return request.session.get('ltc_form_authenticated', False)
|
||||
|
||||
@http.route('/repair-form', type='http', auth='public', website=True,
|
||||
sitemap=False)
|
||||
def repair_form(self, **kw):
|
||||
if not self._is_authenticated():
|
||||
return request.render(
|
||||
'fusion_authorizer_portal.portal_ltc_repair_password',
|
||||
{'error': kw.get('auth_error', False)}
|
||||
)
|
||||
|
||||
facilities = request.env['fusion.ltc.facility'].sudo().search(
|
||||
[('active', '=', True)], order='name'
|
||||
)
|
||||
is_technician = not request.env.user._is_public() and request.env.user.has_group(
|
||||
'base.group_user'
|
||||
)
|
||||
return request.render(
|
||||
'fusion_authorizer_portal.portal_ltc_repair_form',
|
||||
{
|
||||
'facilities': facilities,
|
||||
'today': fields.Date.today(),
|
||||
'is_technician': is_technician,
|
||||
}
|
||||
)
|
||||
|
||||
@http.route('/repair-form/auth', type='http', auth='public',
|
||||
website=True, methods=['POST'], csrf=True)
|
||||
def repair_form_auth(self, **kw):
|
||||
stored_password = request.env['ir.config_parameter'].sudo().get_param(
|
||||
'fusion_claims.ltc_form_password', ''
|
||||
).strip()
|
||||
|
||||
entered_password = (kw.get('password', '') or '').strip()
|
||||
|
||||
if stored_password and entered_password == stored_password:
|
||||
request.session['ltc_form_authenticated'] = True
|
||||
return request.redirect('/repair-form')
|
||||
|
||||
return request.render(
|
||||
'fusion_authorizer_portal.portal_ltc_repair_password',
|
||||
{'error': True}
|
||||
)
|
||||
|
||||
@http.route('/repair-form/submit', type='http', auth='public',
|
||||
website=True, methods=['POST'], csrf=True)
|
||||
def repair_form_submit(self, **kw):
|
||||
if not self._is_authenticated():
|
||||
return request.redirect('/repair-form')
|
||||
|
||||
try:
|
||||
facility_id = int(kw.get('facility_id', 0))
|
||||
if not facility_id:
|
||||
return request.redirect('/repair-form?error=facility')
|
||||
|
||||
vals = {
|
||||
'facility_id': facility_id,
|
||||
'client_name': kw.get('client_name', '').strip(),
|
||||
'room_number': kw.get('room_number', '').strip(),
|
||||
'product_serial': kw.get('product_serial', '').strip(),
|
||||
'issue_description': kw.get('issue_description', '').strip(),
|
||||
'issue_reported_date': kw.get('issue_reported_date') or fields.Date.today(),
|
||||
'is_emergency': kw.get('is_emergency') == 'on',
|
||||
'poa_name': kw.get('poa_name', '').strip() or False,
|
||||
'poa_phone': kw.get('poa_phone', '').strip() or False,
|
||||
'source': 'portal_form',
|
||||
}
|
||||
|
||||
if not vals['client_name']:
|
||||
return request.redirect('/repair-form?error=name')
|
||||
if not vals['issue_description']:
|
||||
return request.redirect('/repair-form?error=description')
|
||||
|
||||
before_files = request.httprequest.files.getlist('before_photos')
|
||||
has_before = any(f and f.filename for f in before_files)
|
||||
if not has_before:
|
||||
return request.redirect('/repair-form?error=photos')
|
||||
|
||||
repair = request.env['fusion.ltc.repair'].sudo().create(vals)
|
||||
|
||||
before_ids = self._process_photos(before_files, repair)
|
||||
if before_ids:
|
||||
repair.sudo().write({
|
||||
'before_photo_ids': [(6, 0, before_ids)],
|
||||
})
|
||||
|
||||
after_files = request.httprequest.files.getlist('after_photos')
|
||||
after_ids = self._process_photos(after_files, repair)
|
||||
if after_ids:
|
||||
repair.sudo().write({
|
||||
'after_photo_ids': [(6, 0, after_ids)],
|
||||
})
|
||||
|
||||
resolved = kw.get('resolved') == 'yes'
|
||||
if resolved:
|
||||
resolution = kw.get('resolution_description', '').strip()
|
||||
if resolution:
|
||||
repair.sudo().write({
|
||||
'resolution_description': resolution,
|
||||
'issue_fixed_date': fields.Date.today(),
|
||||
})
|
||||
|
||||
repair.sudo().activity_schedule(
|
||||
'mail.mail_activity_data_todo',
|
||||
summary=_('New repair request from portal: %s', repair.display_client_name),
|
||||
note=_(
|
||||
'Repair request submitted via portal form for %s at %s (Room %s).',
|
||||
repair.display_client_name,
|
||||
repair.facility_id.name,
|
||||
repair.room_number or 'N/A',
|
||||
),
|
||||
)
|
||||
|
||||
ip_address = request.httprequest.headers.get(
|
||||
'X-Forwarded-For', request.httprequest.remote_addr
|
||||
)
|
||||
if ip_address and ',' in ip_address:
|
||||
ip_address = ip_address.split(',')[0].strip()
|
||||
|
||||
try:
|
||||
request.env['fusion.ltc.form.submission'].sudo().create({
|
||||
'form_type': 'repair',
|
||||
'repair_id': repair.id,
|
||||
'facility_id': facility_id,
|
||||
'client_name': vals['client_name'],
|
||||
'room_number': vals['room_number'],
|
||||
'product_serial': vals['product_serial'],
|
||||
'is_emergency': vals['is_emergency'],
|
||||
'ip_address': ip_address or '',
|
||||
'status': 'processed',
|
||||
})
|
||||
except Exception:
|
||||
_logger.warning('Failed to log form submission', exc_info=True)
|
||||
|
||||
return request.render(
|
||||
'fusion_authorizer_portal.portal_ltc_repair_thank_you',
|
||||
{'repair': repair}
|
||||
)
|
||||
|
||||
except Exception:
|
||||
_logger.exception('Error submitting LTC repair form')
|
||||
return request.redirect('/repair-form?error=server')
|
||||
@@ -7,4 +7,5 @@ from . import adp_document
|
||||
from . import assessment
|
||||
from . import accessibility_assessment
|
||||
from . import sale_order
|
||||
from . import loaner_checkout
|
||||
from . import pdf_template
|
||||
27
fusion_authorizer_portal/models/loaner_checkout.py
Normal file
27
fusion_authorizer_portal/models/loaner_checkout.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
|
||||
class FusionLoanerCheckoutAssessment(models.Model):
|
||||
_inherit = 'fusion.loaner.checkout'
|
||||
|
||||
assessment_id = fields.Many2one(
|
||||
'fusion.assessment',
|
||||
string='Assessment',
|
||||
ondelete='set null',
|
||||
tracking=True,
|
||||
help='Assessment during which this loaner was issued',
|
||||
)
|
||||
|
||||
def action_view_assessment(self):
|
||||
self.ensure_one()
|
||||
if not self.assessment_id:
|
||||
return
|
||||
return {
|
||||
'name': self.assessment_id.display_name,
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'fusion.assessment',
|
||||
'view_mode': 'form',
|
||||
'res_id': self.assessment_id.id,
|
||||
}
|
||||
@@ -6,15 +6,26 @@
|
||||
<field name="name">fusion.assessment.tree</field>
|
||||
<field name="model">fusion.assessment</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Assessments" decoration-info="state == 'draft'" decoration-warning="state == 'pending_signature'" decoration-success="state == 'completed'" decoration-muted="state == 'cancelled'">
|
||||
<list string="Assessments" default_order="assessment_date desc, id desc"
|
||||
decoration-info="state == 'draft'"
|
||||
decoration-warning="state == 'pending_signature'"
|
||||
decoration-success="state == 'completed'"
|
||||
decoration-muted="state == 'cancelled'">
|
||||
<field name="reference"/>
|
||||
<field name="client_name"/>
|
||||
<field name="equipment_type" optional="show"/>
|
||||
<field name="client_type" optional="show"/>
|
||||
<field name="assessment_date"/>
|
||||
<field name="sales_rep_id"/>
|
||||
<field name="authorizer_id"/>
|
||||
<field name="state" widget="badge" decoration-info="state == 'draft'" decoration-warning="state == 'pending_signature'" decoration-success="state == 'completed'" decoration-danger="state == 'cancelled'"/>
|
||||
<field name="signatures_complete" widget="boolean"/>
|
||||
<field name="sale_order_id"/>
|
||||
<field name="reason_for_application" optional="hide"/>
|
||||
<field name="state" widget="badge"
|
||||
decoration-info="state == 'draft'"
|
||||
decoration-warning="state == 'pending_signature'"
|
||||
decoration-success="state == 'completed'"
|
||||
decoration-danger="state == 'cancelled'"/>
|
||||
<field name="signatures_complete" widget="boolean" optional="show"/>
|
||||
<field name="sale_order_id" optional="show"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
@@ -26,124 +37,310 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="Assessment">
|
||||
<header>
|
||||
<button name="action_mark_pending_signature" type="object" string="Mark Pending Signature" class="btn-primary" invisible="state != 'draft'"/>
|
||||
<button name="action_complete" type="object" string="Complete Assessment" class="btn-success" invisible="state not in ['draft', 'pending_signature']"/>
|
||||
<button name="action_cancel" type="object" string="Cancel" invisible="state in ['completed', 'cancelled']"/>
|
||||
<button name="action_reset_draft" type="object" string="Reset to Draft" invisible="state != 'cancelled'"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,pending_signature,completed"/>
|
||||
<button name="action_mark_pending_signature" type="object"
|
||||
string="Mark Pending Signature" class="btn-primary"
|
||||
invisible="state != 'draft'"/>
|
||||
<button name="action_complete" type="object"
|
||||
string="Complete Assessment" class="btn-success"
|
||||
invisible="state not in ['draft', 'pending_signature']"/>
|
||||
<button name="action_complete_express" type="object"
|
||||
string="Express Complete" class="btn-warning"
|
||||
invisible="state not in ['draft', 'pending_signature']"
|
||||
confirm="This will complete the assessment without requiring signatures. Continue?"/>
|
||||
<button name="action_cancel" type="object"
|
||||
string="Cancel"
|
||||
invisible="state in ['completed', 'cancelled']"/>
|
||||
<button name="action_reset_draft" type="object"
|
||||
string="Reset to Draft"
|
||||
invisible="state != 'cancelled'"/>
|
||||
<field name="state" widget="statusbar"
|
||||
statusbar_visible="draft,pending_signature,completed"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="action_view_documents" type="object" class="oe_stat_button" icon="fa-file-pdf-o">
|
||||
<button name="action_view_documents" type="object"
|
||||
class="oe_stat_button" icon="fa-file-pdf-o">
|
||||
<field name="document_count" string="Documents" widget="statinfo"/>
|
||||
</button>
|
||||
<button name="action_view_sale_order" type="object" class="oe_stat_button" icon="fa-shopping-cart" invisible="not sale_order_id">
|
||||
<button name="action_view_sale_order" type="object"
|
||||
class="oe_stat_button" icon="fa-shopping-cart"
|
||||
invisible="not sale_order_id">
|
||||
<span class="o_stat_text">Sale Order</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="oe_title">
|
||||
|
||||
<widget name="web_ribbon" title="Completed" bg_color="text-bg-success"
|
||||
invisible="state != 'completed'"/>
|
||||
<widget name="web_ribbon" title="Cancelled" bg_color="text-bg-danger"
|
||||
invisible="state != 'cancelled'"/>
|
||||
|
||||
<div class="oe_title mb-3">
|
||||
<h1>
|
||||
<field name="reference" readonly="1"/>
|
||||
<field name="reference" readonly="1" class="me-3"/>
|
||||
</h1>
|
||||
<h2 class="text-muted" invisible="not client_name">
|
||||
<field name="client_name" readonly="1"/>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ============ TOP SUMMARY ============ -->
|
||||
<group>
|
||||
<group string="Client Information">
|
||||
<field name="client_name"/>
|
||||
<field name="client_first_name"/>
|
||||
<field name="client_last_name"/>
|
||||
<field name="client_phone"/>
|
||||
<field name="client_mobile"/>
|
||||
<field name="client_email"/>
|
||||
<field name="client_dob"/>
|
||||
<field name="client_health_card"/>
|
||||
<group string="Equipment">
|
||||
<field name="equipment_type"/>
|
||||
<field name="rollator_type" invisible="equipment_type != 'rollator'"/>
|
||||
<field name="wheelchair_type" invisible="equipment_type != 'wheelchair'"/>
|
||||
<field name="powerchair_type" invisible="equipment_type != 'powerchair'"/>
|
||||
<field name="client_type"/>
|
||||
<field name="reason_for_application"/>
|
||||
<field name="previous_funding_date"
|
||||
invisible="reason_for_application not in ['replace_status','replace_size','replace_worn','replace_lost','replace_stolen','replace_damaged','replace_no_longer_meets']"/>
|
||||
</group>
|
||||
<group string="Address">
|
||||
<field name="client_street"/>
|
||||
<field name="client_city"/>
|
||||
<field name="client_state"/>
|
||||
<field name="client_postal_code"/>
|
||||
<field name="client_country_id"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<group>
|
||||
<group string="Assessment Details">
|
||||
<group string="Assessment Info">
|
||||
<field name="assessment_date"/>
|
||||
<field name="assessment_location"/>
|
||||
<field name="assessment_location_notes"/>
|
||||
</group>
|
||||
<group string="Participants">
|
||||
<field name="sales_rep_id"/>
|
||||
<field name="authorizer_id"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="create_new_partner"/>
|
||||
<field name="sale_order_id" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<group>
|
||||
<group string="Client References">
|
||||
<field name="client_reference_1"/>
|
||||
<field name="client_reference_2"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
|
||||
<notebook>
|
||||
<page string="Wheelchair Specifications" name="specs">
|
||||
|
||||
<!-- ============ CLIENT INFORMATION ============ -->
|
||||
<page string="Client" name="client_info">
|
||||
<group>
|
||||
<group string="Seat Measurements">
|
||||
<group string="Personal Details">
|
||||
<field name="client_first_name"/>
|
||||
<field name="client_middle_name"/>
|
||||
<field name="client_last_name"/>
|
||||
<field name="client_dob"/>
|
||||
<field name="client_phone"/>
|
||||
<field name="client_mobile"/>
|
||||
<field name="client_email" widget="email"/>
|
||||
</group>
|
||||
<group string="Health Card">
|
||||
<field name="client_health_card"/>
|
||||
<field name="client_health_card_version"/>
|
||||
<field name="client_weight"/>
|
||||
<field name="client_height"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group string="Address">
|
||||
<field name="client_street"/>
|
||||
<field name="client_unit"/>
|
||||
<field name="client_city"/>
|
||||
<field name="client_state"/>
|
||||
<field name="client_postal_code"/>
|
||||
<field name="client_country_id"/>
|
||||
</group>
|
||||
<group string="References & Linking">
|
||||
<field name="client_reference_1"/>
|
||||
<field name="client_reference_2"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="create_new_partner"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
|
||||
<!-- ============ MEASUREMENTS & SPECS ============ -->
|
||||
<page string="Measurements" name="measurements">
|
||||
|
||||
<!-- Rollator Measurements -->
|
||||
<group string="Rollator Measurements"
|
||||
invisible="equipment_type != 'rollator'">
|
||||
<group>
|
||||
<field name="rollator_handle_height"/>
|
||||
<field name="rollator_seat_height"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="rollator_addons" placeholder="e.g. Basket, Tray, Backrest pad..."/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<!-- Wheelchair / Powerchair Measurements -->
|
||||
<group string="Seat Measurements"
|
||||
invisible="equipment_type not in ['wheelchair', 'powerchair']">
|
||||
<group>
|
||||
<field name="seat_width"/>
|
||||
<field name="seat_depth"/>
|
||||
<field name="seat_to_floor_height"/>
|
||||
<field name="seat_angle"/>
|
||||
</group>
|
||||
<group string="Back & Arms">
|
||||
<group>
|
||||
<field name="back_height"/>
|
||||
<field name="back_angle"/>
|
||||
<field name="armrest_height"/>
|
||||
<field name="footrest_length"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group string="Overall Dimensions">
|
||||
|
||||
<group string="Leg & Foot"
|
||||
invisible="equipment_type not in ['wheelchair', 'powerchair']">
|
||||
<group>
|
||||
<field name="footrest_length"/>
|
||||
<field name="legrest_length"/>
|
||||
<field name="cane_height"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<group string="Overall Dimensions"
|
||||
invisible="equipment_type not in ['wheelchair', 'powerchair']">
|
||||
<group>
|
||||
<field name="overall_width"/>
|
||||
<field name="overall_length"/>
|
||||
<field name="overall_height"/>
|
||||
</group>
|
||||
<group string="Client Measurements">
|
||||
<field name="client_weight"/>
|
||||
<field name="client_height"/>
|
||||
</group>
|
||||
|
||||
</page>
|
||||
|
||||
<!-- ============ OPTIONS & ACCESSORIES ============ -->
|
||||
<page string="Options" name="options" invisible="equipment_type not in ['wheelchair', 'powerchair']">
|
||||
|
||||
<group invisible="equipment_type != 'wheelchair'">
|
||||
<group string="Frame Options">
|
||||
<field name="frame_options" nolabel="1"
|
||||
placeholder="e.g. Recliner Option, Dynamic Tilt Frame, Titanium Frame"/>
|
||||
</group>
|
||||
<group string="Wheel Options">
|
||||
<field name="wheel_options" nolabel="1"
|
||||
placeholder="e.g. Quick Release Axle, Mag Wheels, Anti-Tip..."/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<group invisible="equipment_type != 'wheelchair'">
|
||||
<group string="Legrest Accessories">
|
||||
<field name="legrest_options" nolabel="1"
|
||||
placeholder="e.g. Elevating Legrest, Swing Away..."/>
|
||||
</group>
|
||||
<group string="Additional ADP Options">
|
||||
<field name="additional_adp_options" nolabel="1"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<group invisible="equipment_type != 'powerchair'">
|
||||
<group string="Powerchair Options">
|
||||
<field name="powerchair_options" nolabel="1"/>
|
||||
</group>
|
||||
<group string="Specialty Controls">
|
||||
<field name="specialty_controls" nolabel="1"
|
||||
placeholder="Rationale required for specialty components"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<group>
|
||||
<group string="Seating">
|
||||
<field name="seatbelt_type"/>
|
||||
<field name="cushion_info"/>
|
||||
<field name="backrest_info"/>
|
||||
</group>
|
||||
<group string="Additional Customization">
|
||||
<field name="additional_customization" nolabel="1"
|
||||
placeholder="Free-form notes for any customization..."/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
|
||||
|
||||
<!-- ============ PRODUCT TYPES ============ -->
|
||||
<page string="Product Types" name="products">
|
||||
<group>
|
||||
<group>
|
||||
<group string="Cushion">
|
||||
<field name="cushion_type"/>
|
||||
<field name="cushion_notes"/>
|
||||
<field name="cushion_notes" placeholder="Cushion details..."
|
||||
invisible="not cushion_type"/>
|
||||
</group>
|
||||
<group string="Backrest">
|
||||
<field name="backrest_type"/>
|
||||
<field name="backrest_notes"/>
|
||||
<field name="backrest_notes" placeholder="Backrest details..."
|
||||
invisible="not backrest_type"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group string="Frame">
|
||||
<field name="frame_type"/>
|
||||
<field name="frame_notes" placeholder="Frame details..."
|
||||
invisible="not frame_type"/>
|
||||
</group>
|
||||
<group string="Wheels">
|
||||
<field name="wheel_type"/>
|
||||
<field name="wheel_notes" placeholder="Wheel details..."
|
||||
invisible="not wheel_type"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
|
||||
<!-- ============ CLINICAL NOTES ============ -->
|
||||
<page string="Clinical Notes" name="needs">
|
||||
<group>
|
||||
<group>
|
||||
<field name="diagnosis" placeholder="Relevant medical diagnosis or conditions..."/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="frame_type"/>
|
||||
<field name="frame_notes"/>
|
||||
<field name="wheel_type"/>
|
||||
<field name="wheel_notes"/>
|
||||
<field name="mobility_notes" placeholder="Document mobility needs and challenges..."/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="accessibility_notes" placeholder="Accessibility requirements and home environment..."/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="special_requirements" placeholder="Any special requirements or customizations..."/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
|
||||
<page string="Needs & Requirements" name="needs">
|
||||
|
||||
<!-- ============ KEY DATES ============ -->
|
||||
<page string="Dates" name="dates">
|
||||
<group>
|
||||
<field name="diagnosis"/>
|
||||
<field name="mobility_notes"/>
|
||||
<field name="accessibility_notes"/>
|
||||
<field name="special_requirements"/>
|
||||
<group string="Assessment Period">
|
||||
<field name="assessment_start_date"/>
|
||||
<field name="assessment_end_date"/>
|
||||
</group>
|
||||
<group string="Authorization">
|
||||
<field name="claim_authorization_date"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
|
||||
|
||||
<!-- ============ CONSENT & DECLARATION (PAGE 11) ============ -->
|
||||
<page string="Consent & Declaration" name="consent">
|
||||
<group>
|
||||
<group string="Consent Details">
|
||||
<field name="consent_signed_by"/>
|
||||
<field name="consent_declaration_accepted"/>
|
||||
<field name="consent_date"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<group string="Agent Details"
|
||||
invisible="consent_signed_by != 'agent'">
|
||||
<group>
|
||||
<field name="agent_relationship"/>
|
||||
<field name="agent_first_name"/>
|
||||
<field name="agent_middle_initial"/>
|
||||
<field name="agent_last_name"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="agent_street_number"/>
|
||||
<field name="agent_street_name"/>
|
||||
<field name="agent_unit"/>
|
||||
<field name="agent_city"/>
|
||||
<field name="agent_province"/>
|
||||
<field name="agent_postal_code"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Agent Contact"
|
||||
invisible="consent_signed_by != 'agent'">
|
||||
<group>
|
||||
<field name="agent_home_phone"/>
|
||||
<field name="agent_business_phone"/>
|
||||
<field name="agent_phone_ext"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
|
||||
<!-- ============ SIGNATURES ============ -->
|
||||
<page string="Signatures" name="signatures">
|
||||
<group>
|
||||
<group string="Page 11 - Authorizer Signature">
|
||||
@@ -158,13 +355,17 @@
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<field name="signatures_complete"/>
|
||||
<field name="signatures_complete" readonly="1"/>
|
||||
<field name="signed_page_11_pdf" filename="signed_page_11_pdf_filename"
|
||||
invisible="not signed_page_11_pdf"/>
|
||||
<field name="signed_page_11_pdf_filename" invisible="1"/>
|
||||
</group>
|
||||
</page>
|
||||
|
||||
|
||||
<!-- ============ DOCUMENTS ============ -->
|
||||
<page string="Documents" name="documents">
|
||||
<field name="document_ids">
|
||||
<list string="Documents">
|
||||
<list string="Documents" editable="bottom">
|
||||
<field name="document_type"/>
|
||||
<field name="filename"/>
|
||||
<field name="revision"/>
|
||||
@@ -173,27 +374,21 @@
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
|
||||
|
||||
<!-- ============ COMMENTS ============ -->
|
||||
<page string="Comments" name="comments">
|
||||
<field name="comment_ids">
|
||||
<list string="Comments">
|
||||
<field name="create_date"/>
|
||||
<list string="Comments" editable="bottom">
|
||||
<field name="create_date" string="Date"/>
|
||||
<field name="author_id"/>
|
||||
<field name="comment"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
|
||||
</notebook>
|
||||
|
||||
<group invisible="not sale_order_id">
|
||||
<field name="sale_order_id"/>
|
||||
</group>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids"/>
|
||||
<field name="activity_ids"/>
|
||||
<field name="message_ids"/>
|
||||
</div>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
@@ -207,8 +402,10 @@
|
||||
<field name="reference"/>
|
||||
<field name="client_name"/>
|
||||
<field name="client_email"/>
|
||||
<field name="client_health_card"/>
|
||||
<field name="sales_rep_id"/>
|
||||
<field name="authorizer_id"/>
|
||||
<field name="sale_order_id"/>
|
||||
<separator/>
|
||||
<filter string="In Progress" name="draft" domain="[('state', '=', 'draft')]"/>
|
||||
<filter string="Pending Signature" name="pending" domain="[('state', '=', 'pending_signature')]"/>
|
||||
@@ -216,11 +413,19 @@
|
||||
<filter string="Cancelled" name="cancelled" domain="[('state', '=', 'cancelled')]"/>
|
||||
<separator/>
|
||||
<filter string="My Assessments" name="my_assessments" domain="[('sales_rep_id', '=', uid)]"/>
|
||||
<filter string="Has Sale Order" name="has_so" domain="[('sale_order_id', '!=', False)]"/>
|
||||
<filter string="Signatures Pending" name="sigs_pending" domain="[('signatures_complete', '=', False), ('state', '!=', 'cancelled')]"/>
|
||||
<separator/>
|
||||
<filter string="Wheelchair" name="filter_wheelchair" domain="[('equipment_type', '=', 'wheelchair')]"/>
|
||||
<filter string="Powerchair" name="filter_powerchair" domain="[('equipment_type', '=', 'powerchair')]"/>
|
||||
<filter string="Rollator" name="filter_rollator" domain="[('equipment_type', '=', 'rollator')]"/>
|
||||
<separator/>
|
||||
<filter string="Status" name="group_state" context="{'group_by': 'state'}"/>
|
||||
<filter string="Equipment Type" name="group_equipment" context="{'group_by': 'equipment_type'}"/>
|
||||
<filter string="Client Type" name="group_client_type" context="{'group_by': 'client_type'}"/>
|
||||
<filter string="Sales Rep" name="group_sales_rep" context="{'group_by': 'sales_rep_id'}"/>
|
||||
<filter string="Authorizer" name="group_authorizer" context="{'group_by': 'authorizer_id'}"/>
|
||||
<filter string="Date" name="group_date" context="{'group_by': 'assessment_date:month'}"/>
|
||||
<filter string="Month" name="group_date" context="{'group_by': 'assessment_date:month'}"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
@@ -237,8 +442,8 @@
|
||||
Create your first assessment
|
||||
</p>
|
||||
<p>
|
||||
Assessments are used to record wheelchair specifications and client needs.
|
||||
Once completed, they will create a draft sale order for review.
|
||||
Assessments record wheelchair, powerchair, and rollator specifications
|
||||
along with client needs. Once completed, a draft sale order is created.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
@@ -248,7 +453,7 @@
|
||||
name="Assessments"
|
||||
parent="fusion_claims.menu_adp_claims_root"
|
||||
sequence="42"/>
|
||||
|
||||
|
||||
<menuitem id="menu_fusion_assessment_list"
|
||||
name="All Assessments"
|
||||
parent="menu_fusion_assessment_root"
|
||||
|
||||
25
fusion_authorizer_portal/views/loaner_checkout_views.xml
Normal file
25
fusion_authorizer_portal/views/loaner_checkout_views.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Add Assessment button and field to Loaner Checkout form -->
|
||||
<record id="view_fusion_loaner_checkout_form_assessment" model="ir.ui.view">
|
||||
<field name="name">fusion.loaner.checkout.form.assessment</field>
|
||||
<field name="model">fusion.loaner.checkout</field>
|
||||
<field name="inherit_id" ref="fusion_claims.view_fusion_loaner_checkout_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//button[@name='action_view_partner']" position="before">
|
||||
<button name="action_view_assessment" type="object"
|
||||
class="oe_stat_button" icon="fa-clipboard"
|
||||
invisible="not assessment_id">
|
||||
<div class="o_field_widget o_stat_info">
|
||||
<span class="o_stat_text">Assessment</span>
|
||||
</div>
|
||||
</button>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='sale_order_id']" position="after">
|
||||
<field name="assessment_id"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
320
fusion_authorizer_portal/views/portal_repair_form.xml
Normal file
320
fusion_authorizer_portal/views/portal_repair_form.xml
Normal file
@@ -0,0 +1,320 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<template id="portal_ltc_repair_form"
|
||||
name="LTC Repair Form">
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap" class="oe_structure">
|
||||
<section class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
<div class="text-center mb-4">
|
||||
<h1>LTC Repairs Request</h1>
|
||||
<p class="lead text-muted">
|
||||
Submit a repair request for medical equipment at your facility.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<t t-if="request.params.get('error') == 'facility'">
|
||||
<div class="alert alert-danger">Please select a facility.</div>
|
||||
</t>
|
||||
<t t-if="request.params.get('error') == 'name'">
|
||||
<div class="alert alert-danger">Patient name is required.</div>
|
||||
</t>
|
||||
<t t-if="request.params.get('error') == 'description'">
|
||||
<div class="alert alert-danger">Issue description is required.</div>
|
||||
</t>
|
||||
<t t-if="request.params.get('error') == 'photos'">
|
||||
<div class="alert alert-danger">At least one before photo is required.</div>
|
||||
</t>
|
||||
<t t-if="request.params.get('error') == 'server'">
|
||||
<div class="alert alert-danger">
|
||||
An error occurred. Please try again or contact us.
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<form action="/repair-form/submit" method="POST"
|
||||
enctype="multipart/form-data"
|
||||
class="card shadow-sm">
|
||||
<input type="hidden" name="csrf_token"
|
||||
t-att-value="request.csrf_token()"/>
|
||||
<div class="card-body p-4">
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input"
|
||||
id="is_emergency" name="is_emergency"/>
|
||||
<label class="form-check-label fw-bold text-danger"
|
||||
for="is_emergency">
|
||||
Is this an Emergency Repair Request?
|
||||
</label>
|
||||
</div>
|
||||
<small class="text-muted">
|
||||
Emergency visits may be chargeable at an extra rate.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="facility_id" class="form-label fw-bold">
|
||||
Facility Location *
|
||||
</label>
|
||||
<select name="facility_id" id="facility_id"
|
||||
class="form-select" required="required">
|
||||
<option value="">-- Select Facility --</option>
|
||||
<t t-foreach="facilities" t-as="fac">
|
||||
<option t-att-value="fac.id">
|
||||
<t t-esc="fac.name"/>
|
||||
</option>
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="client_name" class="form-label fw-bold">
|
||||
Patient Name *
|
||||
</label>
|
||||
<input type="text" name="client_name" id="client_name"
|
||||
class="form-control" required="required"
|
||||
placeholder="Enter patient name"/>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="room_number" class="form-label fw-bold">
|
||||
Room Number *
|
||||
</label>
|
||||
<input type="text" name="room_number" id="room_number"
|
||||
class="form-control" required="required"
|
||||
placeholder="e.g. 305"/>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="issue_description" class="form-label fw-bold">
|
||||
Describe the Issue *
|
||||
</label>
|
||||
<textarea name="issue_description" id="issue_description"
|
||||
class="form-control" rows="4"
|
||||
required="required"
|
||||
placeholder="Please provide as much detail as possible about the issue."/>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="issue_reported_date" class="form-label fw-bold">
|
||||
Issue Reported Date *
|
||||
</label>
|
||||
<input type="date" name="issue_reported_date"
|
||||
id="issue_reported_date"
|
||||
class="form-control" required="required"
|
||||
t-att-value="today"/>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="product_serial" class="form-label fw-bold">
|
||||
Product Serial # *
|
||||
</label>
|
||||
<input type="text" name="product_serial"
|
||||
id="product_serial"
|
||||
class="form-control" required="required"
|
||||
placeholder="Serial number is required for repairs"/>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="before_photos" class="form-label fw-bold">
|
||||
Before Photos (Reported Condition) *
|
||||
</label>
|
||||
<input type="file" name="before_photos" id="before_photos"
|
||||
class="form-control" multiple="multiple"
|
||||
accept="image/*" required="required"/>
|
||||
<small class="text-muted">
|
||||
At least 1 photo required. Up to 4 photos (max 10MB each).
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h5>Family / POA Contact</h5>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="poa_name" class="form-label">
|
||||
Relative/POA Name
|
||||
</label>
|
||||
<input type="text" name="poa_name" id="poa_name"
|
||||
class="form-control"
|
||||
placeholder="Contact name"/>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="poa_phone" class="form-label">
|
||||
Relative/POA Phone
|
||||
</label>
|
||||
<input type="tel" name="poa_phone" id="poa_phone"
|
||||
class="form-control"
|
||||
placeholder="Phone number"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<t t-if="is_technician">
|
||||
<hr/>
|
||||
|
||||
<div class="bg-light p-3 rounded mb-3">
|
||||
<p class="fw-bold text-muted mb-2">
|
||||
FOR TECHNICIAN USE ONLY
|
||||
</p>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">
|
||||
Has the issue been resolved?
|
||||
</label>
|
||||
<div class="form-check form-check-inline">
|
||||
<input type="radio" name="resolved" value="yes"
|
||||
class="form-check-input" id="resolved_yes"/>
|
||||
<label class="form-check-label"
|
||||
for="resolved_yes">Yes</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input type="radio" name="resolved" value="no"
|
||||
class="form-check-input" id="resolved_no"
|
||||
checked="checked"/>
|
||||
<label class="form-check-label"
|
||||
for="resolved_no">No</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3" id="resolution_section"
|
||||
style="display: none;">
|
||||
<label for="resolution_description"
|
||||
class="form-label">
|
||||
Describe the Solution
|
||||
</label>
|
||||
<textarea name="resolution_description"
|
||||
id="resolution_description"
|
||||
class="form-control" rows="3"
|
||||
placeholder="How was the issue resolved?"/>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="after_photos" class="form-label fw-bold">
|
||||
After Photos (Completed Repair)
|
||||
</label>
|
||||
<input type="file" name="after_photos" id="after_photos"
|
||||
class="form-control" multiple="multiple"
|
||||
accept="image/*"/>
|
||||
<small class="text-muted">
|
||||
Optional. Attach after repair is completed. Up to 4 photos (max 10MB each).
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<div class="text-center mt-4">
|
||||
<button type="submit" class="btn btn-primary btn-lg px-5">
|
||||
Submit Repair Request
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var section = document.getElementById('resolution_section');
|
||||
if (!section) return;
|
||||
var radios = document.querySelectorAll('input[name="resolved"]');
|
||||
radios.forEach(function(r) {
|
||||
r.addEventListener('change', function() {
|
||||
section.style.display = this.value === 'yes' ? 'block' : 'none';
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="portal_ltc_repair_thank_you"
|
||||
name="Repair Request Submitted">
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap" class="oe_structure">
|
||||
<section class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-6 text-center">
|
||||
<div class="mb-4">
|
||||
<i class="fa fa-check-circle text-success"
|
||||
style="font-size: 4rem;"/>
|
||||
</div>
|
||||
<h2>Thank You!</h2>
|
||||
<p class="lead text-muted">
|
||||
Your repair request has been submitted successfully.
|
||||
</p>
|
||||
<div class="card mt-4">
|
||||
<div class="card-body">
|
||||
<p><strong>Reference:</strong>
|
||||
<t t-esc="repair.name"/></p>
|
||||
<p><strong>Facility:</strong>
|
||||
<t t-esc="repair.facility_id.name"/></p>
|
||||
<p><strong>Patient:</strong>
|
||||
<t t-esc="repair.display_client_name"/></p>
|
||||
<p><strong>Room:</strong>
|
||||
<t t-esc="repair.room_number"/></p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/repair-form" class="btn btn-outline-primary mt-4">
|
||||
Submit Another Request
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="portal_ltc_repair_password"
|
||||
name="LTC Repair Form - Password">
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap" class="oe_structure">
|
||||
<section class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-5">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body p-4 text-center">
|
||||
<div class="mb-3">
|
||||
<i class="fa fa-lock text-primary"
|
||||
style="font-size: 3rem;"/>
|
||||
</div>
|
||||
<h3>LTC Repairs Request</h3>
|
||||
<p class="text-muted">
|
||||
Please enter the access password to continue.
|
||||
</p>
|
||||
|
||||
<t t-if="error">
|
||||
<div class="alert alert-danger">
|
||||
Incorrect password. Please try again.
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<form action="/repair-form/auth" method="POST"
|
||||
class="mt-3">
|
||||
<input type="hidden" name="csrf_token"
|
||||
t-att-value="request.csrf_token()"/>
|
||||
<div class="mb-3">
|
||||
<input type="password" name="password"
|
||||
class="form-control form-control-lg text-center"
|
||||
placeholder="Enter password"
|
||||
minlength="4" required="required"
|
||||
autofocus="autofocus"/>
|
||||
</div>
|
||||
<button type="submit"
|
||||
class="btn btn-primary btn-lg w-100">
|
||||
Access Form
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
@@ -171,12 +171,12 @@
|
||||
|
||||
<!-- Quick Links -->
|
||||
<div class="row g-2 mb-4">
|
||||
<div class="col-6">
|
||||
<div class="col-4">
|
||||
<a href="/my/technician/tasks" class="btn btn-outline-primary w-100 py-3">
|
||||
<i class="fa fa-list me-1"/>All Tasks
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="col-4">
|
||||
<a href="/my/technician/tomorrow" class="btn btn-outline-secondary w-100 py-3">
|
||||
<i class="fa fa-calendar me-1"/>Tomorrow
|
||||
<t t-if="tomorrow_count">
|
||||
@@ -184,6 +184,11 @@
|
||||
</t>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<a href="/repair-form" class="btn btn-outline-warning w-100 py-3">
|
||||
<i class="fa fa-wrench me-1"/>Repair Form
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- My Start Location -->
|
||||
|
||||
Reference in New Issue
Block a user