[REF] pos*: refactor pos screens
pos*: point_of_sale,pos_hr,pos_loyalty,pos_restaurant, pos_sale, In this commit we refactor some parts of the pos ui with the interest of providing a simpler and cleaner codebase. One pattern that is heavily used in the pos ui is that in which views are changed based on the screen size at the component level. This means that it becomes very difficult to understand what parts of a specific template will be seen in mobile or in desktop views. In most cases, the pos adapts for mobile screens by using the same main components, but in different configurations. For example, a component that was visible in desktop mode, might become accesible through a menu in mobile mode. In order to improve the situation, the proposed solution is to separate the logical parts of the ui into `owl templates` and to inject this templates wherever the need arises. Thus, instead of constantly using `t-if="ui.isSmall"` in all places where there is a difference between the mobile and desktop views, we have a main conditional branch at the root level of the component representing a certain screen and then build 2 separate views, one for mobile and one for desktop, each calling the needed templates. In this commit we implement this pattern for the `payment_screen`. This will allow us to observe this pattern over a period of time, before implementing it on the other screens. In this commit we also: - add small fixes and improvements relating to the previous commit, in which the pos was refactored to bootstrap; - replace the `<br>` tags in the numpad component with the bootstrap grid system; - adapt the `cash_move_popup` to rely more on `t-model` and less on `t-on-change` Part-of: odoo/odoo#129544 Co-authored-by: vlst <vlst@odoo.com> Co-authored-by: Xavier Luyckx (xlu) <xlu@odoo.com>
This commit is contained in:
@@ -11,23 +11,24 @@
|
||||
<main class="modal-body body traceback">
|
||||
<t t-esc="props.body" />
|
||||
</main>
|
||||
<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 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">
|
||||
<div class="button icon download">
|
||||
<i class="fa fa-download" role="img"
|
||||
aria-label="Download error traceback"
|
||||
title="Download error traceback"></i>
|
||||
<footer class="footer modal-footer justify-content-between">
|
||||
<div class="d-flex gap-1">
|
||||
<a t-att-download="tracebackFilename" t-att-href="tracebackUrl">
|
||||
<div class="button icon download btn btn-lg btn-secondary">
|
||||
<i class="fa fa-download" role="img" aria-label="Download error traceback" title="Download error traceback"></i>
|
||||
</div>
|
||||
</a>
|
||||
<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>
|
||||
</div>
|
||||
<div class="d-flex gap-1">
|
||||
<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 btn btn-lg btn-secondary" t-on-click="() => this.pos.closePos()">
|
||||
<t t-esc="props.exitButtonText" />
|
||||
</div>
|
||||
</a>
|
||||
<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>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@@ -31,7 +31,7 @@ export class CashMovePopup extends AbstractAwaitablePopup {
|
||||
parsedAmount: 0,
|
||||
});
|
||||
this.amountInput = useAutofocus({ refName: "amountInput" });
|
||||
useValidateCashInput('amountInput');
|
||||
useValidateCashInput("amountInput");
|
||||
}
|
||||
async confirm() {
|
||||
let amount;
|
||||
@@ -117,13 +117,7 @@ export class CashMovePopup extends AbstractAwaitablePopup {
|
||||
this.state.errorMessage = "";
|
||||
this.amountInput.el.focus();
|
||||
}
|
||||
handleInputChange() {
|
||||
if (this.amountInput.el.classList.contains('invalid-cash-input')) {
|
||||
this.amountInput.el.parentElement.classList.add('invalid-cash-input');
|
||||
this.amountInput.el.style.border = 'none';
|
||||
return;
|
||||
}
|
||||
this.amountInput.el.parentElement.classList.remove('invalid-cash-input');
|
||||
this.state.parsedAmount = parseFloat(this.state.amount);
|
||||
format(value) {
|
||||
return this.env.utils.isValidFloat(value) ? this.env.utils.formatCurrency(parseFloat(value)) : "!!!";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,33 +7,21 @@
|
||||
<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' }">
|
||||
<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" >
|
||||
<div class="input-container popup-input align-items-center form-control d-flex">
|
||||
<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"/>
|
||||
<input class="input-field border-0 px-2" 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>
|
||||
<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>
|
||||
<textarea class="form-control" placeholder="Leave a reason here" name="reason" id="reason" t-model="state.reason" style="height:100px;" />
|
||||
<label for="reason">Reason</label>
|
||||
</div>
|
||||
<span t-if="state.errorMessage" class="error-message alert alert-danger mt-2">
|
||||
@@ -42,8 +30,8 @@
|
||||
</div>
|
||||
</main>
|
||||
<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 class="button confirm disable highlight dynamic-size btn btn-lg btn-primary" t-on-click="confirm">
|
||||
Confirm <span t-esc="format(state.amount)"/>
|
||||
</div>
|
||||
<div class="button cancel btn btn-lg btn-secondary" t-on-click="cancel">
|
||||
Discard
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
<tr class="text-dark">
|
||||
<th>Payment Method</th>
|
||||
<th>Expected</th>
|
||||
<th style="width: 20%">Counted</th>
|
||||
<th style="width: 20%">Difference</th>
|
||||
<th style="width:20%">Counted</th>
|
||||
<th>Difference</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<t t-if="cashControl">
|
||||
@@ -26,19 +26,20 @@
|
||||
<tr>
|
||||
<td t-esc="defaultCashDetails.name"/>
|
||||
<td t-esc="env.utils.formatCurrency(defaultCashDetails.amount)"/>
|
||||
<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()"/>
|
||||
<td class="input-group" t-on-input="(event) => this.handleInputChange(defaultCashDetails.id, event)">
|
||||
<input class="pos-input form-control text-end" 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 text-danger fw-bolder': 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 d-flex flex-column mb-2 border-start small text-muted">
|
||||
<tbody class="cash-overview border-start small text-muted">
|
||||
<tr>
|
||||
<td>Opening</td>
|
||||
<td t-esc="env.utils.formatCurrency(defaultCashDetails.opening)"/>
|
||||
<td class="align-top" t-esc="env.utils.formatCurrency(defaultCashDetails.opening)"/>
|
||||
</tr>
|
||||
<tr t-foreach="defaultCashDetails.moves" t-as="move" t-key="move_index">
|
||||
<td>
|
||||
@@ -47,7 +48,7 @@
|
||||
<t t-esc="move.name"/>
|
||||
</div>
|
||||
</td>
|
||||
<td t-esc="env.utils.formatCurrency(Math.abs(move.amount))"/>
|
||||
<td class="align-top" t-esc="env.utils.formatCurrency(Math.abs(move.amount))"/>
|
||||
</tr>
|
||||
<tr t-if="defaultCashDetails.payment_amount">
|
||||
<td>
|
||||
@@ -56,7 +57,7 @@
|
||||
Payments in <t t-esc="defaultCashDetails.name"/>
|
||||
</div>
|
||||
</td>
|
||||
<td t-esc="env.utils.formatCurrency(Math.abs(defaultCashDetails.payment_amount))"/>
|
||||
<td class="align-top" t-esc="env.utils.formatCurrency(Math.abs(defaultCashDetails.payment_amount))"/>
|
||||
</tr>
|
||||
</tbody>
|
||||
</t>
|
||||
@@ -73,22 +74,26 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<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" >
|
||||
<div class="notes-container d-flex flex-column flex-sm-row gap-3 border-top mt-3 pt-3">
|
||||
<div class="opening-notes-container d-flex flex-column flex-grow-1 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 d-flex flex-column align-items-start">
|
||||
<div class="closing-notes-container d-flex flex-column flex-grow-1 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 modal-footer justify-content-between">
|
||||
<!-- Download Sale Details -->
|
||||
<div class="modal-footer-left d-flex gap-2">
|
||||
<button class="button highlight btn btn-lg btn-primary" t-on-click="confirm">Close Session</button>
|
||||
<button class="button btn btn-lg btn-secondary" t-att-class="{'disabled': !canCancel()}" t-on-click="cancel">Discard</button>
|
||||
</div>
|
||||
<!-- Download Sale Details -->
|
||||
<div class="modal-footer-right 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">
|
||||
@@ -100,10 +105,6 @@
|
||||
<SaleDetailsButton/>
|
||||
</button>
|
||||
</div>
|
||||
<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>
|
||||
</t>
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
<t t-name="point_of_sale.Navbar" owl="1">
|
||||
<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 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 t-if="!ui.isSmall" class="pos-branding d-flex justify-content-start flex-grow-1 h-100 p-0 my-0 text-start">
|
||||
<img class="pos-logo h-75 ms-3 me-auto align-self-center" t-on-click="() => debug.toggleWidget()" src="/web/static/img/logo.png" alt="Logo" />
|
||||
</div>
|
||||
<div class="pos-rightheader d-flex flex-grow-1 h-100">
|
||||
<BackButton t-if="showBackButton()"/>
|
||||
@@ -51,8 +51,10 @@
|
||||
Switch Product View
|
||||
</a>
|
||||
</li>
|
||||
<li t-if="this.env.debug" t-on-click="() => debug.toggleWidget()" class="menu-item navbar-button">
|
||||
<span>Debug Window</span>
|
||||
<li t-if="this.env.debug" class="menu-item navbar-button" t-on-click="() => debug.toggleWidget()">
|
||||
<a class="dropdown-item py-2">
|
||||
Debug Window
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@@ -1,19 +1,3 @@
|
||||
/* --- 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;
|
||||
@@ -27,10 +11,3 @@ div.modal.in {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
/* @media screen and (max-width: 768px) {
|
||||
.ui-dialog, .modal-dialog {
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
} */
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
<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 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">
|
||||
<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>
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
text-shadow: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,15 +31,19 @@
|
||||
text-decoration: none;
|
||||
color: $gray-700;
|
||||
}
|
||||
|
||||
.pos ul, .pos ol {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.pos li {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
/* --- Generic Restyling and Resets --- */
|
||||
|
||||
html {
|
||||
@@ -71,27 +74,8 @@ td {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
.pos input::-webkit-outer-spin-button,
|
||||
.pos input::-webkit-inner-spin-button {
|
||||
input::-webkit-outer-spin-button,
|
||||
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,25 +2,17 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="point_of_sale.Chrome" owl="1">
|
||||
<div class="pos">
|
||||
<div class="pos vh-100 d-flex flex-column">
|
||||
<div class="pos-receipt-print"></div>
|
||||
<Navbar />
|
||||
<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) -->
|
||||
<t isShown="!pos.tempScreen" t-component="pos.mainScreen.component"
|
||||
t-props="pos.mainScreen.props"/>
|
||||
<ErrorHandler onError.bind="onTempScreenError">
|
||||
<t t-if="pos.tempScreen" t-component="pos.tempScreen.component"
|
||||
t-props="pos.tempScreen.props" t-key="pos.tempScreen.name" />
|
||||
</ErrorHandler>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pos-content w-100 h-100 bg-200 overflow-auto">
|
||||
<!-- 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) -->
|
||||
<t isShown="!pos.tempScreen" t-component="pos.mainScreen.component"
|
||||
t-props="pos.mainScreen.props"/>
|
||||
<ErrorHandler onError.bind="onTempScreenError">
|
||||
<t t-if="pos.tempScreen" t-component="pos.tempScreen.component"
|
||||
t-props="pos.tempScreen.props" t-key="pos.tempScreen.name" />
|
||||
</ErrorHandler>
|
||||
</div>
|
||||
</div>
|
||||
<MainComponentsContainer/>
|
||||
|
||||
+29
-54
@@ -1,34 +1,29 @@
|
||||
<?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 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" />
|
||||
<section class="partner-details edit d-flex flex-column m-3">
|
||||
<div class="container">
|
||||
<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 form-control form-control-lg" name="name" t-att-value="props.partner.name or ''" placeholder="Name" t-on-change="captureChange" />
|
||||
</div>
|
||||
<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">
|
||||
<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">
|
||||
<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">
|
||||
<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">
|
||||
<div class="partner-details-box row row-cols-1 row-cols-sm-2 gy-3 mt-3">
|
||||
<t t-foreach="['Street', 'City', 'Zip', 'Email', 'Phone', 'Mobile', 'Barcode' ]" t-as="item" t-key="item">
|
||||
<div class="partner-detail col">
|
||||
<label class="form-label label" t-attf-for="{{item}}" t-esc="item"/>
|
||||
<input class="detail form-control" t-attf-id="{{item}}" t-attf-name="{{item}}" t-model="changes[item.toLowerCase()]" t-attf-placeholder="{{item}}" t-att-class="{'border-danger': missingFields.includes(item.toLowerCase())}" />
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<div class="partner-detail col">
|
||||
<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>
|
||||
@@ -39,7 +34,7 @@
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
<div class="partner-detail">
|
||||
<div class="partner-detail col">
|
||||
<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>
|
||||
@@ -50,9 +45,7 @@
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="partner-details-right d-flex flex-column gap-3 w-100">
|
||||
<div class="partner-detail">
|
||||
<div class="partner-detail col">
|
||||
<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">
|
||||
@@ -62,29 +55,7 @@
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
<div class="partner-detail">
|
||||
<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">
|
||||
<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">
|
||||
<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">
|
||||
<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">
|
||||
<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">
|
||||
<div t-if="pos.pricelists.length gt 1" class="partner-detail col">
|
||||
<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">
|
||||
@@ -94,6 +65,10 @@
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
<div class="partner-detail col">
|
||||
<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>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ export class PartnerLine extends Component {
|
||||
static template = "point_of_sale.PartnerLine";
|
||||
|
||||
get highlight() {
|
||||
return this._isPartnerSelected ? "highlight text-bg-primary" : "";
|
||||
return this._isPartnerSelected ? "highlight active" : "";
|
||||
}
|
||||
get _isPartnerSelected() {
|
||||
return this.props.partner === this.props.selectedPartner;
|
||||
|
||||
+4
-5
@@ -1,3 +1,7 @@
|
||||
.partner-line.active {
|
||||
background-color: $o-component-active-bg;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(lg) {
|
||||
|
||||
.partner-line {
|
||||
@@ -21,11 +25,6 @@
|
||||
.partner-line .pos-right-align{
|
||||
text-align: left ;
|
||||
}
|
||||
.partner-line-email {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
.edit-partner-button-cell {
|
||||
order: 4;
|
||||
}
|
||||
|
||||
+11
-11
@@ -5,15 +5,15 @@
|
||||
<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>
|
||||
<b>
|
||||
<t t-esc="props.partner.name or ''" />
|
||||
</div>
|
||||
</b>
|
||||
<div class="company-field text-bg-muted">
|
||||
<t t-esc="props.partner.parent_name or ''" />
|
||||
</div>
|
||||
<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>
|
||||
<span> Unselect </span>
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
@@ -21,23 +21,23 @@
|
||||
<t t-esc="props.partner.address" />
|
||||
</div>
|
||||
</td>
|
||||
<td class="partner-line-email" >
|
||||
<div t-if="props.partner.phone">
|
||||
<td class="partner-line-email ">
|
||||
<div class="mb-2" t-if="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 me-2"/><t t-esc="props.partner.mobile"/>
|
||||
<div class="mb-2" t-if="props.partner.mobile">
|
||||
<i class="fa fa-fw fa-mobile me-2"/><t t-esc="props.partner.mobile"/>
|
||||
</div>
|
||||
<div t-if="props.partner.email" class="email-field">
|
||||
<div t-if="props.partner.email" class="email-field mb-2">
|
||||
<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 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">
|
||||
<button class="edit-partner-button btn btn-light border" 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-light border ms-2">
|
||||
<i class="fa fa-times"></i>
|
||||
<span> unselect </span>
|
||||
<span> UNSELECT </span>
|
||||
</button>
|
||||
</td>
|
||||
<td class="partner-line-last-column-placeholder oe_invisible"></td>
|
||||
|
||||
@@ -7,11 +7,10 @@
|
||||
&.partner-line {
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background: rgba($primary, 0.2);
|
||||
background: $o-gray-200;
|
||||
}
|
||||
&:active {
|
||||
background-color: $primary;
|
||||
color: white;
|
||||
background-color: $o-component-active-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,87 +2,78 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="point_of_sale.PartnerListScreen" owl="1">
|
||||
<div class="partnerlist-screen screen">
|
||||
<div class="screen-content">
|
||||
<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 class="partnerlist-screen screen h-100 d-flex flex-column bg-100">
|
||||
<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>
|
||||
<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>
|
||||
</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 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>
|
||||
<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>
|
||||
</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 t-if="state.detailIsShown && 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="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 && 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 class="search-bar-container sb-partner d-flex align-items-center w-100 w-md-70 w-lg-50 h-100 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>
|
||||
<section class="full-content position-absolute start-0 end-0 bottom-0 overflow-auto">
|
||||
<div class="partner-window">
|
||||
<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 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 table-striped w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<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">
|
||||
<t t-foreach="partners" t-as="partner"
|
||||
t-key="partner.id">
|
||||
<PartnerLine partner="partner"
|
||||
selectedPartner="state.selectedPartner"
|
||||
detailIsShown="state.detailIsShown"
|
||||
isBalanceDisplayed="isBalanceDisplayed"
|
||||
onClickEdit.bind="editPartner"
|
||||
onClickPartner.bind="clickPartner"/>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
<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>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<section class="overflow-auto">
|
||||
<t t-if="state.detailIsShown">
|
||||
<PartnerDetailsEdit
|
||||
t-props="state.editModeProps"
|
||||
saveChanges.bind="saveChanges"
|
||||
imperativeHandle="partnerEditor" />
|
||||
</t>
|
||||
<t t-else="">
|
||||
<table class="partner-list table table-striped w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<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">
|
||||
<t t-foreach="partners" t-as="partner"
|
||||
t-key="partner.id">
|
||||
<PartnerLine partner="partner"
|
||||
selectedPartner="state.selectedPartner"
|
||||
detailIsShown="state.detailIsShown"
|
||||
isBalanceDisplayed="isBalanceDisplayed"
|
||||
onClickEdit.bind="editPartner"
|
||||
onClickPartner.bind="clickPartner"/>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
<div t-if="state.query" class="search-more-button d-flex justify-content-center my-2">
|
||||
<button class="btn btn-lg btn-primary" t-on-click="_onPressEnterKey">Search more</button>
|
||||
</div>
|
||||
</t>
|
||||
</section>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
|
||||
+3
-3
@@ -2,14 +2,14 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="point_of_sale.PSNumpadInputButton" owl="1">
|
||||
<button t-attf-class="{{ _class }} btn btn-secondary w-100 rounded-0 py-3"
|
||||
<button t-attf-class="{{ _class }} btn btn-light w-100 rounded-0 py-3"
|
||||
t-on-click="() => this.numberBuffer.sendKey(props.value)">
|
||||
<t t-slot="default">
|
||||
<t t-if="props.text">
|
||||
<t t-esc="props.text" />
|
||||
<h1 t-esc="props.text" />
|
||||
</t>
|
||||
<t t-else="">
|
||||
<t t-esc="props.value" />
|
||||
<h1 t-esc="props.value" />
|
||||
</t>
|
||||
</t>
|
||||
</button>
|
||||
|
||||
+3
-3
@@ -3,7 +3,7 @@
|
||||
|
||||
<t t-name="point_of_sale.PaymentScreenPaymentLines" owl="1">
|
||||
<div class="paymentlines d-flex flex-column">
|
||||
<p class="title-category py-1 px-3 m-0 text-bg-300 fw-bolder">Summary</p>
|
||||
<h4 class="title-category pt-3 pb-2 px-3 m-0 bg-view border-bottom border-2">Summary</h4>
|
||||
<t t-foreach="props.paymentLines" t-as="line" t-key="line.cid">
|
||||
<t t-if="line.selected">
|
||||
<div class="paymentline selected d-flex align-items-center bg-200"
|
||||
@@ -34,7 +34,7 @@
|
||||
<div class="electronic_status">
|
||||
Payment request pending
|
||||
</div>
|
||||
<div class="button send_payment_request highlight" title="Send Payment Request" t-on-click="() => this.props.sendPaymentRequest(line)">
|
||||
<div class="button send_payment_request highlight text-bg-primary" title="Send Payment Request" t-on-click="() => this.props.sendPaymentRequest(line)">
|
||||
Send
|
||||
</div>
|
||||
</t>
|
||||
@@ -42,7 +42,7 @@
|
||||
<div class="electronic_status">
|
||||
Transaction cancelled
|
||||
</div>
|
||||
<div class="button send_payment_request highlight" title="Send Payment Request" t-on-click="() => this.props.sendPaymentRequest(line)">
|
||||
<div class="button send_payment_request highlight text-bg-primary" title="Send Payment Request" t-on-click="() => this.props.sendPaymentRequest(line)">
|
||||
Retry
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -1,52 +1,5 @@
|
||||
.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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,123 +2,121 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<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 d-flex flex-column h-100">
|
||||
<t t-if="!ui.isSmall">
|
||||
<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 flex-grow-1">
|
||||
<h2 class="mb-0">Payment</h2>
|
||||
<t t-if="ui.isSmall">
|
||||
<div class="payment-screen screen d-flex flex-column h-100 bg-100" t-att-class="{ oe_hidden: !props.isShown }">
|
||||
<PaymentScreenStatus order="currentOrder" />
|
||||
<t t-call="point_of_sale.PaymentScreenMethods" />
|
||||
<t t-call="point_of_sale.PaymentScreenButtons" />
|
||||
<t t-call="point_of_sale.PaymentScreenValidate" />
|
||||
</div>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="payment-screen screen d-flex flex-column h-100 bg-100" t-att-class="{ oe_hidden: !props.isShown }">
|
||||
<t t-call="point_of_sale.PaymentScreenTop" />
|
||||
<div class="main-content d-flex overflow-auto h-100">
|
||||
<div class="left-content d-flex flex-column w-25 bg-400">
|
||||
<t t-call="point_of_sale.PaymentScreenMethods" />
|
||||
<t t-call="point_of_sale.PaymentScreenValidate" />
|
||||
</div>
|
||||
<div class="center-content d-flex flex-column w-50 p-1 border-start border-end bg-300" >
|
||||
<PaymentScreenStatus order="currentOrder" />
|
||||
<div class="payment-numpad flex-grow-1 d-flex flex-wrap">
|
||||
<PaymentScreenNumpad />
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<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 px-4 pt-0">
|
||||
<PaymentScreenStatus order="currentOrder" />
|
||||
</section>
|
||||
</t>
|
||||
<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 btn btn-light rounded-0 fw-bolder"
|
||||
t-on-click="() => this.addNewPaymentLine(paymentMethod)">
|
||||
<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>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
<PaymentScreenPaymentLines
|
||||
paymentLines="paymentLines"
|
||||
deleteLine.bind="deletePaymentLine"
|
||||
sendForceDone.bind="sendForceDone"
|
||||
sendPaymentReverse.bind="sendPaymentReverse"
|
||||
sendPaymentCancel.bind="sendPaymentCancel"
|
||||
sendPaymentRequest.bind="sendPaymentRequest"
|
||||
selectLine.bind="selectPaymentLine"
|
||||
updateSelectedPaymentline.bind="updateSelectedPaymentline"/>
|
||||
</div>
|
||||
<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 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 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 d-flex flex-wrap">
|
||||
<section class="payment-numpad flex-grow-1">
|
||||
<PaymentScreenNumpad />
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-content">
|
||||
<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">
|
||||
<t t-esc="partner.name" />
|
||||
</t>
|
||||
<t t-if="!partner">
|
||||
Customer
|
||||
</t>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<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 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 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 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 me-2" /> Ship Later
|
||||
<span t-if="currentOrder.getShippingDate()">
|
||||
(<t t-esc="currentOrder.getShippingDate()"/>)
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-content w-25 bg-400">
|
||||
<t t-call="point_of_sale.PaymentScreenButtons" />
|
||||
</div>
|
||||
</div>
|
||||
<t t-if="ui.isSmall">
|
||||
<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>
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
|
||||
<t t-name="point_of_sale.PaymentScreenTop" owl="1">
|
||||
<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" />
|
||||
<span class="back_text">Back</span>
|
||||
</div>
|
||||
<div class="top-content-center flex-grow-1">
|
||||
<h2 class="mb-0">Payment</h2>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="point_of_sale.PaymentScreenValidate" owl="1">
|
||||
<t t-if="ui.isSmall">
|
||||
<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>
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div 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 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>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
|
||||
<t t-name="point_of_sale.PaymentScreenButtons" owl="1">
|
||||
<div class="payment-buttons d-flex flex-column flex-wrap">
|
||||
<button class="button partner-button btn btn-light py-3 text-start rounded-0 border-bottom"
|
||||
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" />
|
||||
<span class="partner-name" t-esc="currentOrder.get_partner()?.name or 'Customer'" />
|
||||
</button>
|
||||
<button class="button js_invoice btn btn-light py-3 text-start rounded-0 border-bottom" t-att-class="{ 'highlight text-bg-primary': currentOrder.is_to_invoice() }"
|
||||
t-on-click="toggleIsToInvoice">
|
||||
<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 border-bottom" t-att-class="{ 'highlight text-bg-primary': currentOrder.get_tip() }"
|
||||
t-on-click="addTip">
|
||||
<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 border-bottom"
|
||||
t-on-click="openCashbox">
|
||||
<i class="fa fa-archive me-2" />Open Cashbox
|
||||
</button>
|
||||
<button t-if="pos.config.ship_later" class="button btn btn-light py-3 text-start rounded-0 border-bottom" t-att-class="{ 'highlight text-bg-primary': currentOrder.getShippingDate() }"
|
||||
t-on-click="toggleShippingDatePicker">
|
||||
<i class="fa fa-clock-o me-2" />Ship Later
|
||||
<span t-if="currentOrder.getShippingDate()">
|
||||
(<t t-esc="currentOrder.getShippingDate()" />)
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
|
||||
<t t-name="point_of_sale.PaymentScreenMethods" owl="1">
|
||||
<div class="paymentmethods-container overflow-y-auto flex-grow-1">
|
||||
<div class="paymentmethods d-flex flex-column">
|
||||
<h4 class="title-category pt-3 pb-2 px-3 m-0 bg-view border-bottom border-2">Payment method</h4>
|
||||
<t t-foreach="payment_methods_from_config" t-as="paymentMethod" t-key="paymentMethod.id">
|
||||
<div class="button paymentmethod btn btn-light rounded-0 border-bottom" t-on-click="() => this.addNewPaymentLine(paymentMethod)">
|
||||
<div class="payment-method-display d-flex align-items-center flex-grow-1">
|
||||
<div>
|
||||
<img class="payment-method-icon" t-att-src="paymentMethodImage(paymentMethod.id)" />
|
||||
</div>
|
||||
<span class="payment-name" t-esc="paymentMethod.name" />
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
<PaymentScreenPaymentLines paymentLines="paymentLines" deleteLine.bind="deletePaymentLine" sendForceDone.bind="sendForceDone" sendPaymentReverse.bind="sendPaymentReverse" sendPaymentCancel.bind="sendPaymentCancel" sendPaymentRequest.bind="sendPaymentRequest" selectLine.bind="selectPaymentLine" updateSelectedPaymentline.bind="updateSelectedPaymentline" />
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
|
||||
+36
-36
@@ -1,43 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="point_of_sale.PaymentScreenStatus" owl="1">
|
||||
<div t-if="props.order.get_paymentlines().length === 0" class="paymentlines-empty">
|
||||
<div class="total text-center py-2 text-success">
|
||||
<t t-esc="totalDueText" />
|
||||
</div>
|
||||
<div class="message text-center">
|
||||
<t t-name="point_of_sale.PaymentScreenStatus" owl="1">
|
||||
<section class="paymentlines-container bg-view p-3 mb-1 border-bottom">
|
||||
<div t-if="props.order.get_paymentlines().length === 0" class="paymentlines-empty">
|
||||
<div class="total text-center py-2 text-success">
|
||||
<t t-esc="totalDueText" />
|
||||
</div>
|
||||
<div class="message text-center">
|
||||
Please select a payment method.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div t-else="">
|
||||
<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 pe-2">Remaining</span>
|
||||
<span class="amount"
|
||||
t-att-class="{ 'highlight text-primary fw-bolder': props.order.get_due() > 0 }">
|
||||
<t t-esc="remainingText" />
|
||||
</span>
|
||||
<t t-else="">
|
||||
<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 pe-2">Remaining</span>
|
||||
<span class="amount" 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 py-2 fs-3 text-muted">
|
||||
<span class="label pe-2">Total Due</span>
|
||||
<span>
|
||||
<t t-esc="totalDueText" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="payment-status-change">
|
||||
<span class="label pe-2">Change</span>
|
||||
<span class="amount" t-att-class="{ 'highlight text-primary fw-bolder': props.order.get_change() > 0 }">
|
||||
<t t-esc="changeText" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="payment-status-change">
|
||||
<span class="label pe-2">Change</span>
|
||||
<span class="amount"
|
||||
t-att-class="{ 'highlight text-primary fw-bolder': props.order.get_change() > 0 }">
|
||||
<t t-esc="changeText" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</section>
|
||||
</t>
|
||||
</templates>
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="point_of_sale.ActionpadWidget" owl="1">
|
||||
<div class="actionpad d-flex flex-column flex-grow-1 w-50 p-0 border-end">
|
||||
<div class="actionpad d-flex flex-column flex-grow-1 mw-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>
|
||||
@@ -12,7 +12,7 @@
|
||||
<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">
|
||||
<div t-if="props.partner" class="text-truncate fw-bolder text-action">
|
||||
<t t-esc="props.partner.name" />
|
||||
</div>
|
||||
<div t-else="fw-bolder">
|
||||
|
||||
+1
-7
@@ -5,15 +5,9 @@
|
||||
|
||||
.control-button {
|
||||
flex: 1 1 29%;
|
||||
padding: $o-horizontal-padding * 0.75;
|
||||
padding: $o-horizontal-padding * 0.5;
|
||||
}
|
||||
|
||||
/* .control-button.highlight,
|
||||
.button.highlight {
|
||||
background: $primary !important;
|
||||
border-color: $primary !important;
|
||||
color: white !important;
|
||||
} */
|
||||
.control-button.altlight,
|
||||
.button.altlight {
|
||||
background: #7F82AC !important;
|
||||
|
||||
+2
-1
@@ -1,7 +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 btn btn-light rounded-0 fw-bolder" t-on-click="() => this.pos.selectPartner()">
|
||||
<div class="control-button customer-button btn rounded-0 fw-bolder" t-attf-class="{{this.partner ? 'btn-primary' : 'btn-light'}}"
|
||||
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="">
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="point_of_sale.SetPricelistButton" owl="1">
|
||||
<div class="control-button o_pricelist_button" t-on-click="() => this.click()">
|
||||
<i class="fa fa-th-list" role="img" aria-label="Price list" title="Price list" />
|
||||
<div class="control-button o_pricelist_button btn btn-light rounded-0 fw-bolder" t-on-click="() => this.click()">
|
||||
<i class="fa fa-th-list me-2" role="img" aria-label="Price list" title="Price list" />
|
||||
<t t-esc="currentPricelistName" />
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -1,43 +1,10 @@
|
||||
/* ********* The Numpad ********* */
|
||||
|
||||
.pos .numpad {
|
||||
.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 {
|
||||
.numpad .disabled-mode, .pos .numpad .disabled-mode:hover {
|
||||
background: $gray-200;
|
||||
color: $gray-500;
|
||||
cursor: not-allowed;
|
||||
|
||||
@@ -3,34 +3,34 @@
|
||||
|
||||
<t t-name="point_of_sale.NumpadWidget" owl="1">
|
||||
<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'}"
|
||||
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3 fw-bolder" t-on-click="() => this.sendInput('1')">1</button>
|
||||
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3 fw-bolder" t-on-click="() => this.sendInput('2')">2</button>
|
||||
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3 fw-bolder" t-on-click="() => this.sendInput('3')">3</button>
|
||||
<button class="mode-button btn btn-light w-100 rounded-0 py-3 fw-bolder" t-att-class="{'selected-mode active': props.activeMode === 'quantity'}"
|
||||
t-on-click="() => this.changeMode('quantity')">Qty</button>
|
||||
<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',
|
||||
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3 fw-bolder" t-on-click="() => this.sendInput('4')">4</button>
|
||||
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3 fw-bolder" t-on-click="() => this.sendInput('5')">5</button>
|
||||
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3 fw-bolder" t-on-click="() => this.sendInput('6')">6</button>
|
||||
<button class="mode-button btn btn-light w-100 rounded-0 py-3 fw-bolder text-nowrap" t-att-class="{
|
||||
'selected-mode active': props.activeMode === 'discount',
|
||||
'disabled-mode': !hasManualDiscount
|
||||
}"
|
||||
t-att-disabled="!hasManualDiscount"
|
||||
t-on-click="() => this.changeMode('discount')">% Disc</button>
|
||||
<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',
|
||||
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3 fw-bolder" t-on-click="() => this.sendInput('7')">7</button>
|
||||
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3 fw-bolder" t-on-click="() => this.sendInput('8')">8</button>
|
||||
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3 fw-bolder" t-on-click="() => this.sendInput('9')">9</button>
|
||||
<button class="mode-button btn btn-light w-100 rounded-0 py-3 fw-bolder" t-att-class="{
|
||||
'selected-mode active': props.activeMode === 'price',
|
||||
'disabled-mode': !hasPriceControlRights
|
||||
}" t-att-disabled="!hasPriceControlRights"
|
||||
t-on-click="() => this.changeMode('price')">Price</button>
|
||||
<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)">
|
||||
<button class="input-button numpad-minus btn btn-light w-100 rounded-0 py-3 fw-bolder" 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 fw-bolder" t-on-click="() => this.sendInput('0')">0</button>
|
||||
<button class="input-button number-char btn btn-light w-100 rounded-0 py-3 fw-bolder" t-on-click="() => this.sendInput(decimalSeparator)">
|
||||
<t t-esc="decimalSeparator" />
|
||||
</button>
|
||||
<button class="input-button numpad-backspace btn btn-light w-100 rounded-0 py-3" t-on-click="() => this.sendInput('Backspace')">
|
||||
<button class="input-button numpad-backspace btn btn-light w-100 rounded-0 py-3 fw-bolder" 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>
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
/* ********* The OrderWidget ********* */
|
||||
|
||||
|
||||
.pos .scrollable-y{
|
||||
overflow: hidden !important;
|
||||
overflow-y: auto !important;
|
||||
-webkit-overflow-scrolling: touch !important;
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<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 d-flex flex-column align-items-center justify-content-center h-100 text-muted">
|
||||
<div class="order-empty d-flex flex-column align-items-center justify-content-center h-100 bg-100 text-muted">
|
||||
<i class="fa fa-4x fa-shopping-cart" role="img" aria-label="Shopping cart"
|
||||
title="Shopping cart"/>
|
||||
<h3 class="mt-2">This order is empty</h3>
|
||||
|
||||
+3
-3
@@ -2,16 +2,16 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="point_of_sale.OrderSummary" owl="1">
|
||||
<div class="summary clearfix sticky-top w-100 py-2 px-3 border-top text-end fw-bolder">
|
||||
<div class="summary clearfix sticky-top w-100 py-2 px-3 bg-200 bg-opacity-50 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 fs-2">
|
||||
<div class="entry total fs-2 lh-sm">
|
||||
Total:
|
||||
<span class="value">
|
||||
<t t-esc="_total" />
|
||||
</span>
|
||||
<div t-if="_tax.hasTax" class="subentry fs-5 text-muted">
|
||||
<div t-if="_tax.hasTax" class="subentry fs-6 text-muted">
|
||||
Taxes:
|
||||
<span class="value">
|
||||
<t t-esc="_tax.displayAmount" />
|
||||
|
||||
+4
-11
@@ -1,17 +1,10 @@
|
||||
/* ********* 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;
|
||||
|
||||
&:hover {
|
||||
background-color: $o-gray-200;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background: rgba($primary, 0.2);
|
||||
transition: background 250ms ease-in-out;
|
||||
cursor: default;
|
||||
background-color: $o-component-active-bg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="point_of_sale.Orderline" owl="1">
|
||||
<li t-on-click="selectLine" class="orderline w-100 m-0 py-2 px-3 cursor-pointer" t-att-class="addedClasses">
|
||||
<li t-on-click="selectLine" class="orderline w-100 m-0 py-2 px-3 lh-sm cursor-pointer transition-base" 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()"/>
|
||||
<t t-if="props.line.get_product().tracking!=='none' && (pos.picking_type.use_create_lots || pos.picking_type.use_existing_lots)">
|
||||
@@ -24,12 +24,12 @@
|
||||
</t>
|
||||
</t>
|
||||
</span>
|
||||
<span class="price fw-bold float-end">
|
||||
<span class="price fw-bolder float-end">
|
||||
<t t-esc="env.utils.formatCurrency(props.line.get_display_price())"/>
|
||||
</span>
|
||||
<ul class="info-list ms-2">
|
||||
<ul class="info-list text-dark">
|
||||
<t t-if="props.line.get_quantity_str() !== '1' || props.line.selected ">
|
||||
<li class="info">
|
||||
<li class="info ms-2">
|
||||
<em class="fst-normal fw-bolder">
|
||||
<t t-esc="props.line.get_quantity_str()" />
|
||||
</em>
|
||||
@@ -50,7 +50,7 @@
|
||||
</li>
|
||||
</t>
|
||||
<t t-if="props.line.get_discount_str() !== '0'">
|
||||
<li class="info">
|
||||
<li class="info ms-2">
|
||||
With a
|
||||
<em>
|
||||
<t t-esc="props.line.get_discount_str()" />%
|
||||
@@ -59,8 +59,8 @@
|
||||
</li>
|
||||
</t>
|
||||
<t t-if="customerNote">
|
||||
<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"/>
|
||||
<li class="info orderline-note p-2 my-1 rounded text-bg-warning text-warning bg-opacity-25 text-break">
|
||||
<i class="fa fa-sticky-note me-1" role="img" aria-label="Customer Note" title="Customer Note"/>
|
||||
<t t-esc="customerNote" />
|
||||
</li>
|
||||
</t>
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
.product-img img {
|
||||
height: 100px;
|
||||
object-fit: contain;
|
||||
.product:hover {
|
||||
border: 1px solid $o-gray-100;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.product:active {
|
||||
box-shadow: 0 0 0 3px $primary;
|
||||
}
|
||||
.product-img img {
|
||||
height: 100px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.product-name {
|
||||
@@ -17,6 +15,12 @@
|
||||
-webkit-line-clamp: 2;
|
||||
|
||||
&.no-image {
|
||||
-webkit-line-clamp: 9;
|
||||
-webkit-line-clamp: 7;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.product:active {
|
||||
box-shadow: 0 0 0 2px $primary;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}} 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"
|
||||
<article t-attf-class="product position-relative btn btn-light d-flex flex-column align-items-stretch p-0 m-0 text-start cursor-pointer overflow-hidden transition-base" 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" class="w-100" t-att-src="imageUrl" t-att-alt="props.product.display_name" />
|
||||
</div>
|
||||
<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}}">
|
||||
<div class="product-content d-flex flex-column justify-content-between h-100 mx-2 py-1">
|
||||
<div t-attf-class="overflow-hidden lh-sm fw-bolder #{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 text-primary py-1 px-2 fw-bolder">
|
||||
<span class="price-tag text-primary py-1 fw-bolder">
|
||||
<t t-esc="price" />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
-3
@@ -1,6 +1,3 @@
|
||||
|
||||
/* ********* The product list ********* */
|
||||
|
||||
.product-list {
|
||||
grid-template-columns: repeat(auto-fill, minmax(122px, 1fr));
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="point_of_sale.ProductScreen" owl="1">
|
||||
<div class="product-screen screen" t-att-class="{ oe_hidden: !props.isShown }">
|
||||
<div class="product-screen screen h-100 bg-100" t-att-class="{ oe_hidden: !props.isShown }">
|
||||
<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'">
|
||||
<div class="leftpane pane-border d-flex flex-column flex-grow-1 w-100 h-100 border-end bg-200" t-if="!ui.isSmall || pos.mobile_pane === 'left'">
|
||||
<OrderWidget/>
|
||||
<div class="pads border-top">
|
||||
<div class="control-buttons d-flex flex-wrap border-bottom overflow-hidden">
|
||||
<div class="control-buttons d-flex flex-wrap border-bottom overflow-hidden bg-300">
|
||||
<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"/>
|
||||
@@ -33,13 +33,13 @@
|
||||
<span><t t-esc="selectedOrderlineQuantity"/> <t t-esc="selectedOrderlineDisplayName"/> <t t-esc="selectedOrderlineTotal"/></span>
|
||||
</div>
|
||||
<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()">
|
||||
<button class="btn-switchpane pay-button btn btn-primary w-50 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 btn btn-secondary flex-fill rounded-0 fw-bolder" t-att-class="{'primary': primaryReviewButton(), 'secondary': !primaryReviewButton()}" t-on-click="switchPane">
|
||||
<button class="btn-switchpane btn btn-secondary w-50 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>
|
||||
<span><t t-esc="items"/> items</span>
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="point_of_sale.ReceiptScreen" owl="1">
|
||||
<div class="receipt-screen screen">
|
||||
<div class="receipt-screen screen h-100 bg-100">
|
||||
<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">
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="point_of_sale.ReprintReceiptScreen" owl="1">
|
||||
<div class="receipt-screen screen">
|
||||
<div class="receipt-screen screen h-100 bg-100">
|
||||
<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">
|
||||
@@ -11,7 +11,7 @@
|
||||
<span>Back</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="centered-content d-grid mx-autp mt-3 border-0 overflow-hidden">
|
||||
<div class="centered-content d-grid mx-auto 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>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<t t-esc="productName" />
|
||||
</h1>
|
||||
</div>
|
||||
<div class="centered-content w-100 mx-auto mt-3 border-left border-right text-center overflow-x-hidden overflow-y-auto">
|
||||
<div class="centered-content w-100 mx-auto mt-3 border-start border-end 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>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
/* ********* 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%
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -23,7 +23,7 @@
|
||||
<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">
|
||||
<ul class="orderlines flex-grow-1 overflow-auto">
|
||||
<t t-foreach="orderlines" t-as="orderline" t-key="orderline.id">
|
||||
<OrderlineDetails
|
||||
onClickOrderline.bind="this.props.onClickOrderline"
|
||||
@@ -33,14 +33,14 @@
|
||||
</t>
|
||||
</ul>
|
||||
<!-- Not using OrderSummary because we don't need it's overrides in this view. -->
|
||||
<div class="summary clearfix sticky-top w-100 py-2 px-3 border-top text-end fw-bolder">
|
||||
<div class="summary clearfix sticky-top w-100 py-2 px-3 bg-200 bg-opacity-50 text-end fw-bolder">
|
||||
<div class="line">
|
||||
<div class="entry total fs-2">
|
||||
<div class="entry total fs-2 lh-sm">
|
||||
Total:
|
||||
<span class="value">
|
||||
<t t-esc="total" />
|
||||
</span>
|
||||
<div t-if="tax" class="subentry fs-5 text-muted">
|
||||
<div t-if="tax" class="subentry fs-6 text-muted">
|
||||
Taxes:
|
||||
<span class="value">
|
||||
<t t-esc="tax" />
|
||||
|
||||
+6
-6
@@ -2,23 +2,23 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="point_of_sale.OrderlineDetails" owl="1">
|
||||
<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 }">
|
||||
<li class="orderline w-100 m-0 py-2 px-3 lh-sm 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 fw-bold float-end">
|
||||
<span class="price fw-bolder float-end">
|
||||
<t t-esc="totalPrice" />
|
||||
</span>
|
||||
<ul class="info-list ms-2">
|
||||
<li class="info">
|
||||
<ul class="info-list">
|
||||
<li class="info ms-2">
|
||||
<strong>
|
||||
<t t-esc="quantity" />
|
||||
</strong>
|
||||
<span><t t-esc="pricePerUnit" /></span>
|
||||
</li>
|
||||
<t t-if="customerNote">
|
||||
<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"/>
|
||||
<li class="info orderline-note p-2 my-1 rounded text-bg-warning text-warning bg-opacity-25 text-break">
|
||||
<i class="fa fa-sticky-note me-1" role="img" aria-label="Customer Note" title="Customer Note"/>
|
||||
<t t-esc="customerNote" />
|
||||
</li>
|
||||
</t>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
.search-icon {
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.fields {
|
||||
position: absolute;
|
||||
top: calc(100% + 5px);
|
||||
right: 2px;
|
||||
left: 2px;
|
||||
box-shadow: 1px 1px 3px grey;
|
||||
overflow: auto;
|
||||
|
||||
li:hover, .highlight {
|
||||
background: #DDD;
|
||||
}
|
||||
}
|
||||
|
||||
.options {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: calc(100% + 5px);
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
|
||||
.indented {
|
||||
text-indent: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.search input {
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
+34
-34
@@ -4,42 +4,42 @@
|
||||
<t t-name="point_of_sale.SearchBar" owl="1">
|
||||
<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 }"
|
||||
<div class="input-group">
|
||||
<input class="form-control bg-view border-light ps-5" t-ref="autofocus"
|
||||
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">
|
||||
<ul>
|
||||
<t t-foreach="searchFieldsList" t-as="fieldName" t-key="fieldName">
|
||||
<li t-att-class="{ highlight: fieldName == searchFieldsList[state.selectedSearchFieldId] }"
|
||||
t-on-click="() => this._onClickSearchField(fieldName)">
|
||||
<span class="field">
|
||||
<t t-esc="props.config.searchFields.get(fieldName)"></t>
|
||||
</span>
|
||||
<span>: </span>
|
||||
<span class="term">
|
||||
<t t-esc="state.searchInput"></t>
|
||||
</span>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
<div t-if="state.showSearchFields and state.searchInput" class="fields">
|
||||
<ul class="py-1 bg-view small">
|
||||
<t t-foreach="searchFieldsList" t-as="fieldName" t-key="fieldName">
|
||||
<li class="ps-5 py-1 text-start"
|
||||
t-att-class="{ highlight: fieldName == searchFieldsList[state.selectedSearchFieldId] }"
|
||||
t-on-click="() => this._onClickSearchField(fieldName)">
|
||||
<span class="field">
|
||||
<t t-esc="props.config.searchFields.get(fieldName)"></t>
|
||||
</span>
|
||||
<span>: </span>
|
||||
<span class="term text-primary fw-bolder">
|
||||
<t t-esc="state.searchInput"></t>
|
||||
</span>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
</div>
|
||||
<div t-if="props.config.filter.show" class="filter radius-right btn btn-secondary py-2 dropdown-toggle h-100"
|
||||
t-on-click.stop="() => { state.showFilterOptions = !state.showFilterOptions }">
|
||||
<span>
|
||||
<t t-esc="props.config.filter.options.get(state.selectedFilter).text" />
|
||||
</span>
|
||||
<ul t-if="state.showFilterOptions" class="options dropdown-menu dropdown-menu-end py-1">
|
||||
<t t-foreach="filterOptionsList" t-as="key" t-key="key">
|
||||
<t t-set="_option" t-value="props.config.filter.options.get(key)" />
|
||||
<li class="dropdown-item" t-on-click="() => this._onSelectFilter(key)" t-att-class="{ 'indented': _option.indented }">
|
||||
<t t-esc="_option.text"></t>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
</span>
|
||||
<span>
|
||||
<t t-esc="props.config.filter.options.get(state.selectedFilter).text" />
|
||||
</span>
|
||||
<ul t-if="state.showFilterOptions" class="options">
|
||||
<t t-foreach="filterOptionsList" t-as="key" t-key="key">
|
||||
<t t-set="_option" t-value="props.config.filter.options.get(key)" />
|
||||
<li t-on-click="() => this._onSelectFilter(key)" t-att-class="{ indented: _option.indented }">
|
||||
<t t-esc="_option.text"></t>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
<span class="search-icon position-absolute start-0 top-50 translate-middle ms-4 z-3" t-if="!ui.isSmall"><i class="oi oi-search"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -2,6 +2,27 @@
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.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 (max-width: 640px) {
|
||||
.screen-full-width {
|
||||
flex-direction: column;
|
||||
@@ -28,27 +49,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
@@ -93,139 +93,3 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="point_of_sale.TicketScreen" owl="1">
|
||||
<div class="ticket-screen screen" t-att-class="{ oe_hidden: !props.isShown }">
|
||||
<div class="ticket-screen screen h-100 bg-100" t-att-class="{ oe_hidden: !props.isShown }">
|
||||
<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">
|
||||
<div class="controls d-flex align-items-center justify-content-between mt-1 mt-md-0 p-2 bg-400">
|
||||
<t t-if="!ui.isSmall || !state.showSearchBar">
|
||||
<div class="buttons d-flex gap-2">
|
||||
<button class="discard btn btn-lg btn-light" t-on-click="() => this.pos.closeScreen()">
|
||||
@@ -69,7 +69,7 @@
|
||||
<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 text-bg-primary': isHighlighted(order) }" t-on-click="() => this.onClickOrder(order)">
|
||||
<div class="order-row" t-att-class="{ 'highlight': 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>
|
||||
@@ -104,7 +104,7 @@
|
||||
<t t-else="">
|
||||
<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>
|
||||
<h3 class="mt-2">No orders found</h3>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
@@ -119,7 +119,7 @@
|
||||
selectedOrderlineId="_selectedOrderlineId"
|
||||
highlightHeaderNote="_state.ui.highlightHeaderNote" />
|
||||
<div class="pads border-top">
|
||||
<div class="control-buttons d-flex flex-wrap border-bottom overflow-hidden">
|
||||
<div class="control-buttons d-flex flex-wrap border-bottom overflow-hidden bg-300">
|
||||
<InvoiceButton
|
||||
onInvoiceOrder.bind="onInvoiceOrder"
|
||||
order="_selectedSyncedOrder" />
|
||||
|
||||
+9
-9
@@ -78,12 +78,12 @@
|
||||
</main>
|
||||
|
||||
<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>
|
||||
<div class="button cancel btn btn-lg btn-secondary" t-on-click="cancel">
|
||||
Cancel
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</t>
|
||||
@@ -106,6 +106,12 @@
|
||||
</main>
|
||||
|
||||
<footer class="footer footer-flex mobile modal-footer">
|
||||
<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>
|
||||
<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>
|
||||
@@ -115,12 +121,6 @@
|
||||
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||
</button>
|
||||
</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>
|
||||
</t>
|
||||
|
||||
+7
-4
@@ -2,10 +2,13 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="point_of_sale.EditListInput" owl="1">
|
||||
<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>
|
||||
<div class="input-group mb-3">
|
||||
<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" />
|
||||
<button t-if="props.deletable" class="btn btn-danger btn-lg" t-on-click="props.removeItem">
|
||||
<i class="oe_link_icon fa fa-trash-o" role="img" aria-label="Remove" title="Remove" />
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
</t>
|
||||
</main>
|
||||
<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>
|
||||
<div class="button cancel btn btn-lg btn-secondary" t-on-click="cancel">
|
||||
Cancel
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -2,14 +2,16 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="point_of_sale.AlertPopup" owl="1">
|
||||
<div class="popup">
|
||||
<p class="title">
|
||||
<t t-esc="props.title" />
|
||||
</p>
|
||||
<p class="body">
|
||||
<div class="modal-header">
|
||||
<h4 class="title modal-title">
|
||||
<t t-esc="props.title" />
|
||||
</h4>
|
||||
</div>
|
||||
<p class="modal-body">
|
||||
<t t-esc="props.body" />
|
||||
</p>
|
||||
<div class="footer">
|
||||
<div class="button cancel" t-on-click="confirm">
|
||||
<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>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { formatMonetary, formatFloat } from "@web/views/fields/formatters";
|
||||
import { roundDecimals } from "@web/core/utils/numbers";
|
||||
import { escapeRegExp } from '@web/core/utils/strings';
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
/**
|
||||
@@ -9,10 +10,21 @@ import { registry } from "@web/core/registry";
|
||||
* functions that are parameterized by the data in `pos` service.
|
||||
*/
|
||||
export const contextualUtilsService = {
|
||||
dependencies: ["pos"],
|
||||
start(env, { pos }) {
|
||||
dependencies: ["pos", "localization"],
|
||||
start(env, { pos, localization }) {
|
||||
const currency = pos.currency;
|
||||
const productUoMDecimals = pos.dp["Product Unit of Measure"];
|
||||
const decimalPoint = localization.decimalPoint;
|
||||
const thousandsSep = localization.thousandsSep;
|
||||
// Replace the thousands separator and decimal point with regex-escaped versions
|
||||
const escapedDecimalPoint = escapeRegExp(decimalPoint);
|
||||
let floatRegex;
|
||||
if (thousandsSep) {
|
||||
const escapedThousandsSep = escapeRegExp(thousandsSep);
|
||||
floatRegex = new RegExp(`^-?(?:\\d+(${escapedThousandsSep}\\d+)*)?(?:${escapedDecimalPoint}\\d*)?$`);
|
||||
} else {
|
||||
floatRegex = new RegExp(`^-?(?:\\d+)?(?:${escapedDecimalPoint}\\d*)?$`);
|
||||
}
|
||||
|
||||
const formatProductQty = (qty) => {
|
||||
return formatFloat(qty, { digits: [true, productUoMDecimals] });
|
||||
@@ -29,10 +41,15 @@ export const contextualUtilsService = {
|
||||
return roundDecimals(value, currency.decimal_places);
|
||||
};
|
||||
|
||||
const isValidFloat = (inputValue) => {
|
||||
return ![decimalPoint, "-"].includes(inputValue) && floatRegex.test(inputValue);
|
||||
};
|
||||
|
||||
env.utils = {
|
||||
formatCurrency,
|
||||
roundCurrency,
|
||||
formatProductQty,
|
||||
isValidFloat,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
<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 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 class="button cancel btn btn-lg btn-secondary" t-on-click="cancel">
|
||||
<t t-esc="props.cancelText" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -5,7 +5,6 @@ import { ErrorTracebackPopup } from "@point_of_sale/app/errors/popups/error_trac
|
||||
import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup";
|
||||
import { useEnv, onMounted, onPatched, onWillUnmount, useComponent, useRef } from "@odoo/owl";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { escapeRegExp } from '@web/core/utils/strings';
|
||||
|
||||
/**
|
||||
* Introduce error handlers in the component.
|
||||
@@ -100,24 +99,12 @@ export function useValidateCashInput(inputRef, startingValue) {
|
||||
const cashInput = useRef(inputRef);
|
||||
const localization = useService("localization");
|
||||
const decimalPoint = localization.decimalPoint;
|
||||
const thousandsSep = localization.thousandsSep;
|
||||
// Replace the thousands separator and decimal point with regex-escaped versions
|
||||
const escapedDecimalPoint = escapeRegExp(decimalPoint);
|
||||
let floatRegex;
|
||||
if (thousandsSep) {
|
||||
const escapedThousandsSep = escapeRegExp(thousandsSep);
|
||||
floatRegex = new RegExp(`^-?(?:\\d+(${escapedThousandsSep}\\d+)*)?(?:${escapedDecimalPoint}\\d*)?$`);
|
||||
} else {
|
||||
floatRegex = new RegExp(`^-?(?:\\d+)?(?:${escapedDecimalPoint}\\d*)?$`);
|
||||
}
|
||||
function isValidFloat(inputValue) {
|
||||
return ![decimalPoint, '-'].includes(inputValue) && floatRegex.test(inputValue);
|
||||
}
|
||||
const env = useEnv();
|
||||
function handleCashInputChange(event) {
|
||||
let inputValue = (event.target.value || "").trim();
|
||||
|
||||
// Check if the current input value is a valid float
|
||||
if (!isValidFloat(inputValue)) {
|
||||
if (!env.utils.isValidFloat(inputValue)) {
|
||||
event.target.classList.add('invalid-cash-input');
|
||||
} else {
|
||||
event.target.classList.remove('invalid-cash-input');
|
||||
|
||||
@@ -14,44 +14,48 @@
|
||||
</div>
|
||||
</div>
|
||||
<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 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 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 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 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 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 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 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 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 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 />
|
||||
<!-- FIXME POSREF do we really need to hardcode all of these? -->
|
||||
<div class="row">
|
||||
<button class="col-3 input-button number-char btn btn-secondary p-0 rounded-circle m-1 fw-bolder" t-on-mousedown.prevent="() => this.sendInput('1')">1</button>
|
||||
<button class="col-3 input-button number-char btn btn-secondary p-0 rounded-circle m-1 fw-bolder" t-on-mousedown.prevent="() => this.sendInput('2')">2</button>
|
||||
<button class="col-3 input-button number-char btn btn-secondary p-0 rounded-circle m-1 fw-bolder" t-on-mousedown.prevent="() => this.sendInput('3')">3</button>
|
||||
<t t-if="!props.isPassword and props.cheap">
|
||||
<button class="mode-button add btn btn-secondary p-0 rounded-circle m-1 fw-bolder" t-on-mousedown.prevent="() => this.sendInput('+1')">+1</button>
|
||||
</t>
|
||||
<t t-if="!props.isPassword and !props.cheap">
|
||||
<button class="mode-button add btn btn-secondary p-0 rounded-circle m-1 fw-bolder" t-on-mousedown.prevent="() => this.sendInput('+10')">+10</button>
|
||||
</t>
|
||||
</div>
|
||||
<div class="row">
|
||||
<button class="col-3 input-button number-char btn btn-secondary p-0 rounded-circle m-1 fw-bolder" t-on-mousedown.prevent="() => this.sendInput('4')">4</button>
|
||||
<button class="col-3 input-button number-char btn btn-secondary p-0 rounded-circle m-1 fw-bolder" t-on-mousedown.prevent="() => this.sendInput('5')">5</button>
|
||||
<button class="col-3 input-button number-char btn btn-secondary p-0 rounded-circle m-1 fw-bolder" t-on-mousedown.prevent="() => this.sendInput('6')">6</button>
|
||||
<t t-if="!props.isPassword and props.cheap">
|
||||
<button class="mode-button add btn btn-secondary p-0 rounded-circle m-1 fw-bolder" t-on-mousedown.prevent="() => this.sendInput('+2')">+2</button>
|
||||
</t>
|
||||
<t t-if="!props.isPassword and !props.cheap">
|
||||
<button class="mode-button add btn btn-secondary p-0 rounded-circle m-1 fw-bolder" t-on-mousedown.prevent="() => this.sendInput('+20')">+20</button>
|
||||
</t>
|
||||
</div>
|
||||
<div class="row">
|
||||
<button class="col-3 input-button number-char btn btn-secondary p-0 rounded-circle m-1 fw-bolder" t-on-mousedown.prevent="() => this.sendInput('7')">7</button>
|
||||
<button class="col-3 input-button number-char btn btn-secondary p-0 rounded-circle m-1 fw-bolder" t-on-mousedown.prevent="() => this.sendInput('8')">8</button>
|
||||
<button class="col-3 input-button number-char btn btn-secondary p-0 rounded-circle m-1 fw-bolder" t-on-mousedown.prevent="() => this.sendInput('9')">9</button>
|
||||
<button t-if="!props.isPassword" class="col-3 input-button number-char btn btn-secondary p-0 rounded-circle m-1 fw-bolder" t-on-mousedown.prevent="() => this.sendInput('-')">-</button>
|
||||
</div>
|
||||
<div class="row">
|
||||
<button class="col-3 input-button numpad-char btn btn-secondary p-0 rounded-circle m-1 fw-bolder" t-on-mousedown.prevent="() => this.sendInput('Delete')">C</button>
|
||||
<button class="col-3 input-button number-char btn btn-secondary p-0 rounded-circle m-1 fw-bolder" t-on-mousedown.prevent="() => this.sendInput('0')">0</button>
|
||||
<button t-if="!props.isPassword" class="col-3 input-button number-char dot btn btn-secondary p-0 rounded-circle m-1 fw-bolder" t-on-mousedown.prevent="() => this.sendInput(decimalSeparator)">
|
||||
<t t-esc="decimalSeparator" /></button>
|
||||
<button class="col-3 input-button numpad-backspace btn btn-secondary p-0 rounded-circle m-1 fw-bolder" 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>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="footer centered modal-footer">
|
||||
<footer class="footer centered modal-footer justify-content-center">
|
||||
<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 small">
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</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">
|
||||
<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 }"
|
||||
<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 active': item.isSelected }"
|
||||
t-on-click="() => this.selectItem(item.id)">
|
||||
<div class="d-flex flex-column">
|
||||
<span t-esc="item.label" />
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
<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 class="button cancel btn btn-lg btn-secondary" t-on-click="cancel">
|
||||
<t t-esc="props.cancelText" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
</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>
|
||||
<div class="button cancel btn btn-lg btn-secondary" t-on-click="cancel">
|
||||
<t t-esc="props.cancelText" />
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
.money-details-value label {
|
||||
width: 50px;
|
||||
}
|
||||
+4
-4
@@ -6,11 +6,11 @@
|
||||
<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 class="money-details-info d-flex justify-content-end 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 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()"/>
|
||||
<div class="money-details-value d-flex align-items-center w-100 my-1 mx-auto" t-on-input="updateMoneyDetailsAmount">
|
||||
<input class="pos-input form-control w-50 text-end" 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"/> </t>
|
||||
<t t-esc="moneyValue"/>
|
||||
@@ -26,8 +26,8 @@
|
||||
</h4>
|
||||
</main>
|
||||
<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>
|
||||
<div class="button btn btn-lg btn-secondary" t-on-click="cancel">Discard</div>
|
||||
</footer>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
.pos .opening-cash-control .body{
|
||||
margin: 24px;
|
||||
}
|
||||
@@ -2,15 +2,10 @@
|
||||
max-width: 800px !important;
|
||||
max-height: 800px;
|
||||
|
||||
th:nth-child(1),
|
||||
td:nth-child(1) {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
th:nth-child(2),
|
||||
td:nth-child(2) {
|
||||
text-align: right;
|
||||
padding-right: map-get($spacers, 1);
|
||||
padding-right: map-get($spacers, 3);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@@ -30,10 +25,6 @@
|
||||
padding-left: map-get($spacers, 2);
|
||||
}
|
||||
|
||||
.payment-methods-overview table tr {
|
||||
height: map-get($spacers, 4);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.pos .close-pos-popup {
|
||||
overflow-y: auto;
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/* Input style used in cash control popups */
|
||||
/* .pos .popup .pos-input {
|
||||
text-align: right;
|
||||
font-size: 18px;
|
||||
color: #555555;
|
||||
background: none;
|
||||
min-height: 0;
|
||||
border-radius: unset;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
width: 50px;
|
||||
margin-right: 15px;
|
||||
border-bottom: solid 2px;
|
||||
}
|
||||
|
||||
.pos .popup .pos-input:focus {
|
||||
box-shadow: none ;
|
||||
border-color: blue;
|
||||
font-weight: bold;
|
||||
}
|
||||
*/
|
||||
@@ -1,17 +0,0 @@
|
||||
.pos .money-details.invisible {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pos .money-details .body {
|
||||
max-height: 350px;
|
||||
}
|
||||
|
||||
.pos .money-details .money-details-title {
|
||||
text-align: left;
|
||||
margin-bottom: 15px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.pos .money-details .footer .button {
|
||||
margin-right: unset;
|
||||
}
|
||||
@@ -9,11 +9,6 @@
|
||||
margin-top: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
.pos .dialog p{
|
||||
font-size: 25px;
|
||||
margin-top: 10px;
|
||||
color: #5a5a5a;
|
||||
}
|
||||
|
||||
.pos .modal-dialog .popup{
|
||||
margin: auto;
|
||||
@@ -23,8 +18,6 @@
|
||||
background-color: #fff;
|
||||
box-shadow: 0px 10px 20px rgba(0,0,0,0.4);
|
||||
z-index:1200;
|
||||
font-family: 'Lato';
|
||||
font-family: Lato;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@@ -72,18 +65,6 @@
|
||||
.pos .popup.offline {
|
||||
max-width: 600px;
|
||||
}
|
||||
.pos .popup.popup-error,
|
||||
.pos .popup.popup-error .button {
|
||||
background-color: #F3BBBB;
|
||||
color: rgb(168, 89, 89);
|
||||
}
|
||||
.pos .popup.popup-error {
|
||||
box-shadow: 0px 10px 20px rgba(92,51,51,0.4);
|
||||
}
|
||||
.pos .popup.popup-error .title {
|
||||
color: white;
|
||||
background: rgba(255, 76, 76, 0.5);
|
||||
}
|
||||
|
||||
.popup-numpad {
|
||||
direction: ltr/*rtl:ignore*/; /* rtlcss forced to keep ltr */
|
||||
@@ -92,14 +73,3 @@
|
||||
.pos .popup-textinput main {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.pos .popup-password .mode-button.add,
|
||||
.pos .popup-password .input-button.dot {
|
||||
display: none;
|
||||
}
|
||||
.pos .popup-password .popup-numpad {
|
||||
width: 190px;
|
||||
}
|
||||
.pos .popup-password .popup-input {
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ class Do {
|
||||
trigger: ".pos-topheader .ticket-button",
|
||||
},
|
||||
{
|
||||
trigger: ".subwindow .ticket-screen",
|
||||
trigger: ".screen.ticket-screen",
|
||||
run: () => {},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
<div class="login-title fs-2 mb-3 mb-md-0">Log in to
|
||||
<span class="text-primary"><t t-esc="shopName" /></span>
|
||||
</div>
|
||||
<div class="login-body d-flex d-flex flex-column flex-md-row align-items-center justify-content-around flex-grow-1">
|
||||
<span class="login-element border p-3 px-4 rounded w-50">
|
||||
<div class="login-body d-flex d-flex flex-column flex-sm-row align-items-center justify-content-around px-3 py-4">
|
||||
<span class="login-element border p-3 rounded">
|
||||
<img class="login-barcode-img w-50"
|
||||
src="/point_of_sale/static/img/barcode.png" />
|
||||
<div class="login-barcode-text mt-2">Scan your badge</div>
|
||||
</span>
|
||||
<span class="login-or fs-2 text-muted">or</span>
|
||||
<span class="login-or m-2 fs-2 text-muted">or</span>
|
||||
<span class="login-element">
|
||||
<button class="login-button select-cashier btn btn-lg btn-secondary py-5 px-3"
|
||||
t-on-click="selectCashier">Select Cashier</button>
|
||||
|
||||
@@ -48,6 +48,7 @@ export function useCashierSelector(
|
||||
if (employee.pin !== Sha1.hash(inputPin)) {
|
||||
await popup.add(ErrorPopup, {
|
||||
title: _t("Incorrect Password"),
|
||||
body: _t("Please try again."),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
}
|
||||
|
||||
.screen-login{
|
||||
max-width: 550px;
|
||||
height:300px;
|
||||
max-width: 500px;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 576px) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="pos_loyalty.RewardButton" owl="1">
|
||||
<button class="control-button btn btn-light rounded-0 fw-bolder" t-attf-class="{{hasClaimableRewards() ? 'highlight text-bg-primary' : 'disabled'}}" t-on-click="() => this.click()">
|
||||
<button class="control-button btn btn-light rounded-0 fw-bolder" t-attf-class="{{hasClaimableRewards() ? 'highlight text-action' : 'disabled'}}" t-on-click="() => this.click()">
|
||||
<i class="fa fa-star me-1"></i>
|
||||
<span> </span>
|
||||
<span>Reward</span>
|
||||
|
||||
+21
-18
@@ -2,32 +2,35 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="pos_loyalty.OrderSummary" t-inherit="point_of_sale.OrderSummary" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//div[hasclass('summary')]" position="after">
|
||||
<div class="summary clearfix sticky-top w-100 p-2 border-top text-end fw-bolder">
|
||||
<t t-set="_loyaltyStats" t-value="getLoyaltyPoints()"/>
|
||||
<t t-foreach="_loyaltyStats" t-as="_loyaltyStat" t-key="_loyaltyStat.couponId">
|
||||
<t t-if="_loyaltyStat.points.won || _loyaltyStat.points.spent">
|
||||
<div class='d-flex flex-column w-50 p-2 bg-success bg-opacity-25 text-success text-start'>
|
||||
<div class="loyalty-points-title">
|
||||
<t t-esc="_loyaltyStat.points.name"/>
|
||||
</div>
|
||||
<t t-if='_loyaltyStat.points.won'>
|
||||
<div class="loyalty-points-won">
|
||||
<span class='value'>+<t t-esc='_loyaltyStat.points.won'/></span>
|
||||
<div class="summary clearfix sticky-top w-100 border-top text-end fw-bolder">
|
||||
<div class='d-flex flex-column px-3 py-2 bg-200 text-start'>
|
||||
<div class="loyalty-points-title text-center mb-1">
|
||||
<t t-esc="_loyaltyStat.points.name"/>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if='_loyaltyStat.points.spent'>
|
||||
<div class="loyalty-points-spent bg-danger bg-opacity-25 text-danger">
|
||||
<span class='value'>-<t t-esc='_loyaltyStat.points.spent'/></span>
|
||||
<div class="d-flex justify-content-around gap-2 mt-1">
|
||||
<div
|
||||
t-if='_loyaltyStat.points.won'
|
||||
class="loyalty-points-won d-flex flex-column align-items-center justify-content-center flex-grow-1 rounded bg-300 px-3 py-2" >
|
||||
<span class="text-muted">Points Won</span>
|
||||
<span class='value text-success '>+<t t-esc='_loyaltyStat.points.won'/></span>
|
||||
</div>
|
||||
<div
|
||||
t-if='_loyaltyStat.points.spent'
|
||||
class="loyalty-points-won d-flex flex-column align-items-center justify-content-center flex-grow-1 rounded bg-300 px-3 py-2" >
|
||||
<span class="text-muted">Points Spent</span>
|
||||
<span class='value text-danger'>-<t t-esc='_loyaltyStat.points.spent'/></span>
|
||||
</div>
|
||||
<div class="loyalty-points-total d-flex flex-column align-items-center justify-content-center flex-grow-1 rounded bg-300 px-3 py-2">
|
||||
<span class="text-muted">New Total</span>
|
||||
<span class='value text-primary'><t t-esc='_loyaltyStat.points.total'/></span>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<div class="loyalty-points-total border-top pt-1 mt-1 border-2 border-success">
|
||||
<span class='value'><t t-esc='_loyaltyStat.points.total'/></span>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="d-none">Test</div>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
</xpath>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="pos_restaurant.BillScreen" owl="1">
|
||||
<div class="receipt-screen screen">
|
||||
<div class="receipt-screen screen h-100 bg-100">
|
||||
<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">
|
||||
<div class="top-content-center flex-grow-1">
|
||||
@@ -13,7 +13,7 @@
|
||||
<i class="fa fa-angle-double-right ms-2"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="centered-content mx-auto mt-3 border-left border-right text-center overflow-x-hidden overflow-y-auto">
|
||||
<div class="centered-content mx-auto mt-3 border-start border-end text-center overflow-x-hidden overflow-y-auto">
|
||||
<div class="button print btn btn-lg btn-primary" t-on-click="printReceipt">
|
||||
<i class="fa fa-print" t-ref="order-print-receipt-button"></i>
|
||||
<span> </span>
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
</div>
|
||||
<span class="edit-button btn btn-secondary btn-lg ms-3" t-on-click.stop="props.toggleEditMode">
|
||||
<div class="close-edit-button">
|
||||
<i class="oi oi-close icon-button" role="img" aria-label="Close" title="Close"></i>
|
||||
Done
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -147,7 +147,7 @@ export class EditableTable extends Component {
|
||||
width: `${widthTable}px`,
|
||||
height: `${widthTable}px`,
|
||||
background: table.color || "rgb(53, 211, 116)",
|
||||
// "line-height": `${widthTable}px`,
|
||||
"line-height": `${widthTable}px`,
|
||||
"border-radius": table.shape === "round" ? "1000px" : "3px",
|
||||
"font-size": widthTable >= 150 ? "32px" : "16px",
|
||||
opacity: "0.7",
|
||||
@@ -160,7 +160,7 @@ export class EditableTable extends Component {
|
||||
width: `${table.width}px`,
|
||||
height: `${table.height}px`,
|
||||
background: table.color || "rgb(53, 211, 116)",
|
||||
// "line-height": `${table.height}px`,
|
||||
"line-height": `${table.height}px`,
|
||||
"border-radius": table.shape === "round" ? "1000px" : "3px",
|
||||
"font-size": table.height >= 150 && table.width >= 150 ? "32px" : "16px",
|
||||
});
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="pos_restaurant.EditableTable" owl="1">
|
||||
<div class="table selected d-flex flex-column align-items-center justify-content-between position-relative" t-ref="root">
|
||||
<span class="label drag-handle fw-bolder fs-2">
|
||||
<div class="table selected position-absolute d-flex flex-column align-items-center justify-content-between overflow-hidden"
|
||||
t-ref="root">
|
||||
<span class="label drag-handle d-flex align-items-center flex-grow-1 d fw-bolder fs-2">
|
||||
<t t-esc="props.table.name" />
|
||||
</span>
|
||||
<t t-if="pos.floorPlanStyle != 'kanban'" t-foreach="handles" t-as="handle" t-key="handle">
|
||||
|
||||
@@ -54,4 +54,9 @@
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.order-count {
|
||||
height: $o-horizontal-padding * 1.5;
|
||||
width: $o-horizontal-padding * 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="pos_restaurant.FloorScreen" owl="1">
|
||||
<div class="floor-screen screen position-relative d-flex flex-column flex-nowrap m-0 text-start overflow-hidden">
|
||||
<div class="floor-screen screen h-100 position-relative d-flex flex-column flex-nowrap m-0 bg-100 text-start overflow-hidden">
|
||||
<EditBar t-if="pos.isEditMode" selectedTables="selectedTables" nbrFloors="nbrFloors"
|
||||
floorMapScrollTop="state.floorMapScrollTop" isColorPicker="state.isColorPicker" toggleColorPicker.bind="toggleColorPicker"
|
||||
createTable.bind="createTable" duplicateTableOrFloor.bind="duplicateTableOrFloor" renameTable.bind="renameTable"
|
||||
@@ -12,11 +12,11 @@
|
||||
/>
|
||||
<div class="floor-selector d-flex text-center bg-100 fs-3">
|
||||
<t t-foreach="pos.floors" t-as="floor" t-key="floor.id">
|
||||
<button class="button button-floor btn flex-grow-1 py-3 rounded-0" t-attf-class="{{ floor.id === state.selectedFloorId ? 'btn-primary' : 'btn-light' }}" t-on-click="() => this.selectFloor(floor)">
|
||||
<button class="button button-floor btn p-3 rounded-0" t-attf-class="{{ floor.id === state.selectedFloorId ? 'btn-primary' : 'btn-light' }}" t-on-click="() => this.selectFloor(floor)">
|
||||
<t t-esc="floor.name" />
|
||||
</button>
|
||||
</t>
|
||||
<button class="button button-add btn btn-secondary p-3 rounded-0" t-ref="add-floor-ref" t-on-click="addFloor">
|
||||
<button class="button button-add btn btn-secondary p-3 ms-auto rounded-0" t-ref="add-floor-ref" t-on-click="addFloor">
|
||||
<i class="fa fa-plus icon-button me-2" role="img" aria-label="Add" title="Add"></i>
|
||||
Add Floor
|
||||
</button>
|
||||
|
||||
@@ -149,8 +149,9 @@ export class Table extends Component {
|
||||
const notifications = this._getNotifications();
|
||||
const countClass = {
|
||||
"order-count": true,
|
||||
"notify-printing mt-1 px-2 rounded-pill text-bg-danger": notifications.printing,
|
||||
"notify-skipped mt-1 px-2 rounded-pill text-bg-info": notifications.skipped,
|
||||
"notify-printing text-bg-danger": notifications.printing,
|
||||
"notify-skipped text-bg-info": notifications.skipped,
|
||||
"text-bg-dark": !notifications.printing && !notifications.skipped,
|
||||
};
|
||||
return countClass;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="pos_restaurant.Table" owl="1">
|
||||
<div class="table position-absolute d-flex flex-column align-items-center justify-content-between rounded cursor-pointer" t-on-click.stop="(ev) => props.onClick(props.table, ev)" t-att-style="style">
|
||||
<div class="infos">
|
||||
<span class="label fw-bolder fs-4 z-index-1">
|
||||
<t t-esc="props.table.name" />
|
||||
</span>
|
||||
<span t-att-class="orderCountClass" t-attf-class="{{ orderCount === 0 ? 'opacity-0' : ''}}" t-att-style="badgeStyle">
|
||||
<t t-esc="orderCount" />
|
||||
</span>
|
||||
<div class="table position-absolute d-flex flex-column align-items-center justify-content-between cursor-pointer overflow-hidden"
|
||||
t-on-click.stop="(ev) => props.onClick(props.table, ev)"
|
||||
t-att-style="style">
|
||||
<div class="infos d-flex align-items-center flex-grow-1">
|
||||
<span class="label fw-bolder fs-4 z-index-1"
|
||||
t-esc="props.table.name" />
|
||||
<span
|
||||
t-att-class="orderCountClass"
|
||||
class="badge d-flex align-items-center justify-content-center position-absolute top-0 start-100 translate-middle rounded-pill"
|
||||
t-attf-class="{{ orderCount === 0 ? 'd-none' : ''}}"
|
||||
t-att-style="badgeStyle"
|
||||
t-esc="orderCount"/>
|
||||
</div>
|
||||
<span class="table-seats mb-1 px-2 rounded text-bg-dark bg-opacity-25 fs-4 z-index-1">
|
||||
<span class="table-seats position-absolute bottom-0 start-50 translate-middle-x mb-1 px-2 py-1 rounded text-bg-dark bg-opacity-25 fs-4 z-index-1">
|
||||
<div class="cover" t-att-style="`width: ${Math.ceil(fill * 100)}%`" />
|
||||
<t t-esc="customerCountDisplay" />
|
||||
</span>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="pos_restaurant.OrderWidget" t-inherit="point_of_sale.OrderWidget" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//div[hasclass('order-empty')]" position="after">
|
||||
<button t-if="showReleaseBtn" class="btn btn-primary" style="border:none; font-size: 20px;" t-on-click="releaseTable">Release table</button>
|
||||
<button t-if="showReleaseBtn" class="btn btn-primary py-2 rounded-0" style="border:none; font-size: 20px;" t-on-click="releaseTable">Release table</button>
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="pos_restaurant.SplitBillScreen" owl="1">
|
||||
<div class="splitbill-screen screen">
|
||||
<div class="splitbill-screen screen h-100 bg-100">
|
||||
<div class="contents d-flex flex-column flex-nowrap h-100 my-0 mx-auto">
|
||||
<div class="top-content d-flex align-items-center p-2 border-bottom text-center">
|
||||
<button class="button back btn btn-lg btn-outline-primary" t-on-click="back">
|
||||
|
||||
+2
-2
@@ -2,11 +2,11 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="pos_restaurant.SplitOrderline" owl="1">
|
||||
<li class="orderline w-100 m-0 py-2 px-3 cursor-pointer" t-att-class="{ 'selected text-bg-primary' : isSelected, partially: props.split.quantity !== props.line.get_quantity() }" t-on-click="() => this.click()">
|
||||
<li class="orderline w-100 m-0 py-2 px-3 lh-sm cursor-pointer" t-att-class="{ 'selected text-bg-primary' : isSelected, partially: props.split.quantity !== props.line.get_quantity() }" t-on-click="() => this.click()">
|
||||
<span class="product-name d-inline-block flex-grow-1 fw-bolder text-truncate">
|
||||
<t t-esc="props.line.get_product().display_name" />
|
||||
</span>
|
||||
<span class="price fw-bold float-end">
|
||||
<span class="price fw-bolder float-end">
|
||||
<t t-esc="env.utils.formatCurrency(props.line.get_display_price())" />
|
||||
</span>
|
||||
<ul class="info-list ms-2">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="pos_restaurant.TipScreen" owl="1">
|
||||
<div class="tip-screen screen">
|
||||
<div class="tip-screen screen h-100 bg-100">
|
||||
<div class="pos-receipt-container text-center" t-ref="pos-receipt-container"/>
|
||||
<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">
|
||||
|
||||
+10
-8
@@ -1,18 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="pos_restaurant.PaymentScreen" t-inherit="point_of_sale.PaymentScreen" t-inherit-mode="extension" owl="1">
|
||||
<t t-name="pos_restaurant.PaymentScreenValidate" t-inherit="point_of_sale.PaymentScreenValidate" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//div[hasclass('button') and hasclass('next')]" position="attributes">
|
||||
<attribute name="t-att-hidden">pos.config.set_tip_after_payment and !currentOrder.is_paid()</attribute>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//div[hasclass('button') and hasclass('back')]/span[hasclass('back_text')]" position="replace">
|
||||
<t t-if="pos.config.set_tip_after_payment and currentOrder.is_paid()">
|
||||
<span class="back_text">Keep Open</span>
|
||||
</t>
|
||||
<t t-else="">$0</t>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//div[hasclass('button') and hasclass('next')]/span[hasclass('next_text')]" position="replace">
|
||||
<t t-if="pos.config.set_tip_after_payment and currentOrder.is_paid()">
|
||||
<span class="back_text">Close Tab</span>
|
||||
@@ -20,5 +13,14 @@
|
||||
<t t-else="">$0</t>
|
||||
</xpath>
|
||||
</t>
|
||||
<t t-name="pos_restaurant.PaymentScreenTop" t-inherit="point_of_sale.PaymentScreenTop" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//div[hasclass('button') and hasclass('back')]/span[hasclass('back_text')]" position="replace">
|
||||
<t t-if="pos.config.set_tip_after_payment and currentOrder.is_paid()">
|
||||
<span class="back_text">Keep Open</span>
|
||||
</t>
|
||||
<t t-else="">$0</t>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
|
||||
</templates>
|
||||
|
||||
+2
-2
@@ -4,8 +4,8 @@
|
||||
<t t-name="pos_restaurant.Orderline" t-inherit="point_of_sale.Orderline" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//ul[hasclass('info-list')]" position="inside">
|
||||
<t t-if="props.line.getNote()">
|
||||
<li class="info orderline-note me-2 text-break">
|
||||
<i class="fa fa-tag me-1 text-muted" role="img" aria-label="Note" title="Note"/>
|
||||
<li class="info orderline-note p-2 my-1 rounded text-bg-info text-info bg-opacity-25 text-break">
|
||||
<i class="fa fa-tag me-1" role="img" aria-label="Note" title="Note"/>
|
||||
<t t-esc="props.line.getNote()" />
|
||||
</li>
|
||||
</t>
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@
|
||||
<xpath expr="//button[hasclass('pay-button')]" position="replace">
|
||||
<button
|
||||
t-if="this.swapButton"
|
||||
class="btn-switchpane btn btn-primary flex-fill rounded-0 fw-bolder py-5"
|
||||
class="btn-switchpane btn btn-primary flex-fill rounded-0 fw-bolder py-3"
|
||||
t-on-click="submitOrder"
|
||||
t-att-class="{'primary': primaryPayButton(), 'secondary': !primaryPayButton()}">
|
||||
<!-- Replace the payment button by the order button -->
|
||||
@@ -14,7 +14,7 @@
|
||||
</button>
|
||||
<t t-else="">
|
||||
<button
|
||||
class="btn-switchpane btn btn-primary flex-fill rounded-0 fw-bolder py-5"
|
||||
class="btn-switchpane btn btn-primary flex-fill rounded-0 fw-bolder py-3"
|
||||
t-att-class="{'primary': primaryPayButton(), 'secondary': !primaryPayButton()}"
|
||||
t-on-click="() => currentOrder.pay()">
|
||||
<span class="fs-1 d-block">Pay</span>
|
||||
|
||||
@@ -44,7 +44,7 @@ patch(PosStore.prototype, "pos_restaurant.PosStore", {
|
||||
setIdleTimer() {
|
||||
clearTimeout(this.idleTimer);
|
||||
if (this.shouldResetIdleTimer()) {
|
||||
this.idleTimer = setTimeout(() => this.actionAfterIdle(), 1000000);
|
||||
this.idleTimer = setTimeout(() => this.actionAfterIdle(), 60000);
|
||||
}
|
||||
},
|
||||
async actionAfterIdle() {
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="pos_sale.SaleOrderManagementScreen" owl="1">
|
||||
<div class="order-management-screen screen overflow-auto" t-att-class="{ oe_hidden: !props.isShown }" t-ref="root">
|
||||
<div class="order-management-screen screen h-100 bg-100 overflow-auto" t-att-class="{ oe_hidden: !props.isShown }" t-ref="root">
|
||||
<div class="rightpane">
|
||||
<div class="flex-container flex-container d-flex flex-column h-100">
|
||||
<SaleOrderManagementControlPanel
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
.sale-order-info td {
|
||||
padding-right: 12px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
+6
-6
@@ -4,19 +4,19 @@
|
||||
<t t-name="pos_sale.Orderline" t-inherit="point_of_sale.Orderline" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//ul[hasclass('info-list')]" position="inside">
|
||||
<t t-if="props.line.get_sale_order()">
|
||||
<li class="info orderline-sale-order">
|
||||
<li class="info orderline-sale-order ms-2">
|
||||
<i class="fa fa-basket" role="img" aria-label="SO" title="SO"/>
|
||||
<t t-esc="props.line.get_sale_order().name" />
|
||||
</li>
|
||||
<table t-if="props.line.get_sale_order().details" class="sale-order-info">
|
||||
<table t-if="props.line.get_sale_order().details" class="sale-order-info ms-2">
|
||||
<t t-foreach='props.line.get_sale_order().details' t-as='line' t-key='line_index'>
|
||||
<tr>
|
||||
<td><t t-esc="line['product_uom_qty']"/>x</td>
|
||||
<td style="max-width: 275px;">
|
||||
<td class="text-truncate"><t t-esc="line['product_uom_qty']"/>x</td>
|
||||
<td class="text-truncate" style="max-width: 275px;">
|
||||
<t t-esc="line['product_name']" />
|
||||
</td>
|
||||
<td>:</td>
|
||||
<td><t t-esc="env.utils.formatCurrency(line['total'])" /> (tax incl.)</td>
|
||||
<td class="text-truncate">:</td>
|
||||
<td class="text-truncate"><t t-esc="env.utils.formatCurrency(line['total'])" /> (tax incl.)</td>
|
||||
</tr>
|
||||
</t>
|
||||
</table>
|
||||
|
||||
+3
-3
@@ -5,10 +5,10 @@
|
||||
<xpath expr="//t[@t-foreach]" position="inside">
|
||||
<div class="pos-receipt-left-padding" t-if="line.so_reference">From <t t-esc="line.so_reference"/></div>
|
||||
<div class="pos-receipt-left-padding" t-if="line.down_payment_details">
|
||||
<table class="sale-order-info">
|
||||
<table class="sale-order-info ms-2 text-truncate">
|
||||
<tr t-foreach='line.down_payment_details' t-as='line' t-key='line_index'>
|
||||
<td><t t-esc="line['product_uom_qty']" />x</td>
|
||||
<td style="max-width: 200px;">
|
||||
<td class="text-truncate"><t t-esc="line['product_uom_qty']" />x</td>
|
||||
<td class="text-truncate" style="max-width: 200px;">
|
||||
<t t-esc="line['product_name']" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user