feat: implement robust note splitting and defensive JSON parsing for POS order lines and customer display
This commit is contained in:
parent
35f254ddb0
commit
19f189dab7
@ -39,9 +39,14 @@ Features
|
|||||||
# ProductScreen portrait tab state + layout
|
# ProductScreen portrait tab state + layout
|
||||||
'pos_ui_optimization/static/src/app/screens/product_screen/portrait_mode_patch.js',
|
'pos_ui_optimization/static/src/app/screens/product_screen/portrait_mode_patch.js',
|
||||||
'pos_ui_optimization/static/src/app/screens/product_screen/portrait_screen.xml',
|
'pos_ui_optimization/static/src/app/screens/product_screen/portrait_screen.xml',
|
||||||
|
# Note splitting and defensive parsing patches
|
||||||
|
'pos_ui_optimization/static/src/app/screens/product_screen/notes_patch.js',
|
||||||
# Portrait CSS (must load after all XML patches)
|
# Portrait CSS (must load after all XML patches)
|
||||||
'pos_ui_optimization/static/src/scss/portrait.scss',
|
'pos_ui_optimization/static/src/scss/portrait.scss',
|
||||||
],
|
],
|
||||||
|
'point_of_sale.customer_display_assets': [
|
||||||
|
'pos_ui_optimization/static/src/customer_display/customer_display_patch.js',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
'installable': True,
|
'installable': True,
|
||||||
'application': False,
|
'application': False,
|
||||||
|
|||||||
84
static/src/app/screens/product_screen/notes_patch.js
Normal file
84
static/src/app/screens/product_screen/notes_patch.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
import { NoteButton, InternalNoteButton } from "@point_of_sale/app/screens/product_screen/control_buttons/orderline_note_button/orderline_note_button";
|
||||||
|
import { Orderline } from "@point_of_sale/app/components/orderline/orderline";
|
||||||
|
import { patch } from "@web/core/utils/patch";
|
||||||
|
|
||||||
|
// 1. Patch NoteButton to fix line-splitting bug for customer notes in standard Odoo
|
||||||
|
patch(NoteButton.prototype, {
|
||||||
|
async setChanges(selectedOrderline, payload) {
|
||||||
|
var quantity_with_note = 0;
|
||||||
|
const changes = this.pos.getOrderChanges();
|
||||||
|
for (const key in changes.orderlines) {
|
||||||
|
if (changes.orderlines[key].uuid === selectedOrderline.uuid) {
|
||||||
|
quantity_with_note = changes.orderlines[key].quantity;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const saved_quantity = selectedOrderline.qty - quantity_with_note;
|
||||||
|
if (saved_quantity > 0 && quantity_with_note > 0) {
|
||||||
|
const addLineParams = {
|
||||||
|
product_tmpl_id: selectedOrderline.product_id.product_tmpl_id,
|
||||||
|
qty: quantity_with_note,
|
||||||
|
};
|
||||||
|
if (this.type === "internal") {
|
||||||
|
addLineParams.note = payload;
|
||||||
|
} else {
|
||||||
|
addLineParams.customer_note = payload;
|
||||||
|
}
|
||||||
|
await this.pos.addLineToCurrentOrder(addLineParams);
|
||||||
|
selectedOrderline.qty = saved_quantity;
|
||||||
|
for (const line of selectedOrderline.combo_line_ids) {
|
||||||
|
line.setQuantity(line.uiState.oldQty);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.setOrderlineNote(payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Patch InternalNoteButton to handle invalid JSON gracefully
|
||||||
|
patch(InternalNoteButton.prototype, {
|
||||||
|
async onClick() {
|
||||||
|
const selectedOrderline = this.pos.getOrder().getSelectedOrderline();
|
||||||
|
let selectedNote = [];
|
||||||
|
try {
|
||||||
|
selectedNote = JSON.parse(this.currentNote || "[]");
|
||||||
|
} catch (e) {
|
||||||
|
if (this.currentNote) {
|
||||||
|
selectedNote = [{ text: this.currentNote, colorIndex: 0 }];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const payload = await this.openTextInput(selectedNote.map((n) => n.text).join("\n"));
|
||||||
|
const coloredNotes = payload ? this.reframeNotes(payload) : "[]";
|
||||||
|
if (selectedOrderline) {
|
||||||
|
this.setChanges(selectedOrderline, coloredNotes);
|
||||||
|
} else {
|
||||||
|
this.pos.getOrder().setInternalNote(coloredNotes);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
confirmed: typeof payload === "string",
|
||||||
|
inputNote: coloredNotes,
|
||||||
|
oldNote: JSON.stringify(selectedNote),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. Patch Orderline to prevent UI crashes if note contains invalid JSON
|
||||||
|
patch(Orderline.prototype, {
|
||||||
|
get lineScreenValues() {
|
||||||
|
const originalParse = JSON.parse;
|
||||||
|
JSON.parse = function (str) {
|
||||||
|
try {
|
||||||
|
return originalParse(str);
|
||||||
|
} catch (e) {
|
||||||
|
return [{ text: str, colorIndex: 0 }];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
return super.lineScreenValues;
|
||||||
|
} finally {
|
||||||
|
JSON.parse = originalParse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -1,7 +1,7 @@
|
|||||||
/** @odoo-module **/
|
/** @odoo-module **/
|
||||||
|
|
||||||
import { ProductScreen } from "@point_of_sale/app/screens/product_screen/product_screen";
|
import { ProductScreen } from "@point_of_sale/app/screens/product_screen/product_screen";
|
||||||
import { NoteButton } from "@point_of_sale/app/screens/product_screen/control_buttons/orderline_note_button/orderline_note_button";
|
import { NoteButton, InternalNoteButton } from "@point_of_sale/app/screens/product_screen/control_buttons/orderline_note_button/orderline_note_button";
|
||||||
import { patch } from "@web/core/utils/patch";
|
import { patch } from "@web/core/utils/patch";
|
||||||
import { useState } from "@odoo/owl";
|
import { useState } from "@odoo/owl";
|
||||||
import { useService } from "@web/core/utils/hooks";
|
import { useService } from "@web/core/utils/hooks";
|
||||||
@ -32,4 +32,5 @@ patch(ProductScreen.prototype, {
|
|||||||
ProductScreen.components = {
|
ProductScreen.components = {
|
||||||
...ProductScreen.components,
|
...ProductScreen.components,
|
||||||
NoteButton,
|
NoteButton,
|
||||||
|
InternalNoteButton,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -43,7 +43,7 @@
|
|||||||
<xpath expr="//Numpad" position="before">
|
<xpath expr="//Numpad" position="before">
|
||||||
<div t-if="portraitMode.isPortrait and !currentOrder.isEmpty() and pos.getOrder()?.uiState.selected_orderline_uuid"
|
<div t-if="portraitMode.isPortrait and !currentOrder.isEmpty() and pos.getOrder()?.uiState.selected_orderline_uuid"
|
||||||
class="portrait-numpad-actions d-flex gap-2 mb-2 w-100">
|
class="portrait-numpad-actions d-flex gap-2 mb-2 w-100">
|
||||||
<NoteButton label="'Note'" class="'btn btn-outline-primary flex-fill d-flex align-items-center justify-content-center gap-2 py-3 fs-5 fw-bold rounded-3'" />
|
<InternalNoteButton label="'Note'" class="'btn btn-outline-primary flex-fill d-flex align-items-center justify-content-center gap-2 py-3 fs-5 fw-bold rounded-3'" />
|
||||||
<button class="btn btn-outline-danger flex-fill d-flex align-items-center justify-content-center gap-2 py-3 fs-5 fw-bold rounded-3"
|
<button class="btn btn-outline-danger flex-fill d-flex align-items-center justify-content-center gap-2 py-3 fs-5 fw-bold rounded-3"
|
||||||
t-on-click="onDeleteLine">
|
t-on-click="onDeleteLine">
|
||||||
<i class="fa fa-trash-o fa-lg"/>Delete
|
<i class="fa fa-trash-o fa-lg"/>Delete
|
||||||
|
|||||||
14
static/src/customer_display/customer_display_patch.js
Normal file
14
static/src/customer_display/customer_display_patch.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
import { CustomerDisplay } from "@point_of_sale/customer_display/customer_display";
|
||||||
|
import { patch } from "@web/core/utils/patch";
|
||||||
|
|
||||||
|
patch(CustomerDisplay.prototype, {
|
||||||
|
getInternalNotes() {
|
||||||
|
try {
|
||||||
|
return JSON.parse(this.line.internalNote || "[]");
|
||||||
|
} catch (e) {
|
||||||
|
return [{ text: this.line.internalNote, colorIndex: 0 }];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user