[FIX] point_of_sale: expose diff fields for bank payment methods in the closing popup

There are two changes in this commit:
1. Show input fields for bank payment methods.
2. Move the logic that checks allowed diff amount from backend
to pos ui.

Part-of: odoo/odoo#77859
This commit is contained in:
Jacky (trj)
2021-10-08 11:46:44 +00:00
committed by Joseph Caburnay
parent de765111cc
commit e98ddc8ce6
6 changed files with 68 additions and 43 deletions
+1 -1
View File
@@ -83,7 +83,7 @@ class PosConfig(models.Model):
restrict_price_control = fields.Boolean(string='Restrict Price Modifications to Managers',
help="Only users with Manager access rights for PoS app can modify the product prices on orders.")
cash_control = fields.Boolean(string='Advanced Cash Control', compute='_compute_cash_control', help="Check the amount of the cashbox at opening and closing.")
set_maximum_difference = fields.Boolean('Set Maximum Difference', help="Set a maximum difference allowed between the expected and counted cash during the closing of the session.")
set_maximum_difference = fields.Boolean('Set Maximum Difference', help="Set a maximum difference allowed between the expected and counted money during the closing of the session.")
receipt_header = fields.Text(string='Receipt Header', help="A short text that will be inserted as a header in the printed receipt.")
receipt_footer = fields.Text(string='Receipt Footer', help="A short text that will be inserted as a footer in the printed receipt.")
proxy_ip = fields.Char(string='IP Address', size=45,
+8 -17
View File
@@ -441,20 +441,6 @@ class PosSession(models.Model):
self.cash_register_id.balance_end_real = counted_cash
# No need to check cash_control because it should be True at this point
if self.config_id.set_maximum_difference and abs(self.cash_register_difference) > self.config_id.amount_authorized_diff:
if not self.user_has_groups("point_of_sale.group_pos_manager"):
# We are not raising this as an error because we want the details to persist.
# It will become the starting point on next attempt to close the session.
return {
'successful': False,
'message': _(
"Your ending balance is too different from the theoretical cash closing (%.2f), "
"the maximum allowed is: %.2f.\n You can contact your manager to force it."
) % (self.cash_register_difference, self.config_id.amount_authorized_diff),
'redirect': False
}
return {'successful': True}
def _cannot_close_session(self):
@@ -506,12 +492,17 @@ class PosSession(models.Model):
sum(self.cash_register_id.line_ids.mapped('amount')),
'opening': self.cash_register_id.balance_start,
'payment_amount': total_default_cash_payment_amount,
'moves': cash_in_out_list
'moves': cash_in_out_list,
'id': default_cash_payment_method_id.id
} if default_cash_payment_method_id else None,
'other_payment_methods': [{
'name': pm.name,
'amount': sum(orders.payment_ids.filtered(lambda p: p.payment_method_id == pm).mapped('amount'))
} for pm in other_payment_method_ids]
'amount': sum(orders.payment_ids.filtered(lambda p: p.payment_method_id == pm).mapped('amount')),
'id': pm.id,
'type': pm.type,
} for pm in other_payment_method_ids],
'is_manager': self.user_has_groups("point_of_sale.group_pos_manager"),
'amount_authorized_diff': self.config_id.amount_authorized_diff if self.config_id.set_maximum_difference else None
}
def _create_picking_at_end_of_session(self):
@@ -104,7 +104,7 @@
text-align: left;
}
.pos .close-pos-popup .accept-closing.invisible {
.pos .close-pos-popup .invisible {
visibility: hidden;
}
@@ -112,11 +112,21 @@
cursor: pointer;
}
.pos .close-pos-popup .accept-closing label.disabled {
cursor: default;
color: grey;
}
.pos .close-pos-popup #accept {
all: revert;
margin-right: 10px;
}
.pos .close-pos-popup .accept-closing .warning {
color: red;
font-style: italic;
}
.pos .close-pos-popup .footer .button {
width: 150px;
}
@@ -13,7 +13,7 @@ odoo.define('point_of_sale.ClosePosPopup', function(require) {
class ClosePosPopup extends AbstractAwaitablePopup {
constructor() {
super(...arguments);
this.manualInputCashCount = true;
this.manualInputCashCount = false;
this.cashControl = this.env.pos.config.cash_control;
this.moneyDetailsRef = useRef('moneyDetails');
this.closeSessionClicked = false;
@@ -33,15 +33,19 @@ odoo.define('point_of_sale.ClosePosPopup', function(require) {
this.openingNotes = closingData.opening_notes;
this.defaultCashDetails = closingData.default_cash_details;
this.otherPaymentMethods = closingData.other_payment_methods;
this.isManager = closingData.is_manager;
this.amountAuthorizedDiff = closingData.amount_authorized_diff;
// component state and refs definition
const state = {notes: '', acceptClosing: false};
const state = {notes: '', acceptClosing: false, payments: {}};
if (this.cashControl) {
state[this.defaultCashDetails.name] = {counted: 0, difference: -this.defaultCashDetails.amount};
state.payments[this.defaultCashDetails.id] = {counted: 0, difference: -this.defaultCashDetails.amount};
}
if (this.otherPaymentMethods.length > 0) {
this.otherPaymentMethods.forEach(pm => {
state[pm.name] = {counted: this.env.pos.round_decimals_currency(pm.amount), difference: 0}
if (pm.type === 'bank') {
state.payments[pm.id] = {counted: this.env.pos.round_decimals_currency(pm.amount), difference: 0}
}
})
}
Object.assign(this.state, state);
@@ -68,32 +72,48 @@ odoo.define('point_of_sale.ClosePosPopup', function(require) {
openDetailsPopup() {
if (this.moneyDetailsRef.comp.isClosed()){
this.moneyDetailsRef.comp.openPopup();
this.state[this.defaultCashDetails.name].counted = 0;
this.state.payments[this.defaultCashDetails.id].counted = 0;
this.state.payments[this.defaultCashDetails.id].difference = -this.defaultCashDetails.amount;
this.state.notes = '';
if (this.manualInputCashCount) {
this.moneyDetailsRef.comp.reset();
}
}
}
handleInputChange() {
this.manualInputCashCount = true;
this.state.notes = '';
this.state[this.defaultCashDetails.name].difference =
this.env.pos.round_decimals_currency(this.state[this.defaultCashDetails.name].counted - this.defaultCashDetails.amount);
handleInputChange(paymentId) {
let expectedAmount;
if (paymentId === this.defaultCashDetails.id) {
this.manualInputCashCount = true;
this.state.notes = '';
expectedAmount = this.defaultCashDetails.amount;
} else {
expectedAmount = this.otherPaymentMethods.find(pm => paymentId === pm.id).amount;
}
this.state.payments[paymentId].difference =
this.env.pos.round_decimals_currency(this.state.payments[paymentId].counted - expectedAmount);
this.state.acceptClosing = false;
}
updateCountedCash(event) {
const { total, moneyDetailsNotes, moneyDetails } = event.detail;
this.state[this.defaultCashDetails.name].counted = total;
this.state[this.defaultCashDetails.name].difference =
this.env.pos.round_decimals_currency(this.state[[this.defaultCashDetails.name]].counted - this.defaultCashDetails.amount);
this.state.payments[this.defaultCashDetails.id].counted = total;
this.state.payments[this.defaultCashDetails.id].difference =
this.env.pos.round_decimals_currency(this.state.payments[[this.defaultCashDetails.id]].counted - this.defaultCashDetails.amount);
if (moneyDetailsNotes) {
this.state.notes = moneyDetailsNotes;
}
this.manualInputCashCount = false;
this.moneyDetails = moneyDetails;
this.state.acceptClosing = false;
}
hasDifference() {
return Object.entries(this.state.payments).find(pm => pm[1].difference != 0);
}
hasUserAuthority() {
const absDifferences = Object.entries(this.state.payments).map(pm => Math.abs(pm[1].difference));
return this.isManager || this.amountAuthorizedDiff == null || Math.max(...absDifferences) <= this.amountAuthorizedDiff;
}
canCloseSession() {
return !this.cashControl || !this.state[this.defaultCashDetails.name].difference || this.state.acceptClosing;
return !this.cashControl || !this.hasDifference() || this.state.acceptClosing;
}
closePos() {
this.trigger('close-pos');
@@ -108,7 +128,7 @@ odoo.define('point_of_sale.ClosePosPopup', function(require) {
method: 'post_closing_cash_details',
args: [this.env.pos.pos_session.id],
kwargs: {
counted_cash: this.state[this.defaultCashDetails.name].counted,
counted_cash: this.state.payments[this.defaultCashDetails.id].counted,
}
})
if (!response.successful) {
@@ -40,13 +40,13 @@
<tr>
<td t-esc="defaultCashDetails.name"/>
<td t-esc="env.pos.format_currency(defaultCashDetails.amount)"/>
<td class="flex" t-on-input="handleInputChange()">
<input class="pos-input" type="number" t-model.number="state[defaultCashDetails.name].counted"/>
<td class="flex" t-on-input="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"/>
</div>
</td>
<td t-esc="env.pos.format_currency(state[defaultCashDetails.name].difference)" t-att-class="{'warning': state[defaultCashDetails.name].difference}"/>
<td t-esc="env.pos.format_currency(state.payments[defaultCashDetails.id].difference)" t-att-class="{'warning': state.payments[defaultCashDetails.id].difference}"/>
</tr>
</tbody>
<tbody class="cash-overview">
@@ -75,17 +75,21 @@
</tbody>
</t>
<tbody t-if="otherPaymentMethods.length &gt; 0">
<tr t-foreach="otherPaymentMethods" t-as="pm" t-key="pm.name">
<tr t-foreach="otherPaymentMethods" t-as="pm" t-key="pm.id">
<td t-esc="pm.name"/>
<td t-esc="env.pos.format_currency(pm.amount)"/>
<td t-if="pm.type === 'bank'" t-on-input="handleInputChange(pm.id)"><input class="pos-input" type="number" t-model.number="state.payments[pm.id].counted"/></td>
<td t-if="pm.type === 'bank'" t-esc="env.pos.format_currency(state.payments[pm.id].difference)" t-att-class="{'warning': state.payments[pm.id].difference}"/>
</tr>
</tbody>
</table>
</div>
<textarea placeholder="Notes" class="closing-notes" t-model="state.notes"/>
<div t-if="cashControl" class="accept-closing" t-att-class="{invisible: !state[defaultCashDetails.name].difference}">
<input type="checkbox" id="accept" t-model="state.acceptClosing"/>
<label for="accept">Accept <t t-esc="defaultCashDetails.name"/> difference and post a profit/loss journal entry</label>
<div class="accept-closing" t-att-class="{invisible: !hasDifference()}">
<t t-set="_hasUserAuthority" t-value="hasUserAuthority()" />
<input t-att-disabled="!_hasUserAuthority" type="checkbox" id="accept" t-model="state.acceptClosing"/>
<label t-att-class="{disabled: !_hasUserAuthority}" for="accept">Accept payments difference and post a profit/loss journal entry</label>
<div class="warning" t-att-class="{invisible: _hasUserAuthority}">The maximum difference allowed is <t t-esc="env.pos.format_currency(amountAuthorizedDiff)"/>. Please contact your manager to accept the closing difference.</div>
</div>
</main>
<footer class="footer">
@@ -442,7 +442,7 @@
</div>
</div>
</div>
<div class="col-12 col-lg-6 o_setting_box" attrs="{'invisible': [('cash_control', '=', False)]}">
<div class="col-12 col-lg-6 o_setting_box">
<field name="cash_control" invisible="1" />
<div class="o_setting_left_pane">
<field name="set_maximum_difference" />
@@ -450,7 +450,7 @@
<div class="o_setting_right_pane">
<label for="set_maximum_difference" />
<div class="text-muted">
Set a maximum difference allowed between the expected and counted cash during the closing of the session
Set a maximum difference allowed between the expected and counted money during the closing of the session
</div>
<div class="content-group mt16" attrs="{'invisible': [('set_maximum_difference', '=', False)]}">
<label for="amount_authorized_diff" string="Authorized Difference" class="font-weight-normal"/>