[IMP] point_of_sale,{pos_}loyalty: ux misc improvements
- The calculator logo of the cash opening popup has been changed to a bill logo to be more explicite for the end user. - The cash input is autofocused at the opening of the cash opening popup. Select number input on focus and align numbers right money details popup. - Rearrange Close pos popup layout. - Set the Gift Card amount to the price of the refound if there is one. - Change pos_payement_method_view form id order to sequence. Set the order of payement methods in PayementScreen to sequence. - Fix markut issue in the chatter. Task-3215901 closes odoo/odoo#118288 Signed-off-by: Joseph Caburnay (jcb) <jcb@odoo.com>
This commit is contained in:
@@ -11,8 +11,8 @@
|
||||
'security/loyalty_security.xml',
|
||||
'report/loyalty_report_templates.xml',
|
||||
'report/loyalty_report.xml',
|
||||
'data/loyalty_data.xml',
|
||||
'data/mail_template_data.xml',
|
||||
'data/loyalty_data.xml',
|
||||
'wizard/loyalty_generate_wizard_views.xml',
|
||||
'views/loyalty_card_views.xml',
|
||||
'views/loyalty_mail_views.xml',
|
||||
|
||||
@@ -22,4 +22,30 @@
|
||||
<field name="value">False</field>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<!-- Gift Cards -->
|
||||
<record id="gift_card_program" model="loyalty.program">
|
||||
<field name="name">Gift Cards</field>
|
||||
<field name="program_type">gift_card</field>
|
||||
<field name="applies_on">future</field>
|
||||
<field name="trigger">auto</field>
|
||||
<field name="portal_visible">True</field>
|
||||
<field name="portal_point_name">$</field>
|
||||
<field name="mail_template_id" ref="loyalty.mail_template_gift_card"/>
|
||||
</record>
|
||||
<record id="gift_card_program_reward" model="loyalty.reward">
|
||||
<field name="reward_type">discount</field>
|
||||
<field name="discount_mode">per_point</field>
|
||||
<field name="discount">1</field>
|
||||
<field name="discount_applicability">order</field>
|
||||
<field name="required_points">1</field>
|
||||
<field name="program_id" ref="loyalty.gift_card_program"/>
|
||||
</record>
|
||||
<record id="gift_card_program_rule" model="loyalty.rule">
|
||||
<field name="reward_point_amount">1</field>
|
||||
<field name="reward_point_mode">money</field>
|
||||
<field name="reward_point_split">True</field>
|
||||
<field name="product_ids" eval="[(4, ref('loyalty.gift_card_product_50'))]"/>
|
||||
<field name="program_id" ref="loyalty.gift_card_program"/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -72,31 +72,4 @@
|
||||
<field name="mail_template_id" ref="loyalty.mail_template_loyalty_card"/>
|
||||
<field name="program_id" ref="loyalty.10_percent_coupon"/>
|
||||
</record>
|
||||
<!-- Gift Cards -->
|
||||
<record id="gift_card_program" model="loyalty.program">
|
||||
<field name="name">Gift Cards</field>
|
||||
<field name="program_type">gift_card</field>
|
||||
<field name="applies_on">future</field>
|
||||
<field name="trigger">auto</field>
|
||||
<field name="portal_visible">True</field>
|
||||
<field name="portal_point_name">$</field>
|
||||
<field name="mail_template_id" ref="loyalty.mail_template_gift_card"/>
|
||||
</record>
|
||||
|
||||
<record id="gift_card_program_rule" model="loyalty.rule">
|
||||
<field name="reward_point_amount">1</field>
|
||||
<field name="reward_point_mode">money</field>
|
||||
<field name="reward_point_split">True</field>
|
||||
<field name="product_ids" eval="[(4, ref('loyalty.gift_card_product_50'))]"/>
|
||||
<field name="program_id" ref="loyalty.gift_card_program"/>
|
||||
</record>
|
||||
|
||||
<record id="gift_card_program_reward" model="loyalty.reward">
|
||||
<field name="reward_type">discount</field>
|
||||
<field name="discount_mode">per_point</field>
|
||||
<field name="discount">1</field>
|
||||
<field name="discount_applicability">order</field>
|
||||
<field name="required_points">1</field>
|
||||
<field name="program_id" ref="loyalty.gift_card_program"/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -5,12 +5,13 @@ from odoo.exceptions import UserError
|
||||
class PosPaymentMethod(models.Model):
|
||||
_name = "pos.payment.method"
|
||||
_description = "Point of Sale Payment Methods"
|
||||
_order = "id asc"
|
||||
_order = "sequence"
|
||||
|
||||
def _get_payment_terminal_selection(self):
|
||||
return []
|
||||
|
||||
name = fields.Char(string="Method", required=True, translate=True, help='Defines the name of the payment method that will be displayed in the Point of Sale when the payments are selected.')
|
||||
sequence = fields.Integer(copy=False)
|
||||
outstanding_account_id = fields.Many2one('account.account',
|
||||
string='Outstanding Account',
|
||||
ondelete='restrict',
|
||||
@@ -81,7 +82,8 @@ class PosPaymentMethod(models.Model):
|
||||
pm.is_cash_count = pm.type == 'cash'
|
||||
|
||||
def _is_write_forbidden(self, fields):
|
||||
return bool(fields and self.open_session_ids)
|
||||
whitelisted_fields = {'sequence'}
|
||||
return bool(fields - whitelisted_fields and self.open_session_ids)
|
||||
|
||||
def write(self, vals):
|
||||
if self._is_write_forbidden(set(vals.keys())):
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
from collections import defaultdict
|
||||
from datetime import timedelta
|
||||
from itertools import groupby
|
||||
from markupsafe import Markup
|
||||
from markupsafe import Markup, escape
|
||||
|
||||
from odoo import api, fields, models, _, Command
|
||||
from odoo.exceptions import AccessError, UserError, ValidationError
|
||||
@@ -1499,9 +1499,9 @@ class PosSession(models.Model):
|
||||
message = f"{state} difference: " \
|
||||
f"{self.currency_id.symbol + ' ' if self.currency_id.position == 'before' else ''}" \
|
||||
f"{self.currency_id.round(difference)} " \
|
||||
f"{self.currency_id.symbol if self.currency_id.position == 'after' else ''}<br/>"
|
||||
f"{self.currency_id.symbol if self.currency_id.position == 'after' else ''}" + Markup('<br/>')
|
||||
if notes:
|
||||
message += notes.replace('\n', Markup('<br/>'))
|
||||
message += escape(notes).replace('\n', Markup('<br/>'))
|
||||
if message:
|
||||
self.message_post(body=message)
|
||||
|
||||
@@ -1979,8 +1979,8 @@ class PosSession(models.Model):
|
||||
return {
|
||||
'search_params': {
|
||||
'domain': ['|', ('active', '=', False), ('active', '=', True)],
|
||||
'fields': ['name', 'is_cash_count', 'use_payment_terminal', 'split_transactions', 'type', 'image'],
|
||||
'order': 'is_cash_count desc, id',
|
||||
'fields': ['name', 'is_cash_count', 'use_payment_terminal', 'split_transactions', 'type', 'image', 'sequence'],
|
||||
'order': 'sequence',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -200,7 +200,6 @@ export class PosStore extends Reactive {
|
||||
if (!options) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the product after having the extra information.
|
||||
this.addProductFromUi(product, options);
|
||||
this.numberBuffer.reset();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.pos .opening-cash-control .body{
|
||||
margin: 40px;
|
||||
margin: 24px;
|
||||
}
|
||||
|
||||
.pos .opening-cash-control .opening-cash-section {
|
||||
@@ -33,4 +33,5 @@
|
||||
line-height: 20px;
|
||||
resize: none;
|
||||
height: 150px;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
@@ -8,17 +8,22 @@
|
||||
padding: 3% 5%;
|
||||
}
|
||||
|
||||
.pos .close-pos-popup .body.notes{
|
||||
border-top: solid 1px rgba(60, 60, 60, 0.1);
|
||||
}
|
||||
.pos .close-pos-popup header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: auto;
|
||||
height: 65px;
|
||||
background-color: rgb(255, 255, 255);
|
||||
border-bottom: solid 1px rgba(60, 60, 60, 0.1);
|
||||
}
|
||||
|
||||
.pos .close-pos-popup .title,
|
||||
.pos .close-pos-popup .total-orders {
|
||||
padding: 2.5%;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.pos .close-pos-popup .total-orders .amount {
|
||||
@@ -28,20 +33,38 @@
|
||||
.notes-container {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
width: 100%;
|
||||
padding-top: 1%;
|
||||
box-sizing: border-box;
|
||||
gap:20px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.pos .close-pos-popup .opening-notes-container{
|
||||
text-align: left;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
gap: 5px
|
||||
}
|
||||
|
||||
.pos .close-pos-popup .opening-notes {
|
||||
color: darkgrey;
|
||||
text-align: left;
|
||||
font-size: 16px;
|
||||
font-weight: 350;
|
||||
box-sizing: border-box;
|
||||
min-width: 40%;
|
||||
padding: 5px 10px;
|
||||
border-left: solid 3px darkgray;
|
||||
line-height: 20px;
|
||||
overflow-y: auto;
|
||||
word-break: break-word;
|
||||
border-radius: 0px;
|
||||
resize: none;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.pos .close-pos-popup .closing-notes-container{
|
||||
text-align: left;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.pos .close-pos-popup .closing-notes {
|
||||
@@ -49,10 +72,9 @@
|
||||
font-weight: 350;
|
||||
box-sizing: border-box;
|
||||
line-height: 20px;
|
||||
width: 100%;
|
||||
min-width: 60%;
|
||||
min-height: 100px;
|
||||
resize: none;
|
||||
border-radius: 0px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.pos .close-pos-popup .payment-methods-overview {
|
||||
@@ -71,7 +93,7 @@
|
||||
|
||||
.pos .close-pos-popup .flex {
|
||||
display: flex;
|
||||
}
|
||||
max-width: 200px;}
|
||||
|
||||
.pos .close-pos-popup th:nth-child(1),
|
||||
.pos .close-pos-popup td:nth-child(1) {
|
||||
@@ -92,7 +114,7 @@
|
||||
|
||||
.pos .close-pos-popup th:nth-child(4),
|
||||
.pos .close-pos-popup td:nth-child(4) {
|
||||
text-align: left;
|
||||
text-align: right;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.pos .close-pos-popup .payment-methods-overview table .pos-input {
|
||||
@@ -119,7 +141,9 @@
|
||||
}
|
||||
|
||||
.pos .close-pos-popup .payment-methods-overview .cash-overview {
|
||||
border-left: solid 2px #555555;
|
||||
border-left: solid 2px darkgrey;
|
||||
color: darkgrey;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.pos .close-pos-popup .payment-methods-overview .cash-overview tr td:first-child {
|
||||
@@ -173,7 +197,13 @@
|
||||
.pos .close-pos-popup .footer .small.button {
|
||||
float: right;
|
||||
width: 6%;
|
||||
min-width: 45px;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.pos .close-pos-popup .footer .small.button .daily.sale {
|
||||
font-size : 14px;
|
||||
display: inline;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.pos .close-pos-popup .footer .button.disabled {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { AbstractAwaitablePopup } from "@point_of_sale/js/Popups/AbstractAwaitablePopup";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { useAutofocus, useService } from "@web/core/utils/hooks";
|
||||
import { usePos } from "@point_of_sale/app/pos_hook";
|
||||
import { MoneyDetailsPopup } from "./MoneyDetailsPopup";
|
||||
import { useState } from "@odoo/owl";
|
||||
@@ -21,6 +21,7 @@ export class CashOpeningPopup extends AbstractAwaitablePopup {
|
||||
});
|
||||
this.popup = useService("popup");
|
||||
this.orm = useService("orm");
|
||||
useAutofocus({ refName: "cash-input" });
|
||||
}
|
||||
//@override
|
||||
async confirm() {
|
||||
|
||||
@@ -466,6 +466,8 @@ td {
|
||||
}
|
||||
.pos .oe_link_icon{
|
||||
cursor:pointer;
|
||||
text-align: right;
|
||||
width: 50px;
|
||||
}
|
||||
/* ********* Contains everything below the bar ********* */
|
||||
|
||||
@@ -2516,12 +2518,14 @@ td {
|
||||
height:100%;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
z-index:1000;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
}
|
||||
.pos .modal-dialog header{
|
||||
position: relative;
|
||||
}
|
||||
.pos .modal-dialog .popup{
|
||||
position: absolute;
|
||||
margin: auto;
|
||||
max-width:500px;
|
||||
width: 100%;
|
||||
@@ -2533,10 +2537,6 @@ td {
|
||||
z-index:1200;
|
||||
font-family: 'Lato';
|
||||
font-family: Lato;
|
||||
/* position the popup at center and and still making it draggable*/
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.pos .modal-dialog .popup-med{
|
||||
@@ -2562,7 +2562,7 @@ td {
|
||||
.pos .popup .body {
|
||||
font-weight: normal;
|
||||
font-size: 18px;
|
||||
margin: 16px;
|
||||
padding: 16px;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
@@ -2627,6 +2627,18 @@ td {
|
||||
.pos .popup .button.icon {
|
||||
width: 40px;
|
||||
font-size: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.pos .popup .button.icon.ClosePosPopup{
|
||||
height: 35px;
|
||||
font-size: 20px;
|
||||
margin-top: 0px;
|
||||
margin-right: 0px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.pos .popup .button:active{
|
||||
color: white;
|
||||
@@ -3134,6 +3146,10 @@ td {
|
||||
overflow-y: auto;
|
||||
font-size: medium;
|
||||
}
|
||||
.pos .orders .cashier {
|
||||
font-style: italic;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.pos .orders .header-row{
|
||||
display: flex;
|
||||
@@ -3173,6 +3189,7 @@ td {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: $gray-300;
|
||||
z-index: 100;
|
||||
}
|
||||
.ticket-screen .controls button.search {
|
||||
margin: 0;
|
||||
@@ -3185,6 +3202,9 @@ td {
|
||||
.ticket-screen .rightpane {
|
||||
width: auto;
|
||||
}
|
||||
.pos .orders {
|
||||
position: sticky;
|
||||
}
|
||||
}
|
||||
|
||||
.pos .orders .order-row:hover, .pos .orders .order-row.highlight {
|
||||
@@ -3252,7 +3272,7 @@ td {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0px 10px;
|
||||
flex: 0 0 80px;
|
||||
flex: 0 0 80px
|
||||
}
|
||||
|
||||
.ticket-screen .controls button {
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
<div class="opening-cash-section">
|
||||
<span class="info-title">Opening cash</span>
|
||||
<div class="cash-input-sub-section" t-on-input="handleInputChange">
|
||||
<input class="pos-input" type="number" t-model.number="state.openingCash"/>
|
||||
<input class="pos-input" type="number" t-model.number="state.openingCash" t-ref="cash-input" t-on-focus="ev=>ev.target.select()"/>
|
||||
<div class="button icon" t-on-click="openDetailsPopup">
|
||||
<i class="fa fa-calculator" role="img" title="Open the money details popup"/>
|
||||
<i class="fa fa-money" role="img" title="Open the money details popup"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<tr>
|
||||
<th>Payment Method</th>
|
||||
<th>Expected</th>
|
||||
<th style="width: 25%">Counted</th>
|
||||
<th style="width: 20%">Counted</th>
|
||||
<th style="width: 20%">Difference</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -27,9 +27,9 @@
|
||||
<td t-esc="defaultCashDetails.name"/>
|
||||
<td t-esc="env.utils.formatCurrency(defaultCashDetails.amount)"/>
|
||||
<td class="flex" t-on-input="() => this.handleInputChange(defaultCashDetails.id)">
|
||||
<input class="pos-input" type="number" t-model.number="state.payments[defaultCashDetails.id].counted"/>
|
||||
<div class="button icon" t-on-click="openDetailsPopup">
|
||||
<i class="fa fa-calculator" role="img" title="Open the money details popup"/>
|
||||
<input class="pos-input" type="number" t-model.number="state.payments[defaultCashDetails.id].counted" t-on-focus="ev=>ev.target.select()"/>
|
||||
<div class="button icon ClosePosPopup" t-on-click="openDetailsPopup">
|
||||
<i class="fa fa-money" role="img" title="Open the money details popup"/>
|
||||
</div>
|
||||
</td>
|
||||
<td t-esc="env.utils.formatCurrency(state.payments[defaultCashDetails.id].difference)" t-att-class="{'warning': state.payments[defaultCashDetails.id].difference}"/>
|
||||
@@ -65,17 +65,25 @@
|
||||
<td t-esc="pm.name"/>
|
||||
<td t-esc="env.utils.formatCurrency(pm.amount)"/>
|
||||
<t t-set="_showDiff" t-value="_getShowDiff(pm)" />
|
||||
<td t-if="_showDiff" t-on-input="() => this.handleInputChange(pm.id)"><input class="pos-input" type="number" t-model.number="state.payments[pm.id].counted"/></td>
|
||||
<td t-if="_showDiff" t-on-input="() => this.handleInputChange(pm.id)"><input class="pos-input" type="number" t-model.number="state.payments[pm.id].counted" t-on-focus="ev=>ev.target.select()"/></td>
|
||||
<td t-if="_showDiff" t-esc="env.utils.formatCurrency(state.payments[pm.id].difference)" t-att-class="{'warning': state.payments[pm.id].difference}"/>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</main>
|
||||
<main class="body notes">
|
||||
<div class="notes-container">
|
||||
<div class="opening-notes" t-if="openingNotes" >
|
||||
<t t-esc="openingNotes"/>
|
||||
<div class="opening-notes-container" t-if="openingNotes" >
|
||||
Opening note
|
||||
<textarea class="opening-notes">
|
||||
<t t-esc="openingNotes"/>
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="closing-notes-container">
|
||||
Closing note
|
||||
<textarea class="closing-notes" placeholder="Add a closing note..." t-model="state.notes"/>
|
||||
</div>
|
||||
<textarea class="closing-notes" placeholder="Add a closing note..." t-model="state.notes"/>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="footer">
|
||||
@@ -85,6 +93,7 @@
|
||||
<div class="small button icon"
|
||||
t-on-click="downloadSalesReport"
|
||||
title="Download a report with all the sales of the current PoS Session">
|
||||
<div class="daily sale">Daily Sale</div>
|
||||
<i class="fa fa-download" role="img"/>
|
||||
</div>
|
||||
<!-- Print Sale Details -->
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<div t-foreach="[firstHalfMoneyDetails, lastHalfMoneyDetails]" t-as="moneyDetailsList" t-key="moneyDetailsList_index">
|
||||
<t t-foreach="moneyDetailsList" t-as="moneyValue" t-key="moneyValue">
|
||||
<div class="money-details-value" t-on-input="updateMoneyDetailsAmount">
|
||||
<input class="pos-input" t-att-id="moneyValue" type="number" t-model.number="state.moneyDetails[moneyValue]"/>
|
||||
<input class="pos-input" t-att-id="moneyValue" type="number" t-model.number="state.moneyDetails[moneyValue]" t-on-focus="ev=>ev.target.select()"/>
|
||||
<CurrencyAmount forTarget="moneyValue" currency="currency" amount="moneyValue"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -71,31 +71,27 @@
|
||||
<t t-foreach="_filteredOrderList" t-as="order" t-key="order.cid">
|
||||
<div class="order-row" t-att-class="{ highlight: isHighlighted(order) }" t-on-click="() => this.onClickOrder(order)">
|
||||
<div class="col wide">
|
||||
<div t-if="env.isMobile">Date</div>
|
||||
<div><t t-esc="getDate(order)"></t></div>
|
||||
<div t-if="env.isMobile"><t t-esc="getTotal(order)"></t></div>
|
||||
</div>
|
||||
<div class="col wide">
|
||||
<div t-if="env.isMobile">Receipt Number</div>
|
||||
<div><t t-esc="order.name"></t></div>
|
||||
<div t-if="env.isMobile"><t t-esc="getStatus(order)"></t></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div t-if="env.isMobile">Customer</div>
|
||||
<div class="col" t-if="!env.isMobile">
|
||||
<div><t t-esc="getPartner(order)"></t></div>
|
||||
</div>
|
||||
<div t-if="showCardholderName()" class="col">
|
||||
<div t-if="env.isMobile">Cardholder Name</div>
|
||||
<div t-if="showCardholderName() && !env.isMobile" class="col">
|
||||
<div><t t-esc="getCardholderName(order)"></t></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div t-if="env.isMobile">Cashier</div>
|
||||
<div><t t-esc="getCashier(order)"></t></div>
|
||||
<div t-if="env.isMobile"><t t-esc="getPartner(order)"></t></div>
|
||||
<div t-att-class = "env.isMobile ? 'cashier':''"><t t-esc="getCashier(order)"></t></div>
|
||||
</div>
|
||||
<div class="col end">
|
||||
<div t-if="env.isMobile">Total</div>
|
||||
<div class="col end" t-if="!env.isMobile">
|
||||
<div><t t-esc="getTotal(order)"></t></div>
|
||||
</div>
|
||||
<div class="col narrow">
|
||||
<div t-if="env.isMobile">Status</div>
|
||||
<div class="col narrow" t-if="!env.isMobile">
|
||||
<div><t t-esc="getStatus(order)"></t></div>
|
||||
</div>
|
||||
<div t-if="!shouldHideDeleteButton(order)" class="col very-narrow delete-button" name="delete" t-on-click.stop="() => this.onDeleteOrder(order)">
|
||||
|
||||
@@ -135,6 +135,12 @@ class Check {
|
||||
{
|
||||
trigger: `.ticket-screen .order-row > .col:nth-child(2):contains("${orderName}") ~ .col:nth-child(6):contains(${status})`,
|
||||
run: () => {},
|
||||
mobile: false,
|
||||
},
|
||||
{
|
||||
trigger: `.ticket-screen .order-row .col:nth-child(2) div:contains("${orderName}") ~ div:contains(${status})`,
|
||||
run: () => {},
|
||||
mobile: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -531,7 +531,7 @@ class TestPoSCommon(ValuationReconciliationTestCommon):
|
||||
# 2. generate the payments
|
||||
total_amount_incl = sum(line[2]['price_subtotal_incl'] for line in order_lines)
|
||||
if payments is None:
|
||||
default_cash_pm = self.config.payment_method_ids.filtered(lambda pm: pm.is_cash_count)[:1]
|
||||
default_cash_pm = self.config.payment_method_ids.filtered(lambda pm: pm.is_cash_count and not pm.split_transactions)[:1]
|
||||
if not default_cash_pm:
|
||||
raise Exception('There should be a cash payment method set in the pos.config.')
|
||||
payments = [create_payment(default_cash_pm, total_amount_incl)]
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Payment Methods" create="1" delete="1">
|
||||
<field name="type" invisible="1"/>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name" />
|
||||
<field name="split_transactions" optional="hide" />
|
||||
<field name="journal_id" attrs="{'required': [('split_transactions', '=', False)]}" />
|
||||
|
||||
@@ -8,4 +8,8 @@
|
||||
<field name="available_in_pos">True</field>
|
||||
<field name="taxes_id" eval="False"/>
|
||||
</record>
|
||||
<!-- Gift Cards -->
|
||||
<record id="loyalty.gift_card_program" model="loyalty.program">
|
||||
<field name="pos_report_print_id" ref="loyalty.report_gift_card"/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -90,8 +90,4 @@
|
||||
<field name="program_id" ref="pos_loyalty.loyalty_program"/>
|
||||
</record>
|
||||
|
||||
<!-- Gift Cards -->
|
||||
<record id="loyalty.gift_card_program" model="loyalty.program">
|
||||
<field name="pos_report_print_id" ref="loyalty.report_gift_card"/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -32,6 +32,14 @@ patch(PosStore.prototype, "pos_loyalty.PosStore", {
|
||||
} else if (linkedPrograms.length === 1) {
|
||||
selectedProgram = linkedPrograms[0];
|
||||
}
|
||||
const orderTotal = this.globalState.get_order().get_total_with_tax();
|
||||
if (
|
||||
selectedProgram &&
|
||||
["gift_card", "ewallet"].includes(selectedProgram.program_type) &&
|
||||
orderTotal < 0
|
||||
) {
|
||||
options.price = -orderTotal;
|
||||
}
|
||||
if (selectedProgram && selectedProgram.program_type == "gift_card") {
|
||||
const shouldProceed = await this._setupGiftCardOptions(selectedProgram, options);
|
||||
if (!shouldProceed) {
|
||||
@@ -52,7 +60,6 @@ patch(PosStore.prototype, "pos_loyalty.PosStore", {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await _super(product, options);
|
||||
await order._updatePrograms();
|
||||
if (rewardsToApply.length == 1) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { ProductScreen } from "@point_of_sale/../tests/tours/helpers/ProductScre
|
||||
import { TextInputPopup } from "@point_of_sale/../tests/tours/helpers/TextInputPopupTourMethods";
|
||||
import { getSteps, startSteps } from "@point_of_sale/../tests/tours/helpers/utils";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { TicketScreen } from "@point_of_sale/../tests/tours/helpers/TicketScreenTourMethods";
|
||||
|
||||
//#region GiftCardProgramCreateSetTour1
|
||||
startSteps();
|
||||
@@ -13,7 +14,9 @@ ProductScreen.do.clickHomeCategory();
|
||||
ProductScreen.do.clickDisplayedProduct("Gift Card");
|
||||
PosLoyalty.check.orderTotalIs("50.00");
|
||||
PosLoyalty.exec.finalizeOrder("Cash", "50");
|
||||
registry.category("web_tour.tours").add("GiftCardProgramCreateSetTour1", { test: true, url: "/pos/web", steps: getSteps() });
|
||||
registry
|
||||
.category("web_tour.tours")
|
||||
.add("GiftCardProgramCreateSetTour1", { test: true, url: "/pos/web", steps: getSteps() });
|
||||
//#endregion
|
||||
|
||||
//#region GiftCardProgramCreateSetTour2
|
||||
@@ -23,7 +26,9 @@ ProductScreen.do.clickDisplayedProduct("Whiteboard Pen");
|
||||
PosLoyalty.do.enterCode("044123456");
|
||||
PosLoyalty.check.orderTotalIs("0.00");
|
||||
PosLoyalty.exec.finalizeOrder("Cash", "0");
|
||||
registry.category("web_tour.tours").add("GiftCardProgramCreateSetTour2", { test: true, url: "/pos/web", steps: getSteps() });
|
||||
registry
|
||||
.category("web_tour.tours")
|
||||
.add("GiftCardProgramCreateSetTour2", { test: true, url: "/pos/web", steps: getSteps() });
|
||||
//#endregion
|
||||
|
||||
//#region GiftCardProgramScanUseTour
|
||||
@@ -47,5 +52,27 @@ ProductScreen.exec.addOrderline("Whiteboard Pen", "6", "6", "36.0");
|
||||
PosLoyalty.do.enterCode("044123456");
|
||||
PosLoyalty.check.orderTotalIs("35.00");
|
||||
PosLoyalty.exec.finalizeOrder("Cash", "35");
|
||||
registry.category("web_tour.tours").add("GiftCardProgramScanUseTour", { test: true, url: "/pos/web", steps: getSteps() });
|
||||
registry
|
||||
.category("web_tour.tours")
|
||||
.add("GiftCardProgramScanUseTour", { test: true, url: "/pos/web", steps: getSteps() });
|
||||
//#endregion
|
||||
|
||||
startSteps();
|
||||
ProductScreen.do.confirmOpeningPopup();
|
||||
ProductScreen.do.clickHomeCategory();
|
||||
ProductScreen.exec.addOrderline("Magnetic Board", "1"); // 1.98
|
||||
PosLoyalty.check.orderTotalIs("1.98");
|
||||
PosLoyalty.exec.finalizeOrder("Cash", "20");
|
||||
ProductScreen.do.clickRefund();
|
||||
TicketScreen.do.selectOrder("-0001");
|
||||
TicketScreen.do.clickOrderline("Magnetic Board");
|
||||
TicketScreen.do.pressNumpad("1");
|
||||
TicketScreen.do.confirmRefund();
|
||||
ProductScreen.check.isShown();
|
||||
ProductScreen.check.selectedOrderlineHas("Magnetic Board", "-1.00");
|
||||
ProductScreen.exec.addOrderline("Gift Card", "1");
|
||||
ProductScreen.check.selectedOrderlineHas("Gift Card", "1");
|
||||
PosLoyalty.check.orderTotalIs("0.0");
|
||||
registry
|
||||
.category("web_tour.tours")
|
||||
.add("GiftCardWithRefundtTour", { test: true, url: "/pos/web", steps: getSteps() });
|
||||
|
||||
@@ -815,3 +815,21 @@ class TestUi(TestPointOfSaleHttpCommon):
|
||||
"PosLoyaltyFreeProductTour2",
|
||||
login="accountman",
|
||||
)
|
||||
|
||||
def test_refund_with_gift_card(self):
|
||||
"""When adding a gift card when there is a refund in the order, the amount
|
||||
of the gift card is set to the amount of the refund"""
|
||||
LoyaltyProgram = self.env['loyalty.program']
|
||||
# Deactivate all other programs to avoid interference
|
||||
(LoyaltyProgram.search([])).write({'pos_ok': False})
|
||||
# But activate the gift_card_product_50 because it's shared among new gift card programs.
|
||||
self.env.ref('loyalty.gift_card_product_50').write({'active': True})
|
||||
# Create gift card program
|
||||
self.create_programs([('arbitrary_name', 'gift_card')])
|
||||
# Change the gift card program settings
|
||||
self.main_pos_config.write({'gift_card_settings': 'create_set'})
|
||||
self.start_tour(
|
||||
"/pos/web?config_id=%d" % self.main_pos_config.id,
|
||||
"GiftCardWithRefundtTour",
|
||||
login="accountman"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user