configure container naming in docker-compose, add restaurant receipt template, and implement QZ printer JS wrapper and print ending cutter comment updated
This commit is contained in:
parent
2115f20e95
commit
10509df3fa
@ -2,36 +2,267 @@
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="dine360_order_channels.OrderReceipt" t-inherit="point_of_sale.OrderReceipt" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//div[hasclass('pos-receipt-order-data')]" position="before">
|
||||
<div class="channel-receipt-info mt-2 border-top pt-2">
|
||||
<div t-if="props.data.order_source" class="d-flex justify-content-between">
|
||||
<span>Order Source:</span>
|
||||
<span class="fw-bold text-uppercase" t-esc="props.data.order_source_label"/>
|
||||
</div>
|
||||
<div t-if="props.data.fulfilment_type" class="d-flex justify-content-between">
|
||||
<span>Fulfilment:</span>
|
||||
<span class="fw-bold text-uppercase" t-esc="props.data.fulfilment_type_label"/>
|
||||
</div>
|
||||
<xpath expr="//div[hasclass('pos-receipt')]" position="replace">
|
||||
<div class="pos-receipt custom-restaurant-receipt">
|
||||
<style>
|
||||
.custom-restaurant-receipt {
|
||||
width: 100%;
|
||||
font-family: 'Arial', sans-serif;
|
||||
color: #333;
|
||||
background: #fff;
|
||||
padding: 10px 5px;
|
||||
}
|
||||
.custom-restaurant-receipt .main-title {
|
||||
color: #E67E22;
|
||||
text-align: center;
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
margin: 0 0 15px 0;
|
||||
}
|
||||
.custom-restaurant-receipt .receipt-header {
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.custom-restaurant-receipt .receipt-header img {
|
||||
max-width: 120px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.custom-restaurant-receipt .company-name {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.custom-restaurant-receipt .company-details {
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.custom-restaurant-receipt .subtitle {
|
||||
font-style: italic;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin: 15px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
.custom-restaurant-receipt .info-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 15px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.custom-restaurant-receipt .info-box {
|
||||
background: #FDEBD0;
|
||||
padding: 6px 10px;
|
||||
border-radius: 2px;
|
||||
flex: 1;
|
||||
margin: 0 4px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
.custom-restaurant-receipt .info-box span {
|
||||
font-weight: normal;
|
||||
display: block;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.custom-restaurant-receipt .info-box:first-child { margin-left: 0; }
|
||||
.custom-restaurant-receipt .info-box:last-child { margin-right: 0; }
|
||||
|
||||
.custom-restaurant-receipt table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 15px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.custom-restaurant-receipt th {
|
||||
background: #E67E22;
|
||||
color: white;
|
||||
padding: 8px 6px;
|
||||
text-align: right;
|
||||
}
|
||||
.custom-restaurant-receipt th:first-child { text-align: left; }
|
||||
.custom-restaurant-receipt td {
|
||||
padding: 8px 6px;
|
||||
text-align: right;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
.custom-restaurant-receipt td:first-child { text-align: left; }
|
||||
|
||||
.custom-restaurant-receipt .totals-section {
|
||||
width: 70%;
|
||||
float: right;
|
||||
margin-bottom: 15px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.custom-restaurant-receipt .totals-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 6px 0;
|
||||
}
|
||||
.custom-restaurant-receipt .totals-row.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.custom-restaurant-receipt .totals-row .val-box {
|
||||
background: #FDEBD0;
|
||||
padding: 4px 10px;
|
||||
min-width: 80px;
|
||||
text-align: right;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.custom-restaurant-receipt .clearfix::after {
|
||||
content: "";
|
||||
clear: both;
|
||||
display: table;
|
||||
}
|
||||
.custom-restaurant-receipt .footer-slogan {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
font-size: 16px;
|
||||
margin-top: 20px;
|
||||
clear: both;
|
||||
}
|
||||
.custom-restaurant-receipt .channel-info {
|
||||
font-size: 11px;
|
||||
margin-top: 15px;
|
||||
border-top: 1px dashed #ccc;
|
||||
padding-top: 10px;
|
||||
clear: both;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="main-title">Restaurant Receipt</div>
|
||||
|
||||
<t t-if="props.data.fulfilment_type === 'delivery'">
|
||||
<div class="mt-2 pt-2 border-top">
|
||||
<div class="fw-bold">DELIVERY ADDRESS:</div>
|
||||
<div t-if="props.data.delivery_street" t-esc="props.data.delivery_street"/>
|
||||
<div t-if="props.data.delivery_city || props.data.delivery_zip">
|
||||
<t t-esc="props.data.delivery_city"/> <t t-esc="props.data.delivery_zip"/>
|
||||
<div class="receipt-header">
|
||||
<img t-attf-src="/web/image?model=res.company&id={{props.data.headerData.company.id}}&field=logo" alt="Logo"/>
|
||||
<div class="company-name" t-esc="props.data.headerData.company.name"/>
|
||||
<div class="company-details">
|
||||
<div t-if="props.data.headerData.company.contact_address" t-esc="props.data.headerData.company.contact_address"/>
|
||||
<div>
|
||||
<t t-if="props.data.headerData.company.phone">Tel: <t t-esc="props.data.headerData.company.phone"/> </t>
|
||||
<t t-if="props.data.headerData.company.email"> | <t t-esc="props.data.headerData.company.email"/></t>
|
||||
</div>
|
||||
<div t-if="props.data.delivery_phone">Phone: <t t-esc="props.data.delivery_phone"/></div>
|
||||
<div t-if="props.data.delivery_notes" class="mt-1 small italic">
|
||||
Note: <t t-esc="props.data.delivery_notes"/>
|
||||
<div t-if="props.data.headerData.company.website" t-esc="props.data.headerData.company.website"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="subtitle">"Authentic Indian Food At its Finest!"</div>
|
||||
|
||||
<div class="info-row">
|
||||
<div class="info-box">
|
||||
Receipt No.
|
||||
<span t-esc="props.data.name"/>
|
||||
</div>
|
||||
<div class="info-box">
|
||||
Date & Time
|
||||
<span t-esc="props.data.date"/>
|
||||
</div>
|
||||
<div class="info-box" t-if="props.data.headerData.cashier">
|
||||
Cashier
|
||||
<span t-esc="props.data.headerData.cashier"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>List of Items</th>
|
||||
<th>Qty</th>
|
||||
<th>Unit Cost</th>
|
||||
<th>Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="props.data.orderlines" t-as="line" t-key="line.id">
|
||||
<td>
|
||||
<t t-esc="line.productName"/>
|
||||
<div t-if="line.customerNote" class="small italic text-muted mt-1">
|
||||
<i class="fa fa-sticky-note me-1"/> <t t-esc="line.customerNote"/>
|
||||
</div>
|
||||
</td>
|
||||
<td><t t-esc="line.qty"/></td>
|
||||
<td><t t-esc="props.formatCurrency(line.price)"/></td>
|
||||
<td><t t-esc="props.formatCurrency(line.price_display)"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="clearfix">
|
||||
<div class="totals-section">
|
||||
<div class="totals-row">
|
||||
<span>Total Amount</span>
|
||||
<span class="val-box" t-esc="props.formatCurrency(props.data.total_without_tax)"/>
|
||||
</div>
|
||||
<div class="totals-row" t-if="props.data.amount_tax > 0">
|
||||
<span>VAT</span>
|
||||
<span class="val-box" t-esc="props.formatCurrency(props.data.amount_tax)"/>
|
||||
</div>
|
||||
<div class="totals-row bold">
|
||||
<span>Net Amount</span>
|
||||
<span class="val-box" t-esc="props.formatCurrency(props.data.amount_total)"/>
|
||||
</div>
|
||||
<t t-if="props.data.rounding_applied">
|
||||
<div class="totals-row">
|
||||
<span>Rounding</span>
|
||||
<span class="val-box" t-esc="props.formatCurrency(props.data.rounding_applied)"/>
|
||||
</div>
|
||||
<div class="totals-row bold">
|
||||
<span>To Pay</span>
|
||||
<span class="val-box" t-esc="props.formatCurrency(props.data.amount_total + props.data.rounding_applied)"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Payment Lines -->
|
||||
<div class="totals-row" t-foreach="props.data.paymentlines" t-as="line" t-key="line_index">
|
||||
<span t-esc="line.name"/>
|
||||
<span class="val-box" t-esc="props.formatCurrency(line.amount, false)"/>
|
||||
</div>
|
||||
<div class="totals-row" t-if="props.data.change > 0">
|
||||
<span>Change</span>
|
||||
<span class="val-box" t-esc="props.formatCurrency(props.data.change)"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<div t-if="props.data.social_ref" class="mt-1 small">
|
||||
Ref: <t t-esc="props.data.social_ref"/>
|
||||
</div>
|
||||
<div t-if="props.data.whatsapp_number" class="mt-1 small">
|
||||
WhatsApp: <t t-esc="props.data.whatsapp_number"/>
|
||||
|
||||
<div class="footer-slogan">Eat As much As You Like!</div>
|
||||
|
||||
<!-- Order Channels Info -->
|
||||
<div class="channel-info">
|
||||
<div t-if="props.data.order_source" class="d-flex justify-content-between mb-1">
|
||||
<span>Order Source:</span>
|
||||
<span class="fw-bold text-uppercase" t-esc="props.data.order_source_label"/>
|
||||
</div>
|
||||
<div t-if="props.data.fulfilment_type" class="d-flex justify-content-between mb-1">
|
||||
<span>Fulfilment:</span>
|
||||
<span class="fw-bold text-uppercase" t-esc="props.data.fulfilment_type_label"/>
|
||||
</div>
|
||||
|
||||
<t t-if="props.data.fulfilment_type === 'delivery'">
|
||||
<div class="mt-2 pt-2 border-top">
|
||||
<div class="fw-bold mb-1">DELIVERY ADDRESS:</div>
|
||||
<div t-if="props.data.delivery_street" t-esc="props.data.delivery_street"/>
|
||||
<div t-if="props.data.delivery_city || props.data.delivery_zip">
|
||||
<t t-esc="props.data.delivery_city"/> <t t-esc="props.data.delivery_zip"/>
|
||||
</div>
|
||||
<div t-if="props.data.delivery_phone">Phone: <t t-esc="props.data.delivery_phone"/></div>
|
||||
<div t-if="props.data.delivery_notes" class="mt-1 small" style="font-style: italic;">
|
||||
Note: <t t-esc="props.data.delivery_notes"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<div t-if="props.data.social_ref" class="mt-1 small">
|
||||
Ref: <t t-esc="props.data.social_ref"/>
|
||||
</div>
|
||||
<div t-if="props.data.whatsapp_number" class="mt-1 small">
|
||||
WhatsApp: <t t-esc="props.data.whatsapp_number"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pos-receipt-order-data text-center mt-3" t-if="props.data.footer" style="white-space:pre-line; font-size: 11px;">
|
||||
<t t-esc="props.data.footer" />
|
||||
</div>
|
||||
|
||||
<div class="pos-receipt-order-data mt-3 text-center" style="font-size: 10px;">
|
||||
<p>Powered by Dine360</p>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
|
||||
@ -167,12 +167,14 @@ function buildEscPosReceipt(receiptElement, order) {
|
||||
|
||||
const body = lines.flatMap((line) => wrapLine(line)).join(NEWLINE);
|
||||
|
||||
console.log("Cutter command run: Preparing ESC/POS data with feed and partial cut.");
|
||||
|
||||
return [
|
||||
ESC + "@", // Initialize printer
|
||||
ESC + "a" + "\x00", // Left align
|
||||
body,
|
||||
NEWLINE + NEWLINE + NEWLINE,
|
||||
GS + "V" + "\x00", // Full cut on Epson-compatible printers
|
||||
NEWLINE + NEWLINE + NEWLINE + NEWLINE + NEWLINE,
|
||||
GS + "V" + "\x42" + "\x00", // Feed paper to cutting position and perform partial cut (standard ESC/POS)
|
||||
].join("");
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user