This commit is contained in:
gsinghpal
2026-04-20 22:58:25 -04:00
parent 192aa60d00
commit 4d6095cd2a
15 changed files with 2071 additions and 0 deletions

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
{
'name': 'Fusion Reports — Templates',
'version': '19.0.1.0.0',
'category': 'Tools/Reports',
'summary': 'Branded PDF templates for Quotation, Sales Order, Invoice, Delivery, Purchase Order, and Payment Receipt.',
'description': """
Fusion Reports Templates
========================
Replaces Odoo's default PDF layouts for the customer-facing quote-to-cash
documents with a consistent NEXA Systems branded design. The customer-facing
mail templates are redirected to the new reports so emails always send the
branded PDF, not the stock Odoo one.
Reports included (each in portrait + landscape):
* Quotation / Sales Order
* Customer Invoice / Credit Note
* Delivery / Packing Slip
* Purchase Order / RFQ
* Payment Receipt
""",
'author': 'Nexa Systems Inc.',
'website': 'https://nexasystems.ca',
'depends': [
'sale',
'account',
'stock',
'purchase',
],
'data': [
'security/ir.model.access.csv',
'report/report_base_styles.xml',
'report/report_actions.xml',
'report/report_sale.xml',
'report/report_invoice.xml',
'report/report_delivery.xml',
'report/report_purchase.xml',
'report/report_receipt.xml',
'data/mail_template_override.xml',
'data/hide_default_reports.xml',
],
'installable': True,
'application': False,
'auto_install': False,
'license': 'OPL-1',
}

View File

@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Remove Odoo's default PDF reports from each model's Print dropdown
so users can't accidentally send the unbranded version. Setting
`binding_model_id` to False leaves the underlying report intact;
an admin can re-enable any of these from Settings → Technical →
Actions → Reports.
Loaded last so it overrides the binding declarations shipped by
the base modules.
-->
<odoo noupdate="0">
<!-- sale.order -->
<record id="sale.action_report_saleorder" model="ir.actions.report">
<field name="binding_model_id" eval="False"/>
<field name="binding_type">action</field>
</record>
<!-- account.move -->
<record id="account.account_invoices" model="ir.actions.report">
<field name="binding_model_id" eval="False"/>
<field name="binding_type">action</field>
</record>
<record id="account.account_invoices_without_payment" model="ir.actions.report">
<field name="binding_model_id" eval="False"/>
<field name="binding_type">action</field>
</record>
<!-- stock.picking -->
<record id="stock.action_report_delivery" model="ir.actions.report">
<field name="binding_model_id" eval="False"/>
<field name="binding_type">action</field>
</record>
<!-- purchase.order: PO + RFQ -->
<record id="purchase.action_report_purchase_order" model="ir.actions.report">
<field name="binding_model_id" eval="False"/>
<field name="binding_type">action</field>
</record>
<record id="purchase.report_purchase_quotation" model="ir.actions.report">
<field name="binding_model_id" eval="False"/>
<field name="binding_type">action</field>
</record>
<!-- sale.order: pro-forma -->
<record id="sale.action_report_pro_forma_invoice" model="ir.actions.report">
<field name="binding_model_id" eval="False"/>
<field name="binding_type">action</field>
</record>
<!-- account.payment -->
<record id="account.action_report_payment_receipt" model="ir.actions.report">
<field name="binding_model_id" eval="False"/>
<field name="binding_type">action</field>
</record>
<!-- ================================================================
Print-menu sequencing — pin Fusion reports to the top.
Portrait = 10 (primary), Landscape = 15 (secondary).
================================================================ -->
<record id="fusion_reports_templates.action_report_fr_sale_portrait" model="ir.actions.report">
<field name="sequence" eval="10"/>
</record>
<record id="fusion_reports_templates.action_report_fr_sale_landscape" model="ir.actions.report">
<field name="sequence" eval="15"/>
</record>
<record id="fusion_reports_templates.action_report_fr_invoice_portrait" model="ir.actions.report">
<field name="sequence" eval="10"/>
</record>
<record id="fusion_reports_templates.action_report_fr_invoice_landscape" model="ir.actions.report">
<field name="sequence" eval="15"/>
</record>
<record id="fusion_reports_templates.action_report_fr_delivery_portrait" model="ir.actions.report">
<field name="sequence" eval="10"/>
</record>
<record id="fusion_reports_templates.action_report_fr_delivery_landscape" model="ir.actions.report">
<field name="sequence" eval="15"/>
</record>
<record id="fusion_reports_templates.action_report_fr_purchase_portrait" model="ir.actions.report">
<field name="sequence" eval="10"/>
</record>
<record id="fusion_reports_templates.action_report_fr_purchase_landscape" model="ir.actions.report">
<field name="sequence" eval="15"/>
</record>
<record id="fusion_reports_templates.action_report_fr_receipt_portrait" model="ir.actions.report">
<field name="sequence" eval="10"/>
</record>
<record id="fusion_reports_templates.action_report_fr_receipt_landscape" model="ir.actions.report">
<field name="sequence" eval="15"/>
</record>
</odoo>

View File

@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Re-point the stock "Send by Email" mail templates so the attached PDF
uses Fusion Reports branded design instead of Odoo's default layout.
`report_template_ids` is Many2many on mail.template; replacing with a
`[(6, 0, [...])]` op swaps the attached reports atomically.
noupdate="0" so upgrades re-apply the binding (counter-acts any manual
edit a user may have made in Settings → Technical → Email Templates).
-->
<odoo noupdate="0">
<!-- Sales: Send Quotation -->
<record id="sale.email_template_edi_sale" model="mail.template">
<field name="report_template_ids" eval="[(6, 0, [
ref('fusion_reports_templates.action_report_fr_sale_portrait')
])]"/>
</record>
<!-- Sales: Order Confirmation (confirmed SO) -->
<record id="sale.mail_template_sale_confirmation" model="mail.template">
<field name="report_template_ids" eval="[(6, 0, [
ref('fusion_reports_templates.action_report_fr_sale_portrait')
])]"/>
</record>
<!-- Invoice: Send -->
<record id="account.email_template_edi_invoice" model="mail.template">
<field name="report_template_ids" eval="[(6, 0, [
ref('fusion_reports_templates.action_report_fr_invoice_portrait')
])]"/>
</record>
<!-- Credit Note: Send -->
<record id="account.email_template_edi_credit_note" model="mail.template">
<field name="report_template_ids" eval="[(6, 0, [
ref('fusion_reports_templates.action_report_fr_invoice_portrait')
])]"/>
</record>
<!-- Payment: Receipt -->
<record id="account.mail_template_data_payment_receipt" model="mail.template">
<field name="report_template_ids" eval="[(6, 0, [
ref('fusion_reports_templates.action_report_fr_receipt_portrait')
])]"/>
</record>
<!-- Stock: Delivery confirmation -->
<record id="stock.mail_template_data_delivery_confirmation" model="mail.template">
<field name="report_template_ids" eval="[(6, 0, [
ref('fusion_reports_templates.action_report_fr_delivery_portrait')
])]"/>
</record>
<!-- Purchase: RFQ -->
<record id="purchase.email_template_edi_purchase" model="mail.template">
<field name="report_template_ids" eval="[(6, 0, [
ref('fusion_reports_templates.action_report_fr_purchase_portrait')
])]"/>
</record>
<!-- Purchase: Confirmed PO -->
<record id="purchase.email_template_edi_purchase_done" model="mail.template">
<field name="report_template_ids" eval="[(6, 0, [
ref('fusion_reports_templates.action_report_fr_purchase_portrait')
])]"/>
</record>
<!-- Purchase: Vendor Reminder -->
<record id="purchase.email_template_edi_purchase_reminder" model="mail.template">
<field name="report_template_ids" eval="[(6, 0, [
ref('fusion_reports_templates.action_report_fr_purchase_portrait')
])]"/>
</record>
</odoo>

View File

@@ -0,0 +1 @@
from . import ir_actions_report

View File

@@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
"""Sort the Print-menu report bindings by `sequence`.
Odoo 19 returns `ir.actions.report` bindings in raw insertion order, which
pushes third-party reports to the bottom even when they are the primary
customer-facing document. Adding a sequence field and sorting inside
`ir.actions.actions._get_bindings` restores the expected ordering.
"""
from odoo import api, fields, models
from odoo.tools import frozendict
class IrActionsReport(models.Model):
_inherit = 'ir.actions.report'
sequence = fields.Integer(
default=100,
help='Order in which this report appears in the Print menu '
'(lower = higher in the list).',
)
class IrActionsActions(models.Model):
_inherit = 'ir.actions.actions'
@api.model
def _get_bindings(self, model_name):
result = super()._get_bindings(model_name)
if not result.get('report'):
return result
sorted_reports = tuple(sorted(
result['report'],
key=lambda vals: (
vals.get('sequence', 100),
(vals.get('name') or '').lower(),
),
))
new_result = dict(result)
new_result['report'] = sorted_reports
return frozendict(new_result)

View File

@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
ir.actions.report records for every Fusion Reports template.
-->
<odoo>
<!-- ============================================================= -->
<!-- Quotation / Sales Order -->
<!-- ============================================================= -->
<record id="action_report_fr_sale_portrait" model="ir.actions.report">
<field name="name">Quotation / Order (Portrait)</field>
<field name="model">sale.order</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">fusion_reports_templates.report_fr_sale_portrait</field>
<field name="report_file">fusion_reports_templates.report_fr_sale_portrait</field>
<field name="print_report_name">(object.state in ('draft', 'sent') and 'Quotation - %s' % object.name) or 'Order - %s' % object.name</field>
<field name="binding_model_id" ref="sale.model_sale_order"/>
<field name="binding_type">report</field>
</record>
<record id="action_report_fr_sale_landscape" model="ir.actions.report">
<field name="name">Quotation / Order (Landscape)</field>
<field name="model">sale.order</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">fusion_reports_templates.report_fr_sale_landscape</field>
<field name="report_file">fusion_reports_templates.report_fr_sale_landscape</field>
<field name="print_report_name">(object.state in ('draft', 'sent') and 'Quotation - %s' % object.name) or 'Order - %s' % object.name</field>
<field name="binding_model_id" ref="sale.model_sale_order"/>
<field name="binding_type">report</field>
<field name="paperformat_id" ref="paperformat_fr_a4_landscape"/>
</record>
<!-- ============================================================= -->
<!-- Invoice / Credit Note / Vendor Bill -->
<!-- ============================================================= -->
<record id="action_report_fr_invoice_portrait" model="ir.actions.report">
<field name="name">Invoice (Portrait)</field>
<field name="model">account.move</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">fusion_reports_templates.report_fr_invoice_portrait</field>
<field name="report_file">fusion_reports_templates.report_fr_invoice_portrait</field>
<field name="print_report_name">'Invoice - %s' % (object.name or '')</field>
<field name="binding_model_id" ref="account.model_account_move"/>
<field name="binding_type">report</field>
</record>
<record id="action_report_fr_invoice_landscape" model="ir.actions.report">
<field name="name">Invoice (Landscape)</field>
<field name="model">account.move</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">fusion_reports_templates.report_fr_invoice_landscape</field>
<field name="report_file">fusion_reports_templates.report_fr_invoice_landscape</field>
<field name="print_report_name">'Invoice - %s' % (object.name or '')</field>
<field name="binding_model_id" ref="account.model_account_move"/>
<field name="binding_type">report</field>
<field name="paperformat_id" ref="paperformat_fr_a4_landscape"/>
</record>
<!-- ============================================================= -->
<!-- Delivery Slip / Receipt / Transfer -->
<!-- ============================================================= -->
<record id="action_report_fr_delivery_portrait" model="ir.actions.report">
<field name="name">Delivery Slip (Portrait)</field>
<field name="model">stock.picking</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">fusion_reports_templates.report_fr_delivery_portrait</field>
<field name="report_file">fusion_reports_templates.report_fr_delivery_portrait</field>
<field name="print_report_name">'Delivery - %s' % object.name</field>
<field name="binding_model_id" ref="stock.model_stock_picking"/>
<field name="binding_type">report</field>
</record>
<record id="action_report_fr_delivery_landscape" model="ir.actions.report">
<field name="name">Delivery Slip (Landscape)</field>
<field name="model">stock.picking</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">fusion_reports_templates.report_fr_delivery_landscape</field>
<field name="report_file">fusion_reports_templates.report_fr_delivery_landscape</field>
<field name="print_report_name">'Delivery - %s' % object.name</field>
<field name="binding_model_id" ref="stock.model_stock_picking"/>
<field name="binding_type">report</field>
<field name="paperformat_id" ref="paperformat_fr_a4_landscape"/>
</record>
<!-- ============================================================= -->
<!-- Purchase Order / RFQ -->
<!-- ============================================================= -->
<record id="action_report_fr_purchase_portrait" model="ir.actions.report">
<field name="name">Purchase Order (Portrait)</field>
<field name="model">purchase.order</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">fusion_reports_templates.report_fr_purchase_portrait</field>
<field name="report_file">fusion_reports_templates.report_fr_purchase_portrait</field>
<field name="print_report_name">(object.state in ('draft', 'sent', 'to approve') and 'RFQ - %s' % object.name) or 'PO - %s' % object.name</field>
<field name="binding_model_id" ref="purchase.model_purchase_order"/>
<field name="binding_type">report</field>
</record>
<record id="action_report_fr_purchase_landscape" model="ir.actions.report">
<field name="name">Purchase Order (Landscape)</field>
<field name="model">purchase.order</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">fusion_reports_templates.report_fr_purchase_landscape</field>
<field name="report_file">fusion_reports_templates.report_fr_purchase_landscape</field>
<field name="print_report_name">(object.state in ('draft', 'sent', 'to approve') and 'RFQ - %s' % object.name) or 'PO - %s' % object.name</field>
<field name="binding_model_id" ref="purchase.model_purchase_order"/>
<field name="binding_type">report</field>
<field name="paperformat_id" ref="paperformat_fr_a4_landscape"/>
</record>
<!-- ============================================================= -->
<!-- Payment Receipt -->
<!-- ============================================================= -->
<record id="action_report_fr_receipt_portrait" model="ir.actions.report">
<field name="name">Payment Receipt (Portrait)</field>
<field name="model">account.payment</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">fusion_reports_templates.report_fr_receipt_portrait</field>
<field name="report_file">fusion_reports_templates.report_fr_receipt_portrait</field>
<field name="print_report_name">'Receipt - %s' % (object.name or '')</field>
<field name="binding_model_id" ref="account.model_account_payment"/>
<field name="binding_type">report</field>
</record>
<record id="action_report_fr_receipt_landscape" model="ir.actions.report">
<field name="name">Payment Receipt (Landscape)</field>
<field name="model">account.payment</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">fusion_reports_templates.report_fr_receipt_landscape</field>
<field name="report_file">fusion_reports_templates.report_fr_receipt_landscape</field>
<field name="print_report_name">'Receipt - %s' % (object.name or '')</field>
<field name="binding_model_id" ref="account.model_account_payment"/>
<field name="binding_type">report</field>
<field name="paperformat_id" ref="paperformat_fr_a4_landscape"/>
</record>
</odoo>

View File

@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Shared CSS + paper format for all Fusion Reports templates
(portrait + landscape).
The primary colour is driven by the active company's
res.company.primary_color field (Settings → Company → Report Layout),
falling back to #1d1f1e when unset.
Apply `.fr-header-primary` to any `<th>` or `<td>` that should render
as a primary-coloured section banner.
-->
<odoo>
<!-- ============================================================= -->
<!-- Landscape Paper Format -->
<!-- ============================================================= -->
<record id="paperformat_fr_a4_landscape" model="report.paperformat">
<field name="name">A4 Landscape (Fusion Reports)</field>
<field name="default" eval="False"/>
<field name="format">A4</field>
<field name="orientation">Landscape</field>
<field name="margin_top">20</field>
<field name="margin_bottom">20</field>
<field name="margin_left">7</field>
<field name="margin_right">7</field>
<field name="header_line" eval="False"/>
<field name="header_spacing">20</field>
<field name="dpi">90</field>
</record>
<!-- ============================================================= -->
<!-- Portrait Styles -->
<!-- ============================================================= -->
<template id="fr_portrait_styles">
<t t-set="_fr_company" t-value="doc.company_id if doc and 'company_id' in doc._fields else (company if company else user.company_id)"/>
<t t-set="fr_primary" t-value="(_fr_company.primary_color if _fr_company else False) or '#1d1f1e'"/>
<style>
.fr-report { font-family: Arial, sans-serif; font-size: 10pt; color: #000; }
.fr-report table { width: 100%; border-collapse: collapse; margin-bottom: 10px; }
.fr-report table.bordered, .fr-report table.bordered th, .fr-report table.bordered td { border: 1px solid #000; }
.fr-report th { background-color: <t t-out="fr_primary"/>; color: white; padding: 6px 8px; font-weight: bold; text-align: center; font-size: 9pt; }
.fr-report td { padding: 6px 8px; vertical-align: top; font-size: 10pt; }
.fr-report .text-center { text-align: center; }
.fr-report .text-end { text-align: right; }
.fr-report .text-start { text-align: left; }
.fr-report .section-row { background-color: #f0f0f0; font-weight: bold; }
.fr-report .note-row { font-style: italic; color: #555; font-size: 9pt; }
.fr-report h4 { color: <t t-out="fr_primary"/>; margin: 0 0 15px 0; font-size: 16pt; }
.fr-report .totals-table { border: 1px solid #000; border-collapse: collapse; }
.fr-report .totals-table td { border: 1px solid #000; padding: 6px 8px; }
.fr-report .info-header { background-color: #f5f5f5; color: #333; }
.fr-report .highlight-box { border: 2px solid <t t-out="fr_primary"/>; background-color: #eaf2f8; padding: 10px; margin: 10px 0; }
.fr-report .fr-header-primary { background-color: <t t-out="fr_primary"/>; color: white; }
.fr-report .paid-stamp { color: #28a745; font-size: 36pt; font-weight: bold; border: 4px solid #28a745; padding: 10px 20px; transform: rotate(-8deg); display: inline-block; }
.fr-report .status-ok { color: #2e7d32; font-weight: bold; }
.fr-report .status-warning { color: #f57f17; font-weight: bold; }
.fr-report .status-fail { color: #c62828; font-weight: bold; }
.fr-report .sig-line { border-bottom: 1px solid #000; height: 60px; margin-bottom: 4px; }
.fr-report .small-muted { font-size: 8pt; color: #666; }
.fr-report .fr-cell-mid { vertical-align: middle !important; }
.fr-report .fr-keep-together { page-break-inside: avoid; break-inside: avoid; }
.fr-report table tr { page-break-inside: avoid; break-inside: avoid; }
</style>
</template>
<!-- ============================================================= -->
<!-- Landscape Styles -->
<!-- ============================================================= -->
<template id="fr_landscape_styles">
<t t-set="_fr_company" t-value="doc.company_id if doc and 'company_id' in doc._fields else (company if company else user.company_id)"/>
<t t-set="fr_primary" t-value="(_fr_company.primary_color if _fr_company else False) or '#1d1f1e'"/>
<style>
.fr-landscape { font-family: Arial, sans-serif; font-size: 10pt; color: #000; }
.fr-landscape table { width: 100%; border-collapse: collapse; margin-bottom: 6px; }
.fr-landscape table.bordered, .fr-landscape table.bordered th, .fr-landscape table.bordered td { border: 1px solid #000; }
.fr-landscape th { background-color: <t t-out="fr_primary"/>; color: white; padding: 4px 8px; font-weight: bold; font-size: 9pt; }
.fr-landscape td { padding: 4px 8px; vertical-align: top; font-size: 9.5pt; }
.fr-landscape .text-center { text-align: center; }
.fr-landscape .text-end { text-align: right; }
.fr-landscape .text-start { text-align: left; }
.fr-landscape .section-row { background-color: #f0f0f0; font-weight: bold; }
.fr-landscape .note-row { font-style: italic; color: #555; }
.fr-landscape h2 { color: <t t-out="fr_primary"/>; margin: 4px 0; font-size: 18pt; }
.fr-landscape .info-table td { padding: 8px 12px; font-size: 11pt; }
.fr-landscape .info-table th { background-color: #f5f5f5; color: #333; font-size: 10pt; padding: 6px 12px; }
.fr-landscape .totals-table { border: 1px solid #000; }
.fr-landscape .totals-table td { border: 1px solid #000; padding: 8px 12px; font-size: 11pt; }
.fr-landscape .highlight-box { border: 2px solid <t t-out="fr_primary"/>; background-color: #eaf2f8; padding: 6px 10px; margin: 6px 0; font-size: 9pt; }
.fr-landscape .fr-header-primary { background-color: <t t-out="fr_primary"/>; color: white; }
.fr-landscape .paid-stamp { color: #28a745; font-size: 42pt; font-weight: bold; border: 4px solid #28a745; padding: 10px 20px; transform: rotate(-8deg); display: inline-block; }
.fr-landscape .status-ok { color: #2e7d32; font-weight: bold; }
.fr-landscape .status-warning { color: #f57f17; font-weight: bold; }
.fr-landscape .status-fail { color: #c62828; font-weight: bold; }
.fr-landscape .sig-line { border-bottom: 1px solid #000; height: 45px; margin-bottom: 3px; }
.fr-landscape .small-muted { font-size: 9pt; color: #666; }
.fr-landscape .fr-cell-mid { vertical-align: middle !important; }
.fr-landscape table tr { page-break-inside: avoid; break-inside: avoid; }
</style>
</template>
</odoo>

View File

@@ -0,0 +1,255 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Delivery / Packing Slip — portrait + landscape.
-->
<odoo>
<!-- ============================================================= -->
<!-- PORTRAIT -->
<!-- ============================================================= -->
<template id="report_fr_delivery_portrait">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="doc">
<t t-call="web.external_layout">
<t t-set="doc" t-value="doc.with_context(lang=doc.partner_id.lang)"/>
<t t-call="fusion_reports_templates.fr_portrait_styles"/>
<div class="fr-report">
<div class="page">
<h4>
<t t-if="doc.picking_type_id.code == 'outgoing'">Delivery Slip </t>
<t t-elif="doc.picking_type_id.code == 'incoming'">Receipt </t>
<t t-else="">Transfer </t>
<span t-field="doc.name"/>
</h4>
<table class="bordered">
<thead>
<tr>
<th style="width: 50%;">FROM</th>
<th style="width: 50%;">TO</th>
</tr>
</thead>
<tbody>
<tr>
<td style="height: 70px;">
<strong><span t-field="doc.location_id.display_name"/></strong><br/>
<span t-field="doc.company_id.partner_id"
t-options="{'widget': 'contact', 'fields': ['address', 'phone'], 'no_marker': True}"/>
</td>
<td style="height: 70px;">
<t t-if="doc.partner_id">
<div t-field="doc.partner_id"
t-options="{'widget': 'contact', 'fields': ['name', 'address', 'phone', 'email'], 'no_marker': True}"/>
</t>
<t t-else="">
<strong><span t-field="doc.location_dest_id.display_name"/></strong>
</t>
</td>
</tr>
</tbody>
</table>
<table class="bordered">
<thead>
<tr>
<th class="info-header" style="width: 25%;">SCHEDULED DATE</th>
<th class="info-header" style="width: 25%;">ORDER</th>
<th class="info-header" style="width: 25%;">CARRIER</th>
<th class="info-header" style="width: 25%;">TRACKING #</th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-center"><span t-field="doc.scheduled_date" t-options="{'widget': 'date'}"/></td>
<td class="text-center"><span t-esc="doc.origin or '-'"/></td>
<td class="text-center"><span t-field="doc.carrier_id"/></td>
<td class="text-center"><span t-esc="doc.carrier_tracking_ref or '-'"/></td>
</tr>
</tbody>
</table>
<table class="bordered">
<thead>
<tr>
<th style="width: 15%;">SKU</th>
<th class="text-start" style="width: 55%;">PRODUCT</th>
<th style="width: 15%;">QTY DONE</th>
<th style="width: 15%;">UOM</th>
</tr>
</thead>
<tbody>
<t t-foreach="doc.move_ids_without_package" t-as="move">
<tr>
<td class="text-center"><span t-esc="move.product_id.default_code or ''"/></td>
<td><span t-field="move.product_id.display_name"/>
<t t-if="move.description_picking and move.description_picking != move.product_id.display_name">
<br/><span class="small-muted" t-esc="move.description_picking"/>
</t>
</td>
<td class="text-center">
<t t-if="doc.state == 'done'">
<span t-esc="int(move.quantity) if move.quantity == int(move.quantity) else move.quantity"/>
</t>
<t t-else="">
<span t-esc="int(move.product_uom_qty) if move.product_uom_qty == int(move.product_uom_qty) else move.product_uom_qty"/>
</t>
</td>
<td class="text-center"><span t-field="move.product_uom"/></td>
</tr>
</t>
</tbody>
</table>
<t t-if="doc.note">
<div style="margin-top: 15px;">
<strong>Notes:</strong>
<div t-field="doc.note"/>
</div>
</t>
<div class="row" style="margin-top: 30px;">
<div class="col-6">
<div class="sig-line"/>
<div class="small-muted">Shipper Signature / Date</div>
</div>
<div class="col-6">
<div class="sig-line"/>
<div class="small-muted">Receiver Signature / Date</div>
</div>
</div>
</div>
</div>
</t>
</t>
</t>
</template>
<!-- ============================================================= -->
<!-- LANDSCAPE -->
<!-- ============================================================= -->
<template id="report_fr_delivery_landscape">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="doc">
<t t-call="web.external_layout">
<t t-set="doc" t-value="doc.with_context(lang=doc.partner_id.lang)"/>
<t t-call="fusion_reports_templates.fr_landscape_styles"/>
<div class="fr-landscape">
<div class="page">
<h2>
<t t-if="doc.picking_type_id.code == 'outgoing'">Delivery Slip </t>
<t t-elif="doc.picking_type_id.code == 'incoming'">Receipt </t>
<t t-else="">Transfer </t>
<span t-field="doc.name"/>
</h2>
<table class="bordered">
<thead>
<tr>
<th style="width: 50%;">FROM</th>
<th style="width: 50%;">TO</th>
</tr>
</thead>
<tbody>
<tr>
<td style="height: 70px; font-size: 12pt;">
<strong><span t-field="doc.location_id.display_name"/></strong><br/>
<span t-field="doc.company_id.partner_id"
t-options="{'widget': 'contact', 'fields': ['address', 'phone'], 'no_marker': True}"/>
</td>
<td style="height: 70px; font-size: 12pt;">
<t t-if="doc.partner_id">
<div t-field="doc.partner_id"
t-options="{'widget': 'contact', 'fields': ['name', 'address', 'phone', 'email'], 'no_marker': True}"/>
</t>
<t t-else="">
<strong><span t-field="doc.location_dest_id.display_name"/></strong>
</t>
</td>
</tr>
</tbody>
</table>
<table class="bordered info-table">
<thead>
<tr>
<th>SCHEDULED DATE</th>
<th>ORDER</th>
<th>CARRIER</th>
<th>TRACKING #</th>
<th>WEIGHT</th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-center"><span t-field="doc.scheduled_date" t-options="{'widget': 'date'}"/></td>
<td class="text-center"><span t-esc="doc.origin or '-'"/></td>
<td class="text-center"><span t-field="doc.carrier_id"/></td>
<td class="text-center"><span t-esc="doc.carrier_tracking_ref or '-'"/></td>
<td class="text-center"><span t-field="doc.weight" t-options="{'widget': 'float', 'precision': 2}"/> <span t-field="doc.weight_uom_name"/></td>
</tr>
</tbody>
</table>
<table class="bordered">
<thead>
<tr>
<th style="width: 15%;">SKU</th>
<th class="text-start" style="width: 55%;">PRODUCT</th>
<th style="width: 15%;">QTY</th>
<th style="width: 15%;">UOM</th>
</tr>
</thead>
<tbody>
<t t-foreach="doc.move_ids_without_package" t-as="move">
<tr>
<td class="text-center"><span t-esc="move.product_id.default_code or ''"/></td>
<td><span t-field="move.product_id.display_name"/>
<t t-if="move.description_picking and move.description_picking != move.product_id.display_name">
<br/><span class="small-muted" t-esc="move.description_picking"/>
</t>
</td>
<td class="text-center">
<t t-if="doc.state == 'done'">
<span t-esc="int(move.quantity) if move.quantity == int(move.quantity) else move.quantity"/>
</t>
<t t-else="">
<span t-esc="int(move.product_uom_qty) if move.product_uom_qty == int(move.product_uom_qty) else move.product_uom_qty"/>
</t>
</td>
<td class="text-center"><span t-field="move.product_uom"/></td>
</tr>
</t>
</tbody>
</table>
<t t-if="doc.note">
<div style="margin-top: 15px;">
<strong>Notes:</strong>
<div t-field="doc.note"/>
</div>
</t>
<div class="row" style="margin-top: 30px;">
<div class="col-6">
<div class="sig-line"/>
<div class="small-muted">Shipper Signature / Date</div>
</div>
<div class="col-6">
<div class="sig-line"/>
<div class="small-muted">Receiver Signature / Date</div>
</div>
</div>
</div>
</div>
</t>
</t>
</t>
</template>
</odoo>

View File

@@ -0,0 +1,359 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Customer Invoice / Vendor Bill / Credit Note — portrait + landscape.
-->
<odoo>
<!-- ============================================================= -->
<!-- PORTRAIT -->
<!-- ============================================================= -->
<template id="report_fr_invoice_portrait">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="doc">
<t t-call="web.external_layout">
<t t-set="doc" t-value="doc.with_context(lang=doc.partner_id.lang)"/>
<t t-call="fusion_reports_templates.fr_portrait_styles"/>
<div class="fr-report">
<div class="page">
<div style="display: flex; justify-content: space-between; align-items: center;">
<h4>
<t t-if="doc.move_type == 'out_invoice'">Invoice </t>
<t t-elif="doc.move_type == 'out_refund'">Credit Note </t>
<t t-elif="doc.move_type == 'in_invoice'">Vendor Bill </t>
<t t-elif="doc.move_type == 'in_refund'">Vendor Refund </t>
<t t-else="">Document </t>
<span t-field="doc.name"/>
</h4>
<t t-if="doc.payment_state == 'paid'">
<div class="paid-stamp">PAID</div>
</t>
</div>
<table class="bordered">
<thead>
<tr>
<th style="width: 50%;">BILL TO</th>
<th style="width: 50%;">SHIP TO</th>
</tr>
</thead>
<tbody>
<tr>
<td style="height: 70px;">
<div t-field="doc.partner_id"
t-options="{'widget': 'contact', 'fields': ['name', 'address', 'phone', 'email'], 'no_marker': True}"/>
</td>
<td style="height: 70px;">
<t t-if="doc.partner_shipping_id">
<div t-field="doc.partner_shipping_id"
t-options="{'widget': 'contact', 'fields': ['name', 'address', 'phone'], 'no_marker': True}"/>
</t>
<t t-else="">
<div t-field="doc.partner_id"
t-options="{'widget': 'contact', 'fields': ['name', 'address'], 'no_marker': True}"/>
</t>
</td>
</tr>
</tbody>
</table>
<table class="bordered">
<thead>
<tr>
<th class="info-header" style="width: 25%;">INVOICE DATE</th>
<th class="info-header" style="width: 25%;">DUE DATE</th>
<th class="info-header" style="width: 25%;">REFERENCE</th>
<th class="info-header" style="width: 25%;">PAYMENT TERMS</th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-center"><span t-field="doc.invoice_date"/></td>
<td class="text-center"><span t-field="doc.invoice_date_due"/></td>
<td class="text-center"><span t-esc="doc.ref or doc.invoice_origin or '-'"/></td>
<td class="text-center"><span t-field="doc.invoice_payment_term_id"/></td>
</tr>
</tbody>
</table>
<table class="bordered">
<thead>
<tr>
<th style="width: 12%;">SKU</th>
<th class="text-start" style="width: 40%;">DESCRIPTION</th>
<th style="width: 8%;">QTY</th>
<th style="width: 8%;">UOM</th>
<th style="width: 12%;">UNIT PRICE</th>
<th style="width: 8%;">TAX</th>
<th style="width: 12%;">AMOUNT</th>
</tr>
</thead>
<tbody>
<t t-foreach="doc.invoice_line_ids" t-as="line">
<t t-if="line.display_type == 'line_section'">
<tr class="section-row"><td colspan="7"><strong t-field="line.name"/></td></tr>
</t>
<t t-elif="line.display_type == 'line_note'">
<tr class="note-row"><td colspan="7"><span t-field="line.name"/></td></tr>
</t>
<t t-elif="line.display_type == 'product' or not line.display_type">
<tr>
<td class="text-center"><span t-esc="line.product_id.default_code or ''"/></td>
<td><span t-esc="line.name"/></td>
<td class="text-center">
<span t-esc="int(line.quantity) if line.quantity == int(line.quantity) else line.quantity"/>
</td>
<td class="text-center"><span t-field="line.product_uom_id"/></td>
<td class="text-end">
<span t-field="line.price_unit" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
<td class="text-center">
<t t-esc="', '.join([(tax.invoice_label or tax.name) for tax in line.tax_ids]) or '-'"/>
</td>
<td class="text-end">
<span t-field="line.price_subtotal" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
</tr>
</t>
</t>
</tbody>
</table>
<div class="row" style="margin-top: 15px;">
<div class="col-6">
<t t-if="doc.invoice_payment_term_id.note">
<strong>Payment Terms:</strong><br/>
<span t-field="doc.invoice_payment_term_id.note"/>
</t>
<t t-if="doc.payment_reference">
<div style="margin-top: 10px;">
<strong>Payment Reference:</strong>
<span t-field="doc.payment_reference"/>
</div>
</t>
</div>
<div class="col-6" style="text-align: right;">
<table class="totals-table" style="width: auto; margin-left: auto;">
<tr>
<td style="min-width: 150px;">Subtotal</td>
<td class="text-end" style="min-width: 110px;">
<span t-field="doc.amount_untaxed" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
</tr>
<tr>
<td>Taxes</td>
<td class="text-end">
<span t-field="doc.amount_tax" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
</tr>
<tr style="background-color: #eaf2f8;">
<td><strong>Total</strong></td>
<td class="text-end"><strong>
<span t-field="doc.amount_total" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</strong></td>
</tr>
<t t-if="doc.amount_residual and doc.payment_state != 'paid'">
<tr>
<td><strong>Amount Due</strong></td>
<td class="text-end"><strong>
<span t-field="doc.amount_residual" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</strong></td>
</tr>
</t>
</table>
</div>
</div>
<t t-if="doc.narration">
<div style="margin-top: 15px;">
<strong>Notes:</strong>
<div t-field="doc.narration"/>
</div>
</t>
</div>
</div>
</t>
</t>
</t>
</template>
<!-- ============================================================= -->
<!-- LANDSCAPE -->
<!-- ============================================================= -->
<template id="report_fr_invoice_landscape">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="doc">
<t t-call="web.external_layout">
<t t-set="doc" t-value="doc.with_context(lang=doc.partner_id.lang)"/>
<t t-call="fusion_reports_templates.fr_landscape_styles"/>
<div class="fr-landscape">
<div class="page">
<div style="display: flex; justify-content: space-between; align-items: center;">
<h2>
<t t-if="doc.move_type == 'out_invoice'">Invoice </t>
<t t-elif="doc.move_type == 'out_refund'">Credit Note </t>
<t t-elif="doc.move_type == 'in_invoice'">Vendor Bill </t>
<t t-elif="doc.move_type == 'in_refund'">Vendor Refund </t>
<t t-else="">Document </t>
<span t-field="doc.name"/>
</h2>
<t t-if="doc.payment_state == 'paid'">
<div class="paid-stamp">PAID</div>
</t>
</div>
<table class="bordered">
<thead>
<tr>
<th style="width: 50%;">BILL TO</th>
<th style="width: 50%;">SHIP TO</th>
</tr>
</thead>
<tbody>
<tr>
<td style="height: 70px; font-size: 12pt;">
<div t-field="doc.partner_id"
t-options="{'widget': 'contact', 'fields': ['name', 'address', 'phone', 'email'], 'no_marker': True}"/>
</td>
<td style="height: 70px; font-size: 12pt;">
<t t-if="doc.partner_shipping_id">
<div t-field="doc.partner_shipping_id"
t-options="{'widget': 'contact', 'fields': ['name', 'address', 'phone'], 'no_marker': True}"/>
</t>
<t t-else="">
<div t-field="doc.partner_id"
t-options="{'widget': 'contact', 'fields': ['name', 'address'], 'no_marker': True}"/>
</t>
</td>
</tr>
</tbody>
</table>
<table class="bordered info-table">
<thead>
<tr>
<th>INVOICE DATE</th>
<th>DUE DATE</th>
<th>REFERENCE</th>
<th>PAYMENT TERMS</th>
<th>SALESPERSON</th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-center"><span t-field="doc.invoice_date"/></td>
<td class="text-center"><span t-field="doc.invoice_date_due"/></td>
<td class="text-center"><span t-esc="doc.ref or doc.invoice_origin or '-'"/></td>
<td class="text-center"><span t-field="doc.invoice_payment_term_id"/></td>
<td class="text-center"><span t-field="doc.invoice_user_id"/></td>
</tr>
</tbody>
</table>
<table class="bordered">
<thead>
<tr>
<th style="width: 10%;">SKU</th>
<th class="text-start" style="width: 40%;">DESCRIPTION</th>
<th style="width: 8%;">QTY</th>
<th style="width: 8%;">UOM</th>
<th style="width: 12%;">UNIT PRICE</th>
<th style="width: 10%;">TAX</th>
<th style="width: 12%;">AMOUNT</th>
</tr>
</thead>
<tbody>
<t t-foreach="doc.invoice_line_ids" t-as="line">
<t t-if="line.display_type == 'line_section'">
<tr class="section-row"><td colspan="7"><strong t-field="line.name"/></td></tr>
</t>
<t t-elif="line.display_type == 'line_note'">
<tr class="note-row"><td colspan="7"><span t-field="line.name"/></td></tr>
</t>
<t t-elif="line.display_type == 'product' or not line.display_type">
<tr>
<td class="text-center"><span t-esc="line.product_id.default_code or ''"/></td>
<td><span t-esc="line.name"/></td>
<td class="text-center">
<span t-esc="int(line.quantity) if line.quantity == int(line.quantity) else line.quantity"/>
</td>
<td class="text-center"><span t-field="line.product_uom_id"/></td>
<td class="text-end">
<span t-field="line.price_unit" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
<td class="text-center">
<t t-esc="', '.join([(tax.invoice_label or tax.name) for tax in line.tax_ids]) or '-'"/>
</td>
<td class="text-end">
<span t-field="line.price_subtotal" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
</tr>
</t>
</t>
</tbody>
</table>
<div class="row" style="margin-top: 15px;">
<div class="col-7">
<t t-if="doc.invoice_payment_term_id.note">
<strong>Payment Terms:</strong><br/>
<span t-field="doc.invoice_payment_term_id.note"/>
</t>
<t t-if="doc.payment_reference">
<div style="margin-top: 10px;">
<strong>Payment Reference:</strong>
<span t-field="doc.payment_reference"/>
</div>
</t>
</div>
<div class="col-5" style="text-align: right;">
<table class="totals-table" style="width: auto; margin-left: auto;">
<tr>
<td style="min-width: 200px;">Subtotal</td>
<td class="text-end" style="min-width: 150px;">
<span t-field="doc.amount_untaxed" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
</tr>
<tr>
<td>Taxes</td>
<td class="text-end">
<span t-field="doc.amount_tax" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
</tr>
<tr style="background-color: #eaf2f8;">
<td><strong>Total</strong></td>
<td class="text-end"><strong>
<span t-field="doc.amount_total" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</strong></td>
</tr>
<t t-if="doc.amount_residual and doc.payment_state != 'paid'">
<tr>
<td><strong>Amount Due</strong></td>
<td class="text-end"><strong>
<span t-field="doc.amount_residual" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</strong></td>
</tr>
</t>
</table>
</div>
</div>
<t t-if="doc.narration">
<div style="margin-top: 15px;">
<strong>Notes:</strong>
<div t-field="doc.narration"/>
</div>
</t>
</div>
</div>
</t>
</t>
</t>
</template>
</odoo>

View File

@@ -0,0 +1,317 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Purchase Order / RFQ — portrait + landscape.
-->
<odoo>
<!-- ============================================================= -->
<!-- PORTRAIT -->
<!-- ============================================================= -->
<template id="report_fr_purchase_portrait">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="doc">
<t t-call="web.external_layout">
<t t-set="doc" t-value="doc.with_context(lang=doc.partner_id.lang)"/>
<t t-call="fusion_reports_templates.fr_portrait_styles"/>
<div class="fr-report">
<div class="page">
<h4>
<t t-if="doc.state in ['draft', 'sent', 'to approve']">Request for Quotation </t>
<t t-else="">Purchase Order </t>
<span t-field="doc.name"/>
</h4>
<table class="bordered">
<thead>
<tr>
<th style="width: 50%;">VENDOR</th>
<th style="width: 50%;">SHIP TO</th>
</tr>
</thead>
<tbody>
<tr>
<td style="height: 70px;">
<div t-field="doc.partner_id"
t-options="{'widget': 'contact', 'fields': ['name', 'address', 'phone', 'email'], 'no_marker': True}"/>
</td>
<td style="height: 70px;">
<t t-if="doc.dest_address_id">
<div t-field="doc.dest_address_id"
t-options="{'widget': 'contact', 'fields': ['name', 'address', 'phone'], 'no_marker': True}"/>
</t>
<t t-else="">
<strong><span t-field="doc.company_id.name"/></strong><br/>
<span t-field="doc.company_id.partner_id"
t-options="{'widget': 'contact', 'fields': ['address', 'phone'], 'no_marker': True}"/>
</t>
</td>
</tr>
</tbody>
</table>
<table class="bordered">
<thead>
<tr>
<th class="info-header" style="width: 25%;">ORDER DATE</th>
<th class="info-header" style="width: 25%;">EXPECTED</th>
<th class="info-header" style="width: 25%;">BUYER</th>
<th class="info-header" style="width: 25%;">VENDOR REF</th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-center"><span t-field="doc.date_order" t-options="{'widget': 'date'}"/></td>
<td class="text-center"><span t-field="doc.date_planned" t-options="{'widget': 'date'}"/></td>
<td class="text-center"><span t-field="doc.user_id"/></td>
<td class="text-center"><span t-esc="doc.partner_ref or '-'"/></td>
</tr>
</tbody>
</table>
<table class="bordered">
<thead>
<tr>
<th style="width: 12%;">SKU</th>
<th class="text-start" style="width: 40%;">DESCRIPTION</th>
<th style="width: 8%;">QTY</th>
<th style="width: 8%;">UOM</th>
<th style="width: 12%;">UNIT PRICE</th>
<th style="width: 8%;">TAX</th>
<th style="width: 12%;">AMOUNT</th>
</tr>
</thead>
<tbody>
<t t-foreach="doc.order_line" t-as="line">
<t t-if="line.display_type == 'line_section'">
<tr class="section-row"><td colspan="7"><strong t-field="line.name"/></td></tr>
</t>
<t t-elif="line.display_type == 'line_note'">
<tr class="note-row"><td colspan="7"><span t-field="line.name"/></td></tr>
</t>
<t t-else="">
<tr>
<td class="text-center"><span t-esc="line.product_id.default_code or ''"/></td>
<td><span t-esc="line.name"/></td>
<td class="text-center">
<span t-esc="int(line.product_qty) if line.product_qty == int(line.product_qty) else line.product_qty"/>
</td>
<td class="text-center"><span t-field="line.product_uom_id"/></td>
<td class="text-end">
<span t-field="line.price_unit" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
<td class="text-center">
<t t-esc="', '.join([(tax.invoice_label or tax.name) for tax in line.tax_ids]) or '-'"/>
</td>
<td class="text-end">
<span t-field="line.price_subtotal" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
</tr>
</t>
</t>
</tbody>
</table>
<div class="row" style="margin-top: 15px;">
<div class="col-6">
<t t-if="doc.payment_term_id.note">
<strong>Payment Terms:</strong><br/>
<span t-field="doc.payment_term_id.note"/>
</t>
</div>
<div class="col-6" style="text-align: right;">
<table class="totals-table" style="width: auto; margin-left: auto;">
<tr>
<td style="min-width: 150px;">Subtotal</td>
<td class="text-end" style="min-width: 110px;">
<span t-field="doc.amount_untaxed" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
</tr>
<tr>
<td>Taxes</td>
<td class="text-end">
<span t-field="doc.amount_tax" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
</tr>
<tr style="background-color: #eaf2f8;">
<td><strong>Grand Total</strong></td>
<td class="text-end"><strong>
<span t-field="doc.amount_total" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</strong></td>
</tr>
</table>
</div>
</div>
<t t-if="doc.notes">
<div style="margin-top: 15px;">
<strong>Notes:</strong>
<div t-field="doc.notes"/>
</div>
</t>
</div>
</div>
</t>
</t>
</t>
</template>
<!-- ============================================================= -->
<!-- LANDSCAPE -->
<!-- ============================================================= -->
<template id="report_fr_purchase_landscape">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="doc">
<t t-call="web.external_layout">
<t t-set="doc" t-value="doc.with_context(lang=doc.partner_id.lang)"/>
<t t-call="fusion_reports_templates.fr_landscape_styles"/>
<div class="fr-landscape">
<div class="page">
<h2>
<t t-if="doc.state in ['draft', 'sent', 'to approve']">Request for Quotation </t>
<t t-else="">Purchase Order </t>
<span t-field="doc.name"/>
</h2>
<table class="bordered">
<thead>
<tr>
<th style="width: 50%;">VENDOR</th>
<th style="width: 50%;">SHIP TO</th>
</tr>
</thead>
<tbody>
<tr>
<td style="height: 70px; font-size: 12pt;">
<div t-field="doc.partner_id"
t-options="{'widget': 'contact', 'fields': ['name', 'address', 'phone', 'email'], 'no_marker': True}"/>
</td>
<td style="height: 70px; font-size: 12pt;">
<t t-if="doc.dest_address_id">
<div t-field="doc.dest_address_id"
t-options="{'widget': 'contact', 'fields': ['name', 'address', 'phone'], 'no_marker': True}"/>
</t>
<t t-else="">
<strong><span t-field="doc.company_id.name"/></strong><br/>
<span t-field="doc.company_id.partner_id"
t-options="{'widget': 'contact', 'fields': ['address', 'phone'], 'no_marker': True}"/>
</t>
</td>
</tr>
</tbody>
</table>
<table class="bordered info-table">
<thead>
<tr>
<th>ORDER DATE</th>
<th>EXPECTED ARRIVAL</th>
<th>BUYER</th>
<th>VENDOR REF</th>
<th>PAYMENT TERMS</th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-center"><span t-field="doc.date_order" t-options="{'widget': 'date'}"/></td>
<td class="text-center"><span t-field="doc.date_planned" t-options="{'widget': 'date'}"/></td>
<td class="text-center"><span t-field="doc.user_id"/></td>
<td class="text-center"><span t-esc="doc.partner_ref or '-'"/></td>
<td class="text-center"><span t-field="doc.payment_term_id"/></td>
</tr>
</tbody>
</table>
<table class="bordered">
<thead>
<tr>
<th style="width: 10%;">SKU</th>
<th class="text-start" style="width: 40%;">DESCRIPTION</th>
<th style="width: 8%;">QTY</th>
<th style="width: 8%;">UOM</th>
<th style="width: 12%;">UNIT PRICE</th>
<th style="width: 10%;">TAX</th>
<th style="width: 12%;">AMOUNT</th>
</tr>
</thead>
<tbody>
<t t-foreach="doc.order_line" t-as="line">
<t t-if="line.display_type == 'line_section'">
<tr class="section-row"><td colspan="7"><strong t-field="line.name"/></td></tr>
</t>
<t t-elif="line.display_type == 'line_note'">
<tr class="note-row"><td colspan="7"><span t-field="line.name"/></td></tr>
</t>
<t t-else="">
<tr>
<td class="text-center"><span t-esc="line.product_id.default_code or ''"/></td>
<td><span t-esc="line.name"/></td>
<td class="text-center">
<span t-esc="int(line.product_qty) if line.product_qty == int(line.product_qty) else line.product_qty"/>
</td>
<td class="text-center"><span t-field="line.product_uom_id"/></td>
<td class="text-end">
<span t-field="line.price_unit" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
<td class="text-center">
<t t-esc="', '.join([(tax.invoice_label or tax.name) for tax in line.tax_ids]) or '-'"/>
</td>
<td class="text-end">
<span t-field="line.price_subtotal" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
</tr>
</t>
</t>
</tbody>
</table>
<div class="row" style="margin-top: 15px;">
<div class="col-7">
<t t-if="doc.payment_term_id.note">
<strong>Payment Terms:</strong><br/>
<span t-field="doc.payment_term_id.note"/>
</t>
</div>
<div class="col-5" style="text-align: right;">
<table class="totals-table" style="width: auto; margin-left: auto;">
<tr>
<td style="min-width: 200px;">Subtotal</td>
<td class="text-end" style="min-width: 150px;">
<span t-field="doc.amount_untaxed" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
</tr>
<tr>
<td>Taxes</td>
<td class="text-end">
<span t-field="doc.amount_tax" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
</tr>
<tr style="background-color: #eaf2f8;">
<td><strong>Grand Total</strong></td>
<td class="text-end"><strong>
<span t-field="doc.amount_total" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</strong></td>
</tr>
</table>
</div>
</div>
<t t-if="doc.notes">
<div style="margin-top: 15px;">
<strong>Notes:</strong>
<div t-field="doc.notes"/>
</div>
</t>
</div>
</div>
</t>
</t>
</t>
</template>
</odoo>

View File

@@ -0,0 +1,271 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Payment Receipt — portrait + landscape.
-->
<odoo>
<!-- ============================================================= -->
<!-- PORTRAIT -->
<!-- ============================================================= -->
<template id="report_fr_receipt_portrait">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="doc">
<t t-call="web.external_layout">
<t t-set="doc" t-value="doc.with_context(lang=doc.partner_id.lang if doc.partner_id else 'en_US')"/>
<t t-call="fusion_reports_templates.fr_portrait_styles"/>
<div class="fr-report">
<div class="page">
<div style="display: flex; justify-content: space-between; align-items: center;">
<h4>
Payment Receipt <span t-field="doc.name"/>
</h4>
<div class="paid-stamp">RECEIPT</div>
</div>
<table class="bordered">
<thead>
<tr>
<th style="width: 50%;">
<t t-if="doc.payment_type == 'inbound'">RECEIVED FROM</t>
<t t-else="">PAID TO</t>
</th>
<th style="width: 50%;">
<t t-if="doc.payment_type == 'inbound'">PAID TO</t>
<t t-else="">PAID BY</t>
</th>
</tr>
</thead>
<tbody>
<tr>
<td style="height: 70px;">
<t t-if="doc.partner_id">
<div t-field="doc.partner_id"
t-options="{'widget': 'contact', 'fields': ['name', 'address', 'phone', 'email'], 'no_marker': True}"/>
</t>
</td>
<td style="height: 70px;">
<strong><span t-field="doc.company_id.name"/></strong><br/>
<span t-field="doc.company_id.partner_id"
t-options="{'widget': 'contact', 'fields': ['address', 'phone', 'email'], 'no_marker': True}"/>
</td>
</tr>
</tbody>
</table>
<table class="bordered">
<thead>
<tr>
<th class="info-header" style="width: 25%;">PAYMENT DATE</th>
<th class="info-header" style="width: 25%;">REFERENCE</th>
<th class="info-header" style="width: 25%;">METHOD</th>
<th class="info-header" style="width: 25%;">JOURNAL</th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-center"><span t-field="doc.date" t-options="{'widget': 'date'}"/></td>
<td class="text-center"><span t-esc="doc.ref or doc.memo or '-'"/></td>
<td class="text-center"><span t-field="doc.payment_method_line_id"/></td>
<td class="text-center"><span t-field="doc.journal_id"/></td>
</tr>
</tbody>
</table>
<table class="bordered">
<tr>
<td class="fr-header-primary text-center" style="width: 50%; font-size: 14pt; padding: 14px;">
<strong>AMOUNT</strong>
</td>
<td class="text-center" style="width: 50%; font-size: 18pt; padding: 14px;">
<strong>
<span t-field="doc.amount" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</strong>
</td>
</tr>
</table>
<t t-if="doc.reconciled_invoice_ids">
<div style="margin-top: 15px;">
<strong>Applied to:</strong>
<table class="bordered" style="margin-top: 6px;">
<thead>
<tr>
<th style="width: 30%;">DOCUMENT</th>
<th style="width: 25%;">DATE</th>
<th style="width: 25%;">ORIGINAL AMT</th>
<th style="width: 20%;">OUTSTANDING</th>
</tr>
</thead>
<tbody>
<t t-foreach="doc.reconciled_invoice_ids" t-as="inv">
<tr>
<td class="text-center"><span t-field="inv.name"/></td>
<td class="text-center"><span t-field="inv.invoice_date"/></td>
<td class="text-end">
<span t-field="inv.amount_total" t-options='{"widget": "monetary", "display_currency": inv.currency_id}'/>
</td>
<td class="text-end">
<span t-field="inv.amount_residual" t-options='{"widget": "monetary", "display_currency": inv.currency_id}'/>
</td>
</tr>
</t>
</tbody>
</table>
</div>
</t>
<t t-if="doc.memo">
<div style="margin-top: 15px;">
<strong>Memo:</strong>
<span t-field="doc.memo"/>
</div>
</t>
<div class="row" style="margin-top: 40px;">
<div class="col-6">
<div class="sig-line"/>
<div class="small-muted">Authorized Signature / Date</div>
</div>
</div>
</div>
</div>
</t>
</t>
</t>
</template>
<!-- ============================================================= -->
<!-- LANDSCAPE -->
<!-- ============================================================= -->
<template id="report_fr_receipt_landscape">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="doc">
<t t-call="web.external_layout">
<t t-set="doc" t-value="doc.with_context(lang=doc.partner_id.lang if doc.partner_id else 'en_US')"/>
<t t-call="fusion_reports_templates.fr_landscape_styles"/>
<div class="fr-landscape">
<div class="page">
<div style="display: flex; justify-content: space-between; align-items: center;">
<h2>Payment Receipt <span t-field="doc.name"/></h2>
<div class="paid-stamp">RECEIPT</div>
</div>
<table class="bordered">
<thead>
<tr>
<th style="width: 50%;">
<t t-if="doc.payment_type == 'inbound'">RECEIVED FROM</t>
<t t-else="">PAID TO</t>
</th>
<th style="width: 50%;">
<t t-if="doc.payment_type == 'inbound'">PAID TO</t>
<t t-else="">PAID BY</t>
</th>
</tr>
</thead>
<tbody>
<tr>
<td style="height: 70px; font-size: 12pt;">
<t t-if="doc.partner_id">
<div t-field="doc.partner_id"
t-options="{'widget': 'contact', 'fields': ['name', 'address', 'phone', 'email'], 'no_marker': True}"/>
</t>
</td>
<td style="height: 70px; font-size: 12pt;">
<strong><span t-field="doc.company_id.name"/></strong><br/>
<span t-field="doc.company_id.partner_id"
t-options="{'widget': 'contact', 'fields': ['address', 'phone', 'email'], 'no_marker': True}"/>
</td>
</tr>
</tbody>
</table>
<table class="bordered info-table">
<thead>
<tr>
<th>PAYMENT DATE</th>
<th>REFERENCE</th>
<th>METHOD</th>
<th>JOURNAL</th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-center"><span t-field="doc.date" t-options="{'widget': 'date'}"/></td>
<td class="text-center"><span t-esc="doc.ref or doc.memo or '-'"/></td>
<td class="text-center"><span t-field="doc.payment_method_line_id"/></td>
<td class="text-center"><span t-field="doc.journal_id"/></td>
</tr>
</tbody>
</table>
<table class="bordered">
<tr>
<td class="fr-header-primary text-center" style="width: 50%; font-size: 16pt; padding: 18px;">
<strong>AMOUNT</strong>
</td>
<td class="text-center" style="width: 50%; font-size: 22pt; padding: 18px;">
<strong>
<span t-field="doc.amount" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</strong>
</td>
</tr>
</table>
<t t-if="doc.reconciled_invoice_ids">
<div style="margin-top: 15px;">
<strong>Applied to:</strong>
<table class="bordered" style="margin-top: 6px;">
<thead>
<tr>
<th style="width: 30%;">DOCUMENT</th>
<th style="width: 25%;">DATE</th>
<th style="width: 25%;">ORIGINAL AMT</th>
<th style="width: 20%;">OUTSTANDING</th>
</tr>
</thead>
<tbody>
<t t-foreach="doc.reconciled_invoice_ids" t-as="inv">
<tr>
<td class="text-center"><span t-field="inv.name"/></td>
<td class="text-center"><span t-field="inv.invoice_date"/></td>
<td class="text-end">
<span t-field="inv.amount_total" t-options='{"widget": "monetary", "display_currency": inv.currency_id}'/>
</td>
<td class="text-end">
<span t-field="inv.amount_residual" t-options='{"widget": "monetary", "display_currency": inv.currency_id}'/>
</td>
</tr>
</t>
</tbody>
</table>
</div>
</t>
<t t-if="doc.memo">
<div style="margin-top: 15px;">
<strong>Memo:</strong>
<span t-field="doc.memo"/>
</div>
</t>
<div class="row" style="margin-top: 40px;">
<div class="col-6">
<div class="sig-line"/>
<div class="small-muted">Authorized Signature / Date</div>
</div>
</div>
</div>
</div>
</t>
</t>
</t>
</template>
</odoo>

View File

@@ -0,0 +1,355 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Quotation / Sales Order — portrait + landscape.
-->
<odoo>
<!-- ============================================================= -->
<!-- PORTRAIT -->
<!-- ============================================================= -->
<template id="report_fr_sale_portrait">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="doc">
<t t-call="web.external_layout">
<t t-set="doc" t-value="doc.with_context(lang=doc.partner_id.lang)"/>
<t t-call="fusion_reports_templates.fr_portrait_styles"/>
<div class="fr-report">
<div class="page">
<h4>
<span t-if="doc.state in ['draft','sent']">Quotation </span>
<span t-else="">Sales Order </span>
<span t-field="doc.name"/>
</h4>
<table class="bordered">
<thead>
<tr>
<th style="width: 50%;">BILLING ADDRESS</th>
<th style="width: 50%;">SHIPPING ADDRESS</th>
</tr>
</thead>
<tbody>
<tr>
<td style="height: 70px;">
<div t-field="doc.partner_invoice_id"
t-options="{'widget': 'contact', 'fields': ['name', 'address', 'phone', 'email'], 'no_marker': True}"/>
</td>
<td style="height: 70px;">
<div t-field="doc.partner_shipping_id"
t-options="{'widget': 'contact', 'fields': ['name', 'address', 'phone'], 'no_marker': True}"/>
</td>
</tr>
</tbody>
</table>
<table class="bordered">
<thead>
<tr>
<th class="info-header" style="width: 25%;">ORDER DATE</th>
<th class="info-header" style="width: 25%;">EXPIRATION</th>
<th class="info-header" style="width: 25%;">SALESPERSON</th>
<th class="info-header" style="width: 25%;">CUSTOMER REF</th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-center"><span t-field="doc.date_order" t-options="{'widget': 'date'}"/></td>
<td class="text-center"><span t-field="doc.validity_date"/></td>
<td class="text-center"><span t-field="doc.user_id"/></td>
<td class="text-center"><span t-esc="doc.client_order_ref or '-'"/></td>
</tr>
</tbody>
</table>
<table class="bordered">
<thead>
<tr>
<th style="width: 12%;">SKU</th>
<th class="text-start" style="width: 38%;">DESCRIPTION</th>
<th style="width: 8%;">QTY</th>
<th style="width: 8%;">UOM</th>
<th style="width: 14%;">UNIT PRICE</th>
<th style="width: 8%;">TAX</th>
<th style="width: 12%;">AMOUNT</th>
</tr>
</thead>
<tbody>
<t t-foreach="doc.order_line" t-as="line">
<t t-if="line.display_type == 'line_section'">
<tr class="section-row"><td colspan="7"><strong t-field="line.name"/></td></tr>
</t>
<t t-elif="line.display_type == 'line_note'">
<tr class="note-row"><td colspan="7"><span t-field="line.name"/></td></tr>
</t>
<t t-elif="not line.display_type or line.display_type == 'product'">
<tr>
<td class="text-center"><span t-esc="line.product_id.default_code or ''"/></td>
<td>
<t t-set="clean_name" t-value="line.name"/>
<t t-if="line.name and '] ' in line.name">
<t t-set="clean_name" t-value="line.name.split('] ', 1)[1]"/>
</t>
<span t-esc="clean_name"/>
</td>
<td class="text-center">
<span t-esc="int(line.product_uom_qty) if line.product_uom_qty == int(line.product_uom_qty) else line.product_uom_qty"/>
</td>
<td class="text-center"><span t-field="line.product_uom_id"/></td>
<td class="text-end">
<span t-field="line.price_unit" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
<td class="text-center">
<t t-esc="', '.join([(tax.invoice_label or tax.name) for tax in line.tax_ids]) or '-'"/>
</td>
<td class="text-end">
<span t-field="line.price_subtotal" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
</tr>
</t>
</t>
</tbody>
</table>
<div class="row" style="margin-top: 15px;">
<div class="col-6">
<t t-if="doc.payment_term_id.note">
<strong>Payment Terms:</strong><br/>
<span t-field="doc.payment_term_id.note"/>
</t>
</div>
<div class="col-6" style="text-align: right;">
<table class="totals-table" style="width: auto; margin-left: auto;">
<tr>
<td style="min-width: 150px;">Subtotal</td>
<td class="text-end" style="min-width: 110px;">
<span t-field="doc.amount_untaxed" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
</tr>
<tr>
<td>Taxes</td>
<td class="text-end">
<span t-field="doc.amount_tax" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
</tr>
<tr style="background-color: #eaf2f8;">
<td><strong>Grand Total</strong></td>
<td class="text-end"><strong>
<span t-field="doc.amount_total" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</strong></td>
</tr>
</table>
</div>
</div>
<t t-if="doc.note">
<div style="margin-top: 15px;">
<strong>Terms and Conditions:</strong>
<div t-field="doc.note"/>
</div>
</t>
<div class="row" style="margin-top: 25px;">
<div class="col-6">
<div class="sig-line"/>
<div class="small-muted">Customer Acceptance (Signature / Date)</div>
</div>
<div class="col-6">
<t t-if="doc.signature">
<img t-att-src="image_data_uri(doc.signature)" style="max-height: 3cm; max-width: 8cm;"/><br/>
<span t-field="doc.signed_by"/>
</t>
<t t-else="">
<div class="sig-line"/>
</t>
<div class="small-muted">Authorized Representative</div>
</div>
</div>
</div>
</div>
</t>
</t>
</t>
</template>
<!-- ============================================================= -->
<!-- LANDSCAPE -->
<!-- ============================================================= -->
<template id="report_fr_sale_landscape">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="doc">
<t t-call="web.external_layout">
<t t-set="doc" t-value="doc.with_context(lang=doc.partner_id.lang)"/>
<t t-call="fusion_reports_templates.fr_landscape_styles"/>
<div class="fr-landscape">
<div class="page">
<h2 style="text-align: left;">
<span t-if="doc.state in ['draft','sent']">Quotation </span>
<span t-else="">Sales Order </span>
<span t-field="doc.name"/>
</h2>
<table class="bordered">
<thead>
<tr>
<th style="width: 50%;">BILLING ADDRESS</th>
<th style="width: 50%;">SHIPPING ADDRESS</th>
</tr>
</thead>
<tbody>
<tr>
<td style="height: 70px; font-size: 12pt;">
<div t-field="doc.partner_invoice_id"
t-options="{'widget': 'contact', 'fields': ['name', 'address', 'phone', 'email'], 'no_marker': True}"/>
</td>
<td style="height: 70px; font-size: 12pt;">
<div t-field="doc.partner_shipping_id"
t-options="{'widget': 'contact', 'fields': ['name', 'address', 'phone'], 'no_marker': True}"/>
</td>
</tr>
</tbody>
</table>
<table class="bordered info-table">
<thead>
<tr>
<th>ORDER DATE</th>
<th>EXPIRATION</th>
<th>SALESPERSON</th>
<th>CUSTOMER REF</th>
<th>PAYMENT TERMS</th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-center"><span t-field="doc.date_order" t-options="{'widget': 'date'}"/></td>
<td class="text-center"><span t-field="doc.validity_date"/></td>
<td class="text-center"><span t-field="doc.user_id"/></td>
<td class="text-center"><span t-esc="doc.client_order_ref or '-'"/></td>
<td class="text-center"><span t-field="doc.payment_term_id"/></td>
</tr>
</tbody>
</table>
<t t-set="has_discount" t-value="any(l.discount for l in doc.order_line)"/>
<t t-set="col_count" t-value="8 if has_discount else 7"/>
<table class="bordered">
<thead>
<tr>
<th style="width: 10%;">SKU</th>
<th class="text-start" style="width: 32%;">DESCRIPTION</th>
<th style="width: 8%;">QTY</th>
<th style="width: 8%;">UOM</th>
<th style="width: 12%;">UNIT PRICE</th>
<th t-if="has_discount" style="width: 10%;">DISCOUNT</th>
<th style="width: 10%;">TAX</th>
<th style="width: 10%;">AMOUNT</th>
</tr>
</thead>
<tbody>
<t t-foreach="doc.order_line" t-as="line">
<t t-if="line.display_type == 'line_section'">
<tr class="section-row"><td t-att-colspan="col_count"><strong t-field="line.name"/></td></tr>
</t>
<t t-elif="line.display_type == 'line_note'">
<tr class="note-row"><td t-att-colspan="col_count"><span t-field="line.name"/></td></tr>
</t>
<t t-elif="not line.display_type or line.display_type == 'product'">
<tr>
<td class="text-center"><span t-esc="line.product_id.default_code or ''"/></td>
<td>
<t t-set="clean_name" t-value="line.name"/>
<t t-if="line.name and '] ' in line.name">
<t t-set="clean_name" t-value="line.name.split('] ', 1)[1]"/>
</t>
<span t-esc="clean_name"/>
</td>
<td class="text-center">
<span t-esc="int(line.product_uom_qty) if line.product_uom_qty == int(line.product_uom_qty) else line.product_uom_qty"/>
</td>
<td class="text-center"><span t-field="line.product_uom_id"/></td>
<td class="text-end">
<span t-field="line.price_unit" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
<td t-if="has_discount" class="text-center">
<t t-if="line.discount"><span t-esc="line.discount"/>%</t>
<t t-else="">-</t>
</td>
<td class="text-center">
<t t-esc="', '.join([(tax.invoice_label or tax.name) for tax in line.tax_ids]) or '-'"/>
</td>
<td class="text-end">
<span t-field="line.price_subtotal" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
</tr>
</t>
</t>
</tbody>
</table>
<div class="row" style="margin-top: 15px;">
<div class="col-7">
<t t-if="doc.payment_term_id.note">
<strong>Payment Terms:</strong><br/>
<span t-field="doc.payment_term_id.note"/>
</t>
</div>
<div class="col-5" style="text-align: right;">
<table class="totals-table" style="width: auto; margin-left: auto;">
<tr>
<td style="min-width: 200px;">Subtotal</td>
<td class="text-end" style="min-width: 150px;">
<span t-field="doc.amount_untaxed" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
</tr>
<tr>
<td>Taxes</td>
<td class="text-end">
<span t-field="doc.amount_tax" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</td>
</tr>
<tr style="background-color: #eaf2f8;">
<td><strong>Grand Total</strong></td>
<td class="text-end"><strong>
<span t-field="doc.amount_total" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
</strong></td>
</tr>
</table>
</div>
</div>
<t t-if="doc.note">
<div style="margin-top: 15px;">
<strong>Terms and Conditions:</strong>
<div t-field="doc.note"/>
</div>
</t>
<div class="row" style="margin-top: 25px;">
<div class="col-6">
<div class="sig-line"/>
<div class="small-muted">Customer Acceptance (Signature / Date)</div>
</div>
<div class="col-6">
<t t-if="doc.signature">
<img t-att-src="image_data_uri(doc.signature)" style="max-height: 3cm; max-width: 8cm;"/><br/>
<span t-field="doc.signed_by"/>
</t>
<t t-else="">
<div class="sig-line"/>
</t>
<div class="small-muted">Authorized Representative</div>
</div>
</div>
</div>
</div>
</t>
</t>
</t>
</template>
</odoo>

View File

@@ -0,0 +1 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink