mirror of
https://github.com/bringout/oca-ocb-sale.git
synced 2026-04-27 14:52:00 +02:00
1019 lines
60 KiB
XML
1019 lines
60 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<odoo>
|
|
|
|
<template id="portal_my_home_menu_sale" name="Portal layout : sales menu entries" inherit_id="portal.portal_breadcrumbs" priority="20">
|
|
<xpath expr="//ol[hasclass('o_portal_submenu')]" position="inside">
|
|
<li id="portal_breadcrumbs_sale" t-if="page_name == 'quote' or sale_order and sale_order.state in ('sent', 'cancel')" t-attf-class="breadcrumb-item #{'active ' if not sale_order else ''}">
|
|
<a t-if="sale_order" t-attf-href="/my/quotes?{{ keep_query() }}">Quotations</a>
|
|
<t t-else="">Quotations</t>
|
|
</li>
|
|
<li t-elif="page_name == 'order' or sale_order and sale_order.state not in ('sent', 'cancel')" t-attf-class="breadcrumb-item #{'active ' if not sale_order else ''}">
|
|
<a t-if="sale_order" t-attf-href="/my/orders?{{ keep_query() }}">Sales Orders</a>
|
|
<t t-else="">Sales Orders</t>
|
|
</li>
|
|
<li t-if="sale_order" class="breadcrumb-item active">
|
|
<span t-field="sale_order.type_name"/>
|
|
<t t-out="sale_order.name"/>
|
|
</li>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="portal_my_home_sale" name="Quotations / Sales Orders" customize_show="True" inherit_id="portal.portal_my_home" priority="20">
|
|
<xpath expr="//div[hasclass('o_portal_docs')]" position="before">
|
|
<t t-set="portal_client_category_enable" t-value="True"/>
|
|
<t t-set="portal_alert_category_enable" t-value="True"/>
|
|
</xpath>
|
|
<div id="portal_alert_category" position="inside">
|
|
<t t-call="portal.portal_docs_entry">
|
|
<t t-set="bg_color" t-value="'alert alert-primary align-items-center'"/>
|
|
<t t-set="placeholder_count" t-value="'quotation_count'"/>
|
|
<t t-set="title">Quotations to review</t>
|
|
<t t-set="url" t-value="'/my/quotes'"/>
|
|
<t t-set="show_count" t-value="True"/>
|
|
</t>
|
|
</div>
|
|
<div id="portal_client_category" position="inside">
|
|
<t t-call="portal.portal_docs_entry">
|
|
<t t-set="icon" t-value="'/sale/static/src/img/bag.svg'"/>
|
|
<t t-set="title">Your Orders</t>
|
|
<t t-set="url" t-value="'/my/orders'"/>
|
|
<t t-set="text">Follow, view or pay your orders</t>
|
|
<t t-set="placeholder_count" t-value="'order_count'"/>
|
|
</t>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="portal_my_quotations" name="My Quotations">
|
|
<t t-call="portal.portal_layout">
|
|
<t t-set="breadcrumbs_searchbar" t-value="True"/>
|
|
|
|
<t t-call="portal.portal_searchbar">
|
|
<t t-set="title">Quotations</t>
|
|
</t>
|
|
<div t-if="not quotations" class="alert alert-warning" role="alert">
|
|
There are currently no quotations for your account.
|
|
</div>
|
|
<t t-if="quotations" t-call="portal.portal_table">
|
|
<thead>
|
|
<tr class="active">
|
|
<th>Quotation #</th>
|
|
<th class="text-end">Quotation Date</th>
|
|
<th class="text-end">Valid Until</th>
|
|
<th class="text-center"/>
|
|
<th class="text-end">Total</th>
|
|
</tr>
|
|
</thead>
|
|
<t t-foreach="quotations" t-as="quotation">
|
|
<tr>
|
|
<td><a t-att-href="quotation.get_portal_url()"><t t-out="quotation.name"/></a></td>
|
|
<td class="text-end"><span t-field="quotation.date_order"/></td>
|
|
<td class="text-end"><span t-field="quotation.validity_date"/></td>
|
|
<td class="text-center">
|
|
<span t-if="quotation.state == 'cancel'" class="badge rounded-pill text-bg-danger">
|
|
<i class="fa fa-fw fa-remove"/> Cancelled</span>
|
|
<span t-if="quotation.is_expired" class="badge rounded-pill text-bg-danger">
|
|
<i class="fa fa-fw fa-clock-o"/> Expired</span>
|
|
</td>
|
|
<td class="text-end">
|
|
<span t-field="quotation.amount_total"/>
|
|
</td>
|
|
</tr>
|
|
</t>
|
|
</t>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="portal_my_orders" name="My Sales 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">Your Orders</t>
|
|
</t>
|
|
<div t-if="not orders" class="alert alert-warning" role="alert">
|
|
There are currently no sales orders for your account.
|
|
</div>
|
|
<t t-if="orders" t-call="portal.portal_table">
|
|
<thead>
|
|
<tr class="active">
|
|
<th>
|
|
<span class='d-none d-md-inline'>Sales Order #</span>
|
|
<span class='d-block d-md-none'>Ref.</span>
|
|
</th>
|
|
<th class="text-end">Order Date</th>
|
|
<th name="order_total" class="text-end">Total</th>
|
|
</tr>
|
|
</thead>
|
|
<t t-foreach="orders" t-as="order">
|
|
<tr>
|
|
<td><a t-att-href="order.get_portal_url()"><t t-out="order.name"/></a></td>
|
|
<td class="text-end">
|
|
<span t-field="order.date_order" t-options="{'widget': 'date'}"/>&nbsp;
|
|
<span class='d-none d-md-inline' t-field="order.date_order" t-options="{'time_only': True}"/>
|
|
</td>
|
|
<td name="order_amount_total" class="text-end">
|
|
<span t-field="order.amount_total"/>
|
|
</td>
|
|
</tr>
|
|
</t>
|
|
</t>
|
|
</t>
|
|
</template>
|
|
|
|
<!-- Complete page of the sale_order -->
|
|
<template id="sale_order_portal_template" name="Sales Order" inherit_id="portal.portal_sidebar" primary="True">
|
|
<t t-call="portal.portal_layout" position="inside">
|
|
<t t-set="o_portal_fullwidth_alert" groups="sales_team.group_sale_salesman">
|
|
<!-- Uses backend_url provided in rendering values -->
|
|
<t t-call="portal.portal_back_in_edit_mode"/>
|
|
</t>
|
|
</t>
|
|
|
|
<xpath expr="//div[hasclass('o_portal_sidebar')]" position="inside">
|
|
<div
|
|
class="row o_portal_sale_sidebar"
|
|
t-att-data-order-amount-total="sale_order.amount_total"
|
|
>
|
|
<!-- Sidebar -->
|
|
<t t-call="portal.portal_record_sidebar">
|
|
<t t-set="classes" t-value="'col-lg-4 col-xxl-3 d-print-none'"/>
|
|
<t t-set="title">
|
|
<t t-if="sale_order._has_to_be_paid() and sale_order.prepayment_percent != 1.0">
|
|
<h4>Down payment</h4>
|
|
<h2>
|
|
<t
|
|
t-out="sale_order._get_prepayment_required_amount()"
|
|
t-options="{
|
|
'widget': 'monetary',
|
|
'display_currency': sale_order.currency_id,
|
|
}"
|
|
/>
|
|
(<t t-out="sale_order.prepayment_percent * 100"/>%)
|
|
</h2>
|
|
</t>
|
|
<h2 t-else="" t-field="sale_order.amount_total" class="mb-0 text-break"/>
|
|
</t>
|
|
<t t-set="entries">
|
|
<div
|
|
t-if="0 < sale_order.amount_paid < sale_order.amount_total
|
|
and sale_order.state != 'cancel'"
|
|
class="alert alert-success mt-3"
|
|
role="alert"
|
|
>
|
|
Amount paid:
|
|
<span
|
|
t-field="sale_order.amount_paid"
|
|
t-options="{
|
|
'widget': 'monetary',
|
|
'display_currency': sale_order.currency_id,
|
|
}"
|
|
/>
|
|
</div>
|
|
|
|
<div class="d-flex flex-column gap-4 mt-3">
|
|
<div class="d-flex flex-column gap-2" id="sale_order_sidebar_button">
|
|
<a
|
|
t-if="sale_order._has_to_be_signed() and not payment_amount"
|
|
role="button"
|
|
class="btn btn-primary"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#modalaccept"
|
|
href="#"
|
|
> <!-- If paying by link, signing is skipped. -->
|
|
<i class="fa fa-check"/>
|
|
<t t-if="sale_order._has_to_be_paid()"> Sign & Pay</t>
|
|
<t t-else=""> Accept & Sign</t>
|
|
</a>
|
|
<a
|
|
t-elif="sale_order._has_to_be_paid()
|
|
or (
|
|
payment_amount
|
|
and sale_order.amount_paid < sale_order.amount_total
|
|
and sale_order.state != 'cancel'
|
|
)"
|
|
id="o_sale_portal_paynow"
|
|
role="button"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#modalaccept"
|
|
href="#"
|
|
t-att-class="'btn btn-primary' if not sale_order.transaction_ids else 'btn btn-light'"
|
|
>
|
|
<i class="fa fa-check"/>
|
|
<t t-if="not sale_order.signature
|
|
and sale_order._has_to_be_paid()
|
|
and not payment_amount"> Accept & Pay</t>
|
|
<t t-else=""> Pay Now</t>
|
|
</a>
|
|
<div
|
|
id="print_invoice_sidebar_button"
|
|
class="o_download_pdf d-flex gap-2 flex-lg-column flex-xl-row flex-wrap"
|
|
>
|
|
<a class="btn btn-light o_print_btn o_portal_invoice_print flex-grow-1" t-att-href="sale_order.get_portal_url(report_type='pdf')" id="print_invoice_report" title="View Details" role="button" target="_blank"><i class="fa fa-print me-1"/>View Details</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="navspy flex-grow-1 ps-0" t-ignore="true" role="complementary">
|
|
<ul class="nav flex-column bs-sidenav"></ul>
|
|
</div>
|
|
|
|
<t t-if="not sale_order.is_expired and sale_order.state in ['draft', 'sent']">
|
|
<div t-if="sale_order.amount_undiscounted - sale_order.amount_untaxed > 0.01" class="list-group-item flex-grow-1" name="sale_order_advantage">
|
|
<small><b class="text-muted">Your advantage</b></small>
|
|
<small>
|
|
<b t-field="sale_order.amount_undiscounted"
|
|
t-options='{"widget": "monetary", "display_currency": sale_order.currency_id}'
|
|
style="text-decoration: line-through"
|
|
class="d-block mt-1"
|
|
data-id="amount_undiscounted" />
|
|
</small>
|
|
<t t-if="sale_order.amount_untaxed == sale_order.amount_total">
|
|
<h4 t-field="sale_order.amount_total" class="text-success" data-id="total_amount"/>
|
|
</t>
|
|
<t t-else="">
|
|
<h4 t-field="sale_order.amount_untaxed" class="text-success mb-0" data-id="total_untaxed"/>
|
|
<small>(<span t-field="sale_order.amount_total" data-id="total_amount"/> Incl. tax)</small>
|
|
</t>
|
|
</div>
|
|
</t>
|
|
|
|
<div t-if="sale_order.user_id">
|
|
<h6><small class="text-muted">Your contact</small></h6>
|
|
<t t-call="portal.portal_my_contact">
|
|
<t t-set="_contactAvatar" t-value="image_data_uri(sale_order.user_id.avatar_128)"/>
|
|
<t t-set="_contactName" t-value="sale_order.user_id.name"/>
|
|
<t t-set="_contactLink" t-value="True"/>
|
|
</t>
|
|
</div>
|
|
|
|
<div t-if="sale_order._get_edi_builders()">
|
|
<button id="portal_connect_software_modal_btn" type="button" class="btn btn-primary"
|
|
data-bs-toggle="modal" data-bs-target="#sale_portal_connect_software_modal">
|
|
Connect with your software!
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</t>
|
|
|
|
<div class="modal fade" id="sale_portal_connect_software_modal" tabindex="-1" role="dialog" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-body">
|
|
<div>
|
|
<h5>Want to import this document in Odoo?</h5>
|
|
<p>Drag and drop the request for quotation PDF file into your list of quotations in Odoo. Enjoy the automation!</p>
|
|
</div>
|
|
<div>
|
|
<h5>Not using Odoo?</h5>
|
|
<div class="input-group mb-3 w-50">
|
|
<t t-set="base_address" t-value="sale_order.env['ir.config_parameter'].sudo().get_param('web.base.url')"/>
|
|
<t t-set="doc_url" t-value="base_address + '/my/orders/' + str(sale_order.id) + '/download_edi?' + str(keep_query())"/>
|
|
<input type="text" class="form-control" aria-describedby="copy-button" readonly="readonly" t-att-value="doc_url"/>
|
|
<button class="btn btn-outline-secondary" type="button" id="copy-button"
|
|
t-attf-onclick="navigator.clipboard.writeText('#{doc_url}')">Copy</button>
|
|
</div>
|
|
<p>Use the above REST URL to get structured data of the purchase order in UBL format.
|
|
<a href="https://www.odoo.com/documentation/">Read the documentation </a>to learn all the ways to connect your software with <a href="https://www.odoo.com">Odoo</a>.
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<h5>You don't use a good CRM software?</h5>
|
|
<p><a href="https://www.odoo.com/app/sales">Start on Odoo</a>, it's 100% free! You'll save time creating beautiful quotations and track sales.</p>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer justify-content-between">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Page content -->
|
|
<div id="quote_content" class="col-12 col-lg-8 col-xxl-9 mt-5 mt-lg-0">
|
|
|
|
<!-- Modals container -->
|
|
<div role="dialog" class="modal fade" id="modalaccept" name="sale_order_modal_sign_and_pay">
|
|
<div
|
|
t-if="sale_order._has_to_be_signed() and not payment_amount"
|
|
class="modal-dialog"
|
|
><t t-call="sale.sale_order_portal_sign_modal"/></div>
|
|
<div
|
|
t-elif="not sale_order._has_to_be_signed()
|
|
and sale_order._has_to_be_paid()
|
|
or (payment_amount and sale_order.state != 'cancel')"
|
|
name="sale_order_modal_validate"
|
|
class="modal-dialog"
|
|
><t t-call="sale.sale_order_portal_pay_modal"/></div>
|
|
</div>
|
|
|
|
<!-- modal relative to the action reject -->
|
|
<div role="dialog" class="modal fade" id="modaldecline">
|
|
<div class="modal-dialog">
|
|
<form id="decline" method="POST" t-attf-action="/my/orders/#{sale_order.id}/decline?access_token=#{sale_order.access_token}" class="modal-content">
|
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
|
<header class="modal-header">
|
|
<h4 class="modal-title">Reject This Quotation</h4>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</header>
|
|
<main class="modal-body">
|
|
<p>
|
|
Tell us why you are refusing this quotation, this will help us improve our services.
|
|
</p>
|
|
<textarea rows="4" name="decline_message" required="" placeholder="Your feedback..." class="form-control" />
|
|
</main>
|
|
<footer class="modal-footer">
|
|
<button type="submit" t-att-id="sale_order.id" class="btn btn-danger">
|
|
<i class="fa fa-times"></i> Reject
|
|
</button>
|
|
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">
|
|
Cancel
|
|
</button>
|
|
</footer>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- status messages -->
|
|
<div t-if="message == 'sign_ok'" class="alert alert-success alert-dismissible d-print-none" role="status">
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
<strong>Thank You!</strong><br/>
|
|
<t t-if="message == 'sign_ok' and sale_order.state == 'sale'">
|
|
Your order has been confirmed.
|
|
</t>
|
|
<t t-elif="message == 'sign_ok' and sale_order._has_to_be_paid()">
|
|
Your order has been signed but still needs to be paid to be confirmed.
|
|
</t>
|
|
<t t-else="">Your order has been signed.</t>
|
|
</div>
|
|
|
|
<div t-if="message == 'cant_reject'" class="alert alert-danger alert-dismissible d-print-none" role="alert">
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
Your order is not in a state to be rejected.
|
|
</div>
|
|
|
|
<t t-set="current_tx" t-value="sale_order.get_portal_last_transaction()"/>
|
|
<!-- hide state header when invoice is posted for current transaction. -->
|
|
<t t-if="current_tx and not (current_tx.state == 'done' and len(current_tx.invoice_ids) == 1 and current_tx.invoice_ids.state == 'posted')">
|
|
<t t-call="payment.state_header">
|
|
<t t-set="tx" t-value="current_tx"/>
|
|
</t>
|
|
</t>
|
|
|
|
<div t-if="sale_order.state == 'cancel'" class="alert alert-danger alert-dismissible d-print-none" role="alert">
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="close"></button>
|
|
<strong>This quotation has been cancelled.</strong> <a role="button" href="#discussion"><i class="fa fa-comment"/> Contact us to get a new quotation.</a>
|
|
</div>
|
|
|
|
<div t-if="sale_order.is_expired" class="alert alert-warning alert-dismissible d-print-none" role="alert">
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="close"></button>
|
|
<strong>This offer expired!</strong> <a role="button" href="#discussion"><i class="fa fa-comment"/> Contact us to get a new quotation.</a>
|
|
</div>
|
|
|
|
<!-- main content -->
|
|
<div t-attf-class="#{'pb-5' if report_type == 'html' else ''}" id="portal_sale_content">
|
|
<div><t t-call="#{sale_order._get_name_portal_content_view()}"/></div>
|
|
</div>
|
|
|
|
<!-- bottom actions -->
|
|
<div t-if="sale_order._has_to_be_signed() or sale_order._has_to_be_paid()" class="d-flex justify-content-center gap-1 d-print-none" name="sale_order_actions">
|
|
|
|
<t t-if="sale_order._has_to_be_signed()">
|
|
<div class="col-sm-auto mt8">
|
|
<a role="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#modalaccept" href="#"><i class="fa fa-check"/><t t-if="sale_order._has_to_be_paid()"> Sign & Pay</t><t t-else=""> Accept & Sign</t></a>
|
|
</div>
|
|
<div class="col-sm-auto mt8">
|
|
<a role="button" class="btn btn-light" href="#discussion"><i class="fa fa-comment"/> Feedback</a>
|
|
</div>
|
|
<div class="col-sm-auto mt8">
|
|
<a role="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#modaldecline" href="#"> <i class="fa fa-times"/> Reject</a>
|
|
</div>
|
|
</t>
|
|
<div t-elif="sale_order._has_to_be_paid()" class="col-sm-auto mt8">
|
|
<a role="button" data-bs-toggle="modal" data-bs-target="#modalaccept" href="#" t-att-class="'%s' % ('btn btn-light' if sale_order.transaction_ids else 'btn btn-primary')">
|
|
<i class="fa fa-check"/> <t t-if="not sale_order.signature">Accept & Pay</t><t t-else="">Pay Now</t>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- chatter -->
|
|
<hr/>
|
|
<div id="sale_order_communication">
|
|
<h3>Communication history</h3>
|
|
<t t-call="portal.message_thread"/>
|
|
</div>
|
|
</div><!-- // #quote_content -->
|
|
</div>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="sale_order_portal_sign_modal" name="Sales Order Portal Sign Modal">
|
|
<form
|
|
id="accept"
|
|
method="POST"
|
|
t-att-data-order-id="sale_order.id"
|
|
t-att-data-token="sale_order.access_token"
|
|
class="js_accept_json modal-content js_website_submit_form"
|
|
>
|
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
|
<header class="modal-header">
|
|
<h4 class="modal-title">Validate Order</h4>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"/>
|
|
</header>
|
|
<main class="modal-body" id="sign-dialog">
|
|
<span>
|
|
By signing, you confirm acceptance on behalf of
|
|
<b t-field="sale_order.partner_id.commercial_partner_id"/>
|
|
for the <b data-id="total_amount" t-field="sale_order.amount_total"/> quote.
|
|
</span>
|
|
<b t-if="sale_order.payment_term_id" t-field="sale_order.payment_term_id.note"/>
|
|
<t t-call="portal.signature_form">
|
|
<t t-set="call_url" t-value="sale_order.get_portal_url(suffix='/accept')"/>
|
|
<t
|
|
t-set="default_name"
|
|
t-value="sale_order.partner_id.name or sale_order.partner_id.commercial_partner_id.name"
|
|
/>
|
|
</t>
|
|
</main>
|
|
</form>
|
|
</template>
|
|
|
|
<template id="sale_order_portal_pay_modal" name="Sales Order Portal Pay Modal">
|
|
<div class="modal-content">
|
|
<header class="modal-header">
|
|
<h4 class="modal-title">Pay Order</h4>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"/>
|
|
</header>
|
|
<main class="modal-body" id="sign-dialog">
|
|
<t
|
|
t-set="prepayment_amount"
|
|
t-value="sale_order._get_prepayment_required_amount()"
|
|
/>
|
|
<t
|
|
t-set="prepayment_available"
|
|
t-value="sale_order.prepayment_percent
|
|
and sale_order.prepayment_percent != 1.0"
|
|
/>
|
|
<t
|
|
t-if="sale_order._has_to_be_paid() and prepayment_available"
|
|
t-call="sale.sale_order_portal_pay_modal_amount_selector"
|
|
/>
|
|
<div t-if="sale_order._has_to_be_paid()" class="mb-3">
|
|
<!-- The widget associated with this modal will hide and show spans depending of the selected option. -->
|
|
<span t-if="prepayment_available">
|
|
<span id="o_sale_portal_use_amount_prepayment">
|
|
By paying a <u>down payment</u> of
|
|
<span
|
|
t-out="amount"
|
|
t-options="{
|
|
'widget': 'monetary',
|
|
'display_currency': sale_order.currency_id
|
|
}"
|
|
class="fw-bold"
|
|
/>
|
|
<t t-if="sale_order._get_prepayment_required_amount() == amount">
|
|
(<b t-esc="sale_order.prepayment_percent * 100"/>%),
|
|
</t>
|
|
</span>
|
|
<span id="o_sale_portal_use_amount_total">By paying,</span>
|
|
</span>
|
|
<span t-else="">
|
|
By paying,
|
|
<span
|
|
t-out="amount"
|
|
t-options="{
|
|
'widget': 'monetary',
|
|
'display_currency': sale_order.currency_id,
|
|
}"
|
|
class="fw-bold"
|
|
/>
|
|
</span>
|
|
you confirm acceptance on behalf of
|
|
<b t-field="sale_order.partner_id.commercial_partner_id"/>
|
|
for the <b data-id="total_amount" t-field="sale_order.amount_total"/> quote.
|
|
<b
|
|
t-if="sale_order.payment_term_id"
|
|
t-field="sale_order.payment_term_id.note"
|
|
class="o_sale_payment_terms"
|
|
/>
|
|
</div>
|
|
<div t-if="payment_amount and not sale_order._has_to_be_paid()" class="mb-3">
|
|
Amount:
|
|
<span
|
|
t-out="payment_amount"
|
|
t-options="{
|
|
'widget': 'monetary',
|
|
'display_currency': sale_order.currency_id,
|
|
}"
|
|
class="fw-bold"
|
|
/>
|
|
</div>
|
|
<div t-if="company_mismatch"><t t-call="payment.company_mismatch_warning"/></div>
|
|
<div
|
|
t-elif="not sale_order._has_to_be_paid() and not payment_amount"
|
|
class="alert alert-danger"
|
|
>
|
|
The order is not in a state requiring customer payment.
|
|
</div>
|
|
<div t-else="" id="payment_method" class="text-start mt-0" >
|
|
<!-- Inject the order ID to allow Stripe to check if tokenization is required. -->
|
|
<t t-call="payment.form" sale_order_id="sale_order.id"/>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
</template>
|
|
|
|
<template
|
|
id="sale_order_portal_pay_modal_amount_selector"
|
|
name="Sales Order Portal Pay Modal Amounts Selector"
|
|
>
|
|
<div class="d-flex btn-group mb-3" role="group">
|
|
<button name="o_sale_portal_amount_prepayment_button" class="btn btn-light active">
|
|
Down payment <br/>
|
|
<span
|
|
t-if="not payment_amount or payment_amount == sale_order.amount_total"
|
|
t-out="prepayment_amount"
|
|
t-options="{
|
|
'widget': 'monetary',
|
|
'display_currency': sale_order.currency_id,
|
|
}"
|
|
class="fw-bold"
|
|
/>
|
|
<span
|
|
t-else=""
|
|
t-out="payment_amount"
|
|
t-options="{
|
|
'widget': 'monetary',
|
|
'display_currency': sale_order.currency_id,
|
|
}"
|
|
class="fw-bold"
|
|
/>
|
|
</button>
|
|
<button name="o_sale_portal_amount_total_button" class="btn btn-light">
|
|
Full amount <br/>
|
|
<span
|
|
t-out="sale_order.amount_total"
|
|
t-options="{
|
|
'widget': 'monetary',
|
|
'display_currency': sale_order.currency_id,
|
|
}"
|
|
class="fw-bold"
|
|
/>
|
|
</button>
|
|
</div>
|
|
</template>
|
|
|
|
<!--
|
|
Sales Order content : intro, information, order lines, remarks, descriptions ....
|
|
This template should contain all the printable element of the SO. This is the
|
|
template rendered in PDF with the report engine.
|
|
-->
|
|
<template id="sale_order_portal_content" name="Sales Order Portal Content">
|
|
<!-- Intro -->
|
|
<div id="introduction" t-attf-class="#{'border-bottom-0 pt-0 pb-3' if report_type == 'html' else ''}">
|
|
<div class="d-flex gap-2" id="intro_row">
|
|
<h2>
|
|
<t t-out="sale_order.type_name"/> -
|
|
<em t-out="sale_order.name"/>
|
|
</h2>
|
|
</div>
|
|
</div>
|
|
<div id="content">
|
|
<div id="informations" class="row">
|
|
<span id="transaction_info">
|
|
<div t-if="sale_order.get_portal_last_transaction() and not invoices and sale_order.state in ('sent', 'sale') and portal_confirmation == 'pay' and not success and not error"
|
|
t-att-data-order-id="sale_order.id">
|
|
<t t-if="sale_order.transaction_ids">
|
|
<t t-call="payment.state_header">
|
|
<t t-set="tx" t-value="sale_order.get_portal_last_transaction()"/>
|
|
</t>
|
|
</t>
|
|
</div>
|
|
</span>
|
|
<!-- Information -->
|
|
<div id="sale_info" class="col-12 col-lg-6 mb-4">
|
|
<span id="sale_info_title">
|
|
<h5 class="mb-1">Sale Information</h5>
|
|
<hr class="mt-1 mb-2"/>
|
|
</span>
|
|
<table class="table table-borderless table-sm">
|
|
<tbody style="white-space:nowrap" id="sale_info_table">
|
|
<tr>
|
|
<th t-if="sale_order.state in ['sale', 'cancel']" class="ps-0 pb-0">Order Date:</th>
|
|
<th t-else="" class="ps-0 pb-0">Date:</th>
|
|
<td class="w-100 pb-0 text-wrap"><span t-field="sale_order.date_order" t-options='{"widget": "date"}'/></td>
|
|
</tr>
|
|
<tr t-if="sale_order.validity_date and sale_order.state in ['draft', 'sent']">
|
|
<th class="ps-0 pb-0">Expiration Date:</th>
|
|
<td class="w-100 pb-0 text-wrap"><span t-field="sale_order.validity_date" t-options='{"widget": "date"}'/></td>
|
|
</tr>
|
|
<tr t-if="sale_order.client_order_ref">
|
|
<th class="ps-0 pb-0">Your Reference:</th>
|
|
<td class="w-100 pb-0 text-wrap"><span t-field="sale_order.client_order_ref"/></td>
|
|
</tr>
|
|
<tr t-if="sale_order.commitment_date and sale_order.state in ['sent', 'sale']">
|
|
<th class="ps-0 pb-0">Delivery Date:</th>
|
|
<td class="w-100 pb-0 text-wrap">
|
|
<span
|
|
t-field="sale_order.commitment_date"
|
|
t-options="{'widget': 'date'}"
|
|
/>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- ====== Customer Information ====== -->
|
|
<div id="customer_info" class="col-12 col-lg-6 mb-4">
|
|
<h5 class="mb-1">
|
|
<t t-if="sale_order.partner_shipping_id == sale_order.partner_invoice_id">
|
|
Invoicing and Shipping Address
|
|
</t>
|
|
<t t-else="">
|
|
Invoicing Address
|
|
</t>
|
|
<small t-if="sale_order.partner_id == sale_order.partner_invoice_id == sale_order.env.user.partner_id">
|
|
<a class="small" t-attf-href="/my/account?redirect={{sale_order.get_portal_url()}}">
|
|
<i class="fa fa-fw fa-pencil"/>
|
|
</a>
|
|
</small>
|
|
</h5>
|
|
<hr class="mt-1 mb-2"/>
|
|
<div t-field="sale_order.partner_invoice_id" t-options="{'widget': 'contact', 'fields': ['name', 'address', 'phone', 'email']}"/>
|
|
<span t-if="sale_order.partner_shipping_id != sale_order.partner_invoice_id"
|
|
id="shipping_address"
|
|
class="col-lg-6">
|
|
<br/>
|
|
<h5 class="mb-1">
|
|
Shipping Address
|
|
</h5>
|
|
<hr class="mt-1 mb-2"/>
|
|
<div t-field="sale_order.partner_shipping_id" t-options='{ "widget": "contact", "fields": [ "name", "address"]}'/>
|
|
</span>
|
|
</div>
|
|
<t t-set="invoices" t-value="sale_order.invoice_ids.filtered(lambda i: i.state not in ['draft', 'cancel']).sorted('date', reverse=True)[:3]"/>
|
|
<div id="sale_invoices" t-if="invoices and sale_order.state in ['sale', 'cancel']" class="col-12 col-lg-6 mb-4">
|
|
<h5 class="mb-1">Last Invoices</h5>
|
|
<hr class="mt-1 mb-2"/>
|
|
<t t-foreach="invoices" t-as="i">
|
|
<t t-set="report_url" t-value="i.get_portal_url()"/>
|
|
<t t-set="authorized_tx_ids" t-value="i.sudo().authorized_transaction_ids"/>
|
|
<div class="d-flex flex-column gap-2">
|
|
<div class="d-flex flex-column">
|
|
<div class="d-flex align-items-center justify-content-between">
|
|
<a t-att-href="report_url">
|
|
<span t-out="i.name"/>
|
|
</a>
|
|
<div t-if="i.payment_state in ('paid', 'in_payment')" class="small badge rounded-pill text-bg-success orders_label_text_align">
|
|
<i class="fa fa-fw fa-check"/> Paid
|
|
</div>
|
|
<div t-elif="i.payment_state == 'reversed'" class="small badge text-bg-success orders_label_text_align">
|
|
<i class="fa fa-fw fa-check"/> Reversed
|
|
</div>
|
|
<div t-elif="authorized_tx_ids" class="small badge rounded-pill text-bg-success orders_label_text_align">
|
|
<i class="fa fa-fw fa-check"/> Authorized
|
|
</div>
|
|
<div t-else="" class="small badge rounded-pill text-bg-info orders_label_text_align">
|
|
<i class="fa fa-fw fa-clock-o"/> Waiting Payment
|
|
</div>
|
|
</div>
|
|
<div class="small d-lg-inline-block">Date: <span class="text-muted" t-field="i.invoice_date"/></div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
|
|
<section id="details" style="page-break-inside: auto;">
|
|
<t t-if="product_documents">
|
|
<h4 id="details">Documents</h4>
|
|
<div class="d-flex flex-grow-1 flex-wrap gap-1 mb32">
|
|
<t t-foreach="product_documents" t-as="document_sudo">
|
|
<div class="bg-light p-2 rounded">
|
|
<div class="position-relative text-center">
|
|
<t t-set="attachment_sudo" t-value="document_sudo.ir_attachment_id"/>
|
|
<t t-set="target" t-value="attachment_sudo.type == 'url' and '_blank' or '_self'"/>
|
|
<a t-att-href="sale_order.get_portal_url('/document/' + str(document_sudo.id))" t-att-target="target" class="d-flex flex-row">
|
|
<div class="o_image"
|
|
t-att-title="attachment_sudo.name"
|
|
t-att-data-mimetype="attachment_sudo.mimetype"
|
|
t-attf-data-src="/web/image/#{attachment_sudo.id}/100x80?access_token=#{attachment_sudo.access_token}"/>
|
|
<div class="o_portal_product_document align-self-center" t-out="attachment_sudo.name"/>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</t>
|
|
|
|
<t t-set="lines_to_report" t-value="sale_order._get_order_lines_to_report()"/>
|
|
<t t-set="display_discount" t-value="any(line.discount > 0 for line in lines_to_report)"/>
|
|
<t t-set="hide_taxes_details" t-value="False"/>
|
|
<t t-set="display_taxes" t-value="not hide_taxes_details and any(line._has_taxes() for line in lines_to_report)"/>
|
|
|
|
<div name="sol_table" class="table-responsive">
|
|
<table t-att-data-order-id="sale_order.id" t-att-data-token="sale_order.access_token" class="table table-sm" id="sales_order_table">
|
|
<thead>
|
|
<tr>
|
|
<th class="text-start" id="product_name_header">Products</th>
|
|
<th class="text-end" id="product_qty_header">Quantity</th>
|
|
<th t-attf-class="text-end {{ 'd-none d-sm-table-cell' if report_type == 'html' else '' }}" id="product_unit_price_header">
|
|
Unit Price
|
|
</th>
|
|
<th t-if="display_discount" t-attf-class="text-end {{ 'd-none d-sm-table-cell' if report_type == 'html' else '' }}" id="product_discount_header">
|
|
<span>Disc.%</span>
|
|
</th>
|
|
<th t-if="display_taxes" t-attf-class="text-end {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}" id="taxes_header">
|
|
<span>Taxes</span>
|
|
</th>
|
|
<th class="text-end" id="subtotal_header">
|
|
<span>Amount</span>
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="sale_tbody">
|
|
|
|
<t t-set="current_section" t-value="None"/>
|
|
<t t-set="current_subsection" t-value="None"/>
|
|
<t t-set="price_field" t-value="'price_total' if hide_taxes_details else 'price_subtotal'"/>
|
|
|
|
<t t-foreach="lines_to_report" t-as="line">
|
|
|
|
<t t-set="is_note" t-value="line.display_type == 'line_note'"/>
|
|
<t t-set="is_section" t-value="line.display_type == 'line_section'"/>
|
|
<t t-set="is_subsection" t-value="line.display_type == 'line_subsection'"/>
|
|
<t t-set="is_combo" t-value="line.product_type == 'combo'"/>
|
|
|
|
<t t-if="is_section">
|
|
<t t-set="current_section" t-value="line"/>
|
|
<t t-set="current_subsection" t-value="None"/>
|
|
<t t-set="line_padding" t-value="0"/>
|
|
<t t-set="collapse_prices" t-value="line.collapse_prices"/>
|
|
</t>
|
|
<t t-elif="is_subsection">
|
|
<t t-set="current_subsection" t-value="line"/>
|
|
<t t-set="line_padding" t-value="2"/>
|
|
<t t-set="collapse_prices" t-value="current_section.collapse_prices or line.collapse_prices"/>
|
|
</t>
|
|
<t t-else="">
|
|
<t
|
|
t-set="line_padding"
|
|
t-value="3 if current_subsection else (2 if current_section else 0)"
|
|
/>
|
|
</t>
|
|
|
|
<t t-if="line.combo_item_id">
|
|
<t t-set="line_padding" t-value="line_padding + 1"/>
|
|
</t>
|
|
|
|
<t t-set="padding_class" t-value="line_padding and ('px-' + str(line_padding)) or ''"/>
|
|
|
|
<t t-if="not line.collapse_composition">
|
|
<tr
|
|
t-if="is_section or is_subsection"
|
|
name="tr_section"
|
|
t-att-class="'fw-bolder o_line_section' if is_section else 'fw-bold o_line_subsection'"
|
|
>
|
|
<t t-set="show_section_total" t-value="is_section or not line.parent_id.collapse_prices"/>
|
|
<t
|
|
t-set="section_name_colspan"
|
|
t-value="3 + (1 if display_discount else 0) + (1 if display_taxes else 0)"
|
|
/>
|
|
<t t-if="not show_section_total">
|
|
<t t-set="section_name_colspan" t-value="99"/>
|
|
</t>
|
|
<!-- Some columns (e.g. Discount and Unit Price)
|
|
are not displayed on smaller viewports -->
|
|
<td
|
|
name="td_section_name_xs"
|
|
t-att-colspan="section_name_colspan - 1 - (1 if display_discount else 0) - (1 if display_taxes else 0)"
|
|
t-attf-class="d-sm-none {{padding_class}}"
|
|
>
|
|
<span t-field="line.name"/>
|
|
</td>
|
|
<td
|
|
name="td_section_name_sm"
|
|
t-att-colspan="section_name_colspan - (1 if display_taxes else 0)"
|
|
t-attf-class="d-none d-sm-table-cell d-md-none {{padding_class}}"
|
|
>
|
|
<span t-field="line.name"/>
|
|
</td>
|
|
<td
|
|
name="td_section_name"
|
|
t-att-colspan="section_name_colspan"
|
|
t-attf-class="d-none d-md-table-cell {{padding_class}}"
|
|
>
|
|
<span t-field="line.name"/>
|
|
</td>
|
|
<td
|
|
t-if="show_section_total"
|
|
name="td_section_price"
|
|
class="text-end o_price_total"
|
|
>
|
|
<span
|
|
name="price_subtotal_section"
|
|
t-out="line._get_section_totals(price_field)"
|
|
class="text-nowrap"
|
|
t-options="{'widget': 'monetary', 'display_currency': sale_order.currency_id}"
|
|
/>
|
|
</td>
|
|
</tr>
|
|
<tr t-elif="is_note" name="tr_note" class="fst-italic o_line_note">
|
|
<td
|
|
name="td_note_name"
|
|
colspan="99"
|
|
t-att-class="padding_class"
|
|
>
|
|
<span t-field="line.name"/>
|
|
</td>
|
|
</tr>
|
|
<tr t-elif="is_combo" name="tr_combo" class="fw-bold o_line_subsection">
|
|
<td
|
|
name="td_combo_name"
|
|
t-att-class="padding_class"
|
|
>
|
|
<span t-field="line.name">Combo Product</span>
|
|
</td>
|
|
<td
|
|
name="td_combo_quantity"
|
|
class="text-end"
|
|
>
|
|
<div id="quote_qty">
|
|
<span t-field="line.product_uom_qty"/>
|
|
<span t-field="line.product_uom_id"/>
|
|
</div>
|
|
</td>
|
|
<td
|
|
name="td_combo_priceunit"
|
|
t-attf-class="text-end {{ 'd-none d-sm-table-cell' if report_type == 'html' else '' }}"
|
|
/>
|
|
<td
|
|
t-if="display_discount"
|
|
name="td_combo_discount"
|
|
t-attf-class="text-end {{ 'd-none d-sm-table-cell' if report_type == 'html' else '' }}"
|
|
/>
|
|
<td
|
|
t-if="display_taxes"
|
|
name="td_combo_taxes"
|
|
t-attf-class="text-end {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}"
|
|
/>
|
|
<td name="td_combo_subtotal" class="text-end">
|
|
<span
|
|
t-if="not collapse_prices"
|
|
name="price_subtotal_combo"
|
|
t-out="line._get_combo_totals(price_field)"
|
|
class="text-nowrap"
|
|
t-options="{'widget': 'monetary', 'display_currency': sale_order.currency_id}"
|
|
>
|
|
27.00
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr t-else="" name="tr_product">
|
|
<td
|
|
name="td_product_name"
|
|
t-att-class="padding_class"
|
|
>
|
|
<span name="span_product_name" t-field="line.name"/>
|
|
</td>
|
|
<td name="td_product_quantity" class="text-end">
|
|
<div id="quote_qty">
|
|
<span t-field="line.product_uom_qty"/>
|
|
<span t-field="line.product_uom_id"/>
|
|
</div>
|
|
</td>
|
|
<td
|
|
name="td_product_priceunit"
|
|
t-attf-class="text-end {{ 'd-none d-sm-table-cell' if report_type == 'html' else '' }}"
|
|
>
|
|
<t t-if="not collapse_prices">
|
|
<div
|
|
t-if="line.discount >= 0"
|
|
t-field="line.price_unit"
|
|
t-att-style="line.discount and 'text-decoration: line-through' or None"
|
|
t-att-class="(line.discount and 'text-danger' or '') + ' text-end'"
|
|
t-options="{'widget': 'monetary', 'display_currency': line.currency_id}"
|
|
/>
|
|
<div t-if="line.discount">
|
|
<t
|
|
t-out="(1-line.discount / 100.0) * line.price_unit"
|
|
t-options='{"widget": "float", "decimal_precision": "Product Price"}'
|
|
/>
|
|
</div>
|
|
</t>
|
|
</td>
|
|
<td t-if="display_discount" name="td_product_discount" t-attf-class="text-end {{ 'd-none d-sm-table-cell' if report_type == 'html' else '' }}">
|
|
<t t-if="not collapse_prices">
|
|
<!-- ! Always check collapse_prices separately; keep discount condition below for XPath -->
|
|
<strong t-if="line.discount > 0" class="text-info">
|
|
<t t-out="((line.discount % 1) and '%s' or '%d') % line.discount"/>%
|
|
</strong>
|
|
</t>
|
|
</td>
|
|
<td t-if="display_taxes" name="td_product_taxes" t-attf-class="text-end {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
|
|
<span t-out="', '.join(map(lambda x: (x.name), line.tax_ids))"/>
|
|
</td>
|
|
<td t-if="not line.is_downpayment" name="td_product_subtotal" class="text-end">
|
|
<t t-if="not collapse_prices">
|
|
<span
|
|
t-if="price_field == 'price_subtotal'"
|
|
t-field="line.price_subtotal"
|
|
/>
|
|
<span t-else="" t-field="line.price_total"/>
|
|
</t>
|
|
</td>
|
|
</tr>
|
|
</t>
|
|
<t t-else="">
|
|
<!-- Displaying the section inner content, grouped by taxes -->
|
|
<tr
|
|
t-foreach="line._get_grouped_section_summary(display_taxes)"
|
|
t-as="section_line"
|
|
name="tr_section_group"
|
|
class="o_line_section"
|
|
>
|
|
<td name="td_section_group_name" t-att-class="padding_class">
|
|
<span t-field="line.name">
|
|
Section Summary
|
|
</span>
|
|
</td>
|
|
<td name="td_section_group_quantity" class="text-end text-nowrap">
|
|
<span>1.00 Units</span>
|
|
</td>
|
|
<td name="td_section_group_priceunit" class="d-none d-sm-table-cell text-end text-nowrap">
|
|
<span
|
|
t-out="section_line['price_subtotal']"
|
|
t-options="{'widget': 'monetary', 'display_currency': sale_order.currency_id}"
|
|
/>
|
|
</td>
|
|
<td t-if="display_discount" name="td_section_group_discount" class="d-none d-sm-table-cell"/>
|
|
<td
|
|
t-if="display_taxes"
|
|
name="td_section_group_taxes"
|
|
t-attf-class="d-none d-md-table-cell text-end {{ 'text-nowrap' if len(section_line['tax_labels']) < 10 else '' }}"
|
|
>
|
|
<span t-out="', '.join(section_line['tax_labels'])">
|
|
Tax 15%
|
|
</span>
|
|
</td>
|
|
<td name="td_section_group_total" class="text-end o_price_total">
|
|
<span
|
|
name="price_subtotal_section_grouped"
|
|
t-out="section_line[price_field]"
|
|
t-options="{'widget': 'monetary', 'display_currency': sale_order.currency_id}"
|
|
>
|
|
31.05
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
</t>
|
|
</t>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div id="total" name="total" style="page-break-inside: avoid;">
|
|
<div class="col-xs-7 col-md-5 ms-auto">
|
|
<t t-call="sale.sale_order_portal_content_totals_table"/>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section t-if="sale_order.signature" id="signature" name="Signature">
|
|
<div class="row mt-4" name="signature">
|
|
<div t-attf-class="#{'col-3' if report_type != 'html' else 'col-sm-7 col-md-4'} ms-auto text-center">
|
|
<h5>Signature</h5>
|
|
<img t-att-src="image_data_uri(sale_order.signature)" style="max-height: 6rem; max-width: 100%;"/>
|
|
<p t-field="sale_order.signed_by"/>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section t-if="not is_html_empty(sale_order.note)" id="terms" class="mt-4">
|
|
<h4 class="">Terms & Conditions</h4>
|
|
<hr class="mt-0 mb-1"/>
|
|
<em t-field="sale_order.note"/>
|
|
</section>
|
|
|
|
<section t-if="sale_order.payment_term_id" class="mt-4">
|
|
<h4 class="">Payment terms</h4>
|
|
<hr class="mt-0 mb-1"/>
|
|
<span t-field="sale_order.payment_term_id.note"/>
|
|
</section>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="sale_order_portal_content_totals_table">
|
|
<table class="table table-sm" name="sale_order_totals_table">
|
|
<t t-call="#{sale_order._get_name_tax_totals_view()}">
|
|
<t t-set="tax_totals" t-value="sale_order.tax_totals"/>
|
|
<t t-set="currency" t-value="sale_order.currency_id"/>
|
|
<!--
|
|
NOTE: This variable should not be used in templates before version 19.1, as it might
|
|
not be available in earlier versions if customer does not upgrade the `sale` module.
|
|
-->
|
|
<t t-set="doc" t-value="sale_order"/>
|
|
</t>
|
|
</table>
|
|
</template>
|
|
</odoo>
|