[FIX] point_of_sale: fix crash when closing session with draft orders

In odoo/odoo#112219 we removed the usage of useListener for
cross-component communication, one such use was to close the pos from
the closing popup. The closeSession method was moved from the closing
popup to the store but the corresponding error handling method wasnt. It
appears that the closeSession method doesn't need to be on the store at
all, only the closePos method. This commit moves the closeSession method
back to the closing popup, so that it has access to the
handleClosingError method again.

closes odoo/odoo#114127

Signed-off-by: Trinh Jacky (trj) <trj@odoo.com>
This commit is contained in:
Samuel Degueldre
2023-03-02 16:01:22 +01:00
parent 91303252f4
commit b795fe45f6
4 changed files with 77 additions and 104 deletions
@@ -7,8 +7,6 @@ import { registry } from "@web/core/registry";
import { ConfirmPopup } from "@point_of_sale/js/Popups/ConfirmPopup";
import { reactive, markRaw } from "@odoo/owl";
import { Reactive } from "@point_of_sale/utils";
import { identifyError } from "@point_of_sale/app/error_handlers/error_handlers";
import { ConnectionLostError } from "@web/core/network/rpc_service";
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
import { _t } from "@web/core/l10n/translation";
@@ -130,73 +128,6 @@ export class PosStore extends Reactive {
}
}
}
async closeSession(defaultCashCounted, notes, otherPaymentMethods, payments) {
let response;
if (this.globalState.config.cashControl) {
response = await this.orm.call(
"pos.session",
"post_closing_cash_details"[this.globalState.pos_session.id],
{
counted_cash: defaultCashCounted,
}
);
if (!response.successful) {
return this.handleClosingError(response);
}
}
try {
await this.orm.call("pos.session", "update_closing_control_state_session", [
this.globalState.pos_session.id,
notes,
]);
} catch (error) {
// We have to handle the error manually otherwise the validation check stops the script.
// In case of "rescue session", we want to display the next popup with "handleClosingError".
// FIXME
if (
!error.message &&
!error.message.data &&
error.message.data.message !== "This session is already closed."
) {
throw error;
}
}
try {
const bankPaymentMethodDiffPairs = otherPaymentMethods
.filter((pm) => pm.type == "bank")
.map((pm) => [pm.id, payments[pm.id].difference]);
response = await this.orm.call("pos.session", "close_session_from_ui", [
this.globalState.pos_session.id,
bankPaymentMethodDiffPairs,
]);
if (!response.successful) {
return this.handleClosingError(response);
}
window.location = "/web#action=point_of_sale.action_client_pos_menu";
} catch (error) {
if (identifyError(error) instanceof ConnectionLostError) {
// Cannot redirect to backend when offline, let error handlers show the offline popup
// FIXME POSREF: doing this means closing again when online will redo the beginning of the method
// although it's impossible to close again because this.closeSessionClicked isn't reset to false
// The application state is corrupted.
throw error;
} else {
// FIXME POSREF: why are we catching errors here but not anywhere else in this method?
await this.popup.add(ErrorPopup, {
title: _t("Closing session error"),
body: _t(
"An error has occurred when trying to close the session.\n" +
"You will be redirected to the back-end to manually close the session."
),
});
window.location = "/web#action=point_of_sale.action_client_pos_menu";
}
}
}
async selectPartner() {
// FIXME, find order to refund when we are in the ticketscreen.
const currentOrder = this.globalState.get_order();
+2 -12
View File
@@ -57,18 +57,8 @@ export class Chrome extends Component {
window.posmodel = this.pos.globalState.debug ? reactivePos : this.pos.globalState;
this.wowlEnv = this.env;
for (const service of [
"pos",
"sound",
"debug",
"pos_notification",
"number_buffer",
"popup",
"sale_order_fetcher",
]) {
env.services[service] = this.wowlEnv.services[service];
}
// FIXME POSREF: make wowl services available in legacy env
Object.setPrototypeOf(env.services, this.wowlEnv.services);
this.env = env;
this.__owl__.childEnv = env;
useSubEnv({
@@ -3,11 +3,14 @@
import { AbstractAwaitablePopup } from "@point_of_sale/js/Popups/AbstractAwaitablePopup";
import { SaleDetailsButton } from "../ChromeWidgets/SaleDetailsButton";
import { ConfirmPopup } from "./ConfirmPopup";
import { ErrorPopup } from "./ErrorPopup";
import { MoneyDetailsPopup } from "./MoneyDetailsPopup";
import { AlertPopup } from "./AlertPopup";
import { useService } from "@web/core/utils/hooks";
import { AlertPopup } from "./AlertPopup";
import { ErrorPopup } from "./ErrorPopup";
import { useState } from "@odoo/owl";
import { ConnectionLostError } from "@web/core/network/rpc_service";
import { identifyError } from "@point_of_sale/app/error_handlers/error_handlers";
import { _t } from "@web/core/l10n/translation";
export class ClosePosPopup extends AbstractAwaitablePopup {
static components = { SaleDetailsButton };
@@ -17,6 +20,7 @@ export class ClosePosPopup extends AbstractAwaitablePopup {
super.setup();
this.popup = useService("popup");
this.pos = useService("pos");
this.orm = useService("orm");
this.manualInputCashCount = false;
this.cashControl = this.env.pos.config.cash_control;
this.closeSessionClicked = false;
@@ -120,39 +124,87 @@ export class ClosePosPopup extends AbstractAwaitablePopup {
canCancel() {
return true;
}
closePos() {
this.pos.closePos();
}
async closeSession() {
if (!this.closeSessionClicked) {
this.closeSessionClicked = true;
const defaultCashCounted = this.env.pos.config.cashControl
? this.state.payments[this.defaultCashDetails.id].counted
: 0;
this.pos.closeSession(
defaultCashCounted,
this.state.notes,
this.otherPaymentMethods,
this.state.payments
);
if (this.env.pos.config.cashControl) {
const response = await this.orm.call(
"pos.session",
"post_closing_cash_details"[this.env.pos.pos_session.id],
{
counted_cash: this.env.pos.config.cashControl
? this.state.payments[this.defaultCashDetails.id].counted
: 0,
}
);
if (!response.successful) {
return this.handleClosingError(response);
}
}
try {
await this.orm.call("pos.session", "update_closing_control_state_session", [
this.env.pos.pos_session.id,
this.state.notes,
]);
} catch (error) {
// We have to handle the error manually otherwise the validation check stops the script.
// In case of "rescue session", we want to display the next popup with "handleClosingError".
// FIXME
if (
!error.message &&
!error.message.data &&
error.message.data.message !== "This session is already closed."
) {
throw error;
}
}
try {
const bankPaymentMethodDiffPairs = this.otherPaymentMethods
.filter((pm) => pm.type == "bank")
.map((pm) => [pm.id, this.state.payments[pm.id].difference]);
const response = await this.orm.call("pos.session", "close_session_from_ui", [
this.env.pos.pos_session.id,
bankPaymentMethodDiffPairs,
]);
if (!response.successful) {
return this.handleClosingError(response);
}
window.location = "/web#action=point_of_sale.action_client_pos_menu";
} catch (error) {
if (identifyError(error) instanceof ConnectionLostError) {
// Cannot redirect to backend when offline, let error handlers show the offline popup
// FIXME POSREF: doing this means closing again when online will redo the beginning of the method
// although it's impossible to close again because this.closeSessionClicked isn't reset to false
// The application state is corrupted.
throw error;
} else {
// FIXME POSREF: why are we catching errors here but not anywhere else in this method?
await this.popup.add(ErrorPopup, {
title: _t("Closing session error"),
body: _t(
"An error has occurred when trying to close the session.\n" +
"You will be redirected to the back-end to manually close the session."
),
});
window.location = "/web#action=point_of_sale.action_client_pos_menu";
}
}
}
this.closeSessionClicked = false;
}
async handleClosingError(response) {
let popupType = "";
let title = "";
const body = response.message;
if (response.type == "alert") {
popupType = AlertPopup;
title = response.title ? response.title : "";
await this.popup.add(AlertPopup, { title: response.title || "", body });
} else {
popupType = ErrorPopup;
title = "Error";
await this.popup.add(ErrorPopup, { title: "Error", body });
}
await this.popup.add(popupType, { title: title, body: response.message });
if (response.redirect) {
window.location = "/web#action=point_of_sale.action_client_pos_menu";
}
@@ -80,7 +80,7 @@
</main>
<footer class="footer">
<div class="button highlight" t-on-click="confirm">Close Session</div>
<div class="button" t-on-click="closePos" title="Visit the Backend but keep session open">Backend</div>
<div class="button" t-on-click="() => pos.closePos()" title="Visit the Backend but keep session open">Backend</div>
<div class="button" t-att-class="{'disabled': !canCancel()}" t-on-click="cancel">Discard</div>
<!-- Download Sale Details -->
<div class="small button icon"