changes
This commit is contained in:
31
fusion-plating/fusion_plating_portal/views/fp_menu.xml
Normal file
31
fusion-plating/fusion_plating_portal/views/fp_menu.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
Part of the Fusion Plating product family.
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- Add a Sales section to the Plating root menu for portal-facing -->
|
||||
<!-- records (Quote Requests + Portal Jobs). -->
|
||||
<!-- ================================================================== -->
|
||||
<menuitem id="menu_fp_sales"
|
||||
name="Sales"
|
||||
parent="fusion_plating.menu_fp_root"
|
||||
sequence="20"
|
||||
groups="fusion_plating.group_fusion_plating_supervisor"/>
|
||||
|
||||
<menuitem id="menu_fp_quote_requests"
|
||||
name="Quote Requests"
|
||||
parent="menu_fp_sales"
|
||||
action="action_fp_quote_request"
|
||||
sequence="10"/>
|
||||
|
||||
<menuitem id="menu_fp_portal_jobs"
|
||||
name="Portal Jobs"
|
||||
parent="menu_fp_sales"
|
||||
action="action_fp_portal_job"
|
||||
sequence="20"/>
|
||||
|
||||
</odoo>
|
||||
@@ -0,0 +1,103 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
Part of the Fusion Plating product family.
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- Breadcrumb additions for plating portal pages. -->
|
||||
<!-- Each <li> we add gets picked up by portal.portal_breadcrumbs. -->
|
||||
<!-- ================================================================== -->
|
||||
<template id="portal_breadcrumbs_plating"
|
||||
inherit_id="portal.portal_breadcrumbs"
|
||||
priority="40">
|
||||
<xpath expr="//ol[hasclass('o_portal_submenu')]" position="inside">
|
||||
|
||||
<!-- Dashboard -->
|
||||
<li t-if="page_name == 'fp_dashboard'"
|
||||
class="breadcrumb-item active"
|
||||
aria-current="page">
|
||||
Dashboard
|
||||
</li>
|
||||
|
||||
<!-- Quote Requests list -->
|
||||
<li t-if="page_name == 'fp_quote_requests'"
|
||||
class="breadcrumb-item active"
|
||||
aria-current="page">
|
||||
Quote Requests
|
||||
</li>
|
||||
|
||||
<!-- Quote Request detail -->
|
||||
<li t-if="page_name == 'fp_quote_request'"
|
||||
class="breadcrumb-item">
|
||||
<a href="/my/quote_requests">Quote Requests</a>
|
||||
</li>
|
||||
<li t-if="page_name == 'fp_quote_request'"
|
||||
class="breadcrumb-item active"
|
||||
aria-current="page">
|
||||
<span t-out="quote_request.name"/>
|
||||
</li>
|
||||
|
||||
<!-- Quote Request new -->
|
||||
<li t-if="page_name == 'fp_quote_request_new'"
|
||||
class="breadcrumb-item">
|
||||
<a href="/my/quote_requests">Quote Requests</a>
|
||||
</li>
|
||||
<li t-if="page_name == 'fp_quote_request_new'"
|
||||
class="breadcrumb-item active"
|
||||
aria-current="page">
|
||||
New
|
||||
</li>
|
||||
|
||||
<!-- Jobs list -->
|
||||
<li t-if="page_name == 'fp_jobs'"
|
||||
class="breadcrumb-item active"
|
||||
aria-current="page">
|
||||
Parts Portal
|
||||
</li>
|
||||
|
||||
<!-- Job detail -->
|
||||
<li t-if="page_name == 'fp_portal_job'"
|
||||
class="breadcrumb-item">
|
||||
<a href="/my/jobs">Parts Portal</a>
|
||||
</li>
|
||||
<li t-if="page_name == 'fp_portal_job'"
|
||||
class="breadcrumb-item active"
|
||||
aria-current="page">
|
||||
<span t-out="job.name"/>
|
||||
</li>
|
||||
|
||||
<!-- Purchase Orders -->
|
||||
<li t-if="page_name == 'fp_purchase_orders'"
|
||||
class="breadcrumb-item active"
|
||||
aria-current="page">
|
||||
Purchase Orders
|
||||
</li>
|
||||
|
||||
<!-- Invoices -->
|
||||
<li t-if="page_name == 'fp_invoices'"
|
||||
class="breadcrumb-item active"
|
||||
aria-current="page">
|
||||
Invoices
|
||||
</li>
|
||||
|
||||
<!-- Deliveries / Packing Slips -->
|
||||
<li t-if="page_name == 'fp_deliveries'"
|
||||
class="breadcrumb-item active"
|
||||
aria-current="page">
|
||||
Packing Slips
|
||||
</li>
|
||||
|
||||
<!-- Certifications -->
|
||||
<li t-if="page_name == 'fp_certifications'"
|
||||
class="breadcrumb-item active"
|
||||
aria-current="page">
|
||||
Certifications
|
||||
</li>
|
||||
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
@@ -0,0 +1,390 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
Part of the Fusion Plating product family.
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- Portal Home Dashboard — 6-section grid -->
|
||||
<!-- ================================================================== -->
|
||||
<template id="fp_portal_home_dashboard" name="Plating Portal Dashboard">
|
||||
<t t-call="portal.portal_layout">
|
||||
<div class="o_fp_dashboard mt-3">
|
||||
<!-- Welcome header -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h3 class="mb-1">
|
||||
Welcome back, <span t-out="partner.name"/>
|
||||
</h3>
|
||||
<p class="text-muted mb-0">
|
||||
Your plating portal dashboard — everything at a glance.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions bar -->
|
||||
<div class="d-flex flex-wrap gap-2 mb-4">
|
||||
<a href="/my/quote_requests/new" class="btn btn-primary">
|
||||
<i class="fa fa-plus me-1"/>Request Quote
|
||||
</a>
|
||||
<a href="/my/quote_requests" class="btn btn-outline-secondary">
|
||||
<i class="fa fa-file-text-o me-1"/>My Quotes
|
||||
</a>
|
||||
<a href="/my/jobs" class="btn btn-outline-secondary">
|
||||
<i class="fa fa-cogs me-1"/>Parts Portal
|
||||
</a>
|
||||
<a href="/my/certifications" class="btn btn-outline-secondary">
|
||||
<i class="fa fa-certificate me-1"/>Certifications
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Dashboard Grid -->
|
||||
<div class="row g-4">
|
||||
|
||||
<!-- ====== QUOTES SECTION ====== -->
|
||||
<div class="col-lg-6">
|
||||
<div class="o_fp_dashboard_card card h-100">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h6 class="mb-0">
|
||||
<i class="fa fa-file-text-o me-2"/>Quotes
|
||||
<span class="badge text-bg-primary ms-2" t-out="quote_count"/>
|
||||
</h6>
|
||||
<a href="/my/quote_requests" class="btn btn-sm btn-outline-primary">View All</a>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<t t-if="recent_quotes">
|
||||
<table class="table table-sm mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Reference</th>
|
||||
<th>Date</th>
|
||||
<th class="text-end">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="recent_quotes" t-as="qr">
|
||||
<td>
|
||||
<a t-att-href="'/my/quote_requests/%s' % qr.id"
|
||||
t-out="qr.name"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-field="qr.create_date" t-options='{"widget": "date"}'/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-attf-class="badge #{
|
||||
'text-bg-secondary' if qr.state == 'new' else
|
||||
'text-bg-info' if qr.state == 'under_review' else
|
||||
'text-bg-primary' if qr.state == 'quoted' else
|
||||
'text-bg-success' if qr.state == 'accepted' else
|
||||
'text-bg-danger' if qr.state == 'declined' else
|
||||
'text-bg-warning'}"
|
||||
t-out="dict(qr._fields['state']._description_selection(qr.env)).get(qr.state)"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="p-4 text-center text-muted">
|
||||
<p class="mb-2">No quotes yet.</p>
|
||||
<a href="/my/quote_requests/new" class="btn btn-sm btn-primary">
|
||||
Submit Your First RFQ
|
||||
</a>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====== PURCHASE ORDERS SECTION ====== -->
|
||||
<div class="col-lg-6">
|
||||
<div class="o_fp_dashboard_card card h-100">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h6 class="mb-0">
|
||||
<i class="fa fa-shopping-cart me-2"/>Purchase Orders
|
||||
<span class="badge text-bg-primary ms-2" t-out="po_count"/>
|
||||
</h6>
|
||||
<a href="/my/purchase_orders" class="btn btn-sm btn-outline-primary">View All</a>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<t t-if="recent_pos">
|
||||
<table class="table table-sm mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Order</th>
|
||||
<th>Date</th>
|
||||
<th class="text-end">Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="recent_pos" t-as="po">
|
||||
<td t-out="po.name"/>
|
||||
<td>
|
||||
<span t-field="po.date_order" t-options='{"widget": "date"}'/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-field="po.amount_total"
|
||||
t-options='{"widget": "monetary", "display_currency": po.currency_id}'/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="p-4 text-center text-muted">
|
||||
No purchase orders yet.
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====== PARTS PORTAL / JOBS SECTION ====== -->
|
||||
<div class="col-lg-6">
|
||||
<div class="o_fp_dashboard_card card h-100">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h6 class="mb-0">
|
||||
<i class="fa fa-cogs me-2"/>Parts Portal
|
||||
<span class="badge text-bg-primary ms-2" t-out="job_count"/>
|
||||
</h6>
|
||||
<a href="/my/jobs" class="btn btn-sm btn-outline-primary">View All</a>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<t t-if="recent_jobs">
|
||||
<div class="p-3">
|
||||
<t t-foreach="recent_jobs" t-as="job">
|
||||
<div class="o_fp_dashboard_job_row mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<a t-att-href="'/my/jobs/%s' % job.id"
|
||||
class="fw-semibold text-decoration-none"
|
||||
t-out="job.name"/>
|
||||
<span t-attf-class="badge #{
|
||||
'text-bg-info' if job.state == 'received' else
|
||||
'text-bg-primary' if job.state == 'in_progress' else
|
||||
'text-bg-warning' if job.state == 'quality_check' else
|
||||
'text-bg-secondary' if job.state == 'ready_to_ship' else
|
||||
'text-bg-success'}"
|
||||
t-out="dict(job._fields['state']._description_selection(job.env)).get(job.state)"/>
|
||||
</div>
|
||||
<!-- Segmented progress bar -->
|
||||
<div class="o_fp_seg_progress d-flex" style="height: 8px; border-radius: 4px; overflow: hidden;">
|
||||
<t t-set="pct" t-value="job._progress_percent()"/>
|
||||
<!-- Receiving segment (green, 0-20%) -->
|
||||
<div t-attf-class="o_fp_seg_receiving"
|
||||
t-attf-style="width: #{min(pct, 20)}%; background-color: var(--bs-success); opacity: #{1 if pct >= 1 else 0.3};"/>
|
||||
<!-- In Progress segment (orange, 20-70%) -->
|
||||
<div t-attf-class="o_fp_seg_progress_mid"
|
||||
t-attf-style="width: #{max(0, min(pct - 20, 50))}%; background-color: var(--bs-warning); opacity: #{1 if pct > 20 else 0.3};"/>
|
||||
<!-- Shipping segment (orange-green, 70-100%) -->
|
||||
<div t-attf-class="o_fp_seg_shipping"
|
||||
t-attf-style="width: #{max(0, min(pct - 70, 30))}%; background-color: var(--bs-info); opacity: #{1 if pct > 70 else 0.3};"/>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mt-1" style="font-size: 0.7rem;">
|
||||
<span class="text-muted">Receiving</span>
|
||||
<span class="text-muted">In Progress</span>
|
||||
<span class="text-muted">Shipping</span>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="p-4 text-center text-muted">
|
||||
No active jobs.
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====== CERTIFICATIONS & QUALITY SECTION ====== -->
|
||||
<div class="col-lg-6">
|
||||
<div class="o_fp_dashboard_card card h-100">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h6 class="mb-0">
|
||||
<i class="fa fa-certificate me-2"/>Certifications & Quality
|
||||
<span class="badge text-bg-primary ms-2" t-out="cert_count"/>
|
||||
</h6>
|
||||
<a href="/my/certifications" class="btn btn-sm btn-outline-primary">View All</a>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<t t-if="recent_certs">
|
||||
<table class="table table-sm mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Job</th>
|
||||
<th>Ship Date</th>
|
||||
<th class="text-end">CoC</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="recent_certs" t-as="cj">
|
||||
<td>
|
||||
<a t-att-href="'/my/jobs/%s' % cj.id"
|
||||
t-out="cj.name"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-if="cj.actual_ship_date"
|
||||
t-field="cj.actual_ship_date"
|
||||
t-options='{"widget": "date"}'/>
|
||||
<span t-else="" class="text-muted">--</span>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<a t-att-href="'/my/jobs/%s/coc' % cj.id"
|
||||
class="btn btn-sm btn-outline-success">
|
||||
<i class="fa fa-download me-1"/>Download
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="p-4 text-center text-muted">
|
||||
No certificates available yet.
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====== SHIPPING / DELIVERIES SECTION ====== -->
|
||||
<div class="col-lg-6">
|
||||
<div class="o_fp_dashboard_card card h-100">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h6 class="mb-0">
|
||||
<i class="fa fa-truck me-2"/>Shipping
|
||||
<span class="badge text-bg-primary ms-2" t-out="delivery_count"/>
|
||||
</h6>
|
||||
<a href="/my/deliveries" class="btn btn-sm btn-outline-primary">View All</a>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<t t-if="recent_deliveries">
|
||||
<table class="table table-sm mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Packing Slip</th>
|
||||
<th>Date</th>
|
||||
<th class="text-end">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="recent_deliveries" t-as="dlv">
|
||||
<td t-out="dlv.name"/>
|
||||
<td>
|
||||
<span t-if="dlv.date_done"
|
||||
t-field="dlv.date_done"
|
||||
t-options='{"widget": "date"}'/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span class="badge text-bg-success">Delivered</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="p-4 text-center text-muted">
|
||||
No deliveries yet.
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====== INVOICES SECTION ====== -->
|
||||
<div class="col-lg-6">
|
||||
<div class="o_fp_dashboard_card card h-100">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h6 class="mb-0">
|
||||
<i class="fa fa-file-text me-2"/>Invoices
|
||||
<span class="badge text-bg-primary ms-2" t-out="invoice_count"/>
|
||||
</h6>
|
||||
<a href="/my/fp_invoices" class="btn btn-sm btn-outline-primary">View All</a>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<t t-if="recent_invoices">
|
||||
<table class="table table-sm mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Invoice</th>
|
||||
<th>Due Date</th>
|
||||
<th class="text-end">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="recent_invoices" t-as="inv">
|
||||
<td t-out="inv.name"/>
|
||||
<td>
|
||||
<span t-if="inv.invoice_date_due"
|
||||
t-field="inv.invoice_date_due"
|
||||
t-options='{"widget": "date"}'/>
|
||||
<span t-else="" class="text-muted">--</span>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-field="inv.amount_total"
|
||||
t-options='{"widget": "monetary", "display_currency": inv.currency_id}'/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="p-4 text-center text-muted">
|
||||
No invoices yet.
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /row -->
|
||||
</div><!-- /o_fp_dashboard -->
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- Override portal home to add sidebar badge counts -->
|
||||
<!-- ================================================================== -->
|
||||
<template id="portal_my_home_plating"
|
||||
name="Portal My Home -- Plating"
|
||||
inherit_id="portal.portal_my_home"
|
||||
customize_show="True"
|
||||
priority="40">
|
||||
<xpath expr="//div[hasclass('o_portal_docs')]" position="inside">
|
||||
<t t-call="portal.portal_docs_entry">
|
||||
<t t-set="title">Quote Requests</t>
|
||||
<t t-set="url" t-value="'/my/quote_requests'"/>
|
||||
<t t-set="placeholder_count" t-value="'fp_quote_request_count'"/>
|
||||
</t>
|
||||
<t t-call="portal.portal_docs_entry">
|
||||
<t t-set="title">Plating Jobs</t>
|
||||
<t t-set="url" t-value="'/my/jobs'"/>
|
||||
<t t-set="placeholder_count" t-value="'fp_portal_job_count'"/>
|
||||
</t>
|
||||
<t t-call="portal.portal_docs_entry">
|
||||
<t t-set="title">Purchase Orders</t>
|
||||
<t t-set="url" t-value="'/my/purchase_orders'"/>
|
||||
<t t-set="placeholder_count" t-value="'fp_purchase_order_count'"/>
|
||||
</t>
|
||||
<t t-call="portal.portal_docs_entry">
|
||||
<t t-set="title">Invoices</t>
|
||||
<t t-set="url" t-value="'/my/fp_invoices'"/>
|
||||
<t t-set="placeholder_count" t-value="'fp_invoice_count'"/>
|
||||
</t>
|
||||
<t t-call="portal.portal_docs_entry">
|
||||
<t t-set="title">Packing Slips</t>
|
||||
<t t-set="url" t-value="'/my/deliveries'"/>
|
||||
<t t-set="placeholder_count" t-value="'fp_delivery_count'"/>
|
||||
</t>
|
||||
<t t-call="portal.portal_docs_entry">
|
||||
<t t-set="title">Certifications</t>
|
||||
<t t-set="url" t-value="'/my/certifications'"/>
|
||||
<t t-set="placeholder_count" t-value="'fp_certification_count'"/>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
@@ -0,0 +1,805 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
Part of the Fusion Plating product family.
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- QUOTE REQUESTS — list with tabs (Active / Converted / Declined) -->
|
||||
<!-- ================================================================== -->
|
||||
<template id="portal_my_quote_requests" name="My Quote Requests">
|
||||
<t t-call="portal.portal_layout">
|
||||
<t t-set="breadcrumbs_searchbar" t-value="True"/>
|
||||
<t t-call="portal.portal_searchbar">
|
||||
<t t-set="title">Quote Requests</t>
|
||||
</t>
|
||||
|
||||
<!-- Tab navigation -->
|
||||
<ul class="nav nav-tabs mb-3">
|
||||
<li class="nav-item" t-foreach="searchbar_filters" t-as="f">
|
||||
<a t-attf-class="nav-link #{'active' if filterby == f else ''}"
|
||||
t-attf-href="/my/quote_requests?filterby=#{f}&sortby=#{sortby}">
|
||||
<t t-out="searchbar_filters[f]['label']"/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="d-flex justify-content-end mb-3">
|
||||
<a href="/my/quote_requests/new" class="btn btn-primary">
|
||||
<i class="fa fa-plus me-1"/>New Quote Request
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<t t-if="not quote_requests">
|
||||
<div class="o_fp_portal_card card bg-body-tertiary border-0 p-4 text-center">
|
||||
<p class="text-muted mb-2">No quote requests found for this filter.</p>
|
||||
<p class="small text-muted mb-0">
|
||||
Click "New Quote Request" above to send your first RFQ.
|
||||
</p>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="quote_requests" t-call="portal.portal_table">
|
||||
<thead>
|
||||
<tr class="active">
|
||||
<th>Reference</th>
|
||||
<th>Submitted</th>
|
||||
<th>Parts</th>
|
||||
<th>Target Delivery</th>
|
||||
<th class="text-end">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="quote_requests" t-as="qr">
|
||||
<td>
|
||||
<a t-att-href="'/my/quote_requests/%s' % qr.id"
|
||||
t-out="qr.name"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-field="qr.create_date" t-options='{"widget": "date"}'/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-out="len(qr.line_ids) or qr.quantity"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-if="qr.target_delivery"
|
||||
t-field="qr.target_delivery"
|
||||
t-options='{"widget": "date"}'/>
|
||||
<span t-else="" class="text-muted">--</span>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-attf-class="badge #{
|
||||
'text-bg-secondary' if qr.state == 'new' else
|
||||
'text-bg-info' if qr.state == 'under_review' else
|
||||
'text-bg-primary' if qr.state == 'quoted' else
|
||||
'text-bg-success' if qr.state == 'accepted' else
|
||||
'text-bg-danger' if qr.state == 'declined' else
|
||||
'text-bg-warning'}"
|
||||
t-out="dict(qr._fields['state']._description_selection(qr.env)).get(qr.state)"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- QUOTE REQUEST — detail -->
|
||||
<!-- ================================================================== -->
|
||||
<template id="portal_my_quote_request" name="My Quote Request">
|
||||
<t t-call="portal.portal_layout">
|
||||
<t t-set="o_portal_fullwidth_alert" groups="fusion_plating.group_fusion_plating_operator">
|
||||
<t t-call="portal.portal_back_in_edit_mode">
|
||||
<t t-set="backend_url"
|
||||
t-value="'/odoo/action-base.action_partner_form#id=%s&model=res.partner&view_type=form' % quote_request.partner_id.id"/>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<div class="row mt-2 mb-4">
|
||||
<div class="col-12">
|
||||
<h3 class="mb-1">
|
||||
<span t-out="quote_request.name"/>
|
||||
</h3>
|
||||
<p class="text-muted mb-0">
|
||||
Submitted
|
||||
<span t-field="quote_request.create_date" t-options='{"widget": "date"}'/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card bg-body-tertiary border-0 mb-4 o_fp_portal_card">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h6 class="text-muted small text-uppercase">Status</h6>
|
||||
<span t-attf-class="badge #{
|
||||
'text-bg-secondary' if quote_request.state == 'new' else
|
||||
'text-bg-info' if quote_request.state == 'under_review' else
|
||||
'text-bg-primary' if quote_request.state == 'quoted' else
|
||||
'text-bg-success' if quote_request.state == 'accepted' else
|
||||
'text-bg-danger' if quote_request.state == 'declined' else
|
||||
'text-bg-warning'} fs-6"
|
||||
t-out="dict(quote_request._fields['state']._description_selection(quote_request.env)).get(quote_request.state)"/>
|
||||
</div>
|
||||
<div class="col-md-6 text-md-end">
|
||||
<t t-if="quote_request.state == 'quoted' and quote_request.quoted_price">
|
||||
<h6 class="text-muted small text-uppercase">Quoted Price</h6>
|
||||
<h4 class="mb-0">
|
||||
<span t-field="quote_request.quoted_price"
|
||||
t-options='{"widget": "monetary", "display_currency": quote_request.currency_id}'/>
|
||||
</h4>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-4">
|
||||
<h6 class="text-muted small text-uppercase">Contact</h6>
|
||||
<div t-if="quote_request.contact_name" t-out="quote_request.contact_name"/>
|
||||
<div t-if="quote_request.contact_email"
|
||||
class="text-muted small" t-out="quote_request.contact_email"/>
|
||||
<div t-if="quote_request.contact_phone"
|
||||
class="text-muted small" t-out="quote_request.contact_phone"/>
|
||||
</div>
|
||||
<div class="col-md-6 mb-4">
|
||||
<h6 class="text-muted small text-uppercase">Details</h6>
|
||||
<div>
|
||||
<span class="text-muted small">Quantity:</span>
|
||||
<span t-out="quote_request.quantity"/>
|
||||
</div>
|
||||
<div t-if="quote_request.target_delivery">
|
||||
<span class="text-muted small">Target Delivery:</span>
|
||||
<span t-field="quote_request.target_delivery"
|
||||
t-options='{"widget": "date"}'/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Addresses -->
|
||||
<div class="row" t-if="quote_request.shipping_address_id or quote_request.billing_address_id">
|
||||
<div class="col-md-6 mb-4" t-if="quote_request.shipping_address_id">
|
||||
<h6 class="text-muted small text-uppercase">Shipping Address</h6>
|
||||
<div t-out="quote_request.shipping_address_id.contact_address"/>
|
||||
</div>
|
||||
<div class="col-md-6 mb-4" t-if="quote_request.billing_address_id">
|
||||
<h6 class="text-muted small text-uppercase">Billing Address</h6>
|
||||
<div t-out="quote_request.billing_address_id.contact_address"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Part Lines -->
|
||||
<div class="mb-4" t-if="quote_request.line_ids">
|
||||
<h6 class="text-muted small text-uppercase">Parts</h6>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Part Number</th>
|
||||
<th>Description</th>
|
||||
<th class="text-center">Qty</th>
|
||||
<th class="text-center">Count</th>
|
||||
<th>Files</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="quote_request.line_ids" t-as="line">
|
||||
<td t-out="line_index + 1"/>
|
||||
<td>
|
||||
<span t-if="line.product_id" t-out="line.product_id.default_code or line.product_id.name"/>
|
||||
<span t-elif="line.part_number" t-out="line.part_number"/>
|
||||
<span t-else="" class="text-muted">--</span>
|
||||
</td>
|
||||
<td t-out="line.description or '--'"/>
|
||||
<td class="text-center" t-out="line.quantity"/>
|
||||
<td class="text-center" t-out="line.count"/>
|
||||
<td>
|
||||
<t t-foreach="line.attachment_ids" t-as="att">
|
||||
<a t-att-href="'/web/content/%s?download=true' % att.id" class="me-2">
|
||||
<i class="fa fa-paperclip"/> <span t-out="att.name"/>
|
||||
</a>
|
||||
</t>
|
||||
<span t-if="not line.attachment_ids" class="text-muted">--</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4" t-if="quote_request.process_type_ids">
|
||||
<h6 class="text-muted small text-uppercase">Requested Processes</h6>
|
||||
<span t-foreach="quote_request.process_type_ids" t-as="pt"
|
||||
class="badge text-bg-light border me-1" t-out="pt.name"/>
|
||||
</div>
|
||||
|
||||
<div class="mb-4" t-if="quote_request.part_description">
|
||||
<h6 class="text-muted small text-uppercase">Part Description</h6>
|
||||
<div class="border rounded p-3 bg-body">
|
||||
<span t-out="quote_request.part_description"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4" t-if="quote_request.special_instructions">
|
||||
<h6 class="text-muted small text-uppercase">Special Instructions</h6>
|
||||
<div class="border rounded p-3 bg-body">
|
||||
<span t-out="quote_request.special_instructions"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4" t-if="quote_request.drawing_attachment_ids">
|
||||
<h6 class="text-muted small text-uppercase">Attachments</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li t-foreach="quote_request.drawing_attachment_ids" t-as="att">
|
||||
<a t-att-href="'/web/content/%s?download=true' % att.id">
|
||||
<i class="fa fa-paperclip me-1"/>
|
||||
<span t-out="att.name"/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- QUOTE REQUEST — new form (enhanced with multi-part, addresses) -->
|
||||
<!-- ================================================================== -->
|
||||
<template id="portal_new_quote_request_form" name="New Quote Request">
|
||||
<t t-call="portal.portal_layout">
|
||||
<div class="row mt-2 mb-4">
|
||||
<div class="col-12">
|
||||
<h3>New Quote Request</h3>
|
||||
<p class="text-muted">
|
||||
Fill out the form below and our shop team will follow up with a quote.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div t-if="error" class="alert alert-warning">
|
||||
<t t-if="error == 'missing_description'">
|
||||
Please add at least one part or describe the part you'd like quoted.
|
||||
</t>
|
||||
<t t-else="">
|
||||
There was a problem submitting your request. Please try again.
|
||||
</t>
|
||||
</div>
|
||||
|
||||
<form action="/my/quote_requests/submit"
|
||||
method="POST"
|
||||
enctype="multipart/form-data"
|
||||
class="o_fp_portal_form"
|
||||
id="fp_rfq_form">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
<input type="hidden" name="parts_data" id="fp_parts_data" value="[]"/>
|
||||
|
||||
<!-- Hidden select with all available products for JS to clone into part rows -->
|
||||
<select id="fp_products_source" class="d-none">
|
||||
<t t-foreach="products" t-as="p">
|
||||
<option t-att-value="p.id">
|
||||
<t t-if="p.default_code">[<t t-out="p.default_code"/>] </t><t t-out="p.name"/>
|
||||
</option>
|
||||
</t>
|
||||
</select>
|
||||
|
||||
<!-- Contact Info -->
|
||||
<h5 class="mb-3">Contact Information</h5>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label" for="contact_name">Contact Name</label>
|
||||
<input type="text" class="form-control" id="contact_name"
|
||||
name="contact_name" t-att-value="partner.name"/>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label" for="contact_email">Contact Email</label>
|
||||
<input type="email" class="form-control" id="contact_email"
|
||||
name="contact_email" t-att-value="partner.email"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label" for="contact_phone">Contact Phone</label>
|
||||
<input type="text" class="form-control" id="contact_phone"
|
||||
name="contact_phone" t-att-value="partner.phone"/>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label" for="company_name">Company</label>
|
||||
<input type="text" class="form-control" id="company_name"
|
||||
name="company_name"
|
||||
t-att-value="partner.parent_id.name or partner.name"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Addresses -->
|
||||
<h5 class="mb-3 mt-4">Addresses</h5>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label" for="shipping_address_id">Shipping Address</label>
|
||||
<select class="form-select" id="shipping_address_id" name="shipping_address_id">
|
||||
<option value="">-- Select Address --</option>
|
||||
<t t-foreach="addresses" t-as="addr">
|
||||
<option t-att-value="addr.id">
|
||||
<t t-out="addr.name"/>
|
||||
<t t-if="addr.city"> - <t t-out="addr.city"/></t>
|
||||
<t t-if="addr.street"> (<t t-out="addr.street"/>)</t>
|
||||
</option>
|
||||
</t>
|
||||
<option value="new">+ New Address</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label" for="billing_address_id">Billing Address</label>
|
||||
<div class="form-check mb-2">
|
||||
<input type="checkbox" class="form-check-input" id="billing_same_as_shipping"
|
||||
name="billing_same_as_shipping" value="1" checked="checked"/>
|
||||
<label class="form-check-label" for="billing_same_as_shipping">
|
||||
Same as shipping
|
||||
</label>
|
||||
</div>
|
||||
<select class="form-select" id="billing_address_id" name="billing_address_id"
|
||||
disabled="disabled">
|
||||
<option value="">-- Select Address --</option>
|
||||
<t t-foreach="addresses" t-as="addr">
|
||||
<option t-att-value="addr.id">
|
||||
<t t-out="addr.name"/>
|
||||
<t t-if="addr.city"> - <t t-out="addr.city"/></t>
|
||||
</option>
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Parts Section -->
|
||||
<h5 class="mb-3 mt-4">Parts <span class="text-danger">*</span></h5>
|
||||
<div id="fp_parts_container">
|
||||
<!-- Part rows will be added by JS -->
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-secondary mb-3" id="fp_add_part_btn">
|
||||
<i class="fa fa-plus me-1"/> ADD ANOTHER PART
|
||||
</button>
|
||||
|
||||
<!-- General Description (fallback) -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="part_description">
|
||||
General Part Description
|
||||
</label>
|
||||
<textarea class="form-control" id="part_description"
|
||||
name="part_description" rows="3"
|
||||
placeholder="Describe the part(s) if not using the part lines above"/>
|
||||
</div>
|
||||
|
||||
<!-- Process Types -->
|
||||
<div class="mb-3" t-if="process_types">
|
||||
<label class="form-label">Requested Processes</label>
|
||||
<div class="row">
|
||||
<div class="col-md-6" t-foreach="process_types" t-as="pt">
|
||||
<div class="form-check">
|
||||
<input type="checkbox"
|
||||
class="form-check-input"
|
||||
t-attf-id="process_type_#{pt.id}"
|
||||
name="process_type_ids"
|
||||
t-att-value="pt.id"/>
|
||||
<label class="form-check-label"
|
||||
t-attf-for="process_type_#{pt.id}"
|
||||
t-out="pt.name"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label" for="quantity">Total Quantity</label>
|
||||
<input type="number" class="form-control" id="quantity"
|
||||
name="quantity" min="1" value="1"/>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label" for="target_delivery">Target Delivery</label>
|
||||
<input type="date" class="form-control" id="target_delivery"
|
||||
name="target_delivery"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="special_instructions">Special Instructions</label>
|
||||
<textarea class="form-control" id="special_instructions"
|
||||
name="special_instructions" rows="3"/>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="drawing_attachments">General Drawings & Attachments</label>
|
||||
<input type="file" class="form-control" id="drawing_attachments"
|
||||
name="drawing_attachments" multiple="multiple"/>
|
||||
<div class="form-text">Upload PDF, DWG, STEP, or image files.</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-4">
|
||||
<a href="/my/quote_requests" class="btn btn-link">Cancel</a>
|
||||
<button type="submit" class="btn btn-primary btn-lg" id="fp_submit_rfq">
|
||||
<i class="fa fa-paper-plane me-1"/>SUBMIT RFQ
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- JOBS — list with segmented progress bars -->
|
||||
<!-- ================================================================== -->
|
||||
<template id="portal_my_jobs" name="My Plating Jobs">
|
||||
<t t-call="portal.portal_layout">
|
||||
<t t-set="breadcrumbs_searchbar" t-value="True"/>
|
||||
<t t-call="portal.portal_searchbar">
|
||||
<t t-set="title">Parts Portal</t>
|
||||
</t>
|
||||
|
||||
<t t-if="not jobs">
|
||||
<div class="o_fp_portal_card card bg-body-tertiary border-0 p-4 text-center">
|
||||
<p class="text-muted mb-0">You have no plating jobs yet.</p>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="jobs">
|
||||
<div class="o_fp_jobs_list">
|
||||
<t t-foreach="jobs" t-as="job">
|
||||
<div class="card mb-3 o_fp_portal_card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div>
|
||||
<a t-att-href="'/my/jobs/%s' % job.id"
|
||||
class="fs-6 fw-semibold text-decoration-none"
|
||||
t-out="job.name"/>
|
||||
<div class="text-muted small">
|
||||
<span t-if="job.received_date">
|
||||
Received: <span t-field="job.received_date" t-options='{"widget": "date"}'/>
|
||||
</span>
|
||||
<span t-if="job.target_ship_date" class="ms-3">
|
||||
Target: <span t-field="job.target_ship_date" t-options='{"widget": "date"}'/>
|
||||
</span>
|
||||
<span class="ms-3">Qty: <span t-out="job.quantity"/></span>
|
||||
</div>
|
||||
</div>
|
||||
<span t-attf-class="badge #{
|
||||
'text-bg-info' if job.state == 'received' else
|
||||
'text-bg-primary' if job.state == 'in_progress' else
|
||||
'text-bg-warning' if job.state == 'quality_check' else
|
||||
'text-bg-secondary' if job.state == 'ready_to_ship' else
|
||||
'text-bg-success' if job.state == 'shipped' else
|
||||
'text-bg-success'}"
|
||||
t-out="dict(job._fields['state']._description_selection(job.env)).get(job.state)"/>
|
||||
</div>
|
||||
|
||||
<!-- Segmented progress bar -->
|
||||
<t t-set="pct" t-value="job._progress_percent()"/>
|
||||
<div class="o_fp_seg_progress d-flex" style="height: 10px; border-radius: 5px; overflow: hidden; background: var(--bs-secondary-bg);">
|
||||
<!-- Receiving segment (green) -->
|
||||
<div t-attf-style="width: 20%; opacity: #{1 if pct >= 10 else 0.2};"
|
||||
style="background-color: var(--bs-success);"/>
|
||||
<!-- In Progress segment (orange) -->
|
||||
<div t-attf-style="width: 50%; opacity: #{1 if pct >= 35 else 0.2};"
|
||||
style="background-color: var(--bs-warning); margin-left: 2px;"/>
|
||||
<!-- Shipping segment (blue/green) -->
|
||||
<div t-attf-style="width: 30%; opacity: #{1 if pct >= 80 else 0.2};"
|
||||
style="background-color: var(--bs-info); margin-left: 2px;"/>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mt-1" style="font-size: 0.7rem;">
|
||||
<span class="text-muted">Receiving</span>
|
||||
<span class="text-muted">In Progress</span>
|
||||
<span class="text-muted">Shipping</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- JOB — detail -->
|
||||
<!-- ================================================================== -->
|
||||
<template id="portal_my_job" name="My Plating Job">
|
||||
<t t-call="portal.portal_layout">
|
||||
<div class="row mt-2 mb-4">
|
||||
<div class="col-12">
|
||||
<h3 class="mb-1">
|
||||
<span t-out="job.name"/>
|
||||
</h3>
|
||||
<p class="text-muted mb-0">
|
||||
Received
|
||||
<span t-if="job.received_date" t-field="job.received_date"
|
||||
t-options='{"widget": "date"}'/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Segmented progress bar -->
|
||||
<t t-set="pct" t-value="progress_percent"/>
|
||||
<div class="mb-4">
|
||||
<div class="o_fp_seg_progress d-flex" style="height: 14px; border-radius: 7px; overflow: hidden; background: var(--bs-secondary-bg);">
|
||||
<div t-attf-style="width: 20%; opacity: #{1 if pct >= 10 else 0.2};"
|
||||
style="background-color: var(--bs-success);"/>
|
||||
<div t-attf-style="width: 50%; opacity: #{1 if pct >= 35 else 0.2};"
|
||||
style="background-color: var(--bs-warning); margin-left: 2px;"/>
|
||||
<div t-attf-style="width: 30%; opacity: #{1 if pct >= 80 else 0.2};"
|
||||
style="background-color: var(--bs-info); margin-left: 2px;"/>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between small text-muted mt-1">
|
||||
<span>Receiving</span>
|
||||
<span>In Progress</span>
|
||||
<span>QC</span>
|
||||
<span>Ready</span>
|
||||
<span>Shipped</span>
|
||||
<span>Complete</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card bg-body-tertiary border-0 mb-4 o_fp_portal_card">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h6 class="text-muted small text-uppercase">Current Status</h6>
|
||||
<span t-attf-class="badge #{
|
||||
'text-bg-info' if job.state == 'received' else
|
||||
'text-bg-primary' if job.state == 'in_progress' else
|
||||
'text-bg-warning' if job.state == 'quality_check' else
|
||||
'text-bg-secondary' if job.state == 'ready_to_ship' else
|
||||
'text-bg-success' if job.state == 'shipped' else
|
||||
'text-bg-success'} fs-6"
|
||||
t-out="dict(job._fields['state']._description_selection(job.env)).get(job.state)"/>
|
||||
</div>
|
||||
<div class="col-md-6 text-md-end" t-if="job.target_ship_date">
|
||||
<h6 class="text-muted small text-uppercase">Target Ship Date</h6>
|
||||
<h5 class="mb-0">
|
||||
<span t-field="job.target_ship_date"
|
||||
t-options='{"widget": "date"}'/>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-4">
|
||||
<h6 class="text-muted small text-uppercase">Details</h6>
|
||||
<div>
|
||||
<span class="text-muted small">Quantity:</span>
|
||||
<span t-out="job.quantity"/>
|
||||
</div>
|
||||
<div t-if="job.actual_ship_date">
|
||||
<span class="text-muted small">Actual Ship Date:</span>
|
||||
<span t-field="job.actual_ship_date"
|
||||
t-options='{"widget": "date"}'/>
|
||||
</div>
|
||||
<div t-if="job.tracking_ref">
|
||||
<span class="text-muted small">Tracking:</span>
|
||||
<span t-out="job.tracking_ref"/>
|
||||
</div>
|
||||
<div t-if="job.invoice_ref">
|
||||
<span class="text-muted small">Invoice:</span>
|
||||
<span t-out="job.invoice_ref"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 mb-4" t-if="job.process_type_ids">
|
||||
<h6 class="text-muted small text-uppercase">Processes</h6>
|
||||
<span t-foreach="job.process_type_ids" t-as="pt"
|
||||
class="badge text-bg-light border me-1" t-out="pt.name"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<h6 class="text-muted small text-uppercase">Documents</h6>
|
||||
<div class="list-group">
|
||||
<a t-if="job.coc_attachment_id"
|
||||
t-att-href="'/my/jobs/%s/coc' % job.id"
|
||||
class="list-group-item list-group-item-action">
|
||||
<i class="fa fa-file-pdf-o me-2 text-muted"/>
|
||||
Certificate of Conformance
|
||||
<i class="fa fa-download float-end text-muted"/>
|
||||
</a>
|
||||
<a t-if="job.packing_list_attachment_id"
|
||||
t-att-href="'/web/content/%s?download=true' % job.packing_list_attachment_id.id"
|
||||
class="list-group-item list-group-item-action">
|
||||
<i class="fa fa-file-text-o me-2 text-muted"/>
|
||||
Packing List
|
||||
<i class="fa fa-download float-end text-muted"/>
|
||||
</a>
|
||||
<span t-if="not job.coc_attachment_id and not job.packing_list_attachment_id"
|
||||
class="text-muted small">No documents available yet.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4" t-if="job.notes">
|
||||
<h6 class="text-muted small text-uppercase">Notes</h6>
|
||||
<div class="border rounded p-3 bg-body">
|
||||
<span t-out="job.notes"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- PURCHASE ORDERS — list -->
|
||||
<!-- ================================================================== -->
|
||||
<template id="portal_my_purchase_orders" name="My Purchase Orders">
|
||||
<t t-call="portal.portal_layout">
|
||||
<t t-set="breadcrumbs_searchbar" t-value="True"/>
|
||||
<t t-call="portal.portal_searchbar">
|
||||
<t t-set="title">Purchase Orders</t>
|
||||
</t>
|
||||
|
||||
<t t-if="not orders">
|
||||
<div class="o_fp_portal_card card bg-body-tertiary border-0 p-4 text-center">
|
||||
<p class="text-muted mb-0">No purchase orders found.</p>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="orders" t-call="portal.portal_table">
|
||||
<thead>
|
||||
<tr class="active">
|
||||
<th>Order</th>
|
||||
<th>Date</th>
|
||||
<th class="text-end">Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="orders" t-as="order">
|
||||
<td t-out="order.name"/>
|
||||
<td>
|
||||
<span t-field="order.date_order" t-options='{"widget": "date"}'/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-field="order.amount_total"
|
||||
t-options='{"widget": "monetary", "display_currency": order.currency_id}'/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- INVOICES — list -->
|
||||
<!-- ================================================================== -->
|
||||
<template id="portal_my_fp_invoices" name="My Invoices">
|
||||
<t t-call="portal.portal_layout">
|
||||
<t t-set="breadcrumbs_searchbar" t-value="True"/>
|
||||
<t t-call="portal.portal_searchbar">
|
||||
<t t-set="title">Invoices</t>
|
||||
</t>
|
||||
|
||||
<t t-if="not invoices">
|
||||
<div class="o_fp_portal_card card bg-body-tertiary border-0 p-4 text-center">
|
||||
<p class="text-muted mb-0">No invoices found.</p>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="invoices" t-call="portal.portal_table">
|
||||
<thead>
|
||||
<tr class="active">
|
||||
<th>Invoice</th>
|
||||
<th>Date</th>
|
||||
<th>Due Date</th>
|
||||
<th class="text-end">Amount Due</th>
|
||||
<th class="text-end">Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="invoices" t-as="inv">
|
||||
<td t-out="inv.name"/>
|
||||
<td>
|
||||
<span t-if="inv.invoice_date"
|
||||
t-field="inv.invoice_date"
|
||||
t-options='{"widget": "date"}'/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-if="inv.invoice_date_due"
|
||||
t-field="inv.invoice_date_due"
|
||||
t-options='{"widget": "date"}'/>
|
||||
<span t-else="" class="text-muted">--</span>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-field="inv.amount_residual"
|
||||
t-options='{"widget": "monetary", "display_currency": inv.currency_id}'/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-field="inv.amount_total"
|
||||
t-options='{"widget": "monetary", "display_currency": inv.currency_id}'/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- DELIVERIES / PACKING SLIPS — list -->
|
||||
<!-- ================================================================== -->
|
||||
<template id="portal_my_deliveries" name="My Deliveries">
|
||||
<t t-call="portal.portal_layout">
|
||||
<t t-set="breadcrumbs_searchbar" t-value="True"/>
|
||||
<t t-call="portal.portal_searchbar">
|
||||
<t t-set="title">Packing Slips / Deliveries</t>
|
||||
</t>
|
||||
|
||||
<t t-if="not deliveries">
|
||||
<div class="o_fp_portal_card card bg-body-tertiary border-0 p-4 text-center">
|
||||
<p class="text-muted mb-0">No deliveries found.</p>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="deliveries" t-call="portal.portal_table">
|
||||
<thead>
|
||||
<tr class="active">
|
||||
<th>Reference</th>
|
||||
<th>Date</th>
|
||||
<th class="text-end">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="deliveries" t-as="dlv">
|
||||
<td t-out="dlv.name"/>
|
||||
<td>
|
||||
<span t-if="dlv.date_done"
|
||||
t-field="dlv.date_done"
|
||||
t-options='{"widget": "date"}'/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span class="badge text-bg-success">Delivered</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- CERTIFICATIONS — list -->
|
||||
<!-- ================================================================== -->
|
||||
<template id="portal_my_certifications" name="My Certifications">
|
||||
<t t-call="portal.portal_layout">
|
||||
<t t-set="breadcrumbs_searchbar" t-value="True"/>
|
||||
<t t-call="portal.portal_searchbar">
|
||||
<t t-set="title">Certifications & Quality</t>
|
||||
</t>
|
||||
|
||||
<t t-if="not cert_jobs">
|
||||
<div class="o_fp_portal_card card bg-body-tertiary border-0 p-4 text-center">
|
||||
<p class="text-muted mb-0">No certificates available yet.</p>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="cert_jobs" t-call="portal.portal_table">
|
||||
<thead>
|
||||
<tr class="active">
|
||||
<th>Job</th>
|
||||
<th>Ship Date</th>
|
||||
<th>Processes</th>
|
||||
<th class="text-end">Download</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="cert_jobs" t-as="cj">
|
||||
<td>
|
||||
<a t-att-href="'/my/jobs/%s' % cj.id" t-out="cj.name"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-if="cj.actual_ship_date"
|
||||
t-field="cj.actual_ship_date"
|
||||
t-options='{"widget": "date"}'/>
|
||||
<span t-else="" class="text-muted">--</span>
|
||||
</td>
|
||||
<td>
|
||||
<span t-foreach="cj.process_type_ids" t-as="pt"
|
||||
class="badge text-bg-light border me-1" t-out="pt.name"/>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<a t-att-href="'/my/jobs/%s/coc' % cj.id"
|
||||
class="btn btn-sm btn-outline-success">
|
||||
<i class="fa fa-download me-1"/>CoC
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
@@ -0,0 +1,311 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
Part of the Fusion Plating product family.
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- Quote Request — list -->
|
||||
<!-- ================================================================== -->
|
||||
<record id="view_fp_quote_request_list" model="ir.ui.view">
|
||||
<field name="name">fp.quote.request.list</field>
|
||||
<field name="model">fusion.plating.quote.request</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Quote Requests"
|
||||
decoration-info="state == 'new'"
|
||||
decoration-warning="state == 'under_review'"
|
||||
decoration-success="state == 'accepted'"
|
||||
decoration-muted="state in ('declined','expired')">
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="contact_name" optional="show"/>
|
||||
<field name="quantity"/>
|
||||
<field name="target_delivery"/>
|
||||
<field name="quoted_price" widget="monetary" optional="show"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="state" widget="badge"
|
||||
decoration-info="state == 'new'"
|
||||
decoration-warning="state == 'under_review'"
|
||||
decoration-primary="state == 'quoted'"
|
||||
decoration-success="state == 'accepted'"
|
||||
decoration-danger="state == 'declined'"
|
||||
decoration-muted="state == 'expired'"/>
|
||||
<field name="create_date" optional="show"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- Quote Request — form -->
|
||||
<!-- ================================================================== -->
|
||||
<record id="view_fp_quote_request_form" model="ir.ui.view">
|
||||
<field name="name">fp.quote.request.form</field>
|
||||
<field name="model">fusion.plating.quote.request</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Quote Request">
|
||||
<header>
|
||||
<button name="action_mark_under_review" string="Start Review" type="object"
|
||||
class="oe_highlight" invisible="state != 'new'"/>
|
||||
<button name="action_send_quote" string="Send Quote" type="object"
|
||||
class="oe_highlight"
|
||||
invisible="state not in ('new','under_review')"/>
|
||||
<button name="action_mark_accepted" string="Mark Accepted" type="object"
|
||||
invisible="state != 'quoted'"/>
|
||||
<button name="action_create_sale_order" string="Create Sale Order" type="object"
|
||||
class="oe_highlight"
|
||||
invisible="state != 'accepted'"/>
|
||||
<button name="action_mark_declined" string="Mark Declined" type="object"
|
||||
invisible="state != 'quoted'"/>
|
||||
<button name="action_mark_expired" string="Mark Expired" type="object"
|
||||
invisible="state in ('accepted','declined','expired')"/>
|
||||
<field name="state" widget="statusbar"
|
||||
statusbar_visible="new,under_review,quoted,accepted"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<label for="name"/>
|
||||
<h1><field name="name" readonly="1"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="partner_id"/>
|
||||
<field name="contact_name"/>
|
||||
<field name="contact_email"/>
|
||||
<field name="contact_phone"/>
|
||||
<field name="company_name"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="quantity"/>
|
||||
<field name="target_delivery"/>
|
||||
<field name="process_type_ids" widget="many2many_tags"/>
|
||||
<field name="quoted_price" widget="monetary"
|
||||
readonly="state in ('new','under_review')"/>
|
||||
<field name="currency_id" groups="base.group_multi_currency"/>
|
||||
<field name="quoted_by_id" readonly="1"/>
|
||||
<field name="quote_sent_date" readonly="1"/>
|
||||
<field name="customer_response_date" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="shipping_address_id"/>
|
||||
<field name="billing_same_as_shipping"/>
|
||||
<field name="billing_address_id"
|
||||
invisible="billing_same_as_shipping"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Part Lines">
|
||||
<field name="line_ids">
|
||||
<list editable="bottom">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="product_id"/>
|
||||
<field name="part_number"/>
|
||||
<field name="quantity"/>
|
||||
<field name="count"/>
|
||||
<field name="description"/>
|
||||
<field name="spec_text" optional="hide"/>
|
||||
<field name="attachment_ids" widget="many2many_binary"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Part Description">
|
||||
<field name="part_description"/>
|
||||
</page>
|
||||
<page string="Special Instructions">
|
||||
<field name="special_instructions"/>
|
||||
</page>
|
||||
<page string="Attachments">
|
||||
<field name="drawing_attachment_ids" widget="many2many_binary"/>
|
||||
</page>
|
||||
<page string="Internal Notes">
|
||||
<field name="notes_internal"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- Quote Request — search -->
|
||||
<!-- ================================================================== -->
|
||||
<record id="view_fp_quote_request_search" model="ir.ui.view">
|
||||
<field name="name">fp.quote.request.search</field>
|
||||
<field name="model">fusion.plating.quote.request</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Quote Requests">
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="contact_name"/>
|
||||
<field name="company_name"/>
|
||||
<separator/>
|
||||
<filter string="New" name="new" domain="[('state','=','new')]"/>
|
||||
<filter string="Under Review" name="review" domain="[('state','=','under_review')]"/>
|
||||
<filter string="Quoted" name="quoted" domain="[('state','=','quoted')]"/>
|
||||
<filter string="Accepted" name="accepted" domain="[('state','=','accepted')]"/>
|
||||
<separator/>
|
||||
<group>
|
||||
<filter string="Customer" name="group_partner" context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Status" name="group_state" context="{'group_by':'state'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- Quote Request — action -->
|
||||
<!-- ================================================================== -->
|
||||
<record id="action_fp_quote_request" model="ir.actions.act_window">
|
||||
<field name="name">Quote Requests</field>
|
||||
<field name="res_model">fusion.plating.quote.request</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_fp_quote_request_search"/>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
No quote requests yet
|
||||
</p>
|
||||
<p>
|
||||
Customers can submit Requests for Quote (RFQ) from the portal at
|
||||
<code>/my/quote_requests</code>. Once submitted, they appear here
|
||||
for your team to review and price.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- Portal Job — list -->
|
||||
<!-- ================================================================== -->
|
||||
<record id="view_fp_portal_job_list" model="ir.ui.view">
|
||||
<field name="name">fp.portal.job.list</field>
|
||||
<field name="model">fusion.plating.portal.job</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Plating Jobs"
|
||||
decoration-info="state == 'received'"
|
||||
decoration-success="state == 'complete'">
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="received_date"/>
|
||||
<field name="target_ship_date"/>
|
||||
<field name="actual_ship_date" optional="hide"/>
|
||||
<field name="quantity"/>
|
||||
<field name="state" widget="badge"
|
||||
decoration-info="state == 'received'"
|
||||
decoration-primary="state == 'in_progress'"
|
||||
decoration-warning="state == 'quality_check'"
|
||||
decoration-success="state in ('shipped','complete')"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- Portal Job — form -->
|
||||
<!-- ================================================================== -->
|
||||
<record id="view_fp_portal_job_form" model="ir.ui.view">
|
||||
<field name="name">fp.portal.job.form</field>
|
||||
<field name="model">fusion.plating.portal.job</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Plating Job">
|
||||
<header>
|
||||
<field name="state" widget="statusbar"
|
||||
statusbar_visible="received,in_progress,quality_check,ready_to_ship,shipped,complete"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<label for="name"/>
|
||||
<h1><field name="name"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="partner_id"/>
|
||||
<field name="quantity"/>
|
||||
<field name="process_type_ids" widget="many2many_tags"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="received_date"/>
|
||||
<field name="target_ship_date"/>
|
||||
<field name="actual_ship_date"/>
|
||||
<field name="tracking_ref"/>
|
||||
<field name="invoice_ref"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Documents">
|
||||
<field name="coc_attachment_id"/>
|
||||
<field name="packing_list_attachment_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Customer-Visible Notes">
|
||||
<field name="notes"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- Portal Job — search -->
|
||||
<!-- ================================================================== -->
|
||||
<record id="view_fp_portal_job_search" model="ir.ui.view">
|
||||
<field name="name">fp.portal.job.search</field>
|
||||
<field name="model">fusion.plating.portal.job</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Plating Jobs">
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="invoice_ref"/>
|
||||
<separator/>
|
||||
<filter string="Received" name="received" domain="[('state','=','received')]"/>
|
||||
<filter string="In Progress" name="in_progress" domain="[('state','=','in_progress')]"/>
|
||||
<filter string="Quality Check" name="quality_check" domain="[('state','=','quality_check')]"/>
|
||||
<filter string="Ready to Ship" name="ready_to_ship" domain="[('state','=','ready_to_ship')]"/>
|
||||
<filter string="Shipped" name="shipped" domain="[('state','=','shipped')]"/>
|
||||
<filter string="Complete" name="complete" domain="[('state','=','complete')]"/>
|
||||
<separator/>
|
||||
<group>
|
||||
<filter string="Customer" name="group_partner" context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Status" name="group_state" context="{'group_by':'state'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- Portal Job — action -->
|
||||
<!-- ================================================================== -->
|
||||
<record id="action_fp_portal_job" model="ir.actions.act_window">
|
||||
<field name="name">Plating Jobs</field>
|
||||
<field name="res_model">fusion.plating.portal.job</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_fp_portal_job_search"/>
|
||||
</record>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- res.partner — extend form to surface portal flag + counts -->
|
||||
<!-- ================================================================== -->
|
||||
<record id="view_partner_form_fp_portal" model="ir.ui.view">
|
||||
<field name="name">res.partner.form.fp.portal</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//sheet" position="inside">
|
||||
<group string="Plating Portal" name="fp_portal_group">
|
||||
<field name="x_fc_portal_enabled"/>
|
||||
<field name="x_fc_quote_request_count" readonly="1"/>
|
||||
<field name="x_fc_portal_job_count" readonly="1"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user