[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:
committed by
Joseph Caburnay
parent
de765111cc
commit
e98ddc8ce6
@@ -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,
|
||||
|
||||
@@ -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 > 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"/>
|
||||
|
||||
Reference in New Issue
Block a user