refactor(fusion_portal): rename from fusion_authorizer_portal + modern photo cards on accessibility selector
Rename module fusion_authorizer_portal -> fusion_portal everywhere: manifest/assets, controllers, models, views, JS (odoo.define + asset URLs), migration MODULE constants; plus cross-module refs in fusion_schedule, fusion_repairs, fusion_quotations (depends + inherit_id) and the pdf_filler import in fusion_claims. Add rename_module.sql for the one-time in-place DB rename (ir_module_module, ir_model_data, ir_ui_view.key, ir_module_module_dependency) required on installed envs before -u fusion_portal. Document the rename gotcha as rule 16 in CLAUDE.md. Redesign the Accessibility Assessment selector: replace Font Awesome icon tiles with photo-banner cards using 7 optimized images (1000x750 PNG -> 800x600 JPEG, ~8MB -> 488KB), per-type colour accent bar + centered pill button, hover lift/zoom. Images ship as module static files so they deploy/sync with the module. Drop the regenerable graphify-out cache from the module. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
463
fusion_portal/views/assessment_views.xml
Normal file
463
fusion_portal/views/assessment_views.xml
Normal file
@@ -0,0 +1,463 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Assessment Tree View -->
|
||||
<record id="view_fusion_assessment_tree" model="ir.ui.view">
|
||||
<field name="name">fusion.assessment.tree</field>
|
||||
<field name="model">fusion.assessment</field>
|
||||
<field name="arch" type="xml">
|
||||
<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="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>
|
||||
|
||||
<!-- Assessment Form View -->
|
||||
<record id="view_fusion_assessment_form" model="ir.ui.view">
|
||||
<field name="name">fusion.assessment.form</field>
|
||||
<field name="model">fusion.assessment</field>
|
||||
<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_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">
|
||||
<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">
|
||||
<span class="o_stat_text">Sale Order</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<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" 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="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="Assessment Info">
|
||||
<field name="assessment_date"/>
|
||||
<field name="assessment_location"/>
|
||||
<field name="assessment_location_notes"/>
|
||||
<field name="sales_rep_id"/>
|
||||
<field name="authorizer_id"/>
|
||||
<field name="sale_order_id" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<notebook>
|
||||
|
||||
<!-- ============ CLIENT INFORMATION ============ -->
|
||||
<page string="Client" name="client_info">
|
||||
<group>
|
||||
<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>
|
||||
<field name="back_height"/>
|
||||
<field name="back_angle"/>
|
||||
<field name="armrest_height"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<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>
|
||||
|
||||
</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 string="Cushion">
|
||||
<field name="cushion_type"/>
|
||||
<field name="cushion_notes" placeholder="Cushion details..."
|
||||
invisible="not cushion_type"/>
|
||||
</group>
|
||||
<group string="Backrest">
|
||||
<field name="backrest_type"/>
|
||||
<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="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>
|
||||
|
||||
<!-- ============ KEY DATES ============ -->
|
||||
<page string="Dates" name="dates">
|
||||
<group>
|
||||
<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">
|
||||
<field name="signature_page_11" widget="image" class="oe_avatar"/>
|
||||
<field name="signature_page_11_name"/>
|
||||
<field name="signature_page_11_date"/>
|
||||
</group>
|
||||
<group string="Page 12 - Client Signature">
|
||||
<field name="signature_page_12" widget="image" class="oe_avatar"/>
|
||||
<field name="signature_page_12_name"/>
|
||||
<field name="signature_page_12_date"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<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" editable="bottom">
|
||||
<field name="document_type"/>
|
||||
<field name="filename"/>
|
||||
<field name="revision"/>
|
||||
<field name="upload_date"/>
|
||||
<field name="uploaded_by"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
|
||||
<!-- ============ COMMENTS ============ -->
|
||||
<page string="Comments" name="comments">
|
||||
<field name="comment_ids">
|
||||
<list string="Comments" editable="bottom">
|
||||
<field name="create_date" string="Date"/>
|
||||
<field name="author_id"/>
|
||||
<field name="comment"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
|
||||
</notebook>
|
||||
</sheet>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Assessment Search View -->
|
||||
<record id="view_fusion_assessment_search" model="ir.ui.view">
|
||||
<field name="name">fusion.assessment.search</field>
|
||||
<field name="model">fusion.assessment</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Assessments">
|
||||
<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')]"/>
|
||||
<filter string="Completed" name="completed" domain="[('state', '=', 'completed')]"/>
|
||||
<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="Month" name="group_date" context="{'group_by': 'assessment_date:month'}"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Assessment Action -->
|
||||
<record id="action_fusion_assessment" model="ir.actions.act_window">
|
||||
<field name="name">Assessments</field>
|
||||
<field name="res_model">fusion.assessment</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_fusion_assessment_search"/>
|
||||
<field name="context">{'search_default_my_assessments': 1}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create your first assessment
|
||||
</p>
|
||||
<p>
|
||||
Assessments record wheelchair, powerchair, and rollator specifications
|
||||
along with client needs. Once completed, a draft sale order is created.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Menu Items -->
|
||||
<menuitem id="menu_fusion_assessment_root"
|
||||
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"
|
||||
action="action_fusion_assessment"
|
||||
sequence="10"/>
|
||||
|
||||
</odoo>
|
||||
25
fusion_portal/views/loaner_checkout_views.xml
Normal file
25
fusion_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_loaners_management.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>
|
||||
171
fusion_portal/views/pdf_template_views.xml
Normal file
171
fusion_portal/views/pdf_template_views.xml
Normal file
@@ -0,0 +1,171 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- PDF Template - Form View -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="fusion_pdf_template_form" model="ir.ui.view">
|
||||
<field name="name">fusion.pdf.template.form</field>
|
||||
<field name="model">fusion.pdf.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="PDF Template">
|
||||
<header>
|
||||
<button name="action_activate" string="Activate" type="object"
|
||||
class="btn-primary"
|
||||
invisible="state != 'draft'"/>
|
||||
<button name="action_archive" string="Archive" type="object"
|
||||
class="btn-secondary"
|
||||
invisible="state != 'active'"/>
|
||||
<button name="action_reset_draft" string="Reset to Draft" type="object"
|
||||
class="btn-secondary"
|
||||
invisible="state != 'archived'"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,active"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<h1>
|
||||
<field name="name" placeholder="e.g., ADP Page 11 - Consent"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="category"/>
|
||||
<field name="version"/>
|
||||
<field name="page_count"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="pdf_file" filename="pdf_filename"/>
|
||||
<field name="pdf_filename" invisible="1"/>
|
||||
<field name="field_count"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<div class="alert alert-info" role="alert" style="margin: 10px 0;">
|
||||
<strong>How to use:</strong>
|
||||
1. Upload the agency's PDF form above.
|
||||
2. Upload page preview images (screenshots/photos of each page) in the Previews tab.
|
||||
3. Click "Open Field Editor" to visually position fields on the PDF.
|
||||
4. Activate the template when ready.
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2 mb-3">
|
||||
<button name="action_generate_previews" string="Generate Page Previews"
|
||||
type="object" class="btn-secondary"
|
||||
icon="fa-image"
|
||||
invisible="not pdf_file"/>
|
||||
<button name="action_open_field_editor" string="Open Field Editor"
|
||||
type="object" class="btn-primary"
|
||||
icon="fa-pencil-square-o"
|
||||
invisible="not pdf_file"/>
|
||||
</div>
|
||||
|
||||
<notebook>
|
||||
<page string="Fields" name="fields">
|
||||
<field name="field_ids">
|
||||
<list editable="bottom">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="page"/>
|
||||
<field name="name"/>
|
||||
<field name="label"/>
|
||||
<field name="field_type"/>
|
||||
<field name="field_key"/>
|
||||
<field name="pos_x"/>
|
||||
<field name="pos_y"/>
|
||||
<field name="width"/>
|
||||
<field name="height"/>
|
||||
<field name="font_size"/>
|
||||
<field name="font_name"/>
|
||||
<field name="default_value"/>
|
||||
<field name="is_active"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Page Previews" name="previews">
|
||||
<field name="preview_ids">
|
||||
<list editable="bottom">
|
||||
<field name="page"/>
|
||||
<field name="image" widget="image" options="{'size': [200, 200]}"/>
|
||||
<field name="image_filename"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Notes" name="notes">
|
||||
<field name="notes" placeholder="Usage notes, which assessments use this template..."/>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- PDF Template - List View -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="fusion_pdf_template_list" model="ir.ui.view">
|
||||
<field name="name">fusion.pdf.template.list</field>
|
||||
<field name="model">fusion.pdf.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<list>
|
||||
<field name="name"/>
|
||||
<field name="category"/>
|
||||
<field name="version"/>
|
||||
<field name="page_count"/>
|
||||
<field name="field_count"/>
|
||||
<field name="state" widget="badge"
|
||||
decoration-success="state == 'active'"
|
||||
decoration-info="state == 'draft'"
|
||||
decoration-muted="state == 'archived'"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- PDF Template - Search View -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="fusion_pdf_template_search" model="ir.ui.view">
|
||||
<field name="name">fusion.pdf.template.search</field>
|
||||
<field name="model">fusion.pdf.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="name"/>
|
||||
<field name="category"/>
|
||||
<filter name="active_templates" string="Active" domain="[('state', '=', 'active')]"/>
|
||||
<filter name="draft_templates" string="Draft" domain="[('state', '=', 'draft')]"/>
|
||||
<separator/>
|
||||
<filter name="group_category" string="Agency" context="{'group_by': 'category'}"/>
|
||||
<filter name="group_state" string="Status" context="{'group_by': 'state'}"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- PDF Template - Action -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="action_fusion_pdf_template" model="ir.actions.act_window">
|
||||
<field name="name">PDF Templates</field>
|
||||
<field name="res_model">fusion.pdf.template</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="fusion_pdf_template_search"/>
|
||||
<field name="context">{'search_default_active_templates': 1}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create your first PDF template
|
||||
</p>
|
||||
<p>
|
||||
Upload a funding agency's PDF form, position fields on it using the
|
||||
visual editor, and generate filled PDFs automatically from assessment data.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- Menu Item (under Fusion Claims > Configuration) -->
|
||||
<!-- ============================================================ -->
|
||||
<!-- Direct under Fusion Claims > Configuration -->
|
||||
<menuitem id="menu_fusion_pdf_templates"
|
||||
name="PDF Templates"
|
||||
parent="fusion_claims.menu_adp_config"
|
||||
action="action_fusion_pdf_template"
|
||||
sequence="40"/>
|
||||
|
||||
</odoo>
|
||||
1222
fusion_portal/views/portal_accessibility_forms.xml
Normal file
1222
fusion_portal/views/portal_accessibility_forms.xml
Normal file
File diff suppressed because it is too large
Load Diff
712
fusion_portal/views/portal_accessibility_templates.xml
Normal file
712
fusion_portal/views/portal_accessibility_templates.xml
Normal file
@@ -0,0 +1,712 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 2024-2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
Part of the Fusion Claim Assistant product family.
|
||||
|
||||
Accessibility Assessment Portal Templates
|
||||
-->
|
||||
<odoo>
|
||||
<!-- ============================================================= -->
|
||||
<!-- ASSESSMENT TYPE SELECTOR PAGE -->
|
||||
<!-- ============================================================= -->
|
||||
<template id="portal_accessibility_selector" name="Accessibility Assessment Selector">
|
||||
<t t-call="portal.portal_layout">
|
||||
<t t-set="breadcrumbs_searchbar" t-value="True"/>
|
||||
<t t-set="no_breadcrumbs" t-value="True"/>
|
||||
|
||||
<div class="container py-4">
|
||||
<!-- Custom Breadcrumb -->
|
||||
<nav aria-label="breadcrumb" class="mb-4">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/my">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">Accessibility Assessment</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h2 class="mb-4">
|
||||
<i class="fa fa-wheelchair text-primary"></i>
|
||||
Accessibility Assessment
|
||||
</h2>
|
||||
<p class="text-muted mb-4">
|
||||
Select the type of accessibility assessment you want to perform.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Stair Lifts -->
|
||||
<div class="col-md-6 col-lg-4 mb-4">
|
||||
<a href="/my/accessibility/stairlift/straight" class="card h-100 shadow-sm fp-acc-card text-decoration-none" style="--fp-acc-accent: #0d6efd; --fp-acc-fg: #ffffff;">
|
||||
<div class="fp-acc-thumb">
|
||||
<img src="/fusion_portal/static/src/img/accessibility/straight-stair-lift.jpg" alt="Straight Stair Lift" loading="lazy"/>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title fp-acc-title">Straight Stair Lift</h5>
|
||||
<p class="card-text text-muted small">
|
||||
Standard stair lift for straight staircases.
|
||||
Includes track length calculation.
|
||||
</p>
|
||||
<span class="fp-acc-btn">
|
||||
<i class="fa fa-plus-circle"></i> Start Assessment
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4 mb-4">
|
||||
<a href="/my/accessibility/stairlift/curved" class="card h-100 shadow-sm fp-acc-card text-decoration-none" style="--fp-acc-accent: #17a2b8; --fp-acc-fg: #ffffff;">
|
||||
<div class="fp-acc-thumb">
|
||||
<img src="/fusion_portal/static/src/img/accessibility/curved-stair-lift.jpg" alt="Curved Stair Lift" loading="lazy"/>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title fp-acc-title">Curved Stair Lift</h5>
|
||||
<p class="card-text text-muted small">
|
||||
Custom curved stair lift with parking options.
|
||||
Includes curve and step calculations.
|
||||
</p>
|
||||
<span class="fp-acc-btn">
|
||||
<i class="fa fa-plus-circle"></i> Start Assessment
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- VPL -->
|
||||
<div class="col-md-6 col-lg-4 mb-4">
|
||||
<a href="/my/accessibility/vpl" class="card h-100 shadow-sm fp-acc-card text-decoration-none" style="--fp-acc-accent: #198754; --fp-acc-fg: #ffffff;">
|
||||
<div class="fp-acc-thumb">
|
||||
<img src="/fusion_portal/static/src/img/accessibility/vertical-platform-lift.jpg" alt="Vertical Platform Lift" loading="lazy"/>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title fp-acc-title">Vertical Platform Lift</h5>
|
||||
<p class="card-text text-muted small">
|
||||
VPL assessment with room dimensions,
|
||||
power requirements, and certification.
|
||||
</p>
|
||||
<span class="fp-acc-btn">
|
||||
<i class="fa fa-plus-circle"></i> Start Assessment
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Ceiling Lift -->
|
||||
<div class="col-md-6 col-lg-4 mb-4">
|
||||
<a href="/my/accessibility/ceiling-lift" class="card h-100 shadow-sm fp-acc-card text-decoration-none" style="--fp-acc-accent: #ffc107; --fp-acc-fg: #212529;">
|
||||
<div class="fp-acc-thumb">
|
||||
<img src="/fusion_portal/static/src/img/accessibility/ceiling-lift.jpg" alt="Ceiling Lift" loading="lazy"/>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title fp-acc-title">Ceiling Lift</h5>
|
||||
<p class="card-text text-muted small">
|
||||
Ceiling lift with track length, movement type,
|
||||
and additional features.
|
||||
</p>
|
||||
<span class="fp-acc-btn">
|
||||
<i class="fa fa-plus-circle"></i> Start Assessment
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Custom Ramp -->
|
||||
<div class="col-md-6 col-lg-4 mb-4">
|
||||
<a href="/my/accessibility/ramp" class="card h-100 shadow-sm fp-acc-card text-decoration-none" style="--fp-acc-accent: #dc3545; --fp-acc-fg: #ffffff;">
|
||||
<div class="fp-acc-thumb">
|
||||
<img src="/fusion_portal/static/src/img/accessibility/custom-ramp.jpg" alt="Custom Ramp" loading="lazy"/>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title fp-acc-title">Custom Ramp</h5>
|
||||
<p class="card-text text-muted small">
|
||||
Ramp with Ontario Building Code compliance.
|
||||
Auto-calculates length and landings.
|
||||
</p>
|
||||
<span class="fp-acc-btn">
|
||||
<i class="fa fa-plus-circle"></i> Start Assessment
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Bathroom Modifications -->
|
||||
<div class="col-md-6 col-lg-4 mb-4">
|
||||
<a href="/my/accessibility/bathroom" class="card h-100 shadow-sm fp-acc-card text-decoration-none" style="--fp-acc-accent: #6c757d; --fp-acc-fg: #ffffff;">
|
||||
<div class="fp-acc-thumb">
|
||||
<img src="/fusion_portal/static/src/img/accessibility/bathroom-modification.jpg" alt="Bathroom Modification" loading="lazy"/>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title fp-acc-title">Bathroom Modification</h5>
|
||||
<p class="card-text text-muted small">
|
||||
General bathroom modifications.
|
||||
Free-form description with photos.
|
||||
</p>
|
||||
<span class="fp-acc-btn">
|
||||
<i class="fa fa-plus-circle"></i> Start Assessment
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Tub Cutout -->
|
||||
<div class="col-md-6 col-lg-4 mb-4">
|
||||
<a href="/my/accessibility/tub-cutout" class="card h-100 shadow-sm fp-acc-card text-decoration-none" style="--fp-acc-accent: #6c5ce7; --fp-acc-fg: #ffffff;">
|
||||
<div class="fp-acc-thumb">
|
||||
<img src="/fusion_portal/static/src/img/accessibility/tub-cutout.jpg" alt="Tub Cutout" loading="lazy"/>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title fp-acc-title">Tub Cutout</h5>
|
||||
<p class="card-text text-muted small">
|
||||
Tub cutout assessment with internal and
|
||||
external height measurements.
|
||||
</p>
|
||||
<span class="fp-acc-btn">
|
||||
<i class="fa fa-plus-circle"></i> Start Assessment
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- View All Assessments Link -->
|
||||
<div class="row mt-4">
|
||||
<div class="col-12 text-center">
|
||||
<a href="/my/accessibility/list" class="btn btn-outline-primary">
|
||||
<i class="fa fa-list"></i> View All Assessments
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ============================================================= -->
|
||||
<!-- ASSESSMENT LIST PAGE -->
|
||||
<!-- ============================================================= -->
|
||||
<template id="portal_accessibility_list" name="Accessibility Assessment List">
|
||||
<t t-call="portal.portal_layout">
|
||||
<t t-set="breadcrumbs_searchbar" t-value="True"/>
|
||||
<t t-set="no_breadcrumbs" t-value="True"/>
|
||||
|
||||
<div class="container py-4">
|
||||
<!-- Custom Breadcrumb -->
|
||||
<nav aria-label="breadcrumb" class="mb-4">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/my">Dashboard</a></li>
|
||||
<li class="breadcrumb-item"><a href="/my/accessibility">Accessibility</a></li>
|
||||
<li class="breadcrumb-item active">All Assessments</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-8">
|
||||
<h2>
|
||||
<i class="fa fa-list text-primary"></i>
|
||||
My Accessibility Assessments
|
||||
</h2>
|
||||
</div>
|
||||
<div class="col-md-4 text-right">
|
||||
<a href="/my/accessibility" class="btn btn-primary">
|
||||
<i class="fa fa-plus"></i> New Assessment
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<t t-if="assessments">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th>Reference</th>
|
||||
<th>Type</th>
|
||||
<th>Client</th>
|
||||
<th>Date</th>
|
||||
<th>Status</th>
|
||||
<th>Sale Order</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<t t-foreach="assessments" t-as="assessment">
|
||||
<tr class="o_portal_my_doc_table">
|
||||
<td>
|
||||
<span t-field="assessment.reference"/>
|
||||
</td>
|
||||
<td>
|
||||
<t t-if="assessment.assessment_type == 'stairlift_straight'">
|
||||
<span class="badge badge-primary">Straight Stair Lift</span>
|
||||
</t>
|
||||
<t t-elif="assessment.assessment_type == 'stairlift_curved'">
|
||||
<span class="badge badge-info">Curved Stair Lift</span>
|
||||
</t>
|
||||
<t t-elif="assessment.assessment_type == 'vpl'">
|
||||
<span class="badge badge-success">VPL</span>
|
||||
</t>
|
||||
<t t-elif="assessment.assessment_type == 'ceiling_lift'">
|
||||
<span class="badge badge-warning">Ceiling Lift</span>
|
||||
</t>
|
||||
<t t-elif="assessment.assessment_type == 'ramp'">
|
||||
<span class="badge badge-danger">Ramp</span>
|
||||
</t>
|
||||
<t t-elif="assessment.assessment_type == 'bathroom'">
|
||||
<span class="badge badge-secondary">Bathroom</span>
|
||||
</t>
|
||||
<t t-elif="assessment.assessment_type == 'tub_cutout'">
|
||||
<span class="badge" style="background: #6c5ce7; color: white;">Tub Cutout</span>
|
||||
</t>
|
||||
</td>
|
||||
<td><span t-field="assessment.client_name"/></td>
|
||||
<td><span t-field="assessment.assessment_date"/></td>
|
||||
<td>
|
||||
<t t-if="assessment.state == 'draft'">
|
||||
<span class="badge badge-secondary">Draft</span>
|
||||
</t>
|
||||
<t t-elif="assessment.state == 'completed'">
|
||||
<span class="badge badge-success">Completed</span>
|
||||
</t>
|
||||
<t t-elif="assessment.state == 'cancelled'">
|
||||
<span class="badge badge-danger">Cancelled</span>
|
||||
</t>
|
||||
</td>
|
||||
<td>
|
||||
<t t-if="assessment.sale_order_id">
|
||||
<a t-attf-href="/my/sales/case/#{assessment.sale_order_id.id}">
|
||||
<span t-field="assessment.sale_order_id.name"/>
|
||||
</a>
|
||||
</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Pager -->
|
||||
<div class="text-center">
|
||||
<t t-call="portal.pager"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-else="">
|
||||
<div class="alert alert-info text-center">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
No accessibility assessments found.
|
||||
<a href="/my/accessibility" class="alert-link">Create your first assessment</a>.
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ============================================================= -->
|
||||
<!-- SHARED FORM COMPONENTS -->
|
||||
<!-- ============================================================= -->
|
||||
|
||||
<!-- Client Information Section (shared across all forms) -->
|
||||
<template id="accessibility_client_info_section" name="Accessibility Client Info Section">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="mb-0"><i class="fa fa-user"></i> Client Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Client Name <span class="text-danger">*</span></label>
|
||||
<input type="text" name="client_name" class="form-control" required="required" placeholder="Full name"/>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Assessment Date</label>
|
||||
<input type="date" name="assessment_date" class="form-control" t-att-value="today"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-8 mb-3">
|
||||
<label class="form-label">Street Address</label>
|
||||
<input type="text" name="client_address" id="client_address" class="form-control address-autocomplete"
|
||||
placeholder="Start typing address..."/>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label">Unit/Apt/Suite</label>
|
||||
<input type="text" name="client_unit" id="client_unit" class="form-control"
|
||||
placeholder="e.g., Unit 5, Apt 302"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label">City</label>
|
||||
<input type="text" name="client_address_city" id="client_address_city" class="form-control" placeholder="City"/>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label">Province</label>
|
||||
<select name="client_address_province" id="client_address_province" class="form-select">
|
||||
<option value="">-- Select --</option>
|
||||
<option value="ON">Ontario</option>
|
||||
<option value="QC">Quebec</option>
|
||||
<option value="BC">British Columbia</option>
|
||||
<option value="AB">Alberta</option>
|
||||
<option value="MB">Manitoba</option>
|
||||
<option value="SK">Saskatchewan</option>
|
||||
<option value="NS">Nova Scotia</option>
|
||||
<option value="NB">New Brunswick</option>
|
||||
<option value="NL">Newfoundland and Labrador</option>
|
||||
<option value="PE">Prince Edward Island</option>
|
||||
<option value="NT">Northwest Territories</option>
|
||||
<option value="YT">Yukon</option>
|
||||
<option value="NU">Nunavut</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label">Postal Code</label>
|
||||
<input type="text" name="client_address_postal" id="client_address_postal" class="form-control" placeholder="A1A 1A1"/>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="client_address_street" id="client_address_street"/>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Phone</label>
|
||||
<input type="tel" name="client_phone" class="form-control" placeholder="(xxx) xxx-xxxx"/>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Email</label>
|
||||
<input type="email" name="client_email" class="form-control" placeholder="email@example.com"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Photo Upload Section (shared across all forms) -->
|
||||
<template id="accessibility_photo_section" name="Accessibility Photo Section">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header" style="background: linear-gradient(135deg, #5ba848 0%, #3a8fb7 60%, #2e7aad 100%); color: white;">
|
||||
<h5 class="mb-0"><i class="fa fa-camera"></i> Photos</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<label class="form-label">Attach Photos</label>
|
||||
<input type="file" id="photo_upload" class="form-control" accept="image/*" multiple="multiple"/>
|
||||
<small class="text-muted">You can select multiple photos. Accepted formats: JPG, PNG, GIF, WEBP</small>
|
||||
</div>
|
||||
</div>
|
||||
<div id="photo_preview" class="row mt-3">
|
||||
<!-- Photo previews will appear here -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Notes Section (shared across all forms) -->
|
||||
<template id="accessibility_notes_section" name="Accessibility Notes Section">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-light">
|
||||
<h5 class="mb-0"><i class="fa fa-sticky-note"></i> General Notes</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<textarea name="notes" class="form-control" rows="3" placeholder="Any additional notes..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Form Submit Buttons -->
|
||||
<template id="accessibility_submit_buttons" name="Accessibility Submit Buttons">
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-between">
|
||||
<a href="/my/accessibility" class="btn btn-outline-secondary">
|
||||
<i class="fa fa-arrow-left"></i> Cancel
|
||||
</a>
|
||||
<div>
|
||||
<button type="button" class="btn btn-outline-primary mr-2" onclick="saveAssessment(false)">
|
||||
<i class="fa fa-save"></i> Save Draft
|
||||
</button>
|
||||
<button type="button" class="btn btn-success" onclick="saveAssessment(true)">
|
||||
<i class="fa fa-check-circle"></i> Complete & Create Sale Order
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Google Maps + Form JavaScript -->
|
||||
<template id="accessibility_form_scripts" name="Accessibility Form Scripts">
|
||||
<!-- Google Maps Places API -->
|
||||
<t t-if="google_maps_api_key">
|
||||
<script t-attf-src="https://maps.googleapis.com/maps/api/js?key=#{google_maps_api_key}&libraries=places&callback=initAddressAutocomplete" async="async" defer="defer"></script>
|
||||
</t>
|
||||
|
||||
<script type="text/javascript">
|
||||
// Photo handling - Main photos
|
||||
var photoDataArray = [];
|
||||
|
||||
// Additional photo arrays for curved stair lift
|
||||
var topLandingPhotos = [];
|
||||
var bottomLandingPhotos = [];
|
||||
var assessmentVideoData = null;
|
||||
var assessmentVideoFilename = null;
|
||||
|
||||
document.getElementById('photo_upload').addEventListener('change', function(e) {
|
||||
var files = e.target.files;
|
||||
var previewContainer = document.getElementById('photo_preview');
|
||||
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
var file = files[i];
|
||||
var reader = new FileReader();
|
||||
|
||||
reader.onload = (function(idx) {
|
||||
return function(e) {
|
||||
photoDataArray.push(e.target.result);
|
||||
|
||||
var col = document.createElement('div');
|
||||
col.className = 'col-6 col-md-3 mb-3';
|
||||
col.innerHTML = '<div class="position-relative">' +
|
||||
'<img src="' + e.target.result + '" class="img-fluid rounded" style="max-height: 150px; object-fit: cover; width: 100%;">' +
|
||||
'<button type="button" class="btn btn-danger btn-sm position-absolute" style="top: 5px; right: 5px;" onclick="removePhoto(' + (photoDataArray.length - 1) + ', this)">' +
|
||||
'<i class="fa fa-times"></i>' +
|
||||
'</button>' +
|
||||
'</div>';
|
||||
previewContainer.appendChild(col);
|
||||
};
|
||||
})(i);
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
|
||||
function removePhoto(index, btn) {
|
||||
photoDataArray[index] = null;
|
||||
btn.closest('.col-6').remove();
|
||||
}
|
||||
|
||||
// Initialize landing photo handlers (for curved stair lift forms)
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Top landing photos
|
||||
var topLandingInput = document.getElementById('top_landing_photos');
|
||||
if (topLandingInput) {
|
||||
topLandingInput.addEventListener('change', function(e) {
|
||||
handleLandingPhotos(e.target.files, 'top_landing_preview', topLandingPhotos);
|
||||
});
|
||||
}
|
||||
|
||||
// Bottom landing photos
|
||||
var bottomLandingInput = document.getElementById('bottom_landing_photos');
|
||||
if (bottomLandingInput) {
|
||||
bottomLandingInput.addEventListener('change', function(e) {
|
||||
handleLandingPhotos(e.target.files, 'bottom_landing_preview', bottomLandingPhotos);
|
||||
});
|
||||
}
|
||||
|
||||
// Video upload
|
||||
var videoInput = document.getElementById('assessment_video');
|
||||
if (videoInput) {
|
||||
videoInput.addEventListener('change', function(e) {
|
||||
handleVideoUpload(e.target.files[0]);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function handleLandingPhotos(files, previewId, photoArray) {
|
||||
var previewContainer = document.getElementById(previewId);
|
||||
if (!previewContainer) return;
|
||||
|
||||
// Clear previous previews
|
||||
previewContainer.innerHTML = '';
|
||||
photoArray.length = 0;
|
||||
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
var file = files[i];
|
||||
var reader = new FileReader();
|
||||
|
||||
reader.onload = (function(arr) {
|
||||
return function(e) {
|
||||
arr.push(e.target.result);
|
||||
|
||||
var col = document.createElement('div');
|
||||
col.className = 'col-3 mb-2';
|
||||
col.innerHTML = '<img src="' + e.target.result + '" class="img-thumbnail" style="max-height: 80px; object-fit: cover;"/>';
|
||||
previewContainer.appendChild(col);
|
||||
};
|
||||
})(photoArray);
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
}
|
||||
|
||||
function handleVideoUpload(file) {
|
||||
if (!file) return;
|
||||
|
||||
var videoPreview = document.getElementById('video_preview');
|
||||
var videoPlayer = document.getElementById('video_player');
|
||||
var compressStatus = document.getElementById('video_compress_status');
|
||||
|
||||
// Check file size (max 100MB)
|
||||
var maxSize = 100 * 1024 * 1024;
|
||||
if (file.size > maxSize) {
|
||||
alert('Video file is too large. Maximum size is 100MB.');
|
||||
document.getElementById('assessment_video').value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
// Show preview
|
||||
if (videoPlayer && videoPreview) {
|
||||
videoPlayer.src = URL.createObjectURL(file);
|
||||
videoPreview.style.display = 'block';
|
||||
}
|
||||
|
||||
// Store video as base64
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
assessmentVideoData = e.target.result;
|
||||
assessmentVideoFilename = file.name;
|
||||
console.log('Video loaded: ' + file.name + ' (' + (file.size / (1024 * 1024)).toFixed(2) + ' MB)');
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
// Address autocomplete
|
||||
function initAddressAutocomplete() {
|
||||
var addressInput = document.getElementById('client_address');
|
||||
if (!addressInput) return;
|
||||
|
||||
var autocomplete = new google.maps.places.Autocomplete(addressInput, {
|
||||
componentRestrictions: { country: 'ca' },
|
||||
types: ['address']
|
||||
});
|
||||
|
||||
autocomplete.addListener('place_changed', function() {
|
||||
var place = autocomplete.getPlace();
|
||||
if (!place.address_components) return;
|
||||
|
||||
var streetNumber = '';
|
||||
var streetName = '';
|
||||
var city = '';
|
||||
var province = '';
|
||||
var postalCode = '';
|
||||
|
||||
for (var i = 0; i < place.address_components.length; i++) {
|
||||
var component = place.address_components[i];
|
||||
var types = component.types;
|
||||
|
||||
if (types.includes('street_number')) {
|
||||
streetNumber = component.long_name;
|
||||
} else if (types.includes('route')) {
|
||||
streetName = component.long_name;
|
||||
} else if (types.includes('locality')) {
|
||||
city = component.long_name;
|
||||
} else if (types.includes('administrative_area_level_1')) {
|
||||
province = component.short_name;
|
||||
} else if (types.includes('postal_code')) {
|
||||
postalCode = component.long_name;
|
||||
}
|
||||
}
|
||||
|
||||
// Update street address (hidden field stores original, visible shows formatted)
|
||||
document.getElementById('client_address_street').value = (streetNumber + ' ' + streetName).trim();
|
||||
|
||||
// Update city field
|
||||
var cityField = document.getElementById('client_address_city');
|
||||
if (cityField) cityField.value = city;
|
||||
|
||||
// Update province select - match by code or name
|
||||
var provinceSelect = document.getElementById('client_address_province');
|
||||
if (provinceSelect) {
|
||||
for (var j = 0; j < provinceSelect.options.length; j++) {
|
||||
var optVal = provinceSelect.options[j].value.toUpperCase();
|
||||
var optText = provinceSelect.options[j].text.toLowerCase();
|
||||
if (optVal === province.toUpperCase() || optText === province.toLowerCase()) {
|
||||
provinceSelect.selectedIndex = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update postal code field
|
||||
var postalField = document.getElementById('client_address_postal');
|
||||
if (postalField) postalField.value = postalCode;
|
||||
});
|
||||
}
|
||||
|
||||
// Fallback if Google Maps not loaded
|
||||
window.initAddressAutocomplete = window.initAddressAutocomplete || function() {};
|
||||
|
||||
// Form submission
|
||||
function saveAssessment(createSaleOrder) {
|
||||
var form = document.getElementById('accessibility_form');
|
||||
var formData = new FormData(form);
|
||||
|
||||
// Convert to JSON
|
||||
var data = {};
|
||||
formData.forEach(function(value, key) {
|
||||
data[key] = value;
|
||||
});
|
||||
|
||||
// Add main photos
|
||||
data.photos = photoDataArray.filter(function(p) { return p !== null; });
|
||||
|
||||
// Add top landing photos (curved stair lift)
|
||||
if (topLandingPhotos.length > 0) {
|
||||
data.top_landing_photos = topLandingPhotos.filter(function(p) { return p !== null; });
|
||||
}
|
||||
|
||||
// Add bottom landing photos (curved stair lift)
|
||||
if (bottomLandingPhotos.length > 0) {
|
||||
data.bottom_landing_photos = bottomLandingPhotos.filter(function(p) { return p !== null; });
|
||||
}
|
||||
|
||||
// Add video (curved stair lift)
|
||||
if (assessmentVideoData) {
|
||||
data.assessment_video = assessmentVideoData;
|
||||
data.assessment_video_filename = assessmentVideoFilename;
|
||||
}
|
||||
|
||||
// Add sale order flag
|
||||
data.create_sale_order = createSaleOrder;
|
||||
|
||||
// Show loading
|
||||
var submitBtns = document.querySelectorAll('button[onclick*="saveAssessment"]');
|
||||
submitBtns.forEach(function(btn) {
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fa fa-spinner fa-spin"></i> Saving...';
|
||||
});
|
||||
|
||||
// Send request
|
||||
fetch('/my/accessibility/save', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
jsonrpc: '2.0',
|
||||
method: 'call',
|
||||
params: data,
|
||||
id: Math.floor(Math.random() * 1000000000)
|
||||
})
|
||||
})
|
||||
.then(function(response) { return response.json(); })
|
||||
.then(function(result) {
|
||||
if (result.result && result.result.success) {
|
||||
// Show success and redirect
|
||||
alert(result.result.message);
|
||||
window.location.href = result.result.redirect_url;
|
||||
} else {
|
||||
var errorMsg = result.result ? result.result.error : 'Unknown error';
|
||||
alert('Error: ' + errorMsg);
|
||||
submitBtns.forEach(function(btn) {
|
||||
btn.disabled = false;
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(function(error) {
|
||||
alert('Error saving assessment: ' + error);
|
||||
submitBtns.forEach(function(btn) {
|
||||
btn.disabled = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
1622
fusion_portal/views/portal_assessment_express.xml
Normal file
1622
fusion_portal/views/portal_assessment_express.xml
Normal file
File diff suppressed because it is too large
Load Diff
172
fusion_portal/views/portal_book_assessment.xml
Normal file
172
fusion_portal/views/portal_book_assessment.xml
Normal file
@@ -0,0 +1,172 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<!-- Public Assessment Booking Page -->
|
||||
<template id="portal_book_assessment" name="Book an Assessment">
|
||||
<t t-call="website.layout">
|
||||
<div class="container py-5" style="max-width: 700px;">
|
||||
|
||||
<!-- Success Message -->
|
||||
<t t-if="success">
|
||||
<div class="alert alert-success text-center" role="alert">
|
||||
<h4 class="alert-heading"><i class="fa fa-check-circle"></i> Assessment Booked!</h4>
|
||||
<p>Thank you! Your assessment has been scheduled. We will confirm the date and time shortly.</p>
|
||||
<p>You will receive a text message confirmation.</p>
|
||||
<hr/>
|
||||
<a href="/book-assessment" class="btn btn-outline-success">Book Another</a>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Error Message -->
|
||||
<t t-if="error">
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<i class="fa fa-exclamation-triangle"></i> <t t-esc="error"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Booking Form -->
|
||||
<t t-if="not success">
|
||||
<div class="text-center mb-4">
|
||||
<h2 style="color: #1a5276;">Book an Accessibility Assessment</h2>
|
||||
<p class="text-muted">Fill out the form below and our team will schedule a home visit.</p>
|
||||
</div>
|
||||
|
||||
<form action="/book-assessment/submit" method="post" class="card shadow-sm">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
<div class="card-body p-4">
|
||||
|
||||
<!-- Assessment Type -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">What type of modification?<span class="text-danger"> *</span></label>
|
||||
<select name="assessment_type" class="form-select" required="">
|
||||
<option value="">Select...</option>
|
||||
<t t-foreach="assessment_types" t-as="atype">
|
||||
<option t-att-value="atype[0]"><t t-esc="atype[1]"/></option>
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Funding Source (2026-04 portal audit fix —
|
||||
required so the generated sale order enters
|
||||
the correct downstream workflow: MOD, ODSP, etc) -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">How is this project being funded?<span class="text-danger"> *</span></label>
|
||||
<select name="funding_source" class="form-select" required="">
|
||||
<option value="">Select...</option>
|
||||
<option value="march_of_dimes">March of Dimes</option>
|
||||
<option value="odsp">ODSP</option>
|
||||
<option value="wsib">WSIB</option>
|
||||
<option value="insurance">Private Insurance</option>
|
||||
<option value="direct_private">Private Pay (Direct)</option>
|
||||
<option value="other">Other</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">
|
||||
This determines which workflow the case enters after the visit.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">What are you looking for?</label>
|
||||
<textarea name="modification_requested" class="form-control" rows="2"
|
||||
placeholder="e.g. Curved stairlift for 2-storey home, bathroom grab bars..."></textarea>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
<h5 class="mb-3" style="color: #1a5276;">Client Information</h5>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Client Name<span class="text-danger"> *</span></label>
|
||||
<input type="text" name="client_name" class="form-control" required="" placeholder="Full name"/>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Phone Number<span class="text-danger"> *</span></label>
|
||||
<input type="tel" name="client_phone" class="form-control" required="" placeholder="e.g. 416-555-1234"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Email</label>
|
||||
<input type="email" name="client_email" class="form-control" placeholder="Optional"/>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Street Address</label>
|
||||
<input type="text" name="client_street" class="form-control" placeholder="e.g. 5 Nottawasaga Cres"/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label">City</label>
|
||||
<input type="text" name="client_city" class="form-control" placeholder="e.g. Brampton"/>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label">Province</label>
|
||||
<input type="text" name="client_province" class="form-control" value="Ontario"/>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label">Postal Code</label>
|
||||
<input type="text" name="client_postal" class="form-control" placeholder="e.g. L6Y 4G1"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
<h5 class="mb-3" style="color: #1a5276;">Assessment Details</h5>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Preferred Assessment Date</label>
|
||||
<input type="date" name="assessment_date" class="form-control"/>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Assign Sales Rep</label>
|
||||
<select name="sales_rep_id" class="form-select">
|
||||
<option value="">Auto-assign</option>
|
||||
<t t-foreach="sales_reps" t-as="rep">
|
||||
<option t-att-value="rep.id"><t t-esc="rep.name"/></option>
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Booking Source</label>
|
||||
<select name="booking_source" class="form-select">
|
||||
<option value="portal" selected="">Online Booking</option>
|
||||
<option value="phone_authorizer">Phone - Authorizer Calling</option>
|
||||
<option value="phone_client">Phone - Client Calling</option>
|
||||
<option value="walk_in">Walk-In</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
<h5 class="mb-3" style="color: #1a5276;">Authorizer / OT (Optional)</h5>
|
||||
<p class="text-muted small">If an authorizer or occupational therapist is involved, provide their details.</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Authorizer Name</label>
|
||||
<input type="text" name="authorizer_name" class="form-control"/>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Authorizer Email</label>
|
||||
<input type="email" name="authorizer_email" class="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Authorizer Phone</label>
|
||||
<input type="tel" name="authorizer_phone" class="form-control"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="card-footer text-center bg-white border-top-0 pb-4">
|
||||
<button type="submit" class="btn btn-lg" style="background-color: #1a5276; color: white;">
|
||||
<i class="fa fa-calendar-check-o"></i> Book Assessment
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</t>
|
||||
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
413
fusion_portal/views/portal_page11_sign_templates.xml
Normal file
413
fusion_portal/views/portal_page11_sign_templates.xml
Normal file
@@ -0,0 +1,413 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<!-- ============================================================ -->
|
||||
<!-- Page 11 Public Signing Form -->
|
||||
<!-- ============================================================ -->
|
||||
<template id="portal_page11_public_sign" name="Page 11 - Sign">
|
||||
<t t-call="portal.frontend_layout">
|
||||
<div class="container py-4" style="max-width:720px;">
|
||||
<div class="text-center mb-4">
|
||||
<t t-if="company.logo">
|
||||
<img t-att-src="'/web/image/res.company/%s/logo/200x60' % company.id"
|
||||
alt="Company Logo" style="max-height:60px;" class="mb-2"/>
|
||||
</t>
|
||||
<h3 class="mb-1">ADP Consent and Declaration</h3>
|
||||
<p class="text-muted">Page 11 - Assistive Devices Program</p>
|
||||
</div>
|
||||
|
||||
<t t-if="request.params.get('error') == 'no_signature'">
|
||||
<div class="alert alert-danger">Please draw your signature before submitting.</div>
|
||||
</t>
|
||||
<t t-if="request.params.get('error') == 'no_consent'">
|
||||
<div class="alert alert-danger">You must accept the consent declaration before signing.</div>
|
||||
</t>
|
||||
|
||||
<!-- Consent Declaration -->
|
||||
<form method="POST" t-att-action="'/page11/sign/%s/submit' % token" id="page11Form">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
|
||||
<!-- Applicant Information -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header"><strong>Applicant Information</strong></div>
|
||||
<div class="card-body">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-4">
|
||||
<label class="form-label">Last Name <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" name="client_last_name"
|
||||
t-att-value="client_last_name or ''" required="required"/>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<label class="form-label">First Name <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" name="client_first_name"
|
||||
t-att-value="client_first_name or ''" required="required"/>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<label class="form-label">Middle Name</label>
|
||||
<input type="text" class="form-control" name="client_middle_name"
|
||||
t-att-value="client_middle_name or ''"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label">Health Card Number (10 digits) <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" name="client_health_card"
|
||||
t-att-value="client_health_card or ''" required="required"
|
||||
maxlength="10" pattern="[0-9]{10}" title="10-digit health card number"
|
||||
placeholder="e.g. 1234567890"/>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<label class="form-label">Version <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" name="client_health_card_version"
|
||||
t-att-value="client_health_card_version or ''" required="required"
|
||||
maxlength="2" placeholder="e.g. AB"/>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<label class="form-label">Case Ref</label>
|
||||
<input type="text" class="form-control" readonly="readonly"
|
||||
t-att-value="order.name"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card-header"><strong>Consent and Declaration</strong></div>
|
||||
<div class="card-body">
|
||||
<p class="small">
|
||||
I consent to information being collected and used by the Ministry of Health and Long-Term Care,
|
||||
and agents authorized by the Ministry, for the administration and enforcement of the
|
||||
Assistive Devices Program. I understand this consent is voluntary and I may withdraw it
|
||||
at any time. I declare that the information in this application is true and complete.
|
||||
</p>
|
||||
<div class="form-check mb-3">
|
||||
<input type="checkbox" class="form-check-input" id="consent_declaration"
|
||||
name="consent_declaration" required="required"/>
|
||||
<label class="form-check-label" for="consent_declaration">
|
||||
<strong>I have read and accept the above declaration.</strong>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="signer_type" class="form-label"><strong>I am signing as:</strong></label>
|
||||
<select class="form-select" id="signer_type" name="signer_type" required="required"
|
||||
onchange="toggleAgentFields()">
|
||||
<option value="client" t-att-selected="signer_type == 'client' and 'selected'">Applicant (Client - Self)</option>
|
||||
<option value="spouse" t-att-selected="signer_type == 'spouse' and 'selected'">Spouse</option>
|
||||
<option value="parent" t-att-selected="signer_type == 'parent' and 'selected'">Parent</option>
|
||||
<option value="legal_guardian" t-att-selected="signer_type == 'legal_guardian' and 'selected'">Legal Guardian</option>
|
||||
<option value="poa" t-att-selected="signer_type == 'poa' and 'selected'">Power of Attorney</option>
|
||||
<option value="public_trustee" t-att-selected="signer_type == 'public_trustee' and 'selected'">Public Trustee</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="signer_name" class="form-label">Full Name</label>
|
||||
<input type="text" class="form-control" id="signer_name" name="signer_name"
|
||||
t-att-value="sign_request.signer_name or ''" required="required"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Agent Details (shown/hidden via JS based on signer type selection) -->
|
||||
<div class="card mb-3" id="agent_details_card" t-att-style="'' if is_agent else 'display:none;'">
|
||||
<div class="card-header"><strong>Agent Details</strong></div>
|
||||
<div class="card-body">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-5">
|
||||
<label class="form-label">Last Name</label>
|
||||
<input type="text" class="form-control agent-field" name="agent_last_name"/>
|
||||
</div>
|
||||
<div class="col-sm-5">
|
||||
<label class="form-label">First Name</label>
|
||||
<input type="text" class="form-control agent-field" name="agent_first_name"/>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<label class="form-label">M.I.</label>
|
||||
<input type="text" class="form-control" name="agent_middle_initial"
|
||||
maxlength="2" placeholder="M"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label">Home Phone</label>
|
||||
<input type="tel" class="form-control" name="agent_phone"/>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label">Business Phone</label>
|
||||
<input type="tel" class="form-control" name="agent_business_phone"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Search Address</label>
|
||||
<input type="text" class="form-control" id="agent_street_search"
|
||||
placeholder="Start typing an address..." autocomplete="off"/>
|
||||
</div>
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-3">
|
||||
<label class="form-label">Unit #</label>
|
||||
<input type="text" class="form-control" name="agent_unit" id="agent_unit"/>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<label class="form-label">Street #</label>
|
||||
<input type="text" class="form-control" name="agent_street_number" id="agent_street_number"/>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label">Street Name</label>
|
||||
<input type="text" class="form-control" name="agent_street" id="agent_street"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-5">
|
||||
<label class="form-label">City/Town</label>
|
||||
<input type="text" class="form-control" name="agent_city" id="agent_city"/>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<label class="form-label">Province</label>
|
||||
<input type="text" class="form-control" name="agent_province" id="agent_province" value="Ontario"/>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<label class="form-label">Postal Code</label>
|
||||
<input type="text" class="form-control" name="agent_postal_code" id="agent_postal_code"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Signature Pad -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<strong>Signature</strong>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="clearSignature()">Clear</button>
|
||||
</div>
|
||||
<div class="card-body p-2">
|
||||
<canvas id="signature-canvas" width="660" height="200"
|
||||
style="border:1px dashed rgba(128,128,128,0.35);border-radius:6px;width:100%;touch-action:none;cursor:crosshair;">
|
||||
</canvas>
|
||||
<input type="hidden" name="signature_data" id="signature_data"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center mb-4">
|
||||
<button type="submit" class="btn btn-primary btn-lg px-5" onclick="return prepareSubmit()">
|
||||
Submit Signature
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p class="text-center text-muted small">
|
||||
<t t-out="company.name"/> &middot;
|
||||
<t t-if="company.phone"><t t-out="company.phone"/> &middot; </t>
|
||||
<t t-if="company.email"><t t-out="company.email"/></t>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function toggleAgentFields() {
|
||||
var sel = document.getElementById('signer_type');
|
||||
var card = document.getElementById('agent_details_card');
|
||||
var agentFields = card ? card.querySelectorAll('.agent-field') : [];
|
||||
var isAgent = sel.value !== 'client';
|
||||
if (card) card.style.display = isAgent ? '' : 'none';
|
||||
agentFields.forEach(function(f) {
|
||||
if (isAgent) { f.setAttribute('required', 'required'); }
|
||||
else { f.removeAttribute('required'); f.value = ''; }
|
||||
});
|
||||
}
|
||||
document.addEventListener('DOMContentLoaded', toggleAgentFields);
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
var canvas = document.getElementById('signature-canvas');
|
||||
if (!canvas) return;
|
||||
var ctx = canvas.getContext('2d');
|
||||
var drawing = false;
|
||||
var lastX = 0, lastY = 0;
|
||||
var hasDrawn = false;
|
||||
|
||||
function resizeCanvas() {
|
||||
var rect = canvas.getBoundingClientRect();
|
||||
var dpr = window.devicePixelRatio || 1;
|
||||
canvas.width = rect.width * dpr;
|
||||
canvas.height = rect.height * dpr;
|
||||
ctx.scale(dpr, dpr);
|
||||
ctx.strokeStyle = '#000';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.lineCap = 'round';
|
||||
ctx.lineJoin = 'round';
|
||||
}
|
||||
|
||||
resizeCanvas();
|
||||
|
||||
function getPos(e) {
|
||||
var rect = canvas.getBoundingClientRect();
|
||||
var touch = e.touches ? e.touches[0] : e;
|
||||
return {
|
||||
x: touch.clientX - rect.left,
|
||||
y: touch.clientY - rect.top
|
||||
};
|
||||
}
|
||||
|
||||
function startDraw(e) {
|
||||
e.preventDefault();
|
||||
drawing = true;
|
||||
var pos = getPos(e);
|
||||
lastX = pos.x;
|
||||
lastY = pos.y;
|
||||
}
|
||||
|
||||
function draw(e) {
|
||||
if (!drawing) return;
|
||||
e.preventDefault();
|
||||
var pos = getPos(e);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(lastX, lastY);
|
||||
ctx.lineTo(pos.x, pos.y);
|
||||
ctx.stroke();
|
||||
lastX = pos.x;
|
||||
lastY = pos.y;
|
||||
hasDrawn = true;
|
||||
}
|
||||
|
||||
function stopDraw(e) {
|
||||
if (e) e.preventDefault();
|
||||
drawing = false;
|
||||
}
|
||||
|
||||
canvas.addEventListener('mousedown', startDraw);
|
||||
canvas.addEventListener('mousemove', draw);
|
||||
canvas.addEventListener('mouseup', stopDraw);
|
||||
canvas.addEventListener('mouseleave', stopDraw);
|
||||
canvas.addEventListener('touchstart', startDraw, {passive: false});
|
||||
canvas.addEventListener('touchmove', draw, {passive: false});
|
||||
canvas.addEventListener('touchend', stopDraw, {passive: false});
|
||||
|
||||
window.clearSignature = function() {
|
||||
var dpr = window.devicePixelRatio || 1;
|
||||
ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr);
|
||||
hasDrawn = false;
|
||||
document.getElementById('signature_data').value = '';
|
||||
};
|
||||
|
||||
window.prepareSubmit = function() {
|
||||
if (!hasDrawn) {
|
||||
alert('Please draw your signature before submitting.');
|
||||
return false;
|
||||
}
|
||||
var dataUrl = canvas.toDataURL('image/png');
|
||||
document.getElementById('signature_data').value = dataUrl;
|
||||
return true;
|
||||
};
|
||||
})();
|
||||
</script>
|
||||
<t t-if="google_maps_api_key">
|
||||
<script t-attf-src="https://maps.googleapis.com/maps/api/js?key=#{google_maps_api_key}&libraries=places&callback=initPage11AddressAutocomplete" async="async" defer="defer"></script>
|
||||
<script type="text/javascript">
|
||||
function initPage11AddressAutocomplete() {
|
||||
var searchInput = document.getElementById('agent_street_search');
|
||||
if (!searchInput) return;
|
||||
var autocomplete = new google.maps.places.Autocomplete(searchInput, {
|
||||
types: ['address'],
|
||||
componentRestrictions: { country: 'ca' }
|
||||
});
|
||||
autocomplete.setFields(['address_components', 'formatted_address']);
|
||||
autocomplete.addListener('place_changed', function() {
|
||||
var place = autocomplete.getPlace();
|
||||
if (!place || !place.address_components) return;
|
||||
var street_number = '', route = '', city = '', province = '', postal = '', unit = '';
|
||||
place.address_components.forEach(function(c) {
|
||||
var t = c.types;
|
||||
if (t.indexOf('street_number') >= 0) street_number = c.long_name;
|
||||
else if (t.indexOf('route') >= 0) route = c.long_name;
|
||||
else if (t.indexOf('locality') >= 0) city = c.long_name;
|
||||
else if (t.indexOf('sublocality') >= 0 && !city) city = c.long_name;
|
||||
else if (t.indexOf('administrative_area_level_1') >= 0) province = c.long_name;
|
||||
else if (t.indexOf('postal_code') >= 0) postal = c.long_name;
|
||||
else if (t.indexOf('subpremise') >= 0) unit = c.long_name;
|
||||
});
|
||||
document.getElementById('agent_street_number').value = street_number;
|
||||
document.getElementById('agent_street').value = route;
|
||||
document.getElementById('agent_city').value = city;
|
||||
document.getElementById('agent_province').value = province;
|
||||
document.getElementById('agent_postal_code').value = postal;
|
||||
if (unit) document.getElementById('agent_unit').value = unit;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- Success Page -->
|
||||
<!-- ============================================================ -->
|
||||
<template id="portal_page11_sign_success" name="Page 11 - Signed Successfully">
|
||||
<t t-call="portal.frontend_layout">
|
||||
<div class="container py-5" style="max-width:600px;">
|
||||
<div class="text-center">
|
||||
<div class="mb-4">
|
||||
<i class="fa fa-check-circle text-success" style="font-size:64px;"/>
|
||||
</div>
|
||||
<h3>Signature Submitted Successfully</h3>
|
||||
<p class="text-muted mt-3">
|
||||
Thank you for signing the ADP Consent and Declaration form.
|
||||
Your signature has been recorded and the document has been updated.
|
||||
</p>
|
||||
<t t-if="sign_request and sign_request.sale_order_id">
|
||||
<p class="text-muted">
|
||||
Case Reference: <strong><t t-out="sign_request.sale_order_id.name"/></strong>
|
||||
</p>
|
||||
</t>
|
||||
<t t-if="sign_request and sign_request.signed_pdf and token">
|
||||
<a t-attf-href="/page11/sign/#{token}/download"
|
||||
class="btn btn-outline-primary mt-3">
|
||||
<i class="fa fa-download"/> Download Signed PDF
|
||||
</a>
|
||||
</t>
|
||||
<p class="text-muted mt-4 small">You may close this window.</p>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- Expired / Cancelled Page -->
|
||||
<!-- ============================================================ -->
|
||||
<template id="portal_page11_sign_expired" name="Page 11 - Link Expired">
|
||||
<t t-call="portal.frontend_layout">
|
||||
<div class="container py-5" style="max-width:600px;">
|
||||
<div class="text-center">
|
||||
<div class="mb-4">
|
||||
<i class="fa fa-clock-o text-warning" style="font-size:64px;"/>
|
||||
</div>
|
||||
<h3>Signing Link Expired</h3>
|
||||
<p class="text-muted mt-3">
|
||||
This signing link is no longer valid. It may have expired or been cancelled.
|
||||
</p>
|
||||
<p class="text-muted">
|
||||
Please contact the office to request a new signing link.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- Invalid / Not Found Page -->
|
||||
<!-- ============================================================ -->
|
||||
<template id="portal_page11_sign_invalid" name="Page 11 - Invalid Link">
|
||||
<t t-call="portal.frontend_layout">
|
||||
<div class="container py-5" style="max-width:600px;">
|
||||
<div class="text-center">
|
||||
<div class="mb-4">
|
||||
<i class="fa fa-exclamation-triangle text-danger" style="font-size:64px;"/>
|
||||
</div>
|
||||
<h3>Invalid Link</h3>
|
||||
<p class="text-muted mt-3">
|
||||
This signing link is not valid. Please check that you are using the correct link
|
||||
from the email you received.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
184
fusion_portal/views/portal_pdf_editor.xml
Normal file
184
fusion_portal/views/portal_pdf_editor.xml
Normal file
@@ -0,0 +1,184 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- Visual PDF Field Position Editor -->
|
||||
<!-- Drag field types from sidebar onto PDF, resize on page -->
|
||||
<!-- ============================================================ -->
|
||||
<template id="portal_pdf_field_editor" name="PDF Field Editor">
|
||||
<t t-call="web.frontend_layout">
|
||||
<t t-set="title">PDF Field Editor</t>
|
||||
|
||||
<div class="container-fluid py-3" id="pdf_field_editor"
|
||||
t-att-data-template-id="template.id"
|
||||
t-att-data-page-count="template.page_count or 1"
|
||||
t-att-data-current-page="1"
|
||||
t-att-data-category="template.category or 'other'">
|
||||
|
||||
<!-- Header Bar -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div>
|
||||
<h3 class="mb-0">
|
||||
<i class="fa fa-pencil-square-o me-2"/>
|
||||
<t t-esc="template.name"/>
|
||||
<small class="text-muted ms-2">v<t t-esc="template.version"/></small>
|
||||
</h3>
|
||||
<small class="text-muted">
|
||||
<t t-esc="template.page_count or 0"/> page(s) |
|
||||
<span id="field_count"><t t-esc="len(fields)"/></span> field(s)
|
||||
</small>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="button" class="btn btn-outline-info btn-sm" id="btn_preview">
|
||||
<i class="fa fa-eye me-1"/>Preview PDF
|
||||
</button>
|
||||
<a t-att-href="'/web#id=%d&model=fusion.pdf.template&view_type=form' % template.id"
|
||||
class="btn btn-secondary btn-sm">
|
||||
<i class="fa fa-arrow-left me-1"/>Back
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Page Navigation -->
|
||||
<div class="d-flex justify-content-center align-items-center mb-3 gap-3"
|
||||
t-if="(template.page_count or 1) > 1">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" id="btn_prev_page">
|
||||
<i class="fa fa-chevron-left"/>
|
||||
</button>
|
||||
<span>
|
||||
Page <strong id="current_page_display">1</strong>
|
||||
of <strong><t t-esc="template.page_count or 1"/></strong>
|
||||
</span>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" id="btn_next_page">
|
||||
<i class="fa fa-chevron-right"/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Left Sidebar: Field Type Palette -->
|
||||
<div class="col-md-2">
|
||||
<!-- Draggable Field Types -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header bg-dark text-white py-2">
|
||||
<h6 class="mb-0"><i class="fa fa-th-list me-1"/>Field Types</h6>
|
||||
</div>
|
||||
<div class="card-body p-2">
|
||||
<p class="text-muted small mb-2">Drag a field onto the PDF</p>
|
||||
|
||||
<div class="d-grid gap-2">
|
||||
<div class="pdf-palette-item" draggable="true"
|
||||
data-field-type="text"
|
||||
style="padding: 8px 10px; border: 2px solid #3498db; border-radius: 5px;
|
||||
background: rgba(52,152,219,0.1); cursor: grab; font-size: 13px; font-weight: 600;">
|
||||
<i class="fa fa-font me-2" style="color: #3498db;"/>Text Field
|
||||
</div>
|
||||
|
||||
<div class="pdf-palette-item" draggable="true"
|
||||
data-field-type="checkbox"
|
||||
style="padding: 8px 10px; border: 2px solid #2ecc71; border-radius: 5px;
|
||||
background: rgba(46,204,113,0.1); cursor: grab; font-size: 13px; font-weight: 600;">
|
||||
<i class="fa fa-check-square-o me-2" style="color: #2ecc71;"/>Checkbox
|
||||
</div>
|
||||
|
||||
<div class="pdf-palette-item" draggable="true"
|
||||
data-field-type="date"
|
||||
style="padding: 8px 10px; border: 2px solid #e67e22; border-radius: 5px;
|
||||
background: rgba(230,126,34,0.1); cursor: grab; font-size: 13px; font-weight: 600;">
|
||||
<i class="fa fa-calendar me-2" style="color: #e67e22;"/>Date Field
|
||||
</div>
|
||||
|
||||
<div class="pdf-palette-item" draggable="true"
|
||||
data-field-type="signature"
|
||||
style="padding: 8px 10px; border: 2px solid #9b59b6; border-radius: 5px;
|
||||
background: rgba(155,89,182,0.1); cursor: grab; font-size: 13px; font-weight: 600;">
|
||||
<i class="fa fa-pencil me-2" style="color: #9b59b6;"/>Signature
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Data Keys Reference (collapsible, populated by JS) -->
|
||||
<div class="card">
|
||||
<div class="card-header py-2" style="cursor: pointer;"
|
||||
data-bs-toggle="collapse" data-bs-target="#dataKeysCollapse">
|
||||
<h6 class="mb-0">
|
||||
<i class="fa fa-key me-1"/>Data Keys
|
||||
<i class="fa fa-chevron-down float-end mt-1" style="font-size: 10px;"/>
|
||||
</h6>
|
||||
</div>
|
||||
<div id="dataKeysCollapse" class="collapse">
|
||||
<div class="card-body p-2" id="dataKeysList"
|
||||
style="max-height: 300px; overflow-y: auto; font-size: 11px;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PDF Page Canvas Area -->
|
||||
<div class="col-md-7">
|
||||
<div class="card">
|
||||
<div class="card-body p-0">
|
||||
<div id="pdf_canvas_container"
|
||||
style="position: relative; width: 100%; overflow: hidden;
|
||||
border: 2px solid #dee2e6; background: #f8f9fa;
|
||||
min-height: 600px;">
|
||||
<!-- PDF page image -->
|
||||
<img id="pdf_page_image"
|
||||
style="width: 100%; display: block; user-select: none;"
|
||||
draggable="false"
|
||||
t-att-src="preview_url or ''"
|
||||
t-attf-alt="Page #{1}"
|
||||
t-if="preview_url"/>
|
||||
|
||||
<!-- Upload form when no preview exists -->
|
||||
<div t-if="not preview_url" class="text-center py-5" id="no_preview_placeholder">
|
||||
<div class="mb-3">
|
||||
<i class="fa fa-file-image-o fa-3x text-muted"/>
|
||||
<p class="text-muted mt-2 mb-1">No page preview image yet.</p>
|
||||
<p class="text-muted small">
|
||||
Upload a screenshot/photo of the PDF page, or click
|
||||
"Generate Page Previews" in the backend form.
|
||||
</p>
|
||||
</div>
|
||||
<form t-attf-action="/fusion/pdf-editor/upload-preview"
|
||||
method="post" enctype="multipart/form-data"
|
||||
class="d-inline-block">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
<input type="hidden" name="template_id" t-att-value="template.id"/>
|
||||
<input type="hidden" name="page" value="1"/>
|
||||
<div class="input-group" style="max-width: 400px; margin: 0 auto;">
|
||||
<input type="file" name="preview_image" class="form-control"
|
||||
accept="image/png,image/jpeg,image/jpg" required="required"/>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fa fa-upload me-1"/>Upload
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Field markers rendered by JS -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Sidebar: Field Properties -->
|
||||
<div class="col-md-3">
|
||||
<div class="card" id="field_properties_panel">
|
||||
<div class="card-header bg-primary text-white py-2">
|
||||
<h6 class="mb-0"><i class="fa fa-cog me-1"/>Field Properties</h6>
|
||||
</div>
|
||||
<div class="card-body" id="field_props_body">
|
||||
<p class="text-muted small">Click a field on the PDF to edit its properties.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JavaScript -->
|
||||
<script type="text/javascript" src="/fusion_portal/static/src/js/pdf_field_editor.js"/>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
1887
fusion_portal/views/portal_technician_templates.xml
Normal file
1887
fusion_portal/views/portal_technician_templates.xml
Normal file
File diff suppressed because it is too large
Load Diff
4160
fusion_portal/views/portal_templates.xml
Normal file
4160
fusion_portal/views/portal_templates.xml
Normal file
File diff suppressed because it is too large
Load Diff
123
fusion_portal/views/res_partner_views.xml
Normal file
123
fusion_portal/views/res_partner_views.xml
Normal file
@@ -0,0 +1,123 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Extend Partner Form to add Portal Role fields -->
|
||||
<record id="view_partner_form_portal_roles" model="ir.ui.view">
|
||||
<field name="name">res.partner.form.portal.roles</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||
<field name="priority">50</field>
|
||||
<field name="arch" type="xml">
|
||||
<!-- Add Send Portal Invitation button to the button box -->
|
||||
<xpath expr="//div[@name='button_box']" position="inside">
|
||||
<button name="action_grant_portal_access"
|
||||
type="object"
|
||||
string="Send Portal Invitation"
|
||||
class="oe_stat_button"
|
||||
icon="fa-envelope"
|
||||
invisible="authorizer_portal_user_id or not email"/>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//page[@name='internal_notes']" position="after">
|
||||
<page string="Portal Access" name="portal_access">
|
||||
<group>
|
||||
<group string="Portal Roles">
|
||||
<field name="is_authorizer"/>
|
||||
<field name="is_sales_rep_portal"/>
|
||||
<field name="is_client_portal"/>
|
||||
<field name="is_technician_portal"/>
|
||||
<field name="authorizer_portal_user_id" readonly="1"/>
|
||||
<field name="portal_access_status" widget="badge"
|
||||
decoration-danger="portal_access_status == 'no_access'"
|
||||
decoration-warning="portal_access_status == 'invited'"
|
||||
decoration-success="portal_access_status == 'active'"/>
|
||||
</group>
|
||||
<group string="Statistics" invisible="not is_authorizer and not is_sales_rep_portal and not is_technician_portal">
|
||||
<field name="assigned_case_count" invisible="not is_authorizer and not is_sales_rep_portal"/>
|
||||
<field name="assessment_count" invisible="not is_authorizer and not is_sales_rep_portal"/>
|
||||
<field name="assigned_delivery_count" invisible="not is_technician_portal"/>
|
||||
</group>
|
||||
</group>
|
||||
<group invisible="not is_technician_portal">
|
||||
<group string="Technician Settings">
|
||||
<field name="x_fc_start_address" placeholder="e.g. 123 Main St, Brampton, ON"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<button name="action_grant_portal_access"
|
||||
type="object"
|
||||
string="Send Portal Invitation"
|
||||
class="btn-primary"
|
||||
invisible="authorizer_portal_user_id or not email"
|
||||
icon="fa-envelope"/>
|
||||
<button name="action_resend_portal_invitation"
|
||||
type="object"
|
||||
string="Resend Portal Invitation"
|
||||
class="btn-secondary"
|
||||
invisible="not authorizer_portal_user_id"
|
||||
icon="fa-refresh"/>
|
||||
<button name="action_view_assigned_cases"
|
||||
type="object"
|
||||
string="View Assigned Cases"
|
||||
class="btn-secondary"
|
||||
invisible="assigned_case_count == 0"
|
||||
icon="fa-list"/>
|
||||
<button name="action_view_assessments"
|
||||
type="object"
|
||||
string="View Assessments"
|
||||
class="btn-secondary"
|
||||
invisible="assessment_count == 0"
|
||||
icon="fa-clipboard"/>
|
||||
</group>
|
||||
|
||||
<!-- Warning if no email -->
|
||||
<div class="alert alert-warning" role="alert" invisible="email">
|
||||
<strong>Email Required:</strong> An email address is required to send a portal invitation.
|
||||
</div>
|
||||
|
||||
<!-- Info if already has access -->
|
||||
<div class="alert alert-success" role="alert" invisible="not authorizer_portal_user_id">
|
||||
<strong>Portal Access Granted:</strong> This contact has portal access.
|
||||
<field name="authorizer_portal_user_id" readonly="1" nolabel="1" widget="many2one"/>
|
||||
</div>
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Search view for authorizers -->
|
||||
<record id="view_partner_search_authorizer" model="ir.ui.view">
|
||||
<field name="name">res.partner.search.authorizer</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_res_partner_filter"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//filter[@name='type_company']" position="after">
|
||||
<separator/>
|
||||
<filter string="Authorizers" name="authorizers" domain="[('is_authorizer', '=', True)]"/>
|
||||
<filter string="Sales Reps (Portal)" name="sales_reps_portal" domain="[('is_sales_rep_portal', '=', True)]"/>
|
||||
<filter string="Technicians (Portal)" name="technicians_portal" domain="[('is_technician_portal', '=', True)]"/>
|
||||
<separator/>
|
||||
<filter string="Portal: No Access" name="portal_no_access" domain="[('is_authorizer', '=', True), ('portal_access_status', '=', 'no_access')]"/>
|
||||
<filter string="Portal: Invited" name="portal_invited" domain="[('is_authorizer', '=', True), ('portal_access_status', '=', 'invited')]"/>
|
||||
<filter string="Portal: Active" name="portal_active" domain="[('is_authorizer', '=', True), ('portal_access_status', '=', 'active')]"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Add portal status to contact list view -->
|
||||
<record id="view_partner_list_portal_status" model="ir.ui.view">
|
||||
<field name="name">res.partner.list.portal.status</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='email']" position="after">
|
||||
<field name="is_authorizer" string="Authorizer" optional="hide"/>
|
||||
<field name="portal_access_status" string="Portal Status" optional="hide"
|
||||
decoration-danger="portal_access_status == 'no_access'"
|
||||
decoration-warning="portal_access_status == 'invited'"
|
||||
decoration-success="portal_access_status == 'active'"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
49
fusion_portal/views/sale_order_views.xml
Normal file
49
fusion_portal/views/sale_order_views.xml
Normal file
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Extend Sale Order Form to add Portal Documents Tab and Message Buttons -->
|
||||
<record id="view_sale_order_form_portal_docs" model="ir.ui.view">
|
||||
<field name="name">sale.order.form.portal.docs</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||
<field name="priority">99</field>
|
||||
<field name="arch" type="xml">
|
||||
<!-- Message Authorizer button moved to chatter topbar (icon-only) -->
|
||||
<!-- See static/src/js/chatter_message_authorizer.js -->
|
||||
|
||||
<xpath expr="//page[@name='other_information']" position="after">
|
||||
<page string="Portal" name="portal_info">
|
||||
<group>
|
||||
<group string="Portal Activity">
|
||||
<field name="portal_document_count" string="Documents Uploaded"/>
|
||||
<field name="portal_comment_count" string="Comments"/>
|
||||
</group>
|
||||
<group string="Source">
|
||||
<field name="assessment_id" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<button name="action_view_portal_documents"
|
||||
type="object"
|
||||
string="View Portal Documents"
|
||||
class="btn-secondary"
|
||||
icon="fa-file-pdf-o"
|
||||
invisible="portal_document_count == 0"/>
|
||||
<button name="action_view_portal_comments"
|
||||
type="object"
|
||||
string="View Portal Comments"
|
||||
class="btn-secondary"
|
||||
icon="fa-comments"
|
||||
invisible="portal_comment_count == 0"/>
|
||||
</group>
|
||||
</page>
|
||||
</xpath>
|
||||
|
||||
<!-- Filter authorizer dropdown to only show actual authorizers -->
|
||||
<xpath expr="//field[@name='x_fc_authorizer_id']" position="attributes">
|
||||
<attribute name="domain">[('is_company', '=', False), ('is_authorizer', '=', True)]</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user