[REF] pos*: introduce bootstrap

pos*: point_of_sale,pos_discount,pos_hr,pos_loyalty,
pos_restaurant,pos_sale,pos_sale_product_configurator,pos_six

Prior to this commit, the point_of_sale module relied on custom CSS.

To enhance user experience, increase flexibility, and simplify maintenance,
most of the custom css was replaced in this PR with Bootstrap utility classes.

Task: 3354582
Authored-by:  Xavier Luyckx (xlu) <xlu@odoo.com>
Part-of: odoo/odoo#129544
Co-authored-by:  vlst <vlst@odoo.com>,  Pedram (pebr)
This commit is contained in:
Xavier Luyckx (xlu)
2023-07-28 16:28:45 +02:00
committed by vlst
co-authored by vlst <vlst@odoo.com>, Pedram (pebr)
parent eeee7dc9e7
commit c087603d0b
159 changed files with 2372 additions and 6584 deletions
+20 -12
View File
@@ -77,16 +77,32 @@
# Main PoS assets, they are loaded in the PoS UI and in the PoS unit tests
'point_of_sale._assets_pos': [
# scss variables and utilities
'point_of_sale/static/src/scss/pos_variables_extra.scss',
# 'preparation_display' bootstrap customization layer
'web/static/src/scss/functions.scss',
# 'point_of_sale/static/src/scss/primary_variables.scss', TO DO - CREATE
# 'webclient' bootstrap customization layer
('include', 'web._assets_helpers'),
'web/static/src/scss/bootstrap_overridden.scss',
('include', 'web._assets_backend_helpers'),
'web/static/src/scss/pre_variables.scss',
'web/static/lib/bootstrap/scss/_variables.scss',
'web/static/fonts/fonts.scss',
# Import Bootstrap
('include', 'web._assets_bootstrap_backend'),
# Icons
'web/static/src/libs/fontawesome/css/font-awesome.css',
'web/static/lib/odoo_ui_icons/*',
'web/static/src/webclient/icons.scss',
# scss variables and utilities
'point_of_sale/static/src/scss/pos_variables_extra.scss',
'web/static/src/scss/bootstrap_overridden.scss',
'web/static/fonts/fonts.scss',
'web/static/src/legacy/scss/fontawesome_overridden.scss',
# JS framework
'web/static/src/boot.js',
'web/static/src/session.js',
@@ -228,14 +244,6 @@
'web/static/tests/main.js',
'web/static/tests/setup.js',
# These 2 lines below are taken from web.assets_frontend
# They're required for the web.frontend_legacy to work properly
# It is expected to add other lines coming from the web.assets_frontend
# if we need to add more and more legacy stuff that would require other scss or js.
('include', 'web._assets_helpers'),
'web/static/src/scss/pre_variables.scss',
'web/static/lib/bootstrap/scss/_variables.scss',
('include', 'web.frontend_legacy'),
("include", "web.assets_backend_legacy_lazy"),
## END copy of web.tests_assets
@@ -3,20 +3,15 @@
<t t-name="point_of_sale.ErrorBarcodePopup" owl="1">
<div class="popup popup-barcode">
<header class="title drag-handle">
<span>Unknown Barcode</span>
<br />
<span class="barcode">
<t t-esc="props.code" />
</span>
<header class="title drag-handle modal-header">
<h5 class="modal-title">Unknown Barcode: <span class="fst-italic" t-esc="props.code"/>
</h5>
</header>
<main class="body">
<main class="modal-body">
<t t-esc="translatedMessage" />
</main>
<footer class="footer">
<div class="button cancel" t-on-click="confirm">
Ok
</div>
<footer class="footer modal-footer">
<button type="button" class="btn btn-primary" t-on-click="confirm">Ok</button>
</footer>
</div>
</t>
@@ -4,87 +4,6 @@
right: 10px;
top: 106px;
width: 200px;
font-size: 10px;
background: rgba(0, 0, 0, 0.82);
color: white;
padding-bottom: 10px;
cursor: move;
-webkit-transform: translate3d(0, 0, 0);
}
.debug-widget .toggle {
position: absolute;
font-size: 16px;
cursor: pointer;
top: 0px;
right: 0px;
padding: 10px;
padding-right: 15px;
}
.debug-widget .content {
overflow: hidden;
}
.debug-widget header {
position: relative;
}
.debug-widget h1 {
background: black;
padding-top: 10px;
padding-left: 10px;
margin-top: 0;
margin-bottom: 0;
}
.debug-widget .category {
background: black;
padding-left: 10px;
margin: 0px;
font-weight: bold;
padding-top: 3px;
padding-bottom: 3px;
}
.debug-widget .button {
padding: 5px;
padding-left: 15px;
display: block;
cursor: pointer;
}
.debug-widget .button:active {
background: rgba(96, 21, 177, 0.45);
}
.debug-widget input {
margin-left: 10px;
margin-top: 7px;
padding: 4px;
width: 180px;
border: none;
box-sizing: border-box;
-moz-box-sizing: border-box;
border-radius: 3px;
}
.debug-widget .status {
padding: 5px;
padding-left: 15px;
display: block;
cursor: default;
}
.debug-widget .status.on {
background-color: #6cd11d;
}
.debug-widget .event {
padding: 5px;
padding-left: 15px;
display: block;
cursor: default;
background-color: #1E1E1E;
}
};
@@ -3,88 +3,100 @@
<t t-name="point_of_sale.DebugWidget" owl="1">
<Transition visible="props.state.showWidget" name="'o-fade'" leaveDuration="200" t-slot-scope="transition">
<div class="debug-widget" t-att-class="transition.className" t-ref="root" t-att-style="style">
<div class="debug-widget bg-100 p-2 shadow-lg" t-att-class="transition.className" t-ref="root" t-att-style="style">
<header class="drag-handle">
<h1>Debug Window</h1>
<h2>Debug Window</h2>
</header>
<div class="toggle" t-on-click="() => debug.toggleWidget()" title="Dismiss"
role="img" aria-label="Dismiss"><i class="fa fa-times" /></div>
<div class="content">
<p class="category">Electronic Scale</p>
<ul>
<li>
<input t-model="state.weightInput" type="text" class="weight"></input>
</li>
<li class="button set_weight" t-on-click="setWeight">Set Weight</li>
<li class="button reset_weight" t-on-click="resetWeight">Reset</li>
</ul>
<div class="toggle position-absolute top-0 end-0 p-2" t-on-click="() => debug.toggleWidget()" title="Dismiss"
role="img" aria-label="Dismiss"><i class="fa fa-fw fa-times" /></div>
<div class="content overflow-hidden">
<p class="category">Barcode Scanner</p>
<ul>
<li>
<input t-model="state.barcodeInput" type="text" class="ean" t-att-disabled="!barcodeReader" t-attf-placeholder="{{ !barcodeReader ? 'No nomenclature' : '' }}"/>
</li>
<li class="button barcode" t-on-click="barcodeScan">Scan</li>
<li class="button custom_ean" t-on-click="barcodeScanEAN">Scan EAN-13</li>
</ul>
<div class="debug-electronic-scale">
<h5 class="category">Electronic Scale</h5>
<input t-model="state.weightInput" type="text" class="weight form-control"></input>
<ul class="d-flex flex-column align-items-start mb-3">
<li class="set_weight btn btn-link" t-on-click="setWeight">Set Weight</li>
<li class="reset_weight btn btn-link" t-on-click="resetWeight">Reset</li>
</ul>
</div>
<p class="category">Orders</p>
<ul>
<li class="button" t-on-click="deleteOrders">
Delete Paid Orders
</li>
<li class="button" t-on-click="deleteUnpaidOrders">
Delete Unpaid Orders
</li>
<li t-if="!state.isPaidOrdersReady" class="button"
t-on-click="preparePaidOrders">
Export Paid Orders
</li>
<a t-else="" t-att-download="paidOrdersFilename" t-att-href="paidOrdersURL"
t-on-click="() => { state.isPaidOrdersReady = !state.isPaidOrdersReady }">
<li class="button">
Download Paid Orders
<div class="debug-barcode-scanner">
<h5 class="category">Barcode Scanner</h5>
<ul class="d-flex flex-column align-items-start mb-3">
<li>
<input t-model="state.barcodeInput" type="text" class="ean form-control" t-att-disabled="!barcodeReader" t-attf-placeholder="{{ !barcodeReader ? 'No nomenclature' : '' }}"/>
</li>
</a>
<li t-if="!state.isUnpaidOrdersReady" class="button"
t-on-click="prepareUnpaidOrders">
Export Unpaid Orders
</li>
<a t-else="" t-att-download="unpaidOrdersFilename"
t-att-href="unpaidOrdersURL"
t-on-click="() => { state.isUnpaidOrdersReady = !state.isUnpaidOrdersReady }">
<li class="button">
Download Unpaid Orders
</li>
</a>
<li class="button import_orders" style="position:relative">
Import Orders
<input t-on-change="importOrders" type="file"
style="opacity:0;position:absolute;top:0;left:0;right:0;bottom:0;margin:0;cursor:pointer" />
</li>
</ul>
<li class="button btn btn-link barcode" t-on-click="barcodeScan">Scan</li>
<li class="button btn btn-link custom_ean" t-on-click="barcodeScanEAN">Scan EAN-13</li>
</ul>
</div>
<p class="category">Hardware Status</p>
<ul>
<li class="status weighing">Weighing</li>
<li class="button display_refresh" t-on-click="refreshDisplay">
Refresh Display
</li>
</ul>
<p class="category">Hardware Events</p>
<ul>
<li class="event" t-ref="open_cashbox">Open Cashbox</li>
<li class="event" t-ref="print_receipt">Print Receipt</li>
<li class="event" t-ref="scale_read">Read Weighing Scale</li>
</ul>
<p class="category">Others</p>
<ul>
<li class="event">
<span>Buffer: </span>
<t t-esc="bufferRepr" />
</li>
</ul>
<div class="debug-orders">
<h5 class="category">Orders</h5>
<ul class="mb-3">
<li class="button btn btn-link" t-on-click="deleteOrders">
Delete Paid Orders
</li>
<li class="button btn btn-link" t-on-click="deleteUnpaidOrders">
Delete Unpaid Orders
</li>
<li t-if="!state.isPaidOrdersReady" class="button btn btn-link"
t-on-click="preparePaidOrders">
Export Paid Orders
</li>
<a t-else="" t-att-download="paidOrdersFilename" t-att-href="paidOrdersURL"
t-on-click="() => { state.isPaidOrdersReady = !state.isPaidOrdersReady }">
<li class="button btn btn-link">
Download Paid Orders
</li>
</a>
<li t-if="!state.isUnpaidOrdersReady" class="button btn btn-link"
t-on-click="prepareUnpaidOrders">
Export Unpaid Orders
</li>
<a t-else="" t-att-download="unpaidOrdersFilename"
t-att-href="unpaidOrdersURL"
t-on-click="() => { state.isUnpaidOrdersReady = !state.isUnpaidOrdersReady }">
<li class="button btn btn-link">
Download Unpaid Orders
</li>
</a>
<li class="button btn btn-link import_orders" style="position:relative">
Import Orders
<input t-on-change="importOrders" type="file"
style="opacity:0;position:absolute;top:0;left:0;right:0;bottom:0;margin:0;cursor:pointer" />
</li>
</ul>
</div>
<div class="debug-hardware-status d-flex flex-column">
<h5 class="category">Hardware Status</h5>
<ul class="mb-3">
<li class="status weighing">Weighing</li>
<li class="button btn btn-link display_refresh" t-on-click="refreshDisplay">
Refresh Display
</li>
</ul>
</div>
<div class="debug-hardware-events">
<h5 class="category">Hardware Events</h5>
<ul class="mb-3">
<li class="event" t-ref="open_cashbox">Open Cashbox</li>
<li class="event" t-ref="print_receipt">Print Receipt</li>
<li class="event" t-ref="scale_read">Read Weighing Scale</li>
</ul>
</div>
<div class="debug-others">
<h5 class="category">Others</h5>
<ul class="mb-3">
<li class="event">
<span>Buffer: </span>
<t t-esc="bufferRepr" />
</li>
</ul>
</div>
</div>
</div>
</Transition>
@@ -3,10 +3,12 @@
<t t-name="point_of_sale.OrderImportPopup" owl="1">
<div class="popup popup-import">
<header class="title drag-handle">
<span>Finished Importing Orders</span>
</header>
<ul class="body">
<div class="modal-header">
<h4 class="modal-title title drag-handle">
Finished Importing Orders
</h4>
</div>
<main class="body modal-body">
<li>Successfully imported <b><t t-esc="props.report.paid or 0" /></b> paid orders</li>
<li>Successfully imported <b><t t-esc="props.report.unpaid or 0" /></b> unpaid orders</li>
<t t-if="unpaidSkipped">
@@ -23,9 +25,9 @@
</ul>
</li>
</t>
</ul>
<footer class="footer">
<div class="button cancel" t-on-click="confirm">
</main>
<footer class="footer modal-footer">
<div class="button cancel btn btn-lg btn-primary" t-on-click="confirm">
<t t-esc="props.confirmText" />
</div>
</footer>
@@ -3,14 +3,16 @@
<t t-name="point_of_sale.ErrorPopup" owl="1">
<div class="popup popup-error">
<p class="title">
<t t-esc="props.title" />
</p>
<p class="body">
<div class="modal-header">
<h4 class="modal-title title">
<t t-esc="props.title" />
</h4>
</div>
<main class="modal-body">
<t t-esc="props.body" />
</p>
<div class="footer">
<div class="button cancel" t-on-click="confirm">
</main>
<div class="footer modal-footer">
<div class="button cancel btn btn-lg btn-primary " t-on-click="confirm">
<t t-esc="props.confirmText" />
</div>
</div>
@@ -3,17 +3,19 @@
<t t-name="point_of_sale.ErrorTracebackPopup" owl="1">
<div class="popup popup-error">
<header class="title">
<t t-esc="props.title" />
</header>
<main class="body traceback">
<div class="modal-header">
<h4 class="modal-title title">
<t t-esc="props.title" />
</h4>
</div>
<main class="modal-body body traceback">
<t t-esc="props.body" />
</main>
<footer class="footer">
<div t-if="!props.exitButtonIsShown" class="button cancel" t-on-click="confirm">
<footer class="footer modal-footer">
<div t-if="!props.exitButtonIsShown" class="button cancel btn btn-lg btn-primary" t-on-click="confirm">
<t t-esc="props.confirmText" />
</div>
<div t-if="props.exitButtonIsShown" class="button cancel" t-on-click="() => this.pos.closePos()">
<div t-if="props.exitButtonIsShown" class="button cancel btn btn-lg btn-secondary" t-on-click="() => this.pos.closePos()">
<t t-esc="props.exitButtonText" />
</div>
<a t-att-download="tracebackFilename" t-att-href="tracebackUrl">
@@ -23,7 +25,7 @@
title="Download error traceback"></i>
</div>
</a>
<div class="button icon email" t-on-click="emailTraceback">
<div class="button icon email btn btn-lg btn-secondary" t-on-click="emailTraceback">
<i class="fa fa-paper-plane" role="img" aria-label="Send by email"
title="Send by email"></i>
</div>
@@ -3,14 +3,16 @@
<t t-name="point_of_sale.OfflineErrorPopup" owl="1">
<div class="popup offline">
<header class="title">
<t t-esc="props.title" />
</header>
<main class="body">
<div class="modal-header">
<h4 class="modal-title title">
<t t-esc="props.title" />
</h4>
</div>
<main class="modal-body">
<t t-esc=" props.body" />
</main>
<footer class="footer footer-flex">
<div class="button dont-show-again highlight" t-on-click="confirm">
<footer class="footer footer-flex modal-footer">
<div class="button dont-show-again highlight btn btn-lg btn-primary w-auto" t-on-click="confirm">
<t t-esc="props.confirmText" />
</div>
</footer>
@@ -3,10 +3,10 @@
<t t-name="point_of_sale.BackButton" owl="1">
<span
class="order-button floor-button"
class="order-button floor-button d-flex align-items-center gap-2 px-4 fw-bolder cursor-pointer"
t-on-click="backToFloorScreen"
>
<i class="fa fa-angle-left" role="img" aria-label="Go Back" title="Go Back" />
<i class="fa fa-2x fa-angle-left" role="img" aria-label="Go Back" title="Go Back" />
<span t-if="!ui.isSmall">BACK</span>
</span>
</t>
@@ -1,94 +0,0 @@
.cash-move-popup {
top: 20% !important;
.input-amount {
display: flex;
flex-flow: row;
.input-container {
padding: 0px;
padding-left: 5px;
padding-right: 5px;
display: flex;
align-items: center;
}
}
.input-type {
height: 40px;
line-height: 40px;
text-align: center;
border-radius: 2px;
padding: 0 12px;
margin-right: 10px;
font-size: 14px;
font-weight: bold;
cursor: pointer;
border: solid 1px rgba(60,60,60,0.1);
background: rgba(0,0,0,0.05);
white-space: nowrap;
&.highlight {
background: $primary !important;
border: solid 1px #64AF8A !important;
color: white !important;
}
&.red-highlight {
background: #bd2222 !important;
border: solid 1px #bd2222 !important;
color: white !important;
}
input {
width: 100%;
min-height: auto;
text-align: right;
padding-right: 30px;
}
}
textarea {
resize: none;
margin-top: 10px;
margin-bottom: 10px;
}
.error-message {
color: rgb(197, 52, 0);
text-align: left;
padding-left: 5px;
}
// FIXME POSREF increased selector specificity to override default style. Should just be "footer"
footer.cash-move.cash-move {
display: block;
width: calc(100% - 36px);
padding: 10px 18px 10px 18px;
.button.confirm {
float: left;
}
.button.cancel {
margin-right: 0;
}
}
.input-container .input-field,
.input-container .input-field:focus {
position: relative;
flex-grow: 1;
border: none;
outline: none;
box-shadow: none;
background-color: transparent;
padding: 0px;
padding-left: 5px;
}
}
.cash-move {
display: flex;
flex-flow: column;
}
@@ -3,32 +3,49 @@
<t t-name="point_of_sale.CashMovePopup" owl="1">
<div class="popup cash-move-popup">
<main class="body">
<div class="cash-move">
<div class="input-amount">
<span t-on-click="() => this.onClickButton('in')" class="input-type" t-att-class="{ highlight: state.type === 'in' }">
Cash In
</span>
<span t-on-click="() => this.onClickButton('out')" class="input-type" t-att-class="{ 'red-highlight': state.type === 'out' }">
Cash Out
</span>
<main class="modal-body">
<div class="cash-move d-flex flex-column">
<div class="input-amount d-flex mb-2 gap-2">
<div class="input-group">
<button t-on-click="() => this.onClickButton('in')" class="input-type btn btn-secondary flex-fill py-2 fw-bolder" t-att-class="{ highlight btn-success: state.type === 'in' }">
Cash In
</button>
<button t-on-click="() => this.onClickButton('out')" class="input-type btn btn-secondary flex-fill py-2 fw-bolder" t-att-class="{ 'red-highlight btn-danger': state.type === 'out' }">
Cash Out
</button>
</div>
<div class="input-container popup-input" t-on-input="handleInputChange" >
<span class="currency" t-esc="pos.currency.symbol" t-if="pos.currency.position=='before'"/>
<input class="input-field" type="text" name="amount" t-model="state.amount" t-ref="amountInput" t-on-keypress="_onAmountKeypress"/>
<span class="currency" t-esc="pos.currency.symbol" t-if="pos.currency.position=='after'"/>
</div>
<div class="input-field input-group">
<t t-if="pos.currency.symbol.length > 1">
<span class="left-currency input-group-text" t-esc="pos.currency.symbol" t-if="pos.currency.position=='before'"/>
</t>
<input type="text" class="form-control" name="amount" t-model="state.amount" t-ref="amountInput" t-on-keypress="_onAmountKeypress"/>
<t t-if="pos.currency.symbol.length > 1">
<span class="left-currency input-group-text" t-esc="pos.currency.symbol" t-if="pos.currency.position=='after'"/>
</t>
<t t-else="">
<span class="currency input-group-text" t-esc="pos.currency.symbol" />
</t>
</div>
</div>
<textarea name="reason" t-model="state.reason" placeholder="Reason"></textarea>
<span t-if="state.errorMessage" class="error-message">
<div class="form-floating">
<textarea class="form-control" placeholder="Leave a reason here" name="reason" id="reason" t-model="state.reason" style="height:100px;"></textarea>
<label for="reason">Reason</label>
</div>
<span t-if="state.errorMessage" class="error-message alert alert-danger mt-2">
<t t-esc="state.errorMessage" />
</span>
</div>
</main>
<footer class="footer cash-move">
<div class="button confirm disable highlight dynamic-size" t-on-click="confirm">
<footer class="footer cash-move modal-footer">
<div class="button confirm disable highlight dynamic-size btn-btn-lg btn-primary" t-on-click="confirm">
Confirm <span t-esc="env.utils.formatCurrency(state.parsedAmount)"/>
</div>
<div class="button cancel" t-on-click="cancel">
<div class="button cancel btn btn-lg btn-secondary" t-on-click="cancel">
Discard
</div>
</footer>
@@ -2,8 +2,8 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.CashierName" owl="1">
<div t-att-class="cssClass">
<span class="cashier-name"><img t-att-src="avatar" t-att-alt="username" class="avatar"/> <span t-if="!ui.isSmall" t-esc="username" class="username"/></span>
<div t-att-class="cssClass" class="btn btn-light border-0 rounded-0">
<span class="cashier-name d-flex align-items-center gap-1"><img t-att-src="avatar" t-att-alt="username" class="avatar rounded-3"/> <span t-if="!ui.isSmall" t-esc="username" class="username"/></span>
</div>
</t>
@@ -85,7 +85,7 @@ export class ClosePosPopup extends AbstractAwaitablePopup {
this.hardwareProxy.openCashbox(action);
const { confirmed, payload } = await this.popup.add(MoneyDetailsPopup, {
moneyDetails: this.moneyDetails,
total: this.manualInputCashCount
total: this.manualInputCashCountpayments
? 0
: this.state.payments[this.defaultCashDetails.id].counted,
action: action,
@@ -3,18 +3,18 @@
<t t-name="point_of_sale.ClosePosPopup" owl="1">
<div class="popup close-pos-popup">
<header>
<div class="title">Closing Session</div>
<div class="total-orders">
<div class="modal-header">
<h4 class="modal-title">Closing Session</h4>
<div class="total-orders fw-bolder">
Total <t t-esc="ordersDetails.quantity"/> orders:
<span class="amount" t-esc="env.utils.formatCurrency(ordersDetails.amount)"/>
</div>
</header>
<main class="body">
<div class="payment-methods-overview">
<table>
</div>
<main class="modal-body">
<div class="payment-methods-overview overflow-auto">
<table class="text-start">
<thead>
<tr>
<tr class="text-dark">
<th>Payment Method</th>
<th>Expected</th>
<th style="width: 20%">Counted</th>
@@ -26,24 +26,24 @@
<tr>
<td t-esc="defaultCashDetails.name"/>
<td t-esc="env.utils.formatCurrency(defaultCashDetails.amount)"/>
<td class="flex" t-on-input="(event) => this.handleInputChange(defaultCashDetails.id, event)">
<input class="pos-input" t-ref="closingCashInput" t-on-focus="ev=>ev.target.select()"/>
<div class="button icon ClosePosPopup" t-on-click="openDetailsPopup">
<i class="fa fa-money" role="img" title="Open the money details popup"/>
<td class="d-flex" t-on-input="(event) => this.handleInputChange(defaultCashDetails.id, event)">
<input class="pos-input form-control" t-ref="closingCashInput" t-on-focus="ev=>ev.target.select()"/>
<div class="button icon ClosePosPopup btn btn-secondary" t-on-click="openDetailsPopup">
<i class="fa fa-money fa-2x" role="img" title="Open the money details popup"/>
</div>
</td>
<td t-esc="env.utils.formatCurrency(state.payments[defaultCashDetails.id].difference)" t-att-class="{'warning': state.payments[defaultCashDetails.id].difference}"/>
<td t-esc="env.utils.formatCurrency(state.payments[defaultCashDetails.id].difference)" t-att-class="{'warning text-danger fw-bolder': state.payments[defaultCashDetails.id].difference}"/>
</tr>
</tbody>
<tbody class="cash-overview">
<tbody class="cash-overview d-flex flex-column mb-2 border-start small text-muted">
<tr>
<td>Opening</td>
<td t-esc="env.utils.formatCurrency(defaultCashDetails.opening)"/>
</tr>
<tr t-foreach="defaultCashDetails.moves" t-as="move" t-key="move_index">
<td>
<div class="flex">
<div class="cash-sign" t-esc="move.amount &lt; 0 ? '-' : '+'"/>
<div class="flex d-flex flex-row">
<div class="cash-sign me-1" t-esc="move.amount &lt; 0 ? '-' : '+'"/>
<t t-esc="move.name"/>
</div>
</td>
@@ -51,8 +51,8 @@
</tr>
<tr t-if="defaultCashDetails.payment_amount">
<td>
<div class="flex">
<div class="cash-sign" t-esc="defaultCashDetails.payment_amount &lt; 0 ? '-' : '+'"/>
<div class="flex d-flex flex-row">
<div class="cash-sign me-1" t-esc="defaultCashDetails.payment_amount &lt; 0 ? '-' : '+'"/>
Payments in <t t-esc="defaultCashDetails.name"/>
</div>
</td>
@@ -66,41 +66,43 @@
<td t-esc="env.utils.formatCurrency(pm.amount)"/>
<t t-set="_showDiff" t-value="_getShowDiff(pm)" />
<td t-if="_showDiff" t-on-input="(event) => this.handleInputChange(pm.id, event)">
<input class="pos-input" t-ref="closingCashInput_{{pm.id}}" t-on-focus="ev=>ev.target.select()"/>
<input class="pos-input form-control" t-ref="closingCashInput_{{pm.id}}" t-on-focus="ev=>ev.target.select()"/>
</td>
<td t-if="_showDiff" t-esc="env.utils.formatCurrency(state.payments[pm.id].difference)" t-att-class="{'warning': state.payments[pm.id].difference}"/>
<td t-if="_showDiff" t-esc="env.utils.formatCurrency(state.payments[pm.id].difference)" t-att-class="{'warning text-danger fw-bolder': state.payments[pm.id].difference}"/>
</tr>
</tbody>
</table>
</div>
</main>
<main class="body notes">
<div class="notes-container">
<div class="opening-notes-container" t-if="openingNotes" >
Opening note
<textarea class="opening-notes">
<div class="notes-container d-flex flex-column gap-3 border-top mt-3 pt-3">
<div class="opening-notes-container d-flex flex-column align-items-start" t-if="openingNotes" >
<label class="form-label" for="openingNotes">Opening note</label>
<textarea class="opening-notes form-control" id="openingNotes">
<t t-esc="openingNotes"/>
</textarea>
</div>
<div class="closing-notes-container">
Closing note
<textarea class="closing-notes" placeholder="Add a closing note..." t-model="state.notes"/>
<div class="closing-notes-container d-flex flex-column align-items-start">
<label class="form-label" for="closingNotes">Closing note</label>
<textarea class="closing-notes form-control" id="closingNotes" placeholder="Add a closing note..." t-model="state.notes"/>
</div>
</div>
</main>
<footer class="footer">
<div class="button highlight" t-on-click="confirm">Close Session</div>
<div class="button" t-att-class="{'disabled': !canCancel()}" t-on-click="cancel">Discard</div>
<footer class="footer modal-footer justify-content-between">
<!-- Download Sale Details -->
<div class="small button icon"
t-on-click="downloadSalesReport"
title="Download a report with all the sales of the current PoS Session">
<div class="daily sale">Daily Sale</div>
<i class="fa fa-download" role="img"/>
<div class="modal-footer-left d-flex gap-2">
<button class="button icon btn btn-lg btn-secondary"
t-on-click="downloadSalesReport"
title="Download a report with all the sales of the current PoS Session">
Daily Sale
<i class="fa fa-download" role="img"/>
</button>
<!-- Print Sale Details -->
<button t-if="hardwareProxy.printer" class="button icon btn btn-lg btn-secondary">
<SaleDetailsButton/>
</button>
</div>
<!-- Print Sale Details -->
<div t-if="hardwareProxy.printer" class="small button icon">
<SaleDetailsButton/>
<div class="modal-footer-right d-flex gap-2">
<button class="button btn btn-lg btn-secondary" t-att-class="{'disabled': !canCancel()}" t-on-click="cancel">Discard</button>
<button class="button highlight btn btn-lg btn-primary" t-on-click="confirm">Close Session</button>
</div>
</footer>
</div>
@@ -2,20 +2,21 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.CustomerFacingDisplayButton" owl="1">
<div class="menu-item navbar-button" t-on-click="() => {customerDisplay.connect(); this.displayMessage(message);}">
<div t-if="customerDisplay.status === 'warning'" class="js_warning oe_icon oe_orange">
<button class="menu-item navbar-button btn btn-light rounded-0 d-flex align-items-center px-3 cursor-pointer" t-on-click="() => {customerDisplay.connect(); this.displayMessage(message);}">
<div t-if="customerDisplay.status === 'warning'" class="js_warning oe_icon oe_orange text-warning">
<i class="fa fa-fw fa-desktop" role="img" t-attf-aria-label="Customer Screen Warning : {{message}}" t-attf-title="Customer Screen Warning : {{message}}"></i>
</div>
<div t-if="customerDisplay.status === 'failure'" class="js_disconnected oe_icon oe_red">
<div t-if="customerDisplay.status === 'failure'" class="js_disconnected oe_icon oe_red text-danger">
<i class="fa fa-fw fa-desktop" role="img" t-attf-aria-label="Customer Screen : {{message}}" t-attf-title="Customer Screen : {{message}}"></i>
</div>
<div t-if="customerDisplay.status === 'not_found'" class="js_disconnected oe_icon oe_red">
<div t-if="customerDisplay.status === 'not_found'" class="js_disconnected oe_icon oe_red text-danger">
<i class="fa fa-fw fa-desktop" role="img" t-attf-aria-label="Customer Screen Not Found : {{message}}" t-attf-title="Customer Screen Not Found : {{message}}"></i>
</div>
<div t-if="customerDisplay.status === 'success'" class="js_connected oe_icon oe_green">
<div t-if="customerDisplay.status === 'success'" class="js_connected oe_icon oe_green text-success">
<i class="fa fa-fw fa-desktop" role="img" aria-label="Customer Screen Connected" title="Customer Screen Connected"></i>
</div>
</div>
<span class="ms-1">Customer Screen</span>
</button>
</t>
</templates>
@@ -0,0 +1,31 @@
.pos-topheader {
height: $navbar-height;
}
.pos-branding{
max-width: $left-pane-width;
}
.status-buttons {
> div {
display: flex;
align-items: center;
padding: $o-horizontal-padding * 0.75;
}
.avatar {
width: 25px;
}
}
.menu {
z-index: $zindex-dropdown;
}
.sub-menu {
top: $navbar-height * 2/3;
right: $navbar-height * -1/3 ;
}
@@ -2,51 +2,54 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.Navbar" owl="1">
<div class="pos-topheader">
<div class="pos-topheader d-flex w-100 p-0 m-0 bg-white border-bottom">
<div t-if="pos.tempScreen" class="block-top-header" />
<div t-if="!ui.isSmall" class="pos-branding">
<img class="pos-logo" t-on-click="() => debug.toggleWidget()" src="/point_of_sale/static/src/img/logo.png" alt="Logo" />
<div t-if="!ui.isSmall" class="pos-branding d-flex justify-content-start flex-grow-1 h-100 p-0 my0 text-start">
<img class="pos-logo h-75 ms-2 me-auto align-self-center" t-on-click="() => debug.toggleWidget()" src="/web/static/img/logo.png" alt="Logo" />
</div>
<div class="pos-rightheader">
<div class="pos-rightheader d-flex flex-grow-1 h-100">
<BackButton t-if="showBackButton()"/>
<div class="status-buttons">
<div class="status-buttons d-flex flex-grow-1 flex-basis-0 justify-content-end h-100">
<CashierName />
<SaleDetailsButton t-if="hardwareProxy.printer" isHeaderButton="true"/>
<ProxyStatus t-if="pos.config.use_proxy" />
<CustomerFacingDisplayButton t-if="customerFacingDisplayButtonIsShown" />
<SyncNotification />
</div>
<div t-if="isBurgerMenuClosed()" class="navbar-button menu-button" t-on-click="openMenu">
<div t-if="isBurgerMenuClosed()" class="navbar-button menu-button btn btn-light rounded-0 d-flex align-items-center px-3 cursor-pointer" t-on-click="openMenu">
<i class="fa fa-bars" aria-hidden="true"></i>
</div>
<div t-else="" class="navbar-button menu menu-button" t-on-click="closeMenu">
<div t-else="" class="navbar-button menu menu-button btn btn-light rounded-0 d-flex align-items-center px-3 cursor-pointer" t-on-click="closeMenu">
<ul>
<li class="menu-item">
<li class="menu-item position-relative d-flex">
<i class="fa fa-bars" aria-hidden="true"></i>
<ul class="sub-menu">
<li class="menu-item navbar-button ticket-button" t-on-click="onTicketButtonClick">
<div class="with-badge" t-att-badge="orderCount">
<span>Orders</span>
</div>
<ul class="dropdown-menu sub-menu position-absolute d-flex flex-column align-items-stretch w-100 bg-white shadow-lg z-index-1">
<li class="ticket-button" t-on-click="onTicketButtonClick">
<a class="dropdown-item with-badge py-2">
Orders
<span class="badge text-bg-info rounded-pill py-1 ms-2" t-esc="orderCount"/>
</a>
</li>
<li t-if="showCashMoveButton" class="menu-item navbar-button" t-on-click="onCashMoveButtonClick">
<span>Cash In/Out</span>
<a class="dropdown-item py-2">
Cash In/Out
</a>
</li>
<CustomerFacingDisplayButton t-if="CustomerFacingDisplayButtonIsShown"/>
<li class="menu-item navbar-button backend-button" t-on-click="() => pos.closePos()">
<span>
<li class="backend-button" t-on-click="() => pos.closePos()">
<a class="dropdown-item py-2">
Backend
</span>
</a>
</li>
<li class="menu-item navbar-button close-button" t-on-click="closeSession">
<span>
<li class="close-button" t-on-click="closeSession">
<a class="dropdown-item py-2">
Close Session
</span>
</a>
</li>
<li t-if="showToggleProductView" class="menu-item navbar-button close-button" t-on-click="toggleProductView">
<span>
<a class="dropdown-item py-2">
Switch Product View
</span>
</a>
</li>
<li t-if="this.env.debug" t-on-click="() => debug.toggleWidget()" class="menu-item navbar-button">
<span>Debug Window</span>
@@ -2,11 +2,11 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.ProxyStatus" owl="1">
<div class="oe_status js_proxy" t-on-click="() => this.pos.connectToProxy()">
<button class="oe_status js_proxy btn btn-light" t-on-click="() => this.pos.connectToProxy()">
<span t-if="this.message and !ui.isSmall" class="js_msg">
<t t-esc="this.message" />
</span>
<span t-if="connectionInfo.status === 'connected' and !this.message" class="js_connected oe_green">
<span t-if="connectionInfo.status === 'connected' and !this.message" class="js_connected text-success">
<i class="fa fa-fw fa-sitemap" role="img" aria-label="Proxy Connected"
title="Proxy Connected"></i>
</span>
@@ -14,15 +14,15 @@
<i class="fa fa-fw fa-spin fa-circle-o-notch" role="img" aria-label="Connecting to Proxy"
title="Connecting to Proxy"></i>
</span>
<span t-elif="connectionInfo.status === 'disconnected'" class="js_disconnected oe_red">
<span t-elif="connectionInfo.status === 'disconnected'" class="js_disconnected text-danger">
<i class="fa fa-fw fa-sitemap" role="img" aria-label="Proxy Disconnected"
title="Proxy Disconnected"></i>
</span>
<span t-elif="connectionInfo.status === 'warning' or this.message" class="js_warning oe_orange">
<span t-elif="connectionInfo.status === 'warning' or this.message" class="js_warning text-warning">
<i class="fa fa-fw fa-sitemap" role="img" aria-label="Proxy Warning"
title="Proxy Warning"></i>
</span>
</div>
</button>
</t>
</templates>
@@ -2,8 +2,8 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.SaleDetailsButton" owl="1">
<div t-att-class="{'oe_status': props.isHeaderButton }" t-on-click="onClick">
<div class="js_connected oe_icon"
<div t-att-class="{'oe_status cursor-pointer': props.isHeaderButton }" t-on-click="onClick">
<div class="js_connected oe_icon text-white"
title="Print a report with all the sales of the current PoS Session">
<i class="fa fa-fw fa-print" role="img" aria-label="Print"></i>
</div>
@@ -2,24 +2,24 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.SyncNotification" owl="1">
<div class="oe_status" t-on-click="onClick">
<div class="oe_status btn btn-light rounded-0" t-on-click="onClick">
<span t-if="sync.pending" class="js_msg">
<t t-esc="sync.pending" />
<span> </span>
</span>
<div t-if="sync.status === 'connected'" class="js_connected oe_icon oe_green">
<div t-if="sync.status === 'connected'" class="js_connected oe_icon text-success">
<i class="fa fa-fw fa-wifi" role="img" aria-label="Synchronisation Connected"
title="Synchronisation Connected"></i>
</div>
<div t-if="sync.status === 'connecting'" class="js_connecting oe_icon">
<div t-if="sync.status === 'connecting'" class="js_connecting oe_icon text-white">
<i class="fa fa-fw fa-spin fa-circle-o-notch" role="img"
aria-label="Synchronisation Connecting" title="Synchronisation Connecting"></i>
</div>
<div t-if="sync.status === 'disconnected'" class="js_disconnected oe_icon oe_orange">
<div t-if="sync.status === 'disconnected'" class="js_disconnected oe_icon text-warning">
<i class="fa fa-fw fa-wifi" role="img" aria-label="Synchronisation Disconnected"
title="Synchronisation Disconnected"></i>
</div>
<div t-if="sync.status === 'error'" class="js_error oe_icon oe_red">
<div t-if="sync.status === 'error'" class="js_error oe_icon text-danger">
<i class="fa fa-fw fa-warning" role="img" aria-label="Synchronisation Error"
title="Synchronisation Error"></i>
</div>
@@ -1,29 +0,0 @@
.o_pos_notification_manager {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: flex;
flex-direction: column;
padding: 2rem;
justify-content: end;
align-items: center;
pointer-events: none;
}
.o_notification {
pointer-events: auto;
width: 350px;
background-color: #333333AA;
color: #fff;
text-align: center;
border-radius: 2px;
padding: 16px;
margin: .5rem;
font-size: 1rem;
span {
white-space: pre-line;
}
}
@@ -2,13 +2,13 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.Notification" owl="1">
<div class="o_notification" t-att-class="props.className" t-on-click="props.close">
<div class="o_notification o-fade o-fade-enter-active p-3 m-2 text-bg-dark bg-opacity-75 text-center pe-none" t-att-class="props.className" t-on-click="props.close">
<span t-esc="props.message"/>
</div>
</t>
<t t-name="point_of_sale.NotificationContainer" owl="1">
<div class="o_pos_notification_manager">
<div class="o_pos_notification_manager fixed-bottom d-flex flex-column align-items-center justify-content-end p-4 pe-none">
<t t-foreach="props.notifications" t-as="notification" t-key="notification">
<Transition name="'o-fade'" visible="notification_value.visible" leaveDuration="leaveDuration" t-slot-scope="transition" onLeave="notification_value.delete">
<Notification message="notification_value.message" close="notification_value.close" className="transition.className"/>
@@ -0,0 +1,36 @@
/* --- Styling of OpenERP Elements --- */
/* .ui-dialog, .modal-dialog {
background: white;
padding: 10px;
border-radius: 3px;
font-family: sans-serif;
box-shadow: 0px 10px 40px rgba(0,0,0,0.4);
position: absolute;
top: 30px;
height: 400px;
overflow: scroll;
} */
.ui-dialog .ui-icon-closethick{
float: right;
}
div.modal.in {
position: absolute;
background: white;
padding: 20px;
box-shadow: 0px 10px 20px black;
border-radius: 3px;
max-width: 600px;
max-height: 400px;
margin-top: -200px;
margin-left: -300px;
top: 50%;
left: 50%;
}
/* @media screen and (max-width: 768px) {
.ui-dialog, .modal-dialog {
padding: 0;
overflow: auto;
}
} */
@@ -4,7 +4,7 @@
<t t-name="point_of_sale.PopupContainer" owl="1">
<div t-if="Object.keys(props.popups).length > 0" class="popups">
<t t-foreach="props.popups" t-as="popup" t-key="popup">
<div t-if="popup_last || popup_value.props.keepBehind" role="dialog" class="modal-dialog">
<div t-if="popup_last || popup_value.props.keepBehind" role="dialog" class="modal-dialog position-absolute start-0 top-0 d-flex align-items-center justify-content-center h-100 w-100 mw-100 m-0 p-0 pe-auto bg-dark bg-opacity-50">
<t t-component="popup_value.component" t-props="popup_value.props" isActive="popup_last" />
</div>
</t>
@@ -0,0 +1,97 @@
.pos {
background: $gray-900;
--btn-height-size: 54px;
direction: ltr;
padding: 0;
margin: 0;
background-color: #f0eeee;
color: $gray-700;
/* Some elements inside .pos are allowed to be moved/resized. When
* this 'move/resize' event is performed in touch devices, .pos
* element also tries to scroll (as the default action of touchstart+
* touchmove events).
* Position is fixed to prevent the movement of .pos element during
* the described touch events.
*/
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
text-shadow: none;
overflow: hidden;
}
/* ********* Generic element styling ********* */
.pos a {
text-decoration: none;
color: $gray-700;
}
.pos ul, .pos ol {
padding: 0;
margin: 0;
}
.pos li {
list-style-type: none;
}
/* --- Generic Restyling and Resets --- */
html {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
font-family: sans-serif;
}
table {
border-spacing: 0px;
border-collapse: collapse;
}
td {
padding: 0px;
}
.oe_hidden{
display: none !important;
}
.oe_invisible{
visibility: hidden !important;
}
.clearfix:after {
content:" ";
display: block;
visibility: hidden;
line-height: 0;
height: 0;
clear: both;
}
.pos input::-webkit-outer-spin-button,
.pos input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.pos .oe_hidden{
display: none !important;
}
.pos ul, .pos li {
margin: 0;
padding: 0;
list-style-type: none;
}
/* ********* POS CONTENT ********* */
.pos-content {
height: calc(100% - #{$navbar-height});
}
@@ -2,12 +2,12 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.Chrome" owl="1">
<div class="pos" t-att-class="{ 'big-scrollbars': pos.hasBigScrollBars }">
<div class="pos">
<div class="pos-receipt-print"></div>
<Navbar />
<div class="pos-content">
<div class="window">
<div class="subwindow">
<div class="pos-content position-relative w-100 bg-200">
<div class="window position-relative h-100 w-100 border-0 overflow-hidden">
<div class="subwindow w-100 h-100">
<div class="subwindow-container">
<div class="subwindow-container-fix screens">
<!-- FIXME POSREF: better error handling in main screens (currently, a crash in owl lifecycle of a main screen blows up the application and the error can't be displayed) -->
@@ -0,0 +1,23 @@
.partner-picture {
height: 64px;
width: 64px;
& > img {
position: absolute;
top: -9999px;
bottom: -9999px;
right: -9999px;
left: -9999px;
max-height: 64px;
margin: auto;
}
.image-uploader {
position: absolute;
z-index: 1000;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0;
cursor: pointer;
}
}
@@ -1,34 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.PartnerDetailsEdit" owl="1">
<section class="partner-details edit">
<div class="partner-picture">
<t t-if="partnerImageUrl">
<img t-att-src="partnerImageUrl" alt="Partner" style="width: 64px; height: 64px; object-fit: cover;" />
</t>
<t t-else="">
<i class="fa fa-camera" role="img" aria-label="Picture" title="Picture"></i>
</t>
<input type="file" class="image-uploader" t-on-change="uploadImage" />
<section class="partner-details edit d-flex flex-column w-100 m-3">
<div class="partner-details-header d-flex pe-2 gap-2">
<div class="partner-picture position-relative rounded text-center overflow-hidden">
<t t-if="partnerImageUrl">
<img class="rounded" t-att-src="partnerImageUrl" alt="Partner" style="width: 64px; height: 64px; object-fit: cover;" />
</t>
<t t-else="">
<i class="fa fa-camera" role="img" aria-label="Picture" title="Picture"></i>
</t>
<input type="file" class="image-uploader" t-on-change="uploadImage" />
</div>
<input class="detail partner-name form-control form-control-lg" name="name" t-att-value="props.partner.name or ''" placeholder="Name" t-on-change="captureChange" />
</div>
<input class="detail partner-name" name="name" placeholder="Name" t-model="changes.name" />
<div class="partner-details-box clearfix">
<div class="partner-details-left">
<div class="partner-details-box clearfix d-flex flex-column flex-sm-row gap-2 mt-4">
<div class="partner-details-left d-flex flex-column gap-3 w-100">
<div class="partner-detail">
<span class="label">Street</span>
<input class="detail" name="street" t-att-class="{'border-red': missingFields.includes('street')}" t-model="changes.street" placeholder="Street" />
<label class="form-label label" for="street">Street</label>
<input class="detail form-control" id="street" name="street" t-model="changes.street" placeholder="Street" t-att-class="{'border-danger': missingFields.includes('street')}"/>
</div>
<div class="partner-detail">
<span class="label">City</span>
<input class="detail" name="city" t-att-class="{'border-red': missingFields.includes('city')}" t-model="changes.city" placeholder="City" />
<label class="form-label label" for="city">City</label>
<input class="detail form-control" id="city" name="city" t-model="changes.city" placeholder="City" t-att-class="{'border-danger': missingFields.includes('city')}" />
</div>
<div class="partner-detail">
<span class="label">Postcode</span>
<input class="detail" name="zip" t-att-class="{'border-red': missingFields.includes('zip')}" t-model="changes.zip" placeholder="ZIP" />
<label class="form-label label" for="postcode">Postcode</label>
<input class="detail form-control" id="postcode" name="zip" t-model="changes.zip" placeholder="ZIP" t-att-class="{'border-danger': missingFields.includes('zip')}" />
</div>
<div class="partner-detail" t-if="pos.states.some((state) => state.country_id[0] == changes.country_id)">
<span class="label">State</span>
<select class="detail" name="state_id" t-att-class="{'border-red': missingFields.includes('state_id')}" t-model="changes.state_id">
<div class="partner-detail">
<label class="form-label label" for="state">State</label>
<select class="detail form-select" id="state" name="state_id" t-model="changes.state_id" t-att-class="{'border-danger': missingFields.includes('state_id')}">
<option value="">None</option>
<t t-foreach="pos.states" t-as="state" t-key="state.id">
<option t-if="changes.country_id == state.country_id[0]" t-att-value="state.id">
@@ -38,8 +40,8 @@
</select>
</div>
<div class="partner-detail">
<span class="label">Country</span>
<select class="detail" name="country_id" t-att-class="{'border-red': missingFields.includes('country_id')}" t-model="changes.country_id">
<label class="form-label label" for="country">Country</label>
<select class="detail form-select" id="country" name="country_id" t-model="changes.country_id" t-att-class="{'border-danger': missingFields.includes('country_id')}">
<option value="">None</option>
<t t-foreach="pos.countries" t-as="country" t-key="country.id">
<option t-att-value="country.id">
@@ -49,10 +51,10 @@
</select>
</div>
</div>
<div class="partner-details-right">
<div class="partner-details-right d-flex flex-column gap-3 w-100">
<div class="partner-detail">
<span class="label">Language</span>
<select class="detail" name="lang" t-att-class="{'border-red': missingFields.includes('lang')}" t-model="changes.lang">
<label class="form-label label" for="language">Language</label>
<select class="detail form-select" id="language" name="lang" t-model="changes.lang" t-att-class="{'border-danger': missingFields.includes('lang')}">
<t t-foreach="pos.langs" t-as="lang" t-key="lang.id">
<option t-att-value="lang.code" t-att-selected="changes.lang ? ((lang.code === changes.lang) ? true : undefined) : lang.code === pos.user.lang? true : undefined">
<t t-esc="lang.name" />
@@ -61,30 +63,32 @@
</select>
</div>
<div class="partner-detail">
<span class="label">Email</span>
<input class="detail" name="email" t-att-class="{'border-red': missingFields.includes('email')}" type="email" t-model="changes.email" />
<label class="form-label label" for="email">Email</label>
<input class="detail form-control" id="email" name="email" type="email" t-model="changes.email" t-att-class="{'border-danger': missingFields.includes('email')}" />
</div>
<div class="partner-detail">
<span class="label">Phone</span>
<input class="detail" name="phone" t-att-class="{'border-red': missingFields.includes('phone')}" type="tel" t-model="changes.phone" />
<label class="form-label label" for="phone">Phone</label>
<input class="detail form-control" id="phone" name="phone" type="tel" t-model="changes.phone" t-att-class="{'border-danger': missingFields.includes('phone')}" />
</div>
<div class="partner-detail">
<span class="label">Mobile</span>
<input class="detail" name="mobile" t-att-class="{'border-red': missingFields.includes('mobile')}" type="tel" t-model="changes.mobile" />
<label class="form-label label" for="mobile">Mobile</label>
<input class="detail form-control" id="mobile" name="mobile" type="tel"
t-model="changes.mobile"
t-att-class="{'border-danger': missingFields.includes('tel')}" />
</div>
<div class="partner-detail">
<span class="label">Barcode</span>
<input class="detail barcode" name="barcode" t-model="changes.barcode" t-att-class="{'border-red': missingFields.includes('barcode')}" />
<label class="form-label label" for="barcode">Barcode</label>
<input class="detail barcode form-control" id="barcode" name="barcode" t-model="changes.barcode" t-att-class="{'border-danger': missingFields.includes('barcode')}" />
</div>
<div class="partner-detail">
<span class="label">Tax ID</span>
<input class="detail vat" name="vat" t-model="changes.vat" t-att-class="{'border-red': missingFields.includes('vat')}" />
<label class="form-label label" for="vat">Tax ID</label>
<input class="detail vat form-control" id="vat" name="vat" t-model="changes.vat" t-att-class="{'border-danger': missingFields.includes('vat')}" />
</div>
<div t-if="pos.pricelists.length gt 1" class="partner-detail">
<span class="label">Pricelist</span>
<select class="detail" name="property_product_pricelist" t-att-class="{'border-red': missingFields.includes('property_product_pricelist')}" t-model="changes.property_product_pricelist">
<label class="form-label label" for="pricelist">Pricelist</label>
<select class="detail form-select" id="pricelist" name="property_product_pricelist" t-model="changes.property_product_pricelist" t-att-class="{'border-danger': missingFields.includes('property_product_pricelist')}">
<t t-foreach="pos.pricelists" t-as="pricelist" t-key="pricelist.id">
<option t-att-value="pricelist.id" t-att-selected="changes.property_product_pricelist ? (pricelist.id === changes.property_product_pricelist[0] ? true : undefined) : pricelist.id === pos.default_pricelist.id ? true : undefined">
<option t-att-value="pricelist.id" t-att-selected="props.partner.property_product_pricelist ? (pricelist.id === props.partner.property_product_pricelist[0] ? true : undefined) : pricelist.id === pos.default_pricelist.id ? true : undefined">
<t t-esc="pricelist.display_name" />
</option>
</t>
@@ -6,7 +6,7 @@ export class PartnerLine extends Component {
static template = "point_of_sale.PartnerLine";
get highlight() {
return this._isPartnerSelected ? "highlight" : "";
return this._isPartnerSelected ? "highlight text-bg-primary" : "";
}
get _isPartnerSelected() {
return this.props.partner === this.props.selectedPartner;
@@ -0,0 +1,35 @@
@include media-breakpoint-down(lg) {
.partner-line {
display: flex;
flex-wrap: wrap;
align-items: center;
padding: 12px 24px;
border-bottom: 1px solid $o-gray-300;
td {
border: 0;
flex-basis: 100%;
padding: 5px;
background-color: transparent;
}
}
.partner-line-balance{
order: 3;
}
.partner-line .pos-right-align{
text-align: left ;
}
.partner-line-email {
display: flex;
flex-direction: column;
gap: 8px;
}
.edit-partner-button-cell {
order: 4;
}
.partner-line-last-column-placeholder {
display: none;
}
}
@@ -2,19 +2,19 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.PartnerLine" owl="1">
<tr t-attf-class="partner-line {{highlight}}" t-att-data-id="props.partner.id"
<tr t-attf-class="partner-line gap-2 gap-lg-0 align-top {{highlight}}" t-att-data-id="props.partner.id"
t-on-click="() => this.props.onClickPartner(props.partner)">
<td>
<div>
<t t-esc="props.partner.name or ''" />
</div>
<div class="company-field">
<div class="company-field text-bg-muted">
<t t-esc="props.partner.parent_name or ''" />
</div>
<div t-if="_isPartnerSelected" class="unselect-tag">
<i class="fa fa-times"></i>
<button t-if="_isPartnerSelected" class="unselect-tag d-lg-inline-block d-none btn btn-light mt-2">
<i class="fa fa-times me-1"></i>
<span> unselect </span>
</div>
</button>
</td>
<td>
<div class="partner-line-adress" t-if="props.partner.address">
@@ -23,22 +23,22 @@
</td>
<td class="partner-line-email" >
<div t-if="props.partner.phone">
<i class="fa fa-fw fa-phone"/><t t-esc="props.partner.phone"/>
<i class="fa fa-fw fa-phone me-2"/><t t-esc="props.partner.phone"/>
</div>
<div t-if="props.partner.mobile">
<i class="fa fa-fw fa-mobile fa-lg"/><t t-esc="props.partner.mobile"/>
<i class="fa fa-fw fa-mobile fa-lg me-2"/><t t-esc="props.partner.mobile"/>
</div>
<div t-if="props.partner.email" class="email-field">
<i class="fa fa-fw fa-paper-plane-o"/><t t-esc="props.partner.email" />
<i class="fa fa-fw fa-paper-plane-o me-2"/><t t-esc="props.partner.email" />
</div>
</td>
<td class="partner-line-balance" t-if="props.isBalanceDisplayed"></td>
<td class="edit-partner-button-cell">
<button class="edit-partner-button" t-on-click.stop="() => props.onClickEdit(props.partner)">DETAILS</button>
<div t-if="_isPartnerSelected" class="unselect-tag-mobile">
<button class="edit-partner-button btn btn-light" t-on-click.stop="() => props.onClickEdit(props.partner)">DETAILS</button>
<button t-if="_isPartnerSelected" class="unselect-tag-mobile d-inline-block d-lg-none btn btn-outline-light ms-2">
<i class="fa fa-times"></i>
<span> unselect </span>
</div>
</button>
</td>
<td class="partner-line-last-column-placeholder oe_invisible"></td>
</tr>
@@ -0,0 +1,33 @@
.partner-list th.partner-line-last-column-placeholder,
.partner-list td.partner-line-last-column-placeholder {
padding: 0;
}
.partnerlist-screen {
tr {
&.partner-line {
&:hover {
cursor: pointer;
background: rgba($primary, 0.2);
}
&:active {
background-color: $primary;
color: white;
}
}
}
}
@include media-breakpoint-down(lg) {
.table {
border: transparent;
}
.partner-list {
thead {
display: none;
}
td {
padding: 0;
}
}
}
@@ -4,58 +4,62 @@
<t t-name="point_of_sale.PartnerListScreen" owl="1">
<div class="partnerlist-screen screen">
<div class="screen-content">
<div class="top-content">
<div class="button highlight" t-if="state.detailIsShown" t-on-click="() => this.partnerEditor.save()">
<div class="top-content d-flex align-items-center p-2 border-bottom text-center">
<div class="button highlight btn btn-lg btn-primary" t-if="state.detailIsShown" t-on-click="() => this.partnerEditor.save()">
<i t-if="ui.isSmall" class="fa fa-floppy-o"/>
<t t-else="">
<span> Save</span>
</t>
</div>
<div t-if="!state.detailIsShown" class="button new-customer highlight" role="img" aria-label="Add a customer"
<button t-if="!state.detailIsShown" class="button new-customer highlight btn btn-lg btn-primary" role="img" aria-label="Add a customer"
t-on-click="createPartner"
title="Add a customer">
<i t-if="ui.isSmall" class="fa fa-plus"/>
<t t-else=""> Create</t>
</div>
<div class="button back" t-on-click="back">
</button>
<div class="button back btn btn-lg btn-secondary mx-2" t-on-click="back">
<i t-if="ui.isSmall" class="fa fa-angle-double-left"/>
<t t-else=""> Discard</t>
</div>
<div class="top-right-buttons">
<div t-if="state.detailIsShown and state.editModeProps.partner.id" class="button more-info" t-on-click="goToOrders">
<span> Orders</span>
</div>
<a t-if="state.detailIsShown &amp;&amp; state.editModeProps.partner.id" class="button more-info" t-att-href="partnerLink" target="_blank"> More info</a>
<div class="search-bar-container sb-partner" t-if="!state.detailIsShown">
<div class="pos-search-bar">
<i class="fa fa-search" t-on-click="_onPressEnterKey"/>
<input t-ref="search-word-input-partner" placeholder="Search Customers..." size="1" t-on-keyup="updatePartnerList" />
<i class="fa fa-times" t-on-click="_clearSearch"/>
<div class="top-right-buttons d-flex gap-2 w-100 justify-content-end">
<div t-if="state.detailIsShown and state.editModeProps.partner.id" class="button more-info">
<a t-on-click="goToOrders" class="btn btn-lg btn-outline-secondary" target="_blank"> Orders </a>
</div>
<div t-if="state.detailIsShown &amp;&amp; state.editModeProps.partner.id" class="button more-info">
<a t-att-href="partnerLink" class="btn btn-lg btn-outline-secondary" target="_blank"> More info</a>
</div>
<div class="search-bar-container sb-partner d-flex align-items-center flex-grow-1 h-100" t-if="!state.detailIsShown">
<div class="input-group h-100">
<div class="pos-search-bar form-control d-flex align-items-center bg-view">
<i class="oi oi-search me-2" t-on-click="_onPressEnterKey"/>
<input class="flex-grow-1 w-auto h-100 border-0 p-2 me-4" type="text" autofocus="autofocus" t-ref="search-word-input-partner" placeholder="Search Customers..." size="1" t-on-keyup="updatePartnerList" />
<i class="fa fa-times position-absolute end-0 me-2 pe-1 cursor-pointer" t-on-click="_clearSearch"/>
</div>
</div>
</div>
</div>
</div>
</div>
<section class="full-content">
<section class="full-content position-absolute start-0 end-0 bottom-0 overflow-auto">
<div class="partner-window">
<section class="subwindow collapsed">
<div class="subwindow-container collapsed">
<section class="subwindow collapsed h-0">
<div class="subwindow-container collapsed position-relative d-table-cell h-0">
<div t-if="state.detailIsShown" class="partner-details-contents subwindow-container-fix">
<PartnerDetailsEdit t-props="state.editModeProps" saveChanges.bind="saveChanges" imperativeHandle="partnerEditor" />
</div>
</div>
</section>
<section class="subwindow list">
<div class="subwindow-container">
<div class="subwindow-container position-relative d-table-cell h-100 vw-100">
<div t-if="!state.detailIsShown" class="subwindow-container-fix scrollable-y">
<table class="partner-list">
<table class="partner-list table table-striped w-100">
<thead>
<tr>
<th>Name</th>
<th>Address</th>
<th class="partner-line-email">Contact</th>
<th class="pos-right-align" t-if="isBalanceDisplayed">Balance</th>
<th class="partner-line-details"></th>
<th class="partner-line-last-column-placeholder oe_invisible"></th>
<th class="py-2">Name</th>
<th class="py-2">Address</th>
<th class="partner-line-email py-2">Contact</th>
<th class="pos-right-align py-2" t-if="isBalanceDisplayed">Balance</th>
<th class="partner-line-details py-2"></th>
<th class="partner-line-last-column-placeholder oe_invisible py-2"></th>
</tr>
</thead>
<tbody class="partner-list-contents">
@@ -70,8 +74,8 @@
</t>
</tbody>
</table>
<div t-if="state.query" class="search-more-button">
<div class="button" t-on-click="_onPressEnterKey">Search more</div>
<div t-if="state.query" class="search-more-button d-flex justify-content-center mt-2">
<button class="btn btn-lg btn-primary" t-on-click="_onPressEnterKey">Search more</button>
</div>
</div>
</div>
@@ -0,0 +1,15 @@
.payment-numpad .numpad button {
font-size: clamp(map-get($font-sizes, 6), 3vw, 36px);
}
.payment-numpad .numpad button:nth-child(16) img {
width: 35%;
max-width: 64px;
height: auto;
}
@include media-breakpoint-down(md) {
.payment-numpad .numpad button:nth-child(16) img {
max-width: 48px;
}
}
@@ -2,7 +2,7 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.PaymentScreenNumpad" owl="1">
<div class="numpad">
<div class="numpad d-grid text-center flex-grow-1 h-100 w-100">
<PSNumpadInputButton value="'1'" />
<PSNumpadInputButton value="'2'" />
<PSNumpadInputButton value="'3'" />
@@ -2,7 +2,7 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.PSNumpadInputButton" owl="1">
<button t-attf-class="{{ _class }}"
<button t-attf-class="{{ _class }} btn btn-secondary w-100 rounded-0 py-3"
t-on-click="() => this.numberBuffer.sendKey(props.value)">
<t t-slot="default">
<t t-if="props.text">
@@ -0,0 +1,29 @@
.paymentlines .electronic_payment {
background: $gray-200;
border-collapse: unset;
font-size: 16px;
padding-right: 0;
}
.paymentline .electronic_status {
flex-grow: 1;
margin-left: 10px;
margin-right: 10px;
/* make the name of the payment line to be truncated with ellipsis */
white-space: nowrap;
overflow:hidden;
text-overflow:ellipsis;
padding: map-get($spacers, 3);
}
.paymentlines .electronic_payment div:first-child {
flex-grow: 2;
margin-left: 10px;
margin-right: 10px;
}
.paymentlines .electronic_payment div:last-child {
flex-grow: 1;
text-align: center;
padding: map-get($spacers, 3);
}
@@ -2,15 +2,15 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.PaymentScreenPaymentLines" owl="1">
<div class="paymentlines">
<p class="title-category">Summary</p>
<div class="paymentlines d-flex flex-column">
<p class="title-category py-1 px-3 m-0 text-bg-300 fw-bolder">Summary</p>
<t t-foreach="props.paymentLines" t-as="line" t-key="line.cid">
<t t-if="line.selected">
<div class="paymentline selected"
<div class="paymentline selected d-flex align-items-center bg-200"
t-att-class="selectedLineClass(line)">
<div class="payment-infos" t-on-click="() => this.selectLine(line)">
<div class="payment-infos d-flex align-items-center justify-content-between flex-grow-1 px-3 py-4 my-2 text-truncate cursor-pointer" t-on-click="() => this.selectLine(line)">
<span class="payment-name"><t t-esc="line.payment_method.name"/></span>
<div class="payment-amount">
<div class="payment-amount px-3">
<t t-if="line and line.payment_status and ['done', 'waitingCard', 'waiting', 'reversing', 'reversed'].includes(line.payment_status)">
<t t-esc="env.utils.formatCurrency(line.get_amount(), false)" />
</t>
@@ -21,11 +21,11 @@
</div>
<t t-if="!line.payment_status or !['done', 'reversed', 'waitingCard', 'waitingCapture'].includes(line.payment_status)">
<div class="delete-button"
<button class="delete-button btn btn-link mx-2 px-3"
t-on-click="() => this.props.deleteLine(line.cid)"
aria-label="Delete" title="Delete">
<i class="fa fa-times-circle" />
</div>
<i class="fa fa-2x fa-times-circle" />
</button>
</t>
</div>
<t t-if="line and line.payment_status">
@@ -101,11 +101,11 @@
</t>
</t>
<t t-else="">
<div class="paymentline"
<div class="paymentline d-flex align-items-center bg-view"
t-att-class="unselectedLineClass(line)">
<div class="payment-infos" t-on-click="() => this.selectLine(line)">
<div class="payment-infos d-flex align-items-center justify-content-between flex-grow-1 px-3 py-4 my-2 text-truncate cursor-pointer" t-on-click="() => this.selectLine(line)">
<t t-esc="line.payment_method.name" />
<div class="payment-amount">
<div class="payment-amount px-3">
<t t-if="line and line.payment_status and ['done', 'waitingCard', 'waiting', 'reversing', 'reversed'].includes(line.payment_status)">
<t t-esc="env.utils.formatCurrency(line.get_amount(), false)" />
</t>
@@ -115,10 +115,10 @@
</div>
</div>
<t t-if="!line.payment_status or !['done', 'reversed'].includes(line.payment_status)">
<div class="delete-button"
<div class="delete-button delete-button btn btn-link mx-2 px-3"
t-on-click="() => this.props.deleteLine(line.cid)"
aria-label="Delete" title="Delete">
<i class="fa fa-times-circle" />
<i class="fa fa-2x fa-times-circle" />
</div>
</t>
</div>
@@ -0,0 +1,52 @@
.payment-screen .center-content {
grid-template-rows: auto 1fr;
width: 30rem;
}
@include media-breakpoint-down(lg) {
.payment-screen {
.main-content {
grid-template-columns: 300px 1fr;
grid-template-rows: 1fr auto;
}
.center-content {
border-bottom: none;
}
}
.payment-status-container > div:first-child {
margin-top: map-get($spacers, 2);
}
}
@include media-breakpoint-down(md) {
.payment-screen .main-content {
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr;
}
.payment-screen .center-content {
grid-template-rows: 1fr;
}
.payment-screen .left-content,
.payment-screen .right-content {
justify-content: flex-start;
overflow: initial;
}
.screen .right-content {
grid-column: span 1;
}
.pos .paymentlines-container {
position: sticky;
top: 0;
}
}
.payment-method-icon {
max-width: 50px;
max-height: 50px;
vertical-align: middle;
}
@@ -3,32 +3,33 @@
<t t-name="point_of_sale.PaymentScreen" owl="1">
<div class="payment-screen screen" t-att-class="{ oe_hidden: !props.isShown }">
<div class="screen-content">
<div class="screen-content d-flex flex-column h-100">
<t t-if="!ui.isSmall">
<div class="top-content">
<div class="button back"
t-on-click="() => pos.showScreen('ProductScreen')">
<i class="fa fa-angle-double-left fa-fw"></i>
<div class="top-content d-flex align-items-center p-2 border-bottom text-center">
<div class="button back btn btn-lg btn-secondary" t-on-click="() => pos.showScreen('ProductScreen')">
<i class="fa fa-angle-double-left me-2"></i>
<span class="back_text">Back</span>
</div>
<div class="top-content-center"><h1>Payment</h1></div>
<div class="top-content-center flex-grow-1">
<h2 class="mb-0">Payment</h2>
</div>
</div>
</t>
<div class="main-content">
<div class="left-content">
<div class="main-content d-flex flex-grow-1 flex-wrap overflow-y-auto">
<div class="left-content d-flex flex-column justify-content-between w-100 mh-100 border-0">
<t t-if="ui.isSmall">
<section class="paymentlines-container">
<section class="paymentlines-container px-4 pt-0">
<PaymentScreenStatus order="currentOrder" />
</section>
</t>
<div class="paymentmethods-container">
<div class="paymentmethods">
<p class="title-category">Payment method</p>
<div class="paymentmethods-container overflow-auto">
<div class="paymentmethods d-flex flex-column">
<p class="title-category py-1 px-3 m-0 text-bg-300 fw-bolder">Payment method</p>
<t t-foreach="payment_methods_from_config" t-as="paymentMethod"
t-key="paymentMethod.id">
<div class="button paymentmethod"
<div class="button paymentmethod btn btn-light rounded-0 fw-bolder"
t-on-click="() => this.addNewPaymentLine(paymentMethod)">
<div class="payment-method-display">
<div class="payment-method-display d-flex align-items-center flex-grow-1 py-3">
<div><img class="payment-method-icon" t-att-src="paymentMethodImage(paymentMethod.id)"/></div>
<span class="payment-name"><t t-esc="paymentMethod.name" /></span>
</div>
@@ -45,30 +46,30 @@
selectLine.bind="selectPaymentLine"
updateSelectedPaymentline.bind="updateSelectedPaymentline"/>
</div>
<div t-if="!ui.isSmall" class="button next validation" t-att-class="{ highlight: currentOrder.is_paid() and currentOrder._isValidEmptyOrder() }"
<div t-if="!ui.isSmall" class="button next validation btn btn-primary btn-lg py-5 rounded-0 d-flex flex-column align-items-center justify-content-center fw-bolder" t-attf-class="{{currentOrder.is_paid() and currentOrder._isValidEmptyOrder() ? 'highlight' : 'disabled'}}"
t-on-click="() => this.validateOrder()">
<div class="pay-circle">
<div class="pay-circle d-flex align-items-center justify-content-center mb-2">
<i class="oi oi-chevron-right" role="img" aria-label="Pay" title="Pay" />
</div>
<span class="next_text">Validate</span>
</div>
</div>
<div class="center-content" t-if="!ui.isSmall">
<section class="paymentlines-container">
<div class="center-content d-grid px-3 pb-3 pb-md-0 pb-lg-3 bg-300" t-if="!ui.isSmall">
<section class="paymentlines-container bg-view rounded my-3 p-3">
<PaymentScreenStatus order="currentOrder" />
</section>
<div class="payment-buttons-container">
<section class="payment-numpad">
<div class="payment-buttons-container d-flex flex-wrap">
<section class="payment-numpad flex-grow-1">
<PaymentScreenNumpad />
</section>
</div>
</div>
<div class="right-content">
<div class="payment-buttons">
<div class="partner-button">
<div class="button" t-att-class="{ highlight: currentOrder.get_partner() }" t-on-click="() => this.selectPartner(false, [])">
<i class="fa fa-user" role="img" title="Customer" />
<div class="payment-buttons d-flex flex-column flex-md-row flex-lg-column flex-lg-column flex-grow-1 flex-wrap gap-3 gap-lg-0 p-3 p-lg-0 bg-300 ">
<div class="partner-button d-grid flex-grow-1 flex-basis-25 border">
<div class="button btn btn-light py-3 text-start rounded-0" t-att-class="{ 'highlight text-bg-primary': currentOrder.get_partner() }" t-on-click="() => this.selectPartner(false, [])">
<i class="fa fa-user me-2" role="img" title="Customer" />
<t t-set="partner" t-value="currentOrder.get_partner()" />
<span>
<t t-if="partner">
@@ -80,25 +81,25 @@
</span>
</div>
</div>
<div class="payment-controls">
<div class="button js_invoice"
t-att-class="{ highlight: currentOrder.is_to_invoice() }"
<div class="payment-controls d-grid flex-grow-1 flex-basis-25 border">
<button class="button js_invoice btn btn-light py-3 text-start rounded-0"
t-att-class="{ 'highlight text-bg-primary': currentOrder.is_to_invoice() }"
t-on-click="toggleIsToInvoice">
<i class="fa fa-file-text-o" /> Invoice
</div>
<div t-if="pos.config.iface_tipproduct and pos.config.tip_product_id" class="button js_tip"
t-att-class="{ highlight: currentOrder.get_tip() }"
<i class="fa fa-file-text-o me-2" /> Invoice
</button>
<button t-if="pos.config.iface_tipproduct and pos.config.tip_product_id" class="button js_tip btn btn-light py-3 text-start rounded-0"
t-att-class="{ 'highlight text-bg-primary': currentOrder.get_tip() }"
t-on-click="addTip">
<i class="fa fa-heart" /> Tip <t t-if="currentOrder.get_tip() != 0">(<t t-esc="env.utils.formatCurrency(currentOrder.get_tip())"/>)</t>
</div>
<div t-if="pos.config.iface_cashdrawer" class="button js_cashdrawer"
<i class="fa fa-heart me-2" /> Tip <t t-if="currentOrder.get_tip() != 0">(<t t-esc="env.utils.formatCurrency(currentOrder.get_tip())"/>)</t>
</button>
<button t-if="pos.config.iface_cashdrawer" class="button js_cashdrawer btn btn-light py-3 text-start rounded-0"
t-on-click="openCashbox">
<i class="fa fa-archive" /> Open Cashbox
</div>
<div t-if="pos.config.ship_later" class="button"
t-att-class="{ highlight: currentOrder.getShippingDate() }"
<i class="fa fa-archive me-2" /> Open Cashbox
</button>
<div t-if="pos.config.ship_later" class="button btn btn-light py-3 text-start rounded-0"
t-att-class="{ 'highlight text-bg-primary': currentOrder.getShippingDate() }"
t-on-click="toggleShippingDatePicker">
<i class="fa fa-clock-o" /> Ship Later
<i class="fa fa-clock-o me-2" /> Ship Later
<span t-if="currentOrder.getShippingDate()">
(<t t-esc="currentOrder.getShippingDate()"/>)
</span>
@@ -108,8 +109,8 @@
</div>
</div>
<t t-if="ui.isSmall">
<div class="switchpane">
<button class="btn-switchpane validation-button"
<div class="switchpane d-flex">
<button class="btn-switchpane validation-button btn btn-primary flex-fill py-5 rounded-0 fw-bolder"
t-att-class="{ secondary: !(currentOrder.is_paid() and currentOrder._isValidEmptyOrder()) }"
t-on-click="() => this.validateOrder()">
<span>Validate</span>
@@ -0,0 +1,10 @@
.paymentlines-empty .total {
font-size: 3rem;
text-shadow: 0px 2px white, 0px 2px 2px rgba(0, 0, 0, 0.27);
}
@include media-breakpoint-down(md) {
.paymentlines-empty .message {
padding-bottom: map-get($spacers, 3);
}
}
@@ -3,26 +3,26 @@
<t t-name="point_of_sale.PaymentScreenStatus" owl="1">
<div t-if="props.order.get_paymentlines().length === 0" class="paymentlines-empty">
<div class="total">
<div class="total text-center py-2 text-success">
<t t-esc="totalDueText" />
</div>
<div class="message">
<div class="message text-center">
Please select a payment method.
</div>
</div>
<div t-else="">
<div class="payment-status-container">
<div class="payment-status-container d-flex flex-column-reverse flex-lg-row justify-content-between fs-2">
<div>
<div class="payment-status-remaining">
<span class="label">Remaining</span>
<span class="label pe-2">Remaining</span>
<span class="amount"
t-att-class="{ highlight: props.order.get_due() > 0 }">
t-att-class="{ 'highlight text-primary fw-bolder': props.order.get_due() > 0 }">
<t t-esc="remainingText" />
</span>
</div>
<div class="payment-status-total-due">
<span class="label">Total Due</span>
<div class="payment-status-total-due py-2 fs-3 text-muted">
<span class="label pe-2">Total Due</span>
<span>
<t t-esc="totalDueText" />
</span>
@@ -30,9 +30,9 @@
</div>
<div>
<div class="payment-status-change">
<span class="label">Change</span>
<span class="label pe-2">Change</span>
<span class="amount"
t-att-class="{ highlight: props.order.get_change() > 0 }">
t-att-class="{ 'highlight text-primary fw-bolder': props.order.get_change() > 0 }">
<t t-esc="changeText" />
</span>
</div>
@@ -0,0 +1,9 @@
.set-partner span {
height: 32px;
width: 32px;
min-width: 32px;
}
.pay-order-button, .set-partner {
height: 56px;
}
@@ -2,25 +2,31 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.ActionpadWidget" owl="1">
<div class="actionpad">
<button class="button mobile-more-button" t-if="ui.isSmall and props.onClickMore" t-on-click="props.onClickMore">
<div class="actionpad d-flex flex-column flex-grow-1 w-50 p-0 border-end">
<button class="button mobile-more-button btn btn-secondary flex-fill border-bottom" t-if="ui.isSmall and props.onClickMore" t-on-click="props.onClickMore">
<span>More...</span>
</button>
<button class="button set-partner" t-att-class="{'decentered': isLongName}"
<button class="button set-partner btn btn-light rounded-0 py-2 flex-shrink-1 fw-bolder"
t-on-click="() => this.pos.selectPartner()">
<t t-if="!ui.isSmall"><i class="fa fa-user" role="img" aria-label="Customer" title="Customer" /></t>
<t t-if="props.partner">
<t t-esc="props.partner.name" />
</t>
<t t-else="">
Customer
</t>
<div class="d-flex justify-content-center align-items-center ">
<span class="d-flex justify-content-center align-items-center rounded-circle me-2 text-bg-dark" t-if="!ui.isSmall">
<i class="fa fa-user" role="img" aria-label="Customer" title="Customer" />
</span>
<div t-if="props.partner" class="text-truncate fw-bolder">
<t t-esc="props.partner.name" />
</div>
<div t-else="fw-bolder">
Customer
</div>
</div>
</button>
<button class="button pay validation pay-order-button" t-att-class="{ 'with-more-button': props.onClickMore and ui.isSmall }" t-on-click="props.actionToTrigger ? this.props.actionToTrigger : () => pos.get_order().pay()">
<div class="pay-circle" t-att-class="{ 'highlight': this.highlightPay }">
<button class="button pay validation pay-order-button btn btn-primary btn-lg py-5 rounded-0 highlight d-flex flex-column flex-fill align-items-center justify-content-center fw-bolder"
t-att-class="{ 'with-more-button': props.onClickMore and ui.isSmall }"
t-on-click="props.actionToTrigger ? this.props.actionToTrigger : () => pos.get_order().pay()">
<div class="pay-circle d-flex align-items-center justify-content-center py-2 mb-2" t-att-class="{ 'highlight': this.highlightPay }">
<i class="oi oi-chevron-right" role="img" aria-label="Pay" title="Pay" />
</div>
<t t-esc="props.actionName" />
<t class="text-white" t-esc="props.actionName" />
</button>
</div>
</t>
@@ -2,14 +2,14 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.CategoryButton" owl="1">
<span class="category-button" t-on-click="() => this.props.switchCategory(props.category.id)">
<button class="category-button btn btn-light" t-on-click="() => this.props.switchCategory(props.category.id)">
<div class="category-img">
<img t-att-src="imageUrl" alt="Category" />
</div>
<div class="category-name">
<div class="category-name mt-2 fw-bolder">
<t t-esc="props.category.name" />
</div>
</span>
</button>
</t>
</templates>
@@ -0,0 +1,22 @@
.control-buttons {
white-space: nowrap;
gap: 1px;
}
.control-button {
flex: 1 1 29%;
padding: $o-horizontal-padding * 0.75;
}
/* .control-button.highlight,
.button.highlight {
background: $primary !important;
border-color: $primary !important;
color: white !important;
} */
.control-button.altlight,
.button.altlight {
background: #7F82AC !important;
border: solid 1px #756A99 !important;
color: white !important;
}
@@ -2,15 +2,15 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.ControlButtonPopup" owl="1">
<div class="popup popup-control-buttons">
<div class="popup popup-control-buttons d-flex flex-column">
<!-- When one of the buttons is clicked, close this popup. -->
<div class="control-buttons" t-on-click="cancel">
<div class="control-buttons d-flex flex-column p-2" t-on-click="cancel">
<t t-foreach="controlButtons" t-as="cb" t-key="cb.name">
<t t-component="cb.component" t-key="cb.name"/>
</t>
</div>
<footer class="footer">
<div class="button cancel" t-on-click="cancel">
<footer class="footer modal-footer">
<div class="button cancel btn btn-lg btn-primary" t-on-click="cancel">
<t t-esc="props.cancelText" />
</div>
</footer>
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.CustomerButton" owl="1">
<div class="control-button customer-button set-partner" t-on-click="() => this.pos.selectPartner()">
<i class="fa fa-user" role="img" aria-label="Customer" title="Customer" />
<div class="control-button customer-button btn btn-light rounded-0 fw-bolder" t-on-click="() => this.pos.selectPartner()">
<i class="fa fa-user me-1" role="img" aria-label="Customer" title="Customer" />
<t t-esc="this.partner.name" t-if="this.partner" />
<t t-else="">
Customer
@@ -2,11 +2,11 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.OrderlineCustomerNoteButton" owl="1">
<div class="control-button" t-on-click="() => this.onClick()">
<i class="fa fa-sticky-note" />
<button class="control-button btn btn-light rounded-0 fw-bolder" t-on-click="() => this.onClick()">
<i class="fa fa-sticky-note me-1" />
<span> </span>
<span>Customer Note</span>
</div>
</button>
</t>
</templates>
@@ -2,8 +2,8 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.SetFiscalPositionButton" owl="1">
<div class="control-button o_fiscal_position_button" t-on-click="() => this.click()">
<i class="fa fa-book" role="img" aria-label="Set fiscal position"
<div class="control-button o_fiscal_position_button btn btn-light rounded-0 fw-bolder" t-on-click="() => this.click()">
<i class="fa fa-book me-1" role="img" aria-label="Set fiscal position"
title="Set fiscal position" />
<t t-esc='currentFiscalPositionName' />
</div>
@@ -2,10 +2,10 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.ProductInfoButton" owl="1">
<div class="control-button" t-on-click="() => this.click()">
<i class="fa fa-info-circle" role="img" aria-label="Info" title="Info"/>
<button class="control-button btn btn-light rounded-0 fw-bolder" t-on-click="() => this.click()">
<i class="fa fa-info-circle me-1" role="img" aria-label="Info" title="Info"/>
Info
</div>
</button>
</t>
</templates>
@@ -2,10 +2,10 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.RefundButton" owl="1">
<div class="control-button" t-on-click="() => this.click()">
<i class="fa fa-undo" role="img" aria-label="Refund" title="Refund" />
<button class="control-button btn btn-light rounded-0 fw-bolder" t-on-click="() => this.click()">
<i class="fa fa-undo me-1" role="img" aria-label="Refund" title="Refund" />
Refund
</div>
</button>
</t>
</templates>
@@ -0,0 +1,44 @@
/* ********* The Numpad ********* */
.pos .numpad {
grid-template-columns: repeat(4, 1fr);
min-width: 216px;
gap: 1px;
}
/* .pos .numpad button {
float: left
height: 54px;
vertical-align: middle;
color: $gray-700;
border-radius: 0;
border: none;
border-right: 1px solid;
border-bottom: 1px solid;
border-color: $gray-300;
transition: all 150ms linear;
padding: unset;
width: 100%;
font-weight: bold;
} */
.pos .mode-button {
font-size: 14px;
}
.pos .mode-button.selected-mode {
color: white;
background: $o-brand-odoo;
border-color: transparent;
}
.pos .mode-button.selected-mode:hover {
background: $primary;
color: white;
border-color: transparent;
}
.pos .numpad .disabled-mode, .pos .numpad .disabled-mode:hover {
background: $gray-200;
color: $gray-500;
cursor: not-allowed;
}
@@ -2,35 +2,35 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.NumpadWidget" owl="1">
<div class="numpad">
<button class="input-button number-char" t-on-click="() => this.sendInput('1')">1</button>
<button class="input-button number-char" t-on-click="() => this.sendInput('2')">2</button>
<button class="input-button number-char" t-on-click="() => this.sendInput('3')">3</button>
<button class="mode-button" t-att-class="{'selected-mode': props.activeMode === 'quantity'}"
<div class="numpad d-grid text-center flex-grow-1 w-50 bg-300">
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3" t-on-click="() => this.sendInput('1')">1</button>
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3" t-on-click="() => this.sendInput('2')">2</button>
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3" t-on-click="() => this.sendInput('3')">3</button>
<button class="mode-button btn btn-light w-100 rounded-0 py-3" t-att-class="{'selected-mode': props.activeMode === 'quantity'}"
t-on-click="() => this.changeMode('quantity')">Qty</button>
<button class="input-button number-char" t-on-click="() => this.sendInput('4')">4</button>
<button class="input-button number-char" t-on-click="() => this.sendInput('5')">5</button>
<button class="input-button number-char" t-on-click="() => this.sendInput('6')">6</button>
<button class="mode-button" t-att-class="{
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3" t-on-click="() => this.sendInput('4')">4</button>
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3" t-on-click="() => this.sendInput('5')">5</button>
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3" t-on-click="() => this.sendInput('6')">6</button>
<button class="mode-button btn btn-light w-100 rounded-0 py-3 text-nowrap" t-att-class="{
'selected-mode': props.activeMode === 'discount',
'disabled-mode': !hasManualDiscount
}"
t-att-disabled="!hasManualDiscount"
t-on-click="() => this.changeMode('discount')">% Disc</button>
<button class="input-button number-char" t-on-click="() => this.sendInput('7')">7</button>
<button class="input-button number-char" t-on-click="() => this.sendInput('8')">8</button>
<button class="input-button number-char" t-on-click="() => this.sendInput('9')">9</button>
<button class="mode-button" t-att-class="{
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3" t-on-click="() => this.sendInput('7')">7</button>
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3" t-on-click="() => this.sendInput('8')">8</button>
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3" t-on-click="() => this.sendInput('9')">9</button>
<button class="mode-button btn btn-light w-100 rounded-0 py-3" t-att-class="{
'selected-mode': props.activeMode === 'price',
'disabled-mode': !hasPriceControlRights
}" t-att-disabled="!hasPriceControlRights"
t-on-click="() => this.changeMode('price')">Price</button>
<button class="input-button numpad-minus" t-att-class="{ 'disabled-mode': props.disableSign }" t-on-click="() => this.sendInput('-')">+/-</button>
<button class="input-button number-char" t-on-click="() => this.sendInput('0')">0</button>
<button class="input-button number-char" t-on-click="() => this.sendInput(decimalSeparator)">
<button class="input-button numpad-minus btn btn-light w-100 rounded-0 py-3" t-att-class="{ 'disabled-mode': props.disableSign }" t-on-click="() => this.sendInput('-')">+/-</button>
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3" t-on-click="() => this.sendInput('0')">0</button>
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3" t-on-click="() => this.sendInput(decimalSeparator)">
<t t-esc="decimalSeparator" />
</button>
<button class="input-button numpad-backspace" t-on-click="() => this.sendInput('Backspace')">
<button class="input-button numpad-backspace btn btn-light w-100 rounded-0 py-3" t-on-click="() => this.sendInput('Backspace')">
<img style="pointer-events: none;" src="/point_of_sale/static/src/img/backspace.png"
width="24" height="21" alt="Backspace" />
</button>
@@ -0,0 +1,8 @@
/* ********* The OrderWidget ********* */
.pos .scrollable-y{
overflow: hidden !important;
overflow-y: auto !important;
-webkit-overflow-scrolling: touch !important;
}
@@ -2,17 +2,17 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.OrderWidget" owl="1">
<div class="order-container" t-ref="scrollable">
<div class="order">
<div class="order-container w-100 h-100 overflow-y-auto bg-view" t-ref="scrollable">
<div class="order d-flex flex-column h-100 text-start">
<t t-if="orderlinesArray.length === 0" >
<div class='order-empty'>
<i class='fa fa-shopping-cart' role="img" aria-label="Shopping cart"
<div class="order-empty d-flex flex-column align-items-center justify-content-center h-100 text-muted">
<i class="fa fa-4x fa-shopping-cart" role="img" aria-label="Shopping cart"
title="Shopping cart"/>
<h1>This order is empty</h1>
<h3 class="mt-2">This order is empty</h3>
</div>
</t>
<t t-else="">
<ul class="orderlines">
<ul class="orderlines flex-grow-1 overflow-auto">
<t t-foreach="orderlinesArray" t-as="orderline" t-key="orderline.id">
<Orderline
editPackLotLines.bind="editPackLotLines"
@@ -2,16 +2,16 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.OrderSummary" owl="1">
<div class="summary clearfix">
<div class="summary clearfix sticky-top w-100 py-2 px-3 border-top text-end fw-bolder">
<t t-set="_total" t-value="getTotal()" />
<t t-set="_tax" t-value="getTax()" />
<div class="line">
<div class="entry total">
<span class="badge">Total: </span>
<div class="entry total fs-2">
Total:
<span class="value">
<t t-esc="_total" />
</span>
<div t-if="_tax.hasTax" class="subentry">
<div t-if="_tax.hasTax" class="subentry fs-5 text-muted">
Taxes:
<span class="value">
<t t-esc="_tax.displayAmount" />
@@ -36,9 +36,9 @@ export class Orderline extends Component {
get addedClasses() {
return {
selected: this.props.line.selected,
"has-change":
"has-change ps-2 text-success border-start border-success border-4":
this.props.line.hasChange && this.pos.config.module_pos_restaurant,
"skip-change":
"skip-change ps-2 text-secondary border-start border-secondary border-4":
this.props.line.skipChange && this.pos.config.module_pos_restaurant,
};
}
@@ -0,0 +1,17 @@
/* ********* The OrderLineWidget ********* */
.orderline {
box-sizing: border-box;
transition: background 250ms ease-in-out;
&:active {
background: rgba(140,143,183,0.05);
transition: background 50ms ease-in-out;
}
&.selected {
background: rgba($primary, 0.2);
transition: background 250ms ease-in-out;
cursor: default;
}
}
@@ -2,14 +2,13 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.Orderline" owl="1">
<li t-on-click="selectLine" class="orderline" t-att-class="addedClasses">
<span class="product-name">
<li t-on-click="selectLine" class="orderline w-100 m-0 py-2 px-3 cursor-pointer" t-att-class="addedClasses">
<span class="product-name d-inline-block flex-grow-1 fw-bolder text-truncate">
<t t-esc="props.line.get_full_product_name()"/>
<span> </span>
<t t-if="props.line.get_product().tracking!=='none' &amp;&amp; (pos.picking_type.use_create_lots || pos.picking_type.use_existing_lots)">
<t t-if="props.line.has_valid_product_lot()">
<i t-on-click.stop="lotIconClicked"
class="oe_link_icon fa fa-list oe_icon line-lot-icon oe_green"
class="oe_link_icon fa fa-list oe_icon line-lot-icon ms-1 text-success"
aria-label="Valid product lot"
role="img"
title="Valid product lot"
@@ -17,7 +16,7 @@
</t>
<t t-else="">
<i t-on-click.stop="lotIconClicked"
class="oe_link_icon fa fa-list oe_icon line-lot-icon oe_red"
class="oe_link_icon fa fa-list oe_icon line-lot-icon ms-1 text-danger"
aria-label="Invalid product lot"
role="img"
title="Invalid product lot"
@@ -25,13 +24,13 @@
</t>
</t>
</span>
<span class="price">
<span class="price fw-bold float-end">
<t t-esc="env.utils.formatCurrency(props.line.get_display_price())"/>
</span>
<ul class="info-list">
<ul class="info-list ms-2">
<t t-if="props.line.get_quantity_str() !== '1' || props.line.selected ">
<li class="info">
<em>
<em class="fst-normal fw-bolder">
<t t-esc="props.line.get_quantity_str()" />
</em>
<span> </span><t t-esc="props.line.get_unit().name" />
@@ -60,14 +59,14 @@
</li>
</t>
<t t-if="customerNote">
<li class="info orderline-note">
<i class="fa fa-sticky-note" role="img" aria-label="Customer Note" title="Customer Note"/>
<li class="info orderline-note m-2 text-break">
<i class="fa fa-sticky-note me-1 text-muted" role="img" aria-label="Customer Note" title="Customer Note"/>
<t t-esc="customerNote" />
</li>
</t>
</ul>
<t t-if="props.line.pack_lot_lines">
<ul class="info-list">
<ul class="info-list ms-2">
<t t-foreach="props.line.get_lot_lines()" t-as="lot" t-key="lot.cid">
<li>
SN <t t-esc="lot.lot_name"/>
@@ -0,0 +1,22 @@
.product-img img {
height: 100px;
object-fit: contain;
}
@media screen and (max-width: 768px) {
.product:active {
box-shadow: 0 0 0 3px $primary;
}
}
.product-name {
box-sizing: border-box;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
&.no-image {
-webkit-line-clamp: 9;
}
}
@@ -2,18 +2,18 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.ProductItem" owl="1">
<article t-attf-class="product {{this.pos.productListViewMode}}" tabindex="0" t-on-keypress="spaceClickProduct"
<article t-attf-class="product {{this.pos.productListViewMode}} position-relative btn btn-light d-flex flex-column align-items-stretch p-0 m-0 text-start cursor-pointer overflow-hidden" tabindex="0" t-on-keypress="spaceClickProduct"
t-on-click="() => this.pos.addProductToCurrentOrder(props.product)"
t-att-data-product-id="props.product.id"
t-attf-aria-labelledby="article_product_{{props.product.id}}">
<div class="product-img">
<img t-if="props.product.image_128" t-att-src="imageUrl" t-att-alt="props.product.display_name" />
<img t-if="props.product.image_128" class="w-100" t-att-src="imageUrl" t-att-alt="props.product.display_name" />
</div>
<div class="product-content">
<div t-attf-class="#{props.product.image_128 ? 'product-name' : 'product-name no-image' }" t-attf-id="article_product_{{props.product.id}}">
<div class="product-content d-flex flex-column justify-content-between h-100 py-1">
<div t-attf-class="mx-2 overflow-hidden lh-sm #{props.product.image_128 ? 'product-name' : 'product-name no-image' }" t-attf-id="article_product_{{props.product.id}}">
<t t-esc="props.product.display_name" />
</div>
<span class="price-tag">
<span class="price-tag text-primary py-1 px-2 fw-bolder">
<t t-esc="price" />
</span>
</div>
@@ -24,8 +24,7 @@ export class ProductInfoPopup extends AbstractAwaitablePopup {
this.cancel();
}
_hasMarginsCostsAccessRights() {
const isAccessibleToEveryUser =
this.pos.config.is_margins_costs_accessible_to_every_user;
const isAccessibleToEveryUser = this.pos.config.is_margins_costs_accessible_to_every_user;
const isCashierManager = this.pos.get_cashier().role === "manager";
return isAccessibleToEveryUser || isCashierManager;
}
@@ -0,0 +1,7 @@
.product-info-popup {
max-width: 800px !important;
.modal-body {
max-height: 70vh;
}
}
@@ -2,29 +2,28 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.ProductInfoPopup" owl="1">
<div class="popup product-info-popup">
<header class="title">
Product information
</header>
<main class="body">
<div class="section-product-info-title">
<div class="column flex-start">
<span t-esc="props.product.display_name" class="global-info-title product-name"/>
<span><t t-if="props.product.default_code" t-esc="props.product.default_code"/> <t t-if="props.product.default_code and props.product.barcode"> - </t> <t t-if="props.product.barcode" t-esc="props.product.barcode"/></span>
<div class="modal-header">
<h4 class="modal-title">Product information</h4>
</div>
<main class="body modal-body overflow-auto">
<div class="section-product-info-title d-flex flex-column flex-sm-row text-start gap-3 bg-info text-info bg-opacity-25 mx-n3 mt-n3 p-3">
<div class="d-flex flex-column flex-grow-1 text-start w-100 w-sm-25">
<span t-esc="props.product.display_name" class="global-info-title fs-2 fw-bolder text-truncate"/>
<span class="fs-3"><t t-if="props.product.default_code" t-esc="props.product.default_code"/> <t t-if="props.product.default_code and props.product.barcode"> - </t> <t t-if="props.product.barcode" t-esc="props.product.barcode"/></span>
</div>
<div class="column flex-end">
<span t-esc="env.utils.formatCurrency(productInfo.all_prices.price_with_tax)" class="global-info-title" />
<t t-foreach="productInfo.all_prices.tax_details" t-as="tax" t-key="tax.name">
<div><t t-esc="tax.name"/>: <t t-esc="env.utils.formatCurrency(tax.amount)"/></div>
</t>
<div class="d-flex flex-column justify-content-center text-start text-sm-end w-100 w-sm-25">
<span t-esc="env.utils.formatCurrency(productInfo.all_prices.price_with_tax)" class="global-info-title fs-2 fw-bolder" />
<span class="fs-3">
<t t-foreach="productInfo.all_prices.tax_details" t-as="tax" t-key="tax.name">
<div><t t-esc="tax.name"/>: <t t-esc="env.utils.formatCurrency(tax.amount)"/></div>
</t>
</span>
</div>
</div>
<div class="section-financials">
<div class="section-title">
<span>Financials</span>
<div class="section-title-line"></div>
</div>
<div class="section-financials-body">
<table>
<div class="section-financials mt-3 mb-4 pb-4 border-bottom text-start">
<h3 class="section-title">Financials</h3>
<div class="section-financials-body d-flex flex-column flex-sm-row">
<table class="table table-borderless mb-0">
<tr>
<td>Price excl. VAT:</td>
<td><t t-esc="env.utils.formatCurrency(productInfo.all_prices.price_without_tax)"/></td>
@@ -38,7 +37,7 @@
<td><t t-esc="marginCurrency"/> (<t t-esc="marginPercent"/>%) </td>
</tr>
</table>
<table>
<table class="table table-borderless">
<t t-foreach="productInfo.pricelists" t-as="pricelist" t-key="pricelist.name">
<tr>
<td t-esc="pricelist.name"/>
@@ -48,71 +47,75 @@
</table>
</div>
</div>
<div class="section-inventory" t-if="productInfo.warehouses.length > 0">
<div class="section-title">
<span>Inventory <t t-if="pos.pos_session.update_stock_at_closing">(as of opening)</t></span>
<div class="section-title-line"></div>
</div>
<div class="section-inventory mt-3 mb-4 pb-4 border-bottom text-start" t-if="productInfo.warehouses.length > 0">
<h3 class="section-title">
Inventory
<t t-if="pos.pos_session.update_stock_at_closing">(as of opening)</t>
</h3>
<div class="section-inventory-body">
<table class="mobile-table">
<t t-foreach="productInfo.warehouses" t-as="warehouse" t-key="warehouse.name">
<tr>
<td><span t-esc="warehouse.name" class="table-name"/>:</td>
<td><t t-esc="warehouse.available_quantity"/> <t t-esc="warehouse.uom"/> available,</td>
<td><t t-esc="warehouse.forecasted_quantity"/> forecasted</td>
</tr>
</t>
</table>
<t t-foreach="productInfo.warehouses" t-as="warehouse" t-key="warehouse.name">
<div class="d-flex flex-column flex-md-row gap-2">
<div>
<t t-esc="warehouse.name" class="table-name"/>
:
</div>
<div>
<span class="me-1 fw-bolder"><t t-esc="warehouse.available_quantity" class="table-name"/></span>
<t t-esc="warehouse.uom"/> available,
</div>
<div>
<span class="me-1 fw-bolder"><t t-esc="warehouse.forecasted_quantity"/></span>
forecasted
</div>
</div>
</t>
</div>
</div>
<div class="section-supplier" t-if="productInfo.suppliers.length > 0">
<div class="section-title">
<span>Replenishment</span>
<div class="section-title-line"></div>
</div>
<div class="section-supplier mt-3 mb-4 pb-4 border-bottom text-start" t-if="productInfo.suppliers.length > 0">
<h3 class="section-title">Replenishment</h3>
<div class="section-supplier-body">
<table class="mobile-table">
<t t-foreach="productInfo.suppliers" t-as="supplier" t-key="supplier.name">
<tr>
<td><span t-esc="supplier.name" class="table-name"/>:</td>
<div class="mobile-line">
<td><t t-esc="supplier.delay"/> Days</td>
<td t-if="_hasMarginsCostsAccessRights()"><t t-esc="env.utils.formatCurrency(supplier.price)"/></td>
</div>
</tr>
</t>
</table>
<t t-foreach="productInfo.suppliers" t-as="supplier" t-key="supplier.name">
<div class="d-flex flex-column flex-md-row gap-2">
<div>
<span t-esc="supplier.name" class="table-name"/>:
</div>
<span class="me-1 fw-bolder">
<t t-esc="supplier.delay"/>
Days
</span>
<span t-if="_hasMarginsCostsAccessRights()">
<t t-esc="env.utils.formatCurrency(supplier.price)"/>
</span>
</div>
</t>
</div>
</div>
<div class="extra">
<div class="section-variants" t-if="productInfo.variants.length > 0">
<div class="section-title">
<span>Attributes</span>
<div class="section-title-line"></div>
</div>
<div class="extra mt-3 mb-4 pb-4 border-bottom text-start" t-if="productInfo.variants.length > 0">
<div class="section-variants">
<h3 class="section-title">Attributes</h3>
<div class="section-variants-body">
<table class="mobile-table">
<t t-foreach="productInfo.variants" t-as="variant" t-key="variant.name">
<tr>
<td><span t-esc="variant.name" class="table-name"/>:</td>
<td class="table-value">
<t t-foreach="variant.values" t-as="attribute_value" t-key="attribute_value.name">
<span class="searchable" t-on-click="() => this.searchProduct(attribute_value.search)"><t t-esc="attribute_value.name"/></span> <t t-if="attribute_value_index lt variant.values.length - 1">, </t>
</t>
</td>
</tr>
</t>
</table>
<t t-foreach="productInfo.variants" t-as="variant" t-key="variant.name">
<div class="d-flex flex-column align-items-start gap-2 mb-2">
<div>
<span t-esc="variant.name" class="table-name"/>:
</div>
<div class="d-flex flex-wrap gap-1">
<t t-foreach="variant.values" t-as="attribute_value" t-key="attribute_value.name">
<span class="searchable btn btn-secondary" t-on-click="() => this.searchProduct(attribute_value.search)">
<t t-esc="attribute_value.name"/>
</span>
<t t-if="attribute_value_index lt variant.values.length - 1"> </t>
</t>
</div>
</div>
</t>
</div>
</div>
</div>
<div class="section-order">
<div class="section-title">
<span>Order</span>
<div class="section-title-line"></div>
</div>
<div class="section-order mt-3 mb-4 pb-4 border-bottom text-start">
<h3 class="section-title">Order</h3>
<div class="section-order-body">
<table>
<table class="table table-borderless w-100 w-sm-50 mb-0">
<tr>
<td>Total Price excl. VAT:</td>
<td t-esc="orderPriceWithoutTaxCurrency" class="table-value"/>
@@ -129,8 +132,8 @@
</div>
</div>
</main>
<footer class="footer">
<div class="button cancel" t-on-click="cancel">
<footer class="footer modal-footer">
<div class="button cancel btn btn-lg btn-primary" t-on-click="cancel">
Close
</div>
</footer>
@@ -0,0 +1,43 @@
.category-list-scroller{
max-height:40vh;
}
.category-button {
.category-img {
width: 120px;
height: 100px;
}
& img {
max-height: 100px;
max-width: 120px;
}
}
/* Search Bar (PartnerList & Product Screens*/
.pos-search-bar:focus-within {
background-color: $input-focus-bg;
border-color: $input-focus-border-color;
color: $input-focus-color;
}
/* Product Screen Search Bar */
.info-load-product-message {
background: lightblue;
color: #017e84;
padding: 8px;
width: fit-content;
display: block;
margin : 3px auto 0 auto;
}
@media screen and (max-width: 768px) {
.pos .search-bar-container.sb-product {
max-width: none;
width: auto;
background-color: #FFFFFF;
}
}
@@ -2,57 +2,57 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.ProductsWidgetControlPanel" owl="1">
<div class="products-widget-control" t-ref="products-widget-control">
<div class="rightpane-header">
<t t-if="this.pos.posHasValidProduct()">
<div class="search-bar-container sb-product">
<span t-if="state.isMobile" class="oe_status oe_icon icon" t-on-click="toggleMobileSearchBar">
<i t-attf-class="fa {{state.mobileSearchBarIsShown ? 'fa-arrow-right' : 'fa-search'}}"/>
</span>
<t t-if="!state.isMobile || state.mobileSearchBarIsShown">
<div class="pos-search-bar">
<i class="fa fa-search"/>
<input t-model="this.pos.searchProductWord" placeholder="Search Products..." type="text" autofocus="autofocus" t-on-keyup="updateSearch" />
<i t-if="this.pos.searchProductWord" class="fa fa-times" t-on-click="_clearSearch"/>
</div>
</t>
</div>
</t>
<div class="products-widget-control d-grid shadow-sm z-index-1" t-ref="products-widget-control">
<div class="rightpane-header d-flex justify-content-between align-items-center text-center bg-view p-1 overflow-x-auto overflow-y-hidden">
<t t-if="!props.hasNoCategories">
<div class="categories-header">
<div class="categories-header d-flex overflow-x-auto">
<!-- Breadcrumbs -->
<div class="breadcrumbs">
<span class="breadcrumb">
<span class="breadcrumb-button breadcrumb-home" t-on-click="() => this.props.switchCategory(0)">
<i class="fa fa-home" role="img" aria-label="Home" title="Home"></i>
</span>
<div class="breadcrumbs d-flex">
<span class="breadcrumb float-start d-flex align-items-center">
<button class="breadcrumb-button breadcrumb-home btn btn-light fw-bolder" t-on-click="() => this.props.switchCategory(0)">
<i class="fa fa-home fa-2x py-1" role="img" aria-label="Home" title="Home"></i>
</button>
</span>
<t t-foreach="props.breadcrumbs" t-as="category" t-key="category.id">
<span class="breadcrumb">
<i class="breadcrumb-arrow fa fa-caret-right" aria-hidden="true"/>
<span class="breadcrumb-button"
<span class="breadcrumb float-start d-flex align-items-center">
<i class="breadcrumb-arrow fa fa-caret-right px-2" aria-hidden="true"/>
<button class="breadcrumb-button btn btn-light h-100 fw-bolder"
t-on-click="() => this.props.switchCategory(category.id)">
<t t-esc="category.name"></t>
</span>
</button>
</span>
</t>
</div>
<!-- Subcategories -->
<div t-if="props.subcategories.length > 0 and !displayCategImages" class="category-list simple">
<div t-if="props.subcategories.length > 0 and !displayCategImages" class="category-list simple d-flex align-items-center ms-1">
<t t-foreach="props.subcategories" t-as="category" t-key="category.id">
<span class="category-simple-button"
<button class="category-simple-button btn btn-light h-100 fw-bolder"
t-on-click="() => this.props.switchCategory(category.id)">
<t t-esc="category.name" />
</span>
</button>
</t>
</div>
</div>
</t>
<div class="search-bar-container sb-product d-flex align-items-center h-100">
<span t-if="state.isMobile" class="oe_status oe_icon icon d-flex align-items-center h-100 me-1 px-4 rounded text-bg-secondary cursor-pointer" t-on-click="toggleMobileSearchBar">
<i t-attf-class="fa {{state.mobileSearchBarIsShown ? 'fa-arrow-right' : 'fa-search'}}"/>
</span>
<t t-if="!state.isMobile || state.mobileSearchBarIsShown">
<div class="input-group h-100">
<div class="pos-search-bar form-control d-flex align-items-center py-1 bg-view">
<i class="oi oi-search me-2"/>
<input class="flex-grow-1 w-auto h-100 border-0 px-2 me-4" t-model="this.pos.searchProductWord" placeholder="Search Products..." type="text" autofocus="autofocus" t-on-keyup="updateSearch" />
<i t-if="this.pos.searchProductWord" class="fa fa-times position-absolute end-0 me-2 pe-1 cursor-pointer" t-on-click="_clearSearch"/>
</div>
</div>
</t>
</div>
</div>
<t t-if="props.subcategories.length > 0 and displayCategImages">
<div class="categories">
<div class="category-list-scroller">
<div class="category-list">
<div class="categories border-bottom shadow-sm">
<div class="category-list-scroller w-100 h-100 overflow-hidden overflow-y-auto">
<div class="category-list d-flex flex-wrap px-1 py-2 gap-3">
<t t-foreach="props.subcategories" t-as="category" t-key="category.id">
<CategoryButton
switchCategory.bind="props.switchCategory"
@@ -0,0 +1,12 @@
/* ********* The product list ********* */
.product-list {
grid-template-columns: repeat(auto-fill, minmax(122px, 1fr));
}
@media only screen and (max-width: 768px) {
.product-list {
grid-template-columns: repeat(3, 1fr);
}
}
@@ -2,7 +2,7 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.ProductsWidget" owl="1">
<div class="products-widget">
<div class="products-widget d-flex flex-column flex-grow-1 overflow-hidden">
<ProductsWidgetControlPanel
updateProductList.bind="updateProductList"
clearSearch.bind="clearSearch"
@@ -12,14 +12,14 @@
subcategories="subcategories"
hasNoCategories="hasNoCategories"
loadProductFromServer.bind="onPressEnterKey" />
<div class="product-list-container">
<div t-if="productsToDisplay.length != 0 and posHasValidProduct" t-attf-class="{{this.pos.productListViewMode}} product-list">
<div class="product-list-container flex-grow-1 overflow-hidden overflow-y-auto">
<div t-if="productsToDisplay.length != 0 and posHasValidProduct" t-attf-class="{{this.pos.productListViewMode}} product-list d-grid gap-1 p-1">
<t t-foreach="productsToDisplay" t-as="product" t-key="product.id">
<ProductItem
product="product" />
</t>
</div>
<div t-else="" class="product-list-empty no-results-message">
<div t-else="" class="product-list-empty no-results-message text-center mt-5">
<t t-if="searchWord">
<p>No products found for <b>"<t t-esc="searchWord"/>"</b> in this category.</p>
</t>
@@ -30,7 +30,7 @@
<t t-else="">
<div>
<p>No products here, but don't worry, we've got your back.</p>
<p>Play with <a role="button" class="button-no-demo" t-on-click="loadDemoDataProducts">our demo products</a> or <a role="button" class="button-no-demo" t-on-click="createNewProducts">create your own products</a></p>
<p>Play with <a role="button" class="button-no-demo btn btn-lg btn-primary" t-on-click="loadDemoDataProducts">our demo products</a> or <a role="button" class="button-no-demo" t-on-click="createNewProducts">create your own products</a></p>
<div t-if="state.showReloadMessage" class="info-load-product-message">
Please reload this page once products have been created
</div>
@@ -38,8 +38,8 @@
</t>
</t>
</div>
<div t-if="searchWord" class="search-more-button">
<div class="button" t-on-click="onPressEnterKey">Search more</div>
<div t-if="searchWord" class="search-more-button d-flex justify-content-center">
<button class="btn btn-primary btn-lg" t-on-click="onPressEnterKey">Search more</button>
</div>
<div class="portal search-database-button no-results-message" t-att-class="{ oe_hidden: !shouldShowButton }"></div>
</div>
@@ -3,18 +3,18 @@
<t t-name="point_of_sale.ProductScreen" owl="1">
<div class="product-screen screen" t-att-class="{ oe_hidden: !props.isShown }">
<div class="screen-full-width">
<div class="leftpane pane-border" t-if="!ui.isSmall || pos.mobile_pane === 'left'">
<div class="screen-full-width d-flex w-100 h-100">
<div class="leftpane pane-border d-flex flex-column flex-grow-1 w-100 h-100 border-end border-3 bg-200" t-if="!ui.isSmall || pos.mobile_pane === 'left'">
<OrderWidget/>
<div class="pads">
<div class="control-buttons">
<div class="pads border-top">
<div class="control-buttons d-flex flex-wrap border-bottom overflow-hidden">
<t t-if="!ui.isSmall">
<t t-foreach="controlButtons" t-as="cb" t-key="cb.name">
<t t-component="cb.component" t-key="cb.name"/>
</t>
</t>
</div>
<div class="subpads">
<div class="subpads d-flex">
<ActionpadWidget
partner="partner"
actionName="constructor.numpadActionName"
@@ -26,20 +26,19 @@
</div>
</div>
</div>
<div class="rightpane" t-if="!ui.isSmall || pos.mobile_pane === 'right'">
<div class="rightpane d-flex flex-column flex-grow-1 w-100 h-100 bg-300" t-if="!ui.isSmall || pos.mobile_pane === 'right'">
<ProductsWidget />
<t t-if="ui.isSmall">
<div class="product-reminder" t-if="showProductReminder" t-key="animationKey">
<span><t t-esc="selectedOrderlineQuantity"/> <t t-esc="selectedOrderlineDisplayName"/></span>
<span><t t-esc="selectedOrderlineTotal"/></span>
<div class="product-reminder d-flex justify-content-center align-items-center py-1 text-bg-warning bg-opacity-50 fw-bolder" t-if="showProductReminder" t-key="animationKey" >
<span><t t-esc="selectedOrderlineQuantity"/> <t t-esc="selectedOrderlineDisplayName"/> <t t-esc="selectedOrderlineTotal"/></span>
</div>
<div class="switchpane">
<button class="btn-switchpane pay-button" t-att-class="{'primary': primaryPayButton(), 'secondary': !primaryPayButton()}" t-on-click="() => currentOrder.pay()">
<h1>Pay</h1>
<div class="switchpane d-flex">
<button class="btn-switchpane pay-button btn btn-primary flex-fill rounded-0 fw-bolder" t-att-class="{'primary': primaryPayButton(), 'secondary': !primaryPayButton()}" t-on-click="() => currentOrder.pay()">
<span class="fs-1 d-block">Pay</span>
<span><t t-esc="total" /></span>
</button>
<button class="btn-switchpane" t-att-class="{'primary': primaryReviewButton(), 'secondary': !primaryReviewButton()}" t-on-click="switchPane">
<h1>Review</h1>
<button class="btn-switchpane btn btn-secondary flex-fill rounded-0 fw-bolder" t-att-class="{'primary': primaryReviewButton(), 'secondary': !primaryReviewButton()}" t-on-click="switchPane">
<span class="fs-1 d-block">Review</span>
<span><t t-esc="items_number"/> items</span>
</button>
</div>
@@ -2,7 +2,7 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.OrderReceipt" owl="1">
<div class="pos-receipt">
<div class="pos-receipt d-inline-block m-3 p-3 border rounded bg-view text-start overflow-hidden">
<t t-if="receipt.company.logo">
<img class="pos-receipt-logo" t-att-src="receipt.company.logo" alt="Logo"/>
<br/>
@@ -0,0 +1,48 @@
.pos-receipt-container {
direction: ltr;
}
.receipt-screen .default-view .pos-receipt-container {
flex: 0 1 400px;
}
@include media-breakpoint-down(md) {
.receipt-screen .default-view .pos-receipt-container {
flex: 1;
overflow: visible;
}
}
@page {
margin: 0;
}
@media print {
body {
background: white;
}
body * {
visibility: hidden;
}
.pos, .pos * {
position: static !important;
}
.pos .receipt-screen .pos-receipt-container {
position: absolute !important;
top: 0;
left: 0;
}
.pos .receipt-screen .pos-receipt-container, .pos .receipt-screen .pos-receipt-container * {
visibility: visible;
background: white !important;
color: black !important;
}
.pos .pos-receipt {
margin: 0 !important;
margin-left: auto !important;
margin-right: auto !important;
border: none !important;
font-size: 14px !important;
width: 266px !important;
}
}
@@ -3,80 +3,71 @@
<t t-name="point_of_sale.ReceiptScreen" owl="1">
<div class="receipt-screen screen">
<div class="screen-content">
<div class="top-content" t-if="!ui.isSmall">
<div class="top-content-center">
<h1>
<div class="screen-content d-flex flex-column justify-content-between h-100">
<div class="top-content d-flex align-items-center py-3 border-bottom text-center" t-if="!ui.isSmall">
<div class="top-content-center flex-grow-1">
<h1 class="mb-0">
<t t-esc="orderAmountPlusTip" />
</h1>
</div>
</div>
<div class="default-view">
<div class="actions">
<div class="default-view d-flex flex-md-row flex-column overflow-hidden">
<div class="actions flex-md-grow-1 flex-grow-0 flex-shrink-1 flex-basis-0 m-4">
<h1>Payment Successful</h1>
<div class="buttons">
<div class="button print" t-on-click="printReceipt">
<i class="fa fa-print" t-ref="order-print-receipt-button"></i> Print Receipt
</div>
<div class="buttons my-3">
<button class="button print btn btn-lg btn-secondary w-100 py-3" t-on-click="printReceipt">
<i class="fa fa-print ms-2" t-ref="order-print-receipt-button"></i> Print Receipt
</button>
</div>
<form t-on-submit.prevent="onSendEmail" class="send-email">
<div class="input-email">
<input type="email" t-attf-placeholder="Email: {{currentOrder.getEmailItems().join(', ')}}" t-model="orderUiState.inputEmail" />
<button class="send" t-att-class="{ highlight: isValidEmail() }" type="submit">
<form t-on-submit.prevent="onSendEmail" class="send-email d-flex">
<div class="input-email input-group">
<input type="email" class="form-control py-3 bg-view" t-attf-placeholder="Email: {{currentOrder.getEmailItems().join(', ')}}" t-model="orderUiState.inputEmail" />
<button class="send btn px-5" t-att-class="isValidEmail() ? 'highlight btn btn-primary' : 'btn-secondary disabled'" type="submit">
<i class="fa fa-paper-plane" aria-hidden="true" t-ref="order-mail-receipt-button" />
</button>
</div>
</form>
<div class="notice">
<div t-if="orderUiState.emailSuccessful !== null" t-attf-class="{{ orderUiState.emailSuccessful ? 'successful' : 'failed' }}">
<div class="notice mt-2">
<div t-if="orderUiState.emailSuccessful !== null" t-attf-class="{{ orderUiState.emailSuccessful ? 'successful text-success' : 'failed text-danger' }}">
<t t-esc="orderUiState.emailNotice"></t>
</div>
</div>
</div>
<div class="pos-receipt-container" t-ref="order-receipt">
<div class="pos-receipt-container bg-200 text-center overflow-auto" t-ref="order-receipt">
<OrderReceipt order="currentOrder"/>
</div>
</div>
<t t-set="splittedOrder" t-value="this.currentOrder.originalSplittedOrder and !this.currentOrder.originalSplittedOrder.finalized"/>
<div t-if="!ui.isSmall" class="validation-buttons">
<div class="button next validation" t-att-class="{ highlight: !locked }"
<div t-if="!ui.isSmall" class="validation-buttons d-flex w-100 gap-1 sticky-bottom">
<button class="button next validation btn btn-primary w-100 py-5 rounded-0 fs-2" t-att-class="{ highlight: !locked }"
t-if="!splittedOrder"
t-on-click="orderDone" name="done">
<div class="pay-circle">
<i class="oi oi-chevron-right" role="img" aria-label="Pay" title="Pay" />
</div>
<i class="oi oi-chevron-right" role="img" aria-label="Pay" title="Pay" />
New Order
</div>
<div t-if="isResumeVisible() and !splittedOrder" class="button next validation" t-att-class="{ highlight: !locked }"
</button>
<button t-if="isResumeVisible() and !splittedOrder" class="button next validation btn btn-primary w-100 py-5 rounded-0 fs-2" t-att-class="{ highlight: !locked }"
t-on-click="resumeOrder" name="resume">
<div class="pay-circle">
<i class="oi oi-chevron-right" role="img" aria-label="Pay" title="Pay" />
</div>
<i class="oi oi-chevron-right" role="img" aria-label="Pay" title="Pay" />
Resume Order
</div>
<div t-if="splittedOrder" class="button next validation" t-att-class="{ highlight: !locked }"
</button>
<button t-if="splittedOrder" class="button next validation btn btn-primary w-100 py-5 rounded-0 fs-2" t-att-class="{ highlight: !locked }"
t-on-click="continueSplitting" name="resume">
<div class="pay-circle">
<i class="fa fa-chevron-right" role="img" aria-label="Pay" title="Pay" />
</div>
Continue
</div>
</button>
</div>
<div t-else="" class="switchpane">
<div class="btn-switchpane validation-button" t-att-class="{ highlight: !locked }"
<div t-else="" class="switchpane d-flex">
<div class="btn-switchpane validation-button btn btn-primary flex-fill py-3 rounded-0 fw-bolder" t-att-class="{ highlight: !locked }"
t-if="!splittedOrder"
t-on-click="orderDone" name="done">
New Order
</div>
<div t-if="isResumeVisible() and !splittedOrder" class="btn-switchpane validation-button" t-att-class="{ highlight: !locked }"
<div t-if="isResumeVisible() and !splittedOrder" class="btn-switchpane validation-button btn btn-primary flex-fill py-3 rounded-0 fw-bolder" t-att-class="{ highlight: !locked }"
t-on-click="resumeOrder" name="resume">
Resume Order
</div>
<div t-if="splittedOrder" class="btn-switchpane validation-button" t-att-class="{ highlight: !locked }"
<div t-if="splittedOrder" class="btn-switchpane validation-button btn btn-primary flex-fill py-3 rounded-0 fw-bolder" t-att-class="{ highlight: !locked }"
t-on-click="continueSplitting" name="resume">
<div class="pay-circle">
<i class="fa fa-chevron-right" role="img" aria-label="Pay" title="Pay" />
</div>
Continue
</div>
</div>
@@ -3,19 +3,19 @@
<t t-name="point_of_sale.ReprintReceiptScreen" owl="1">
<div class="receipt-screen screen">
<div class="screen-content">
<div class="top-content">
<span class="button back" t-on-click="confirm">
<i class="fa fa-angle-double-left"></i>
<div class="screen-content d-flex flex-column h-100">
<div class="top-content d-flex align-items-center p-2 border-bottom text-center">
<button class="button back btn btn-lg btn-secondary" t-on-click="confirm">
<i class="fa fa-angle-double-left me-2"></i>
<span> </span>
<span>Back</span>
</span>
</button>
</div>
<div class="centered-content">
<div class="button print" t-on-click="tryReprint">
<i class="fa fa-print"></i> Print Receipt
</div>
<div class="pos-receipt-container" t-ref="order-receipt">
<div class="centered-content d-grid mx-autp mt-3 border-0 overflow-hidden">
<button class="button print btn btn-lg btn-primary" t-on-click="tryReprint">
<i class="fa fa-print ms-2"></i> Print Receipt
</button>
<div class="pos-receipt-container mt-2 bg-200 text-center overflow-y-auto" t-ref="order-receipt">
<OrderReceipt order="props.order"/>
</div>
</div>
@@ -0,0 +1,5 @@
.computed-price, .weight {
font-family: Inconsolata;
text-shadow: 0px 2px 0px rgb(210,210,210);
box-shadow: 0px 2px 0px rgb(225,225,225) inset;
}
@@ -3,34 +3,36 @@
<t t-name="point_of_sale.ScaleScreen" owl="1">
<div class="scale-screen screen">
<div class="screen-content">
<div class="top-content">
<span class="button back" t-on-click="back">
<i class="fa fa-angle-double-left"></i>
<div class="screen-content d-flex flex-column h-100">
<div class="top-content d-flex align-items-center p-2 border-bottom text-center">
<button class="button back btn btn-lg btn-secondary" t-on-click="back">
<i class="fa fa-angle-double-left me-2"></i>
Back
</span>
<h1 class="product-name">
</button>
<h1 class="product-name flex-grow-1 m-0">
<t t-esc="productName" />
</h1>
</div>
<div class="centered-content">
<div class="weight js-weight">
<div class="centered-content w-100 mx-auto mt-3 border-left border-right text-center overflow-x-hidden overflow-y-auto">
<div class="weight js-weight m-2 p-4 rounded bg-view text-center fs-1">
<t t-esc="productWeightString" />
</div>
<div class="product-price">
<t
t-esc="env.utils.formatCurrency(productPrice) + '/' + productUom" />
<div class="d-flex flex-row gap-2 m-2">
<div class="product-price d-flex align-items-center justify-content-center flex-grow-1 rounded text-bg-info bg-opacity-25 text-info fs-2">
<t
t-esc="env.utils.formatCurrency(productPrice) + '/' + productUom" />
</div>
<div class="computed-price flex-grow-1 p-3 bg-view rounded text-center fs-2 fw-bold">
<t t-esc="computedPriceString" />
</div>
</div>
<div class="computed-price">
<t t-esc="computedPriceString" />
</div>
<div class="buy-product" t-on-click="confirm">
<div class="buy-product btn btn-lg btn-primary d-flex align-items-center justify-content-center mx-2 mb-2 cursor-pointer" t-on-click="confirm">
Order
<i class="fa fa-angle-double-right"></i>
<i class="fa fa-angle-double-right ms-2"></i>
</div>
</div>
</div>
</div>
</t>
</templates>
</templates>
@@ -0,0 +1,39 @@
/* ********* The Screens ********* */
.pos .screen {
height: calc(100vh - #{$navbar-height});
background: $gray-100;
}
/* a) Generic Screen Layout Constructs */
.full-content{
top: 65px;
}
.left-content{
overflow-x: hidden;
overflow-y: auto;
border-right: solid 1px $gray-300;
flex-grow: 1;
min-width: 200px;
}
.center-content{
flex-grow: 2;
display: flex;
flex-direction: column;
}
.right-content {
flex-grow: 1;
min-width: 200px;
overflow-x: hidden;
overflow-y: auto;
border-left: solid 1px $gray-300;
}
@media screen and (min-width: 768px) {
.left-content {
max-width: 34%
}
}
@@ -2,11 +2,11 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.InvoiceButton" owl="1">
<div class="control-button" t-on-click="() => this.click()" t-ref="invoice-button">
<i class="fa fa-file-pdf-o"></i>
<button class="control-button btn btn-light rounded-0 fw-bolder" t-on-click="() => this.click()" t-ref="invoice-button">
<i class="fa fa-file-pdf-o me-1"></i>
<span> </span>
<span><t t-esc="commandName"></t></span>
</div>
</button>
</t>
</templates>
@@ -2,25 +2,25 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.OrderDetails" owl="1">
<div class="order-container">
<div t-ref="scrollable" class="order-scroller touch-scrollable">
<div class="order">
<div class="order-container h-100 overflow-y-auto bg-view">
<div t-ref="scrollable" class="order-scroller touch-scrollable h-100">
<div class="order d-flex flex-column h-100 text-start">
<t t-if="!props.order">
<div class="order-empty">
<i class="fa fa-shopping-cart" role="img" aria-label="Shopping cart"
<div class="order-empty d-flex flex-column align-items-center justify-content-center h-100 text-muted">
<i class="fa fa fa-4x fa-shopping-cart" role="img" aria-label="Shopping cart"
title="Shopping cart" />
<h1>Select an order</h1>
<h3 class="mt-2">Select an order</h3>
</div>
</t>
<t t-elif="orderlines.length === 0">
<div class="order-empty">
<i class="fa fa-shopping-cart" role="img" aria-label="Shopping cart"
<i class="fa fa fa-4x fa-shopping-cart" role="img" aria-label="Shopping cart"
title="Shopping cart" />
<h1>Order is empty</h1>
<h3 class="mt-2">Order is empty</h3>
</div>
</t>
<t t-else="">
<div class="header-note" t-att-class="{ highlight: props.highlightHeaderNote }">
<div class="header-note text-bg-view py-2 px-3 border-bottom" t-att-class="{ 'highlight text-danger': props.highlightHeaderNote }">
Select the product(s) to refund and set the quantity
</div>
<ul class="orderlines">
@@ -33,14 +33,14 @@
</t>
</ul>
<!-- Not using OrderSummary because we don't need it's overrides in this view. -->
<div class="summary clearfix">
<div class="summary clearfix sticky-top w-100 py-2 px-3 border-top text-end fw-bolder">
<div class="line">
<div class="entry total">
<span class="badge">Total: </span>
<div class="entry total fs-2">
Total:
<span class="value">
<t t-esc="total" />
</span>
<div t-if="tax" class="subentry">
<div t-if="tax" class="subentry fs-5 text-muted">
Taxes:
<span class="value">
<t t-esc="tax" />
@@ -2,14 +2,14 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.OrderlineDetails" owl="1">
<li class="orderline" t-on-click="() => this.props.onClickOrderline(props.line)" t-att-class="{ selected: props.isSelected }">
<span class="product-name">
<li class="orderline w-100 m-0 py-2 px-3 cursor-pointer" t-on-click="() => this.props.onClickOrderline(props.line)" t-att-class="{ selected: props.isSelected }">
<span class="product-name d-inline-block flex-grow-1 fw-bolder text-truncate">
<t t-esc="productName" />
</span>
<span class="price">
<span class="price fw-bold float-end">
<t t-esc="totalPrice" />
</span>
<ul class="info-list">
<ul class="info-list ms-2">
<li class="info">
<strong>
<t t-esc="quantity" />
@@ -17,13 +17,13 @@
<span><t t-esc="pricePerUnit" /></span>
</li>
<t t-if="customerNote">
<li class="info orderline-note">
<i class="fa fa-sticky-note" role="img" aria-label="Customer Note" title="Customer Note"/>
<li class="info orderline-note me-2 text-break">
<i class="fa fa-sticky-note me-1 text-muted" role="img" aria-label="Customer Note" title="Customer Note"/>
<t t-esc="customerNote" />
</li>
</t>
<t t-if="hasRefundedQty()">
<li class="info refund-note">
<li class="info refund-note mt-1">
<strong>
<t t-esc="getFormattedRefundedQty()" />
</strong>
@@ -31,11 +31,11 @@
</li>
</t>
<t t-if="hasToRefundQty()">
<li class="info to-refund-highlight refund-note">
<li class="info to-refund-highlight refund-note mt-1 fw-bold text-primary">
<t t-set="_destinationOrderUid" t-value="getToRefundDetail().destinationOrderUid"/>
<t t-if="_destinationOrderUid">
<t t-esc="getRefundingMessage()" />
<span t-esc="_destinationOrderUid" class="order-link" t-on-click.stop="() => this.props.onClickRefundOrderUid(_destinationOrderUid)" />
<span t-esc="_destinationOrderUid" class="order-link text-decoration-underline cursor-pointer" t-on-click.stop="() => this.props.onClickRefundOrderUid(_destinationOrderUid)" />
</t>
<t t-else="">
<t t-esc="getToRefundMessage()" />
@@ -2,11 +2,11 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.ReprintReceiptButton" owl="1">
<div class="control-button" t-on-click="() => this.click()">
<i class="fa fa-print"></i>
<button class="control-button btn btn-light rounded-0 fw-bolder" t-on-click="() => this.click()">
<i class="fa fa-print me-1"></i>
<span> </span>
<span>Print Receipt</span>
</div>
</button>
</t>
</templates>
@@ -2,9 +2,9 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.SearchBar" owl="1">
<div class="pos-search-bar">
<div class="search">
<span class="search-icon" t-if="!ui.isSmall"><i class="fa fa-search"></i></span>
<div class="pos-search-bar d-flex align-items-center flex-grow-1 h-100 ms-3 rounded">
<div class="search position-relative h-100 d-flex flex-grow-1">
<span class="search-icon" t-if="!ui.isSmall"><i class="oi oi-search"></i></span>
<input class="radius-left" t-ref="autofocus" t-att-class="{ 'radius-right': !props.config.filter.show }"
t-model="state.searchInput" t-on-keydown="onSearchInputKeydown" t-on-keyup="onSearchInputKeyup" type="text" t-att-placeholder="props.placeholder" />
<div t-if="state.showSearchFields and state.searchInput" class="fields">
@@ -24,7 +24,7 @@
</ul>
</div>
</div>
<div t-if="props.config.filter.show" class="filter radius-right"
<div t-if="props.config.filter.show" class="filter radius-right h-100"
t-on-click.stop="() => { state.showFilterOptions = !state.showFilterOptions }">
<span class="down-icon">
<i class="oi oi-chevron-down" aria-hidden="true"></i>
@@ -0,0 +1,231 @@
.left-pane {
max-width: 500px;
}
@media screen and (max-width: 640px) {
.screen-full-width {
flex-direction: column;
height: auto;
min-height: 100%;
}
.controls {
position: sticky;
top: 0;
background: $gray-300;
z-index: 100;
}
.ticket-screen .order,
.ticket-screen .leftpane {
max-width: inherit;
}
.ticket-screen .rightpane {
width: auto;
}
.pos .orders {
position: sticky;
}
}
.orders .col {
flex: 1;
&.very-narrow {
flex: 0.2;
}
&.narrow {
flex: 0.5;
}
&.wide {
flex: 1.5;
}
}
.order-row:hover {
color: white;
background-color: $primary !important;
}
@media screen and (min-width: 768px) {
.order-row {
display: flex;
cursor: pointer;
}
.order-row:nth-child(odd) {
background: $gray-200;
}
.order-row:nth-child(even) {
background: white;
}
}
@media screen and (max-width: 768px) {
.order-row {
margin: 5px;
border: 1px solid #C9CCD2; /* $border-color */
background: white; /* $o-view-background-color */
}
}
@media screen and (min-width: 768px) {
.pos .orders .col.end {
justify-content: flex-end;
}
}
@media screen and (max-width: 768px) {
.pos .orders .order-row > .col > div:first-child {
font-weight: bold;
}
.ticket-screen .order-row .delete-button {
margin-top: 5px;
justify-content: center;
background: #d23f3a /* o-text-color('danger') */ ;
color: white;
}
.ticket-screen .order-row .delete-button .fa-trash {
margin-right: 5px;
}
}
/* Search bar ticket screen */
.ticket-screen .pos-search-bar .search input {
width: 0;
color: #63717f;
padding-left: 40px;
border: solid 1px rgb(209, 209, 209);
flex: 1;
}
.ticket-screen .pos-search-bar .radius-right {
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}
.ticket-screen .pos-search-bar .radius-left {
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}
.ticket-screen .pos-search-bar .fa {
font-size: medium;
}
.ticket-screen .pos-search-bar .search input:focus {
outline: none;
}
.ticket-screen .pos-search-bar .search .search-icon {
position: absolute;
left: 15px;
top: 10px;
z-index: 1;
color: #4f5b66;
}
.ticket-screen .pos-search-bar .search .fields {
position: absolute;
top: calc(100% + 5px);
right: 2px;
left: 2px;
box-shadow: 1px 1px 3px grey;
overflow: auto;
}
.ticket-screen .pos-search-bar .search ul {
background: white;
font-size: small;
display: inline-block;
min-width: 100%;
}
.ticket-screen .pos-search-bar .search li {
margin: 0.2em 0;
padding-top: 0.2em;
padding-bottom: 0.2em;
padding-left: 35px;
text-align: left;
}
.ticket-screen .pos-search-bar .search li .term {
color: $primary;
}
.ticket-screen .pos-search-bar .search li:hover {
background: #DDD;
}
.ticket-screen .pos-search-bar .search li .field {
font-style: italic;
}
.ticket-screen .pos-search-bar .search li .term {
font-weight: bold;
}
.ticket-screen .pos-search-bar .search li.highlight {
background: #DDD;
}
.ticket-screen .pos-search-bar .filter .down-icon {
position: absolute;
right: 13px;
}
.ticket-screen .pos-search-bar .filter {
background: white;
padding-top: 1px;
padding-bottom: 1px;
padding-left: 15px;
padding-right: 40px;
border: solid 1px rgb(209, 209, 209);
border-left: none;
position: relative;
display: flex;
align-items: center;
}
.ticket-screen .pos-search-bar .filter:hover {
color: #868686;
}
.ticket-screen .pos-search-bar .filter .options {
display: block;
position: absolute;
top: calc(100% + 5px);
right: 0;
z-index: 1;
box-shadow: 1px 1px 5px grey;
padding: 0.5em 0;
background: white;
color: $gray-700;
}
.ticket-screen .pos-search-bar .filter ul.options li {
padding: 0.2em 1.2em;
border-top: none;
display: flex;
justify-content: start;
align-items: center;
}
.ticket-screen .filter ul.options li.indented {
text-indent: 1em;
}
.ticket-screen .pos-search-bar .filter ul.options li:hover {
background-color: #DDD;
}
@media screen and (max-width: 768px) {
.ticket-screen .pos-search-bar .search input {
padding-left: 10px;
}
}
@@ -3,12 +3,12 @@
<t t-name="point_of_sale.TicketScreen" owl="1">
<div class="ticket-screen screen" t-att-class="{ oe_hidden: !props.isShown }">
<div class="screen-full-width">
<div class="rightpane pane-border">
<div class="controls">
<div class="screen-full-width d-flex w-100 h-100">
<div class="rightpane pane-border d-flex flex-column flex-grow-1 w-100 h-50 h-md-100 bg-300 overflow-y-auto">
<div class="controls d-flex align-items-center justify-content-between p-3">
<t t-if="!ui.isSmall || !state.showSearchBar">
<div class="buttons">
<button class="discard" t-on-click="() => this.pos.closeScreen()">
<div class="buttons d-flex gap-2">
<button class="discard btn btn-lg btn-light" t-on-click="() => this.pos.closeScreen()">
<span class="search-icon">
<i class="fa fa-angle-double-left"/>
</span>
@@ -16,19 +16,19 @@
Back
</t>
</button>
<button t-if="allowNewOrders" class="highlight" t-on-click="() => this.onCreateNewOrder()">New Order</button>
<button t-if="allowNewOrders" class="highlight btn btn-lg btn-primary" t-on-click="() => this.onCreateNewOrder()">New Order</button>
</div>
</t>
<t t-if="ui.isSmall">
<t t-if="state.showSearchBar">
<button class="arrow-left" t-on-click="() => { state.showSearchBar = !state.showSearchBar }">
<button class="arrow-left btn btn-lg btn-light" t-on-click="() => { state.showSearchBar = !state.showSearchBar }">
<span class="search-icon">
<i class="fa fa-angle-double-left"/>
</span>
</button>
</t>
<t t-else="">
<button class="search" t-on-click="() => { state.showSearchBar = !state.showSearchBar }">
<button class="search btn btn-lg btn-light" t-on-click="() => { state.showSearchBar = !state.showSearchBar }">
<span class="search-icon">
<i class="fa fa-search"/>
</span>
@@ -55,61 +55,61 @@
</div>
</div>
</div>
<div class="orders">
<div class="orders overflow-y-auto">
<t t-set="_filteredOrderList" t-value="getFilteredOrderList()" />
<t t-if="_filteredOrderList.length !== 0">
<div class="header-row" t-att-class="{ oe_hidden: ui.isSmall }">
<div class="col wide">Date</div>
<div class="col wide">Receipt Number</div>
<div class="col">Customer</div>
<div class="col wide" t-if="showCardholderName()">Cardholder Name</div>
<div class="col">Cashier</div>
<div class="col end">Total</div>
<div class="col narrow">Status</div>
<div class="col very-narrow" name="delete"></div>
<div class="header-row d-flex text-bg-700 fw-bolder" t-att-class="{ oe_hidden: ui.isSmall }">
<div class="col wide p-2">Date</div>
<div class="col wide p-2">Receipt Number</div>
<div class="col p-2">Customer</div>
<div class="col wide p-2" t-if="showCardholderName()">Cardholder Name</div>
<div class="col p-2">Cashier</div>
<div class="col end p-2">Total</div>
<div class="col narrow p-2">Status</div>
<div class="col very-narrow p-2" name="delete"></div>
</div>
<t t-foreach="_filteredOrderList" t-as="order" t-key="order.cid">
<div class="order-row" t-att-class="{ highlight: isHighlighted(order) }" t-on-click="() => this.onClickOrder(order)">
<div class="col wide">
<div class="order-row" t-att-class="{ 'highlight text-bg-primary': isHighlighted(order) }" t-on-click="() => this.onClickOrder(order)">
<div class="col wide p-2 ">
<div><t t-esc="getDate(order)"></t></div>
<div t-if="ui.isSmall"><t t-esc="getTotal(order)"></t></div>
</div>
<div class="col wide">
<div class="col wide p-2">
<div><t t-esc="order.name"></t></div>
<div t-if="ui.isSmall"><t t-esc="getStatus(order)"></t></div>
</div>
<div class="col" t-if="!ui.isSmall">
<div class="col p-2" t-if="!ui.isSmall">
<div><t t-esc="getPartner(order)"></t></div>
</div>
<div t-if="showCardholderName() and !ui.isSmall" class="col">
<div t-if="showCardholderName() and !ui.isSmall" class="col p-2">
<div><t t-esc="getCardholderName(order)"></t></div>
</div>
<div class="col">
<div class="col p-2">
<div t-if="ui.isSmall"><t t-esc="getPartner(order)"></t></div>
<div t-att-class = "ui.isSmall ? 'cashier':''"><t t-esc="getCashier(order)"></t></div>
</div>
<div class="col end" t-if="!ui.isSmall">
<div class="col end p-2" t-if="!ui.isSmall">
<div><t t-esc="getTotal(order)"></t></div>
</div>
<div class="col narrow" t-if="!ui.isSmall">
<div class="col narrow p-2" t-if="!ui.isSmall">
<div><t t-esc="getStatus(order)"></t></div>
</div>
<div t-if="!shouldHideDeleteButton(order)" class="col very-narrow delete-button" name="delete" t-on-click.stop="() => this.onDeleteOrder(order)">
<div t-if="!shouldHideDeleteButton(order)" class="col very-narrow delete-button p-2" name="delete" t-on-click.stop="() => this.onDeleteOrder(order)">
<i class="fa fa-trash" aria-hidden="true"/><t t-if="ui.isSmall"> Delete</t>
</div>
<div t-else="" class="col very-narrow"></div>
<div t-else="" class="col very-narrow p-2"></div>
</div>
</t>
</t>
<t t-else="">
<div class="empty-order-list">
<i role="img" aria-label="Shopping cart" title="Shopping cart" class="fa fa-shopping-cart"></i>
<h1>No orders found</h1>
<div class="empty-order-list m-5 text-center text-muted">
<i role="img" aria-label="Shopping cart" title="Shopping cart" class="fa fa-4x fa-shopping-cart"></i>
<h3 class="mt-2">No orders found</h1>
</div>
</t>
</div>
</div>
<div class="leftpane">
<div class="leftpane d-flex flex-column flex-grow-1 w-100 h-50 h-md-100 bg-200">
<t t-set="_selectedSyncedOrder" t-value="getSelectedSyncedOrder()" />
<t t-set="_selectedOrderlineId" t-value="getSelectedOrderlineId()" />
<OrderDetails
@@ -118,14 +118,14 @@
order="_selectedSyncedOrder"
selectedOrderlineId="_selectedOrderlineId"
highlightHeaderNote="_state.ui.highlightHeaderNote" />
<div class="pads">
<div class="control-buttons">
<div class="pads border-top">
<div class="control-buttons d-flex flex-wrap border-bottom overflow-hidden">
<InvoiceButton
onInvoiceOrder.bind="onInvoiceOrder"
order="_selectedSyncedOrder" />
<ReprintReceiptButton order="_selectedSyncedOrder" />
</div>
<div class="subpads">
<div class="subpads d-flex">
<ActionpadWidget
partner="getSelectedPartner()"
actionName="constructor.numpadActionName"
@@ -2,25 +2,28 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.CashOpeningPopup" owl="1">
<div class="popup opening-cash-control">
<header class="title drag-handle">
OPENING CASH CONTROL
</header>
<main class="body">
<div class="opening-cash-section">
<span class="info-title">Opening cash</span>
<div class="cash-input-sub-section" t-on-input="handleInputChange">
<input class="pos-input" t-ref="cash-input" t-on-focus="ev=>ev.target.select()"/>
<div class="button icon" t-on-click="openDetailsPopup">
<i class="fa fa-money" role="img" title="Open the money details popup"/>
<div class="modal-header drag-handle">
<h4 class="modal-title">Opening Cash Control</h4>
</div>
<main class="modal-body">
<div class="opening-cash-section mb-3">
<span class="info-title d-flex flex-grow-1 align-items-start">Opening cash</span>
<div class="cash-input-sub-section input-group" t-on-input="handleInputChange">
<input class="pos-input form-control" type="number" t-model.number="state.openingCash" t-ref="cash-input" t-on-focus="ev=>ev.target.select()"/>
<div class="button icon btn btn-secondary" t-on-click="openDetailsPopup">
<i class="fa fa-money fa-2x" role="img" title="Open the money details popup"/>
</div>
</div>
</div>
<textarea placeholder="Add an opening note..." class="opening-cash-notes" t-model="state.notes"/>
</main>
<footer class="footer">
<div class="button dynamic-size" t-on-click="confirm">
Open session <span t-esc="env.utils.formatCurrency(state.openingCash)"/>
<div class="opening-notes-container d-flex flex-column align-items-start">
<label class="form-label" for="openingNotes">Opening note</label>
<textarea class="opening-notes form-control" id="openingNotes" rows="4" t-model="state.notes" placeholder="Add an opening note...">
<t t-esc="openingNotes"/>
</textarea>
</div>
</main>
<footer class="footer modal-footer">
<div class="button btn btn-lg btn-primary" t-on-click="confirm">Open session <span t-esc="env.utils.formatCurrency(state.openingCash)"/></div>
</footer>
</div>
</t>
@@ -70,7 +70,6 @@ const getProductImage = memoize(function getProductImage(productId, writeDate) {
});
export class PosStore extends Reactive {
hasBigScrollBars = false;
loadingSkipButtonIsShown = false;
mainScreen = { name: null, component: null };
tempScreen = null;
@@ -205,7 +204,6 @@ export class PosStore extends Reactive {
);
const { start_category, iface_start_categ_id } = this.config;
this.selectedCategoryId = (start_category && iface_start_categ_id?.[0]) || 0;
this.hasBigScrollBars = this.config.iface_big_scrollbars;
// Push orders in background, do not await
this.push_orders();
this.markReady();
@@ -3,21 +3,22 @@
<t t-name="point_of_sale.RadioProductAttribute" owl="1">
<div class="configurator_radio" t-ref="root">
<div class="table">
<div class="d-flex flex-column gap-3">
<t t-foreach="values" t-as="value" t-key="value.id">
<div class="attribute-name-cell">
<input type="radio" t-model="state.selected_value" t-att-name="attribute.id"
<div class="attribute-name-cell form-check">
<input class="form-check-input" type="radio" t-model="state.selected_value" t-att-name="attribute.id"
t-attf-id="{{ attribute.id }}_{{ value.id }}" t-att-value="value.id"/>
<span t-esc="value.name"/>
<label t-att-name="value.name" t-attf-for="{{ attribute.id }}_{{ value.id }}"/>
</div>
<div t-if="value.price_extra" class="price-extra-cell">
<span class="price_extra">
+ <t t-esc="env.utils.formatCurrency(value.price_extra)"/>
</span>
<label class="form-check-label" t-att-name="value.name" t-attf-for="{{ attribute.id }}_{{ value.id }}">
<span t-esc="value.name"/>
</label>
<div t-if="value.price_extra" class="price-extra-cell d-inline-block ms-3">
<span class="price_extra px-2 py-1 rounded-pill text-bg-info">
+ <t t-esc="env.utils.formatCurrency(value.price_extra)"/>
</span>
</div>
</div>
<div t-if="value.id == state.selected_value &amp;&amp; value.is_custom" class="custom-value-cell">
<input class="custom_value" type="text" t-model="state.custom_value"/>
<input class="custom_value form-control form-control-lg mt-2" type="text" t-model="state.custom_value"/>
</div>
</t>
</div>
@@ -28,7 +29,7 @@
<div>
<t t-set="is_custom" t-value="false"/>
<select class="configurator_select" t-model="state.selected_value">
<select class="configurator_select form-select form-select-lg" t-model="state.selected_value">
<option t-foreach="values" t-as="value" t-key="value.id" t-att-value="value.id">
<t t-set="is_custom" t-value="is_custom || (value.is_custom &amp;&amp; value.id == state.selected_value)"/>
<t t-esc="value.name"/>
@@ -38,7 +39,7 @@
</option>
</select>
<input class="custom_value" t-if="is_custom" type="text" t-model="state.custom_value"/>
<input class="custom_value form-control form-control-lg mt-2" t-if="is_custom" type="text" t-model="state.custom_value"/>
</div>
</t>
@@ -46,82 +47,79 @@
<div>
<t t-set="is_custom" t-value="false"/>
<ul class="color_attribute_list">
<ul class="color_attribute_list d-flex gap-3">
<li t-foreach="values" t-as="value" t-key="value.id" class="color_attribute_list_item">
<t t-set="is_custom" t-value="is_custom || (value.is_custom &amp;&amp; value.id == state.selected_value)"/>
<label t-attf-class="configurator_color {{ value.id == state.selected_value ? 'active' : '' }}"
<label t-attf-class="configurator_color rounded border {{ value.id == state.selected_value ? 'active border-3 border-primary' : 'border-3 border-secondary' }}"
t-attf-style="background-color: {{ value.html_color }};" t-att-data-color="value.name">
<input type="radio" t-model="state.selected_value" t-att-value="value.id" t-att-name="attribute.id"/>
<input class="m-4 opacity-0" type="radio" t-model="state.selected_value" t-att-value="value.id" t-att-name="attribute.id"/>
</label>
</li>
</ul>
<input class="custom_value" t-if="is_custom" type="text" t-model="state.custom_value"/>
<input class="custom_value form-control form-control-lg mt-2" t-if="is_custom" type="text" t-model="state.custom_value"/>
</div>
</t>
<t t-name="point_of_sale.ProductConfiguratorPopup" owl="1">
<t t-if="!ui.isSmall">
<div class="popup popup-text popup-med product-configurator-popup">
<header class="title">
<t t-esc="props.product.display_name" />
</header>
<div class="modal-header drag-handle">
<h4 class="modal-title"><t t-esc="props.product.display_name" /></h4>
</div>
<main class="body product_configurator_attributes col-lg-4 col-md-6 col-sm-12">
<div t-foreach="props.attributes" t-as="attribute" t-key="attribute.id" class="attribute">
<div class="attribute_name" t-esc="attribute.name"/>
<main class="body modal-body product_configurator_attributes m-0 text-start">
<div t-foreach="props.attributes" t-as="attribute" t-key="attribute.id" class="attribute mb-3">
<div class="attribute_name mb-2 fw-bolder" t-esc="attribute.name"/>
<RadioProductAttribute t-if="attribute.display_type === 'radio' or attribute.display_type === 'pills'" attribute="attribute"/>
<SelectProductAttribute t-elif="attribute.display_type === 'select'" attribute="attribute"/>
<ColorProductAttribute t-elif="attribute.display_type === 'color'" attribute="attribute"/>
</div>
</main>
<footer class="footer footer-flex">
<div class="button highlight confirm" t-on-click="confirm">
Add
</div>
<div class="button cancel" t-on-click="cancel">
<footer class="footer footer-flex modal-footer">
<div class="button cancel btn btn-lg btn-secondary" t-on-click="cancel">
Cancel
</div>
<div class="button highlight confirm btn btn-lg btn-primary" t-on-click="confirm">
Add
</div>
</footer>
</div>
</t>
<t t-else="">
<div class="popup mobile popup-text popup-med product-configurator-popup">
<main class="body mobile product_configurator_attributes col-lg-4 col-md-6 col-sm-12">
<div class="product-img">
<img t-att-src="imageUrl" t-att-alt="props.product.display_name" />
<div class="product-info">
<span><t t-esc="props.product.display_name" ></t></span>
<span><t t-esc="unitPrice" ></t></span>
</div>
<div class="modal-header">
<h4 class="modal-title"><t t-esc="props.product.display_name" /></h4>
<span><t t-esc="unitPrice" ></t></span>
</div>
<main class="body modal-body mobile product_configurator_attributes d-flex flex-column text-start">
<div class="product-img d-flex justify-content-center h-50 p-2" >
<img class="h-100" t-att-src="imageUrl" t-att-alt="props.product.display_name" />
</div>
<div t-foreach="props.attributes" t-as="attribute" t-key="attribute.id" class="attribute">
<div class="attribute_name" t-esc="attribute.name"/>
<div t-foreach="props.attributes" t-as="attribute" t-key="attribute.id" class="attribute mb-3">
<div class="attribute_name mb-2 fw-bolder" t-esc="attribute.name"/>
<RadioProductAttribute t-if="attribute.display_type === 'radio' or attribute.display_type === 'pills'" attribute="attribute"/>
<SelectProductAttribute t-elif="attribute.display_type === 'select'" attribute="attribute"/>
<ColorProductAttribute t-elif="attribute.display_type === 'color'" attribute="attribute"/>
</div>
</main>
<footer class="footer footer-flex mobile">
<div class="quantity-selector">
<button t-on-click="addOneQuantity">
<i class="fa fa-plus" aria-hidden="true"></i>
</button>
<span class="quantity"><t t-esc="state.quantity"></t></span>
<button t-on-click="removeOneQuantity">
<footer class="footer footer-flex mobile modal-footer">
<div class="quantity-selector d-flex align-items-center justify-content-center w-100 py-2 fw-bolder">
<button class="btn btn-lg btn-secondary" t-on-click="removeOneQuantity">
<i class="fa fa-minus" aria-hidden="true"></i>
</button>
<span class="quantity mx-3"><t t-esc="state.quantity"></t></span>
<button class="btn btn-lg btn-secondary" t-on-click="addOneQuantity">
<i class="fa fa-plus" aria-hidden="true"></i>
</button>
</div>
<div class="confirm-line">
<div class="button highlight confirm" t-on-click="confirm">
Add
</div>
<div class="button cancel" t-on-click="cancel">
Cancel
</div>
<div class="button highlight confirm btn btn-lg btn-primary" t-on-click="confirm">
Add
</div>
<div class="button cancel btn btn-lg btn-secondary" t-on-click="cancel">
Cancel
</div>
</footer>
</div>
@@ -2,12 +2,10 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.EditListInput" owl="1">
<div>
<input type="text" t-att-value="props.item.text" t-on-input="onInput" class="popup-input list-line-input"
placeholder="Serial/Lot Number" t-on-keyup="onKeyup" />
<i t-if="props.deletable" class="oe_link_icon fa fa-trash-o" role="img" aria-label="Remove" title="Remove"
t-on-click="props.removeItem"></i>
</div>
<input type="text" t-att-value="props.item.text" t-on-input="onInput" class="popup-input list-line-input form-control form-control-lg w-75 mx-auto"
placeholder="Serial/Lot Number" t-on-keyup="onKeyup" />
<i t-if="props.deletable" class="oe_link_icon fa fa-trash-o" role="img" aria-label="Remove" title="Remove"
t-on-click="props.removeItem"></i>
</t>
</templates>
@@ -3,25 +3,25 @@
<t t-name="point_of_sale.EditListPopup" owl="1">
<div class="popup popup-text edit-list-popup" t-ref="root">
<header class="title">
<t t-esc="props.title" />
<div class="modal-header">
<h4 class="modal-title title">
<t t-esc="props.title" />
</h4>
<span class="sub-title" t-esc="props.name"/>
</header>
<main>
<div class="list-lines">
<t t-foreach="state.array" t-as="item" t-key="item._id">
<EditListInput item="item" createNewItem.bind="createNewItem" removeItem="() => this.removeItem(item._id)"
deletable="_hasMoreThanOneItem()" onInputChange.bind="onInputChange" />
</t>
</div>
</div>
<main class="modal-body">
<t t-foreach="state.array" t-as="item" t-key="item._id">
<EditListInput item="item" createNewItem.bind="createNewItem" removeItem="() => this.removeItem(item._id)"
deletable="_hasMoreThanOneItem()" onInputChange.bind="onInputChange" />
</t>
</main>
<footer class="footer footer-flex">
<div class="button confirm highlight" t-on-click="confirm">
Ok
</div>
<div class="button cancel" t-on-click="cancel">
<footer class="footer footer-flex modal-footer">
<div class="button cancel btn btn-lg btn-secondary" t-on-click="cancel">
Cancel
</div>
<div class="button confirm highlight btn btn-lg btn-primary" t-on-click="confirm">
Ok
</div>
</footer>
</div>
</t>
@@ -3,17 +3,19 @@
<t t-name="point_of_sale.ConfirmPopup" owl="1">
<div class="popup popup-confirm">
<header class="title drag-handle">
<t t-esc="props.title" />
</header>
<main class="body">
<div class="modal-header">
<h4 class="modal-title title drag-handle">
<t t-esc="props.title" />
</h4>
</div>
<main class="modal-body">
<t t-esc=" props.body" />
</main>
<footer class="footer">
<div class="button confirm" t-on-click="confirm">
<footer class="footer modal-footer">
<div class="button confirm btn btn-lg btn-primary" t-on-click="confirm">
<t t-esc="props.confirmText" />
</div>
<div class="button cancel" t-on-click="cancel">
<div class="button cancel btn btn-lg btn-secondary" t-on-click="cancel">
<t t-esc="props.cancelText" />
</div>
</footer>
@@ -3,19 +3,21 @@
<t t-name="point_of_sale.DatePickerPopup" owl="1">
<div class="popup">
<header class="title">
<t t-esc="props.title" />
</header>
<main>
<input type="date" t-model="state.shippingDate" t-ref="input" t-att-min="_today()"/>
<div class="modal-header">
<h4 class="modal-title title">
<t t-esc="props.title" />
</h4>
</div>
<main class="modal-body">
<input class="form-control form-control-lg w-75 mx-auto" type="date" t-model="state.shippingDate" t-ref="input" t-att-min="_today()"/>
</main>
<div class="footer footer-flex">
<div class="button confirm highlight" t-on-click="confirm">
<t t-esc="props.confirmText" />
</div>
<div class="button cancel" t-on-click="cancel">
<div class="footer footer-flex modal-footer">
<div class="button cancel btn btn-lg btn-secondary" t-on-click="cancel">
<t t-esc="props.cancelText" />
</div>
<div class="button confirm highlight btn btn-lg btn-primary" t-on-click="confirm">
<t t-esc="props.confirmText" />
</div>
</div>
</div>
</t>
@@ -0,0 +1,5 @@
.popup-numpad .input-button,
.popup-numpad .mode-button {
height: 50px;
width: 50px;
}
@@ -2,67 +2,65 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.NumberPopup" owl="1">
<div class="popup popup-number" t-att-class="{ 'popup-password': props.isPassword }" t-if="!isMobile()">
<header class="title drag-handle">
<span><t t-esc="props.title"/></span>
<span t-if="props.subtitle" class="subtitle"><t t-esc="props.subtitle"/></span>
</header>
<div class="input-symbol">
<div class="popup-input value active">
<div class="popup popup-number w-auto" t-att-class="{ 'popup-password': props.isPassword }" t-if="!isMobile()">
<div class="modal-header drag-handle">
<h4 class="modal-title title"><t t-esc="props.title" /></h4>
<span t-if="props.subtitle" class="subtitle p-1"><t t-esc="props.subtitle"/></span>
</div>
<div class="input-symbol pt-3">
<div class="popup-input value active form-control form-control-lg w-75 mx-auto">
<span class="input-value" t-att-class="{ 'highlight': state.toStartOver }"><t t-esc="inputBuffer"/></span>
<span t-if="props.inputSuffix"><t t-esc="props.inputSuffix" /></span>
</div>
</div>
<div class="popup-numpad">
<div class="popup-numpad px-5 my-3">
<!-- FIXME POSREF do we really need to hardcode all of these? -->
<button class="input-button number-char" t-on-mousedown.prevent="() => this.sendInput('1')">1</button>
<button class="input-button number-char" t-on-mousedown.prevent="() => this.sendInput('2')">2</button>
<button class="input-button number-char" t-on-mousedown.prevent="() => this.sendInput('3')">3</button>
<button class="input-button number-char btn btn-secondary p-0 rounded-circle m-1" t-on-mousedown.prevent="() => this.sendInput('1')">1</button>
<button class="input-button number-char btn btn-secondary p-0 rounded-circle m-1" t-on-mousedown.prevent="() => this.sendInput('2')">2</button>
<button class="input-button number-char btn btn-secondary p-0 rounded-circle m-1" t-on-mousedown.prevent="() => this.sendInput('3')">3</button>
<t t-if="props.cheap">
<button class="mode-button add" t-on-mousedown.prevent="() => this.sendInput('+1')">+1</button>
<button class="mode-button add btn btn-secondary p-0 rounded-circle m-1" t-on-mousedown.prevent="() => this.sendInput('+1')">+1</button>
</t>
<t t-if="!props.cheap">
<button class="mode-button add" t-on-mousedown.prevent="() => this.sendInput('+10')">+10</button>
<button class="mode-button add btn btn-secondary p-0 rounded-circle m-1" t-on-mousedown.prevent="() => this.sendInput('+10')">+10</button>
</t>
<br />
<button class="input-button number-char" t-on-mousedown.prevent="() => this.sendInput('4')">4</button>
<button class="input-button number-char" t-on-mousedown.prevent="() => this.sendInput('5')">5</button>
<button class="input-button number-char" t-on-mousedown.prevent="() => this.sendInput('6')">6</button>
<button class="input-button number-char btn btn-secondary p-0 rounded-circle m-1" t-on-mousedown.prevent="() => this.sendInput('4')">4</button>
<button class="input-button number-char btn btn-secondary p-0 rounded-circle m-1" t-on-mousedown.prevent="() => this.sendInput('5')">5</button>
<button class="input-button number-char btn btn-secondary p-0 rounded-circle m-1" t-on-mousedown.prevent="() => this.sendInput('6')">6</button>
<t t-if="props.cheap">
<button class="mode-button add" t-on-mousedown.prevent="() => this.sendInput('+2')">+2</button>
<button class="mode-button add btn btn-secondary p-0 rounded-circle m-1" t-on-mousedown.prevent="() => this.sendInput('+2')">+2</button>
</t>
<t t-if="!props.cheap">
<button class="mode-button add" t-on-mousedown.prevent="() => this.sendInput('+20')">+20</button>
<button class="mode-button add btn btn-secondary p-0 rounded-circle m-1" t-on-mousedown.prevent="() => this.sendInput('+20')">+20</button>
</t>
<br />
<button class="input-button number-char" t-on-mousedown.prevent="() => this.sendInput('7')">7</button>
<button class="input-button number-char" t-on-mousedown.prevent="() => this.sendInput('8')">8</button>
<button class="input-button number-char" t-on-mousedown.prevent="() => this.sendInput('9')">9</button>
<button t-if="!props.isPassword" class="input-button number-char" t-on-mousedown.prevent="() => this.sendInput('-')">-</button>
<button class="input-button number-char btn btn-secondary p-0 rounded-circle m-1" t-on-mousedown.prevent="() => this.sendInput('7')">7</button>
<button class="input-button number-char btn btn-secondary p-0 rounded-circle m-1" t-on-mousedown.prevent="() => this.sendInput('8')">8</button>
<button class="input-button number-char btn btn-secondary p-0 rounded-circle m-1" t-on-mousedown.prevent="() => this.sendInput('9')">9</button>
<button t-if="!props.isPassword" class="input-button number-char btn btn-secondary p-0 rounded-circle m-1" t-on-mousedown.prevent="() => this.sendInput('-')">-</button>
<br />
<button class="input-button numpad-char" t-on-mousedown.prevent="() => this.sendInput('Delete')">C</button>
<button class="input-button number-char" t-on-mousedown.prevent="() => this.sendInput('0')">0</button>
<button class="input-button number-char dot" t-on-mousedown.prevent="() => this.sendInput(decimalSeparator)">
<button class="input-button numpad-char btn btn-secondary p-0 rounded-circle m-1" t-on-mousedown.prevent="() => this.sendInput('Delete')">C</button>
<button class="input-button number-char btn btn-secondary p-0 rounded-circle m-1" t-on-mousedown.prevent="() => this.sendInput('0')">0</button>
<button class="input-button number-char dot btn btn-secondary p-0 rounded-circle m-1" t-on-mousedown.prevent="() => this.sendInput(decimalSeparator)">
<t t-esc="decimalSeparator" /></button>
<button class="input-button numpad-backspace" t-on-mousedown.prevent="() => this.sendInput('Backspace')">
<button class="input-button numpad-backspace btn btn-secondary p-0 rounded-circle m-1" t-on-mousedown.prevent="() => this.sendInput('Backspace')">
<img style="pointer-events: none;"
src="/point_of_sale/static/src/img/backspace.png" width="24"
height="21" alt="Backspace" />
</button>
<br />
</div>
<footer class="footer centered">
<div class="button confirm highlight" t-on-mousedown.prevent="confirm">
<div class="flex-column">
<footer class="footer centered modal-footer">
<button class="button confirm highlight btn btn-lg btn-primary" t-on-mousedown.prevent="confirm">
<span><t t-esc="props.confirmText" /></span>
<span t-if="props.getInputBufferReminder(state.buffer)" class="input-buffer-reminder">
<span t-if="props.getInputBufferReminder(state.buffer)" class="input-buffer-reminder small">
(<t t-esc="props.getInputBufferReminder(state.buffer)" />)
</span>
</div>
</div>
<div class="button cancel" t-on-mousedown.prevent="cancel">
</button>
<button class="button cancel btn btn-lg btn-secondary" t-on-mousedown.prevent="cancel">
<t t-esc="props.cancelText" />
</div>
</button>
</footer>
</div>
<div class="popup" t-att-class="{ 'popup-password': props.isPassword }" t-else="">
@@ -72,13 +70,13 @@
<main>
<input type="text" t-model="state.payload" t-ref="input" class="value payment-input-number" inputmode="decimal">
</main>
<footer class="footer centered">
<div class="button confirm highlight" t-on-mousedown.prevent="confirm">
<footer class="footer centered modal-footer">
<button class="button confirm highlight btn btn-lg btn-primary" t-on-mousedown.prevent="confirm">
<t t-esc="props.confirmText" />
</div>
<div class="button cancel" t-on-mousedown.prevent="cancel">
</button>
<button class="button cancel btn btn-lg btn-secondary" t-on-mousedown.prevent="cancel">
<t t-esc="props.cancelText" />
</div>
</button>
</footer>
</div>
</t>
@@ -0,0 +1,3 @@
.popup-selection .selection {
max-height: 300px;
}
@@ -3,20 +3,23 @@
<t t-name="point_of_sale.SelectionPopup" owl="1">
<div class="popup popup-selection">
<header class="title drag-handle">
<t t-esc="props.title" />
</header>
<div class="selection scrollable-y">
<div class="modal-header drag-handle">
<h4 class="modal-title"><t t-esc="props.title" /></h4>
</div>
<div class="selection btn-group-vertical justify-content-start w-100 p-3 overflow-y-auto">
<t t-foreach="props.list" t-as="item" t-key="item.id">
<div class="selection-item" t-att-class="{ selected: item.isSelected }"
<button type="button" class="selection-item d-flex align-items-center justify-content-between btn btn-lg btn-outline-secondary w-100 p-3 text-start" t-att-class="{ 'selected text-bg-primary': item.isSelected }"
t-on-click="() => this.selectItem(item.id)">
<span t-esc="item.label" />
<span t-esc="item.description" t-if="item.description" />
</div>
<div class="d-flex flex-column">
<span t-esc="item.label" />
<span t-esc="item.description" t-if="item.description" />
</div>
<i class="oi oi-chevron-right"/>
</button>
</t>
</div>
<footer class="footer">
<div class="button cancel" t-on-click="cancel">
<footer class="footer modal-footer">
<div class="button cancel btn btn-lg btn-primary" t-on-click="cancel">
<t t-esc="props.cancelText" />
</div>
</footer>
@@ -3,20 +3,19 @@
<t t-name="point_of_sale.TextInputPopup" owl="1">
<div class="popup popup-textinput">
<header class="title">
<t t-esc="props.title" />
</header>
<main>
<p t-esc="props.body" />
<input type="text" t-model="state.inputValue" t-ref="input" t-att-placeholder="props.placeholder"/>
</main>
<div class="footer footer-flex">
<div class="button confirm highlight" t-on-click="confirm">
<t t-esc="props.confirmText" />
</div>
<div class="button cancel" t-on-click="cancel">
<div class="modal-header">
<h4 class="modal-title"><t t-esc="props.title" /></h4>
</div>
<div class="modal-body">
<input class="form-control form-control-lg w-75 mx-auto" type="text" t-model="state.inputValue" t-ref="input" t-att-placeholder="props.placeholder"/>
</div>
<div class="footer footer-flex modal-footer">
<div class="button cancel btn btn-lg btn-secondary" t-on-click="cancel">
<t t-esc="props.cancelText" />
</div>
<div class="button confirm highlight btn btn-lg btn-primary" t-on-click="confirm">
<t t-esc="props.confirmText" />
</div>
</div>
</div>
</t>
@@ -3,17 +3,22 @@
<t t-name="point_of_sale.TextAreaPopup" owl="1">
<div class="popup popup-textarea">
<header class="title drag-handle">
<t t-esc="props.title" />
</header>
<textarea t-model="state.inputValue" t-ref="input"></textarea>
<footer class="footer footer-flex">
<div class="button confirm highlight" t-on-click="confirm">
<t t-esc="props.confirmText" />
</div>
<div class="button cancel" t-on-click="cancel">
<div class="modal-header">
<h4 class="modal-title"><t t-esc="props.title" /></h4>
<!-- when modal fixed <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>-->
</div>
<div class="modal-body">
<textarea class="form-control" t-ref="input" placeholder="Add a note..." t-model="state.inputValue" rows="4" />
</div>
<footer class="footer footer-flex modal-footer">
<div class="button cancel btn btn-lg btn-secondary" t-on-click="cancel">
<t t-esc="props.cancelText" />
</div>
<div class="button confirm highlight btn btn-lg btn-primary" t-on-click="confirm">
<t t-esc="props.confirmText" />
</div>
</footer>
</div>
</t>
@@ -0,0 +1,3 @@
.money-details-value label {
width: 50px;
}
@@ -2,16 +2,16 @@
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.MoneyDetailsPopup" owl="1">
<div class="popup money-details">
<main class="body">
<div class="money-details-title">
Coins/Bills
</div>
<div class="money-details-info">
<div class="modal-header">
<h4 class="modal-title">Coins/Bills</h4>
</div>
<main class="modal-body">
<div class="money-details-info d-flex justify-content-between mb-2 oveflow-auto">
<div t-foreach="[firstHalfMoneyDetails, lastHalfMoneyDetails]" t-as="moneyDetailsList" t-key="moneyDetailsList_index">
<t t-foreach="moneyDetailsList" t-as="moneyValue" t-key="moneyValue">
<div class="money-details-value" t-on-input="updateMoneyDetailsAmount">
<input class="pos-input" t-att-id="moneyValue" type="number" t-model.number="state.moneyDetails[moneyValue]" t-on-focus="ev=>ev.target.select()"/>
<label class="oe_link_icon" t-att-for="moneyValue">
<div class="money-details-value d-flex align-items-center w-100 my-1" t-on-input="updateMoneyDetailsAmount">
<input class="pos-input form-control w-75" t-att-id="moneyValue" type="number" t-model.number="state.moneyDetails[moneyValue]" t-on-focus="ev=>ev.target.select()"/>
<label class="oe_link_icon w-25 text-end" t-att-for="moneyValue">
<t t-if="currency.position === 'before'"><t t-esc="currency.symbol"/>&nbsp;</t>
<t t-esc="moneyValue"/>
<t t-if="currency.position === 'after'">&nbsp;<t t-esc="currency.symbol"/></t>
@@ -20,14 +20,14 @@
</t>
</div>
</div>
<div class="total-section">
<span>Total </span>
<label t-esc="env.utils.formatCurrency(state.total)"/>
</div>
<h4 class="total-section rounded py-2">
Total
<t t-esc="env.utils.formatCurrency(state.total)"/>
</h4>
</main>
<footer class="footer footer-flex">
<div class="button highlight" t-on-click="confirm">Confirm</div>
<div class="button" t-on-click="cancel">Discard</div>
<footer class="footer footer-flex modal-footer">
<div class="button btn btn-lg btn-secondary" t-on-click="cancel">Discard</div>
<div class="button highlight btn btn-lg btn-primary" t-on-click="confirm">Confirm</div>
</footer>
</div>
</t>

Some files were not shown because too many files have changed in this diff Show More