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:
Alaguraj0361 2026-05-06 17:08:13 +05:30
parent 2115f20e95
commit 10509df3fa
2 changed files with 260 additions and 27 deletions

View File

@ -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&amp;id={{props.data.headerData.company.id}}&amp;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 &amp; 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>

View File

@ -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("");
}