From e98ddc8ce67385274a963c07548abf59b68371c4 Mon Sep 17 00:00:00 2001 From: "Jacky (trj)" Date: Tue, 28 Sep 2021 13:25:19 +0000 Subject: [PATCH] [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 --- addons/point_of_sale/models/pos_config.py | 2 +- addons/point_of_sale/models/pos_session.py | 25 +++------- .../src/css/popups/closing_pos_popup.css | 12 ++++- .../static/src/js/Popups/ClosePosPopup.js | 50 +++++++++++++------ .../static/src/xml/Popups/ClosePosPopup.xml | 18 ++++--- .../point_of_sale/views/pos_config_view.xml | 4 +- 6 files changed, 68 insertions(+), 43 deletions(-) diff --git a/addons/point_of_sale/models/pos_config.py b/addons/point_of_sale/models/pos_config.py index e26e3e43789..147b60bcca1 100644 --- a/addons/point_of_sale/models/pos_config.py +++ b/addons/point_of_sale/models/pos_config.py @@ -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, diff --git a/addons/point_of_sale/models/pos_session.py b/addons/point_of_sale/models/pos_session.py index faaf4bf3b25..d123b7586c2 100644 --- a/addons/point_of_sale/models/pos_session.py +++ b/addons/point_of_sale/models/pos_session.py @@ -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): diff --git a/addons/point_of_sale/static/src/css/popups/closing_pos_popup.css b/addons/point_of_sale/static/src/css/popups/closing_pos_popup.css index 0d5c3591bcb..2cb08cfefd5 100644 --- a/addons/point_of_sale/static/src/css/popups/closing_pos_popup.css +++ b/addons/point_of_sale/static/src/css/popups/closing_pos_popup.css @@ -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; } diff --git a/addons/point_of_sale/static/src/js/Popups/ClosePosPopup.js b/addons/point_of_sale/static/src/js/Popups/ClosePosPopup.js index 41d8880f68a..e17e8406bb8 100644 --- a/addons/point_of_sale/static/src/js/Popups/ClosePosPopup.js +++ b/addons/point_of_sale/static/src/js/Popups/ClosePosPopup.js @@ -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) { diff --git a/addons/point_of_sale/static/src/xml/Popups/ClosePosPopup.xml b/addons/point_of_sale/static/src/xml/Popups/ClosePosPopup.xml index 1ca0ddaa42d..ee4b934e9fd 100644 --- a/addons/point_of_sale/static/src/xml/Popups/ClosePosPopup.xml +++ b/addons/point_of_sale/static/src/xml/Popups/ClosePosPopup.xml @@ -40,13 +40,13 @@ - - + +
- + @@ -75,17 +75,21 @@ - + + +