mirror of
https://github.com/bringout/oca-ocb-pos.git
synced 2026-04-24 12:02:07 +02:00
19.0 vanilla
This commit is contained in:
parent
6e54c1af6c
commit
3ca647e428
1087 changed files with 132065 additions and 108499 deletions
|
|
@ -0,0 +1,19 @@
|
|||
import * as Dialog from "@point_of_sale/../tests/generic_helpers/dialog_util";
|
||||
|
||||
export function isTabActive(tabText) {
|
||||
return [
|
||||
{
|
||||
content: "Check if the active tab contains the text" + tabText,
|
||||
trigger: `.pos-leftheader span.text-bg-info:contains(${tabText})`,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function closePrintingWarning() {
|
||||
return [
|
||||
{
|
||||
...Dialog.confirm(),
|
||||
content: "acknowledge printing error ( because we don't have printer in the test. )",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
/* global posmodel */
|
||||
|
||||
const getData = ({ lineProductName, productName, partnerName } = {}) => {
|
||||
const order = posmodel.models["pos.order"].find((o) => o.pos_reference.includes("device_sync"));
|
||||
|
||||
let partner = null;
|
||||
if (partnerName) {
|
||||
partner = posmodel.models["res.partner"].find((p) => p.name === partnerName);
|
||||
}
|
||||
|
||||
let line = null;
|
||||
if (lineProductName) {
|
||||
line = order.lines.find((l) => l.product_id.display_name === lineProductName);
|
||||
}
|
||||
|
||||
let product = null;
|
||||
if (productName) {
|
||||
product = posmodel.models["product.product"].find((p) => p.display_name === productName);
|
||||
}
|
||||
|
||||
return { order, line, product, partner };
|
||||
};
|
||||
|
||||
const notify = async () => {
|
||||
const orm = posmodel.env.services.orm;
|
||||
await orm.call("pos.config", "notify_synchronisation", [
|
||||
posmodel.config.id,
|
||||
posmodel.session.id,
|
||||
999,
|
||||
]);
|
||||
};
|
||||
|
||||
const getLineData = (product, order, quantity) => ({
|
||||
name: product.display_name,
|
||||
order_id: order.id,
|
||||
product_id: product.id,
|
||||
price_unit: product.lst_price,
|
||||
price_subtotal: product.lst_price * quantity,
|
||||
price_subtotal_incl: product.lst_price * quantity,
|
||||
discount: 0,
|
||||
qty: quantity,
|
||||
});
|
||||
|
||||
// In the point-of-sale code, we consider that synchronization is necessary
|
||||
// when the write_date of the local order is smaller than that of the server.
|
||||
// To prevent the PoS from ignoring our synchronization.
|
||||
const writeOnOrder = async (order, data) => {
|
||||
const sec = new Date(order.write_date).getMilliseconds() + 1010;
|
||||
const timeout = Math.ceil(sec - new Date().getMilliseconds(), 0);
|
||||
await new Promise((res) => setTimeout(res, timeout));
|
||||
const orm = posmodel.env.services.orm;
|
||||
await orm.write("pos.order", [order.id], data);
|
||||
await notify();
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} productName
|
||||
* @param {number} quantity
|
||||
* @returns StepSchema[]
|
||||
*/
|
||||
export function createNewLine(productName, quantity) {
|
||||
return [
|
||||
{
|
||||
trigger: "body",
|
||||
run: async () => {
|
||||
const { order, product } = getData({ productName });
|
||||
await writeOnOrder(order, {
|
||||
lines: [[0, 0, getLineData(product, order, quantity)]],
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} productName
|
||||
* @param {number} quantity
|
||||
* @returns StepSchema[]
|
||||
*/
|
||||
export function changeLineQuantity(productName, quantity) {
|
||||
return [
|
||||
{
|
||||
trigger: "body",
|
||||
run: async () => {
|
||||
const { order, line } = getData({ lineProductName: productName });
|
||||
await writeOnOrder(order, {
|
||||
lines: [[1, line.id, getLineData(line.product_id, order, quantity)]],
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} partnerName
|
||||
* @returns StepSchema[]
|
||||
*/
|
||||
export function changePartner(partnerName) {
|
||||
return [
|
||||
{
|
||||
trigger: "body",
|
||||
run: async () => {
|
||||
const { order, partner } = getData({ partnerName });
|
||||
await writeOnOrder(order, {
|
||||
partner_id: partner.id,
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function markOrderAsPaid() {
|
||||
return [
|
||||
{
|
||||
trigger: "body",
|
||||
run: async () => {
|
||||
const { order } = getData({});
|
||||
await writeOnOrder(order, {
|
||||
state: "paid",
|
||||
amount_paid: order.amount_total,
|
||||
amount_return: 0,
|
||||
amount_tax: 0,
|
||||
amount_total: 0,
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function createNewOrderOnTable(tableName, productTuple) {
|
||||
return [
|
||||
{
|
||||
trigger: "body",
|
||||
run: async () => {
|
||||
const orm = posmodel.env.services.orm;
|
||||
const prices = {
|
||||
amount_paid: 0,
|
||||
amount_return: 0,
|
||||
amount_tax: 0,
|
||||
amount_total: 0,
|
||||
};
|
||||
const lines = productTuple.map(([productName, quantity]) => {
|
||||
const product = posmodel.models["product.product"].find(
|
||||
(p) => p.display_name === productName
|
||||
);
|
||||
const lineData = getLineData(product, false, quantity);
|
||||
prices.amount_paid += lineData.price_subtotal;
|
||||
prices.amount_return += lineData.price_subtotal;
|
||||
return [
|
||||
0,
|
||||
0,
|
||||
{
|
||||
...lineData,
|
||||
price_subtotal: lineData.price_subtotal,
|
||||
price_subtotal_incl: lineData.price_subtotal_incl,
|
||||
},
|
||||
];
|
||||
});
|
||||
const table = posmodel.models["restaurant.table"].find(
|
||||
(t) => t.table_number === parseInt(tableName)
|
||||
);
|
||||
await orm.create("pos.order", [
|
||||
{
|
||||
...prices,
|
||||
pos_reference: `device_sync_${Math.floor(Math.random() * 9999)}`,
|
||||
session_id: posmodel.session.id,
|
||||
table_id: table.id,
|
||||
lines,
|
||||
},
|
||||
]);
|
||||
await notify();
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
import * as Numpad from "@point_of_sale/../tests/generic_helpers/numpad_util";
|
||||
import { negate } from "@point_of_sale/../tests/generic_helpers/utils";
|
||||
|
||||
export function table({ name, withClass = "", withoutClass, run = () => {}, numOfSeats }) {
|
||||
let trigger = `.floor-map .table${withClass}`;
|
||||
if (withoutClass) {
|
||||
trigger += `:not(${withoutClass})`;
|
||||
}
|
||||
if (name) {
|
||||
trigger += `:has(.label:contains("${name}"))`;
|
||||
}
|
||||
return {
|
||||
content: `Check table with attributes: ${JSON.stringify(arguments[0])}`,
|
||||
trigger,
|
||||
run: typeof run === "string" ? run : (helpers) => run(helpers, trigger),
|
||||
};
|
||||
}
|
||||
export const clickTable = (name) => table({ name, run: "click" });
|
||||
export const hasTable = (name) => table({ name });
|
||||
export const selectedTableIs = (name) => table({ name, withClass: ".selected" });
|
||||
export const ctrlClickTable = (name) =>
|
||||
table({
|
||||
name,
|
||||
run: (helpers, trigger) => {
|
||||
helpers
|
||||
.queryOne(trigger)
|
||||
.dispatchEvent(new MouseEvent("click", { bubbles: true, ctrlKey: true }));
|
||||
},
|
||||
});
|
||||
export function clickFloor(name) {
|
||||
return [
|
||||
{
|
||||
content: `click '${name}' floor`,
|
||||
trigger: `.floor-selector .button-floor:contains("${name}")`,
|
||||
run: "click",
|
||||
},
|
||||
];
|
||||
}
|
||||
export function hasFloor(name) {
|
||||
return [
|
||||
{
|
||||
content: `has '${name}' floor`,
|
||||
trigger: `.floor-selector .button-floor:contains("${name}")`,
|
||||
},
|
||||
];
|
||||
}
|
||||
export function hasNotFloor(name) {
|
||||
return [
|
||||
{
|
||||
content: `has not '${name}' floor`,
|
||||
trigger: negate(`.floor-selector .button-floor:contains("${name}")`),
|
||||
},
|
||||
];
|
||||
}
|
||||
export function clickEditButton(button) {
|
||||
return [
|
||||
{
|
||||
content: "add table",
|
||||
trigger: `.edit-buttons i[aria-label="${button}"]`,
|
||||
run: "click",
|
||||
},
|
||||
];
|
||||
}
|
||||
export function clickSaveEditButton() {
|
||||
return [
|
||||
{
|
||||
content: "add table",
|
||||
trigger: '.edit-buttons button:contains("Save")',
|
||||
run: "click",
|
||||
},
|
||||
{
|
||||
trigger: negate(".edit-buttons button:contains('Save')"),
|
||||
},
|
||||
];
|
||||
}
|
||||
export function clickTableSelectorButton() {
|
||||
return [
|
||||
{
|
||||
content: "click on table selector button",
|
||||
trigger: ".floor-screen .right-buttons button i.fa-hashtag",
|
||||
run: "click",
|
||||
},
|
||||
];
|
||||
}
|
||||
export function goTo(name) {
|
||||
return [
|
||||
...clickTableSelectorButton(),
|
||||
...Numpad.enterValue(name),
|
||||
{
|
||||
trigger: ".floor-screen .right-buttons .jump-button",
|
||||
run: "click",
|
||||
},
|
||||
];
|
||||
}
|
||||
export function selectedFloorIs(name) {
|
||||
return [
|
||||
{
|
||||
content: `selected floor is '${name}'`,
|
||||
trigger: `.button-floor.active:contains("${name}")`,
|
||||
},
|
||||
];
|
||||
}
|
||||
export function orderCountSyncedInTableIs(table, count) {
|
||||
if (count === 0 || count === "0") {
|
||||
return [
|
||||
{
|
||||
trigger: `.floor-map .table:has(.label:contains("${table}")):not(:has(.order-count))`,
|
||||
},
|
||||
];
|
||||
}
|
||||
return [
|
||||
{
|
||||
trigger: `.floor-map .table:has(.label:contains("${table}")):has(.order-count:contains("${count}"))`,
|
||||
},
|
||||
];
|
||||
}
|
||||
export function isShown() {
|
||||
return [
|
||||
{
|
||||
trigger: ".floor-map",
|
||||
},
|
||||
];
|
||||
}
|
||||
export function linkTables(child, parent) {
|
||||
async function drag_multiple_and_then_drop(helpers, ...drags) {
|
||||
const dragEffectDelay = async () => {
|
||||
console.log(helpers.delay);
|
||||
await new Promise((resolve) => requestAnimationFrame(resolve));
|
||||
await new Promise((resolve) => setTimeout(resolve, helpers.delay));
|
||||
};
|
||||
const element = helpers.anchor;
|
||||
const { drag } = odoo.loader.modules.get("@odoo/hoot-dom");
|
||||
const { drop, moveTo } = await drag(element);
|
||||
await dragEffectDelay();
|
||||
await helpers.hover(element, {
|
||||
position: {
|
||||
top: 20,
|
||||
left: 20,
|
||||
},
|
||||
relative: true,
|
||||
});
|
||||
await dragEffectDelay();
|
||||
for (const [selector, options] of drags) {
|
||||
console.log("Selector", selector, options);
|
||||
const target = await helpers.waitFor(selector, {
|
||||
visible: true,
|
||||
timeout: 500,
|
||||
});
|
||||
await moveTo(target, options);
|
||||
await dragEffectDelay();
|
||||
}
|
||||
await drop();
|
||||
await dragEffectDelay();
|
||||
}
|
||||
return {
|
||||
content: `Drag table ${child} onto table ${parent} in order to link them`,
|
||||
trigger: table({ name: child }).trigger,
|
||||
async run(helpers) {
|
||||
helpers.delay = 500;
|
||||
await drag_multiple_and_then_drop(
|
||||
helpers,
|
||||
[
|
||||
table({ name: parent }).trigger,
|
||||
{
|
||||
position: "top",
|
||||
relative: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
table({ name: parent }).trigger,
|
||||
{
|
||||
position: "center",
|
||||
relative: true,
|
||||
},
|
||||
]
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
export function unlinkTables(child, parent) {
|
||||
return {
|
||||
content: `Drag table ${child} away from table ${parent} to unlink them`,
|
||||
trigger: table({ name: child }).trigger,
|
||||
async run(helpers) {
|
||||
await helpers.drag_and_drop(`div.floor-map`, {
|
||||
position: {
|
||||
bottom: 0,
|
||||
},
|
||||
relative: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
export function isChildTable(child) {
|
||||
return {
|
||||
content: `Verify that table ${child} is a child table`,
|
||||
trigger: table({ name: child }).trigger + ` .info.opacity-25`,
|
||||
};
|
||||
}
|
||||
export function clickNewOrder() {
|
||||
return { trigger: ".new-order", run: "click" };
|
||||
}
|
||||
|
||||
export function addFloor(floorName) {
|
||||
return [
|
||||
{
|
||||
trigger: ".floor-selector button i[aria-label='Add Floor']",
|
||||
run: "click",
|
||||
},
|
||||
{
|
||||
trigger: ".modal-body textarea",
|
||||
run: `edit ${floorName}`,
|
||||
},
|
||||
{
|
||||
trigger: ".modal-footer button.btn-primary",
|
||||
run: "click",
|
||||
},
|
||||
...selectedFloorIs(floorName),
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
import * as Order from "@point_of_sale/../tests/generic_helpers/order_widget_util";
|
||||
import * as ProductScreen from "@point_of_sale/../tests/pos/tours/utils/product_screen_util";
|
||||
import * as TextInputPopup from "@point_of_sale/../tests/generic_helpers/text_input_popup_util";
|
||||
import * as Dialog from "@point_of_sale/../tests/generic_helpers/dialog_util";
|
||||
|
||||
export function clickOrderButton() {
|
||||
return [
|
||||
{
|
||||
content: "click order button",
|
||||
trigger: ".actionpad .submit-order",
|
||||
run: "click",
|
||||
},
|
||||
];
|
||||
}
|
||||
export function orderlinesHaveNoChange() {
|
||||
return Order.doesNotHaveLine({ withClass: ".has-change" });
|
||||
}
|
||||
export function orderlineIsToOrder(name) {
|
||||
return Order.hasLine({
|
||||
productName: name,
|
||||
withClass: ".orderline.has-change",
|
||||
});
|
||||
}
|
||||
export function guestNumberIs(num) {
|
||||
return [
|
||||
...ProductScreen.clickControlButtonMore(),
|
||||
{
|
||||
content: `guest number is ${num}`,
|
||||
trigger: ProductScreen.controlButtonTrigger("Guests") + `:contains(${num})`,
|
||||
},
|
||||
];
|
||||
}
|
||||
export function OrderButtonNotContain(data) {
|
||||
const steps = [
|
||||
{
|
||||
isActive: ["desktop"],
|
||||
content: "check order button not contain data",
|
||||
trigger: `.product-screen .submit-order:not(:contains("${data}"))`,
|
||||
run: function () {}, // it's a check
|
||||
},
|
||||
];
|
||||
return steps;
|
||||
}
|
||||
export function clickCourseButton() {
|
||||
return [
|
||||
{
|
||||
content: "click course button",
|
||||
trigger: `.course-btn`,
|
||||
run: "click",
|
||||
},
|
||||
];
|
||||
}
|
||||
export function selectCourseLine(name) {
|
||||
return [
|
||||
{
|
||||
content: `select course ${name}`,
|
||||
trigger: `.order-course-name:contains(${name})`,
|
||||
run: "click",
|
||||
},
|
||||
];
|
||||
}
|
||||
export function fireCourseButton() {
|
||||
return [
|
||||
{
|
||||
content: "fire course button",
|
||||
trigger: `.actionpad .fire-btn`,
|
||||
run: "click",
|
||||
},
|
||||
];
|
||||
}
|
||||
export function fireCourseButtonHighlighted(courseName) {
|
||||
return [
|
||||
{
|
||||
content: "fire course button highlighted",
|
||||
trigger: `.actionpad .fire-btn.btn-primary:contains('Fire ${courseName}')`,
|
||||
},
|
||||
];
|
||||
}
|
||||
export function payButtonNotHighlighted() {
|
||||
return [
|
||||
{
|
||||
content: "pay button not highlighted",
|
||||
trigger:
|
||||
".actionpad .pay-order-button:not('.highlight'):not('.btn-primary'):contains('Payment')",
|
||||
},
|
||||
];
|
||||
}
|
||||
export function setTab(name) {
|
||||
return [
|
||||
{
|
||||
content: `set tab to ${name}`,
|
||||
trigger: `.product-screen .new-tab`,
|
||||
run: "click",
|
||||
},
|
||||
TextInputPopup.inputText(name),
|
||||
Dialog.confirm(),
|
||||
];
|
||||
}
|
||||
|
||||
export function releaseTable() {
|
||||
return [
|
||||
{
|
||||
content: "release table",
|
||||
trigger: ".product-screen .leftpane .unbook-table",
|
||||
run: "click",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function addCourse() {
|
||||
return {
|
||||
content: `click Course button`,
|
||||
trigger: ProductScreen.controlButtonTrigger("Course"),
|
||||
run: "click",
|
||||
};
|
||||
}
|
||||
|
||||
export function transferCourseTo(destCourse) {
|
||||
return [
|
||||
...ProductScreen.clickControlButton("Transfer course"),
|
||||
{
|
||||
content: `click ${destCourse} from available courses`,
|
||||
trigger: `.modal-body button:contains(${destCourse})`,
|
||||
run: "click",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function discardOrderWarningDialog() {
|
||||
return [
|
||||
{
|
||||
trigger: `.modal-dialog:contains("It seems that the order has not been sent. Would you like to send it to preparation?")`,
|
||||
},
|
||||
Dialog.discard(),
|
||||
];
|
||||
}
|
||||
|
||||
export function confirmOrderWarningDialog() {
|
||||
return [
|
||||
{
|
||||
trigger: `.modal-dialog:contains("It seems that the order has not been sent. Would you like to send it to preparation?")`,
|
||||
},
|
||||
Dialog.confirm(),
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
import * as Order from "@point_of_sale/../tests/generic_helpers/order_widget_util";
|
||||
|
||||
export function clickOrderline(productName) {
|
||||
return Order.hasLine({ productName, run: "click" });
|
||||
}
|
||||
export function clickBack() {
|
||||
return [
|
||||
{
|
||||
content: "click back button",
|
||||
trigger: `.splitbill-screen .button.back`,
|
||||
run: "click",
|
||||
},
|
||||
];
|
||||
}
|
||||
export function clickButton(name) {
|
||||
return [
|
||||
{
|
||||
content: `click '${name}' button`,
|
||||
trigger: `.splitbill-screen .pay-button button:contains("${name}")`,
|
||||
run: "click",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function orderlineHas(name, totalQuantity, splitQuantity) {
|
||||
return Order.hasLine({
|
||||
productName: name,
|
||||
quantity: splitQuantity != 0 ? `${splitQuantity} / ${totalQuantity}` : totalQuantity,
|
||||
});
|
||||
}
|
||||
export function subtotalIs(amount) {
|
||||
return [
|
||||
{
|
||||
content: `total amount of split is '${amount}'`,
|
||||
trigger: `.splitbill-screen .order-info .subtotal:contains("${amount}")`,
|
||||
run: "click",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
export function clickPercentTip(percent) {
|
||||
return [
|
||||
{
|
||||
trigger: `.tip-screen .percentage:contains("${percent}")`,
|
||||
run: "click",
|
||||
},
|
||||
];
|
||||
}
|
||||
export function setCustomTip(amount) {
|
||||
return [
|
||||
{
|
||||
trigger: `.tip-screen .custom-amount-form input`,
|
||||
run: `edit ${amount}`,
|
||||
},
|
||||
];
|
||||
}
|
||||
export function clickSettle() {
|
||||
return [
|
||||
{
|
||||
trigger: `.button.highlight.next`,
|
||||
run: "click",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function isShown() {
|
||||
return [
|
||||
{
|
||||
trigger: ".pos .tip-screen",
|
||||
},
|
||||
];
|
||||
}
|
||||
export function totalAmountIs(amount) {
|
||||
return [
|
||||
{
|
||||
trigger: `.tip-screen .total-amount:contains("${amount}")`,
|
||||
},
|
||||
];
|
||||
}
|
||||
export function percentAmountIs(percent, amount) {
|
||||
return [
|
||||
{
|
||||
trigger: `.tip-screen .percentage:contains("${percent}") ~ .amount:contains("${amount}")`,
|
||||
},
|
||||
];
|
||||
}
|
||||
export function inputAmountIs(amount) {
|
||||
return [
|
||||
{
|
||||
trigger: `.tip-screen .custom-amount-form input[data-amount="${amount}"]`,
|
||||
},
|
||||
];
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue