[IMP] pos_self_order: add barcode scanning

In this commit we implement barcode scanning functionality
in the `pos_self_order` module.

This is useful for kiosk devices in shops.

closes odoo/odoo#148480

Task: 3637834
Signed-off-by: Joseph Caburnay (jcb) <jcb@odoo.com>
This commit is contained in:
vlst-odoo
2024-01-11 17:00:45 +00:00
parent 5924b75dbd
commit a0496586ff
6 changed files with 92 additions and 77 deletions
+1
View File
@@ -55,6 +55,7 @@
'bus/static/src/bus_parameters_service.js',
'bus/static/src/multi_tab_service.js',
'bus/static/src/workers/*',
'barcodes/static/src/barcode_service.js',
'web/static/lib/bootstrap/js/dist/dom/data.js',
'web/static/lib/bootstrap/js/dist/dom/event-handler.js',
'web/static/lib/bootstrap/js/dist/dom/manipulator.js',
@@ -196,6 +196,7 @@ class ProductProduct(models.Model):
"is_pos_groupable": self.uom_id.is_pos_groupable,
"write_date": self.write_date.timestamp(),
"self_order_available": self.self_order_available,
"barcode": self.barcode,
}
def _get_self_order_data(self, pos_config: PosConfig) -> List[Dict]:
@@ -35,6 +35,7 @@ export class Product extends Reactive {
this.is_pos_groupable = product.is_pos_groupable;
this.write_date = product.write_date;
this.self_order_available = product.self_order_available;
this.barcode = product.barcode;
// data
this.showPriceTaxIncluded = showPriceTaxIncluded;
@@ -27,7 +27,7 @@ export class ComboPage extends Component {
});
if (!this.props.product) {
this.router.navigate("productList");
this.router.navigate("product_list");
return;
}
@@ -3,10 +3,7 @@
import { Component, onWillUnmount, useState, useSubEnv } from "@odoo/owl";
import { useSelfOrder } from "@pos_self_order/app/self_order_service";
import { AttributeSelection } from "@pos_self_order/app/components/attribute_selection/attribute_selection";
import { Line } from "@pos_self_order/app/models/line";
import { useService } from "@web/core/utils/hooks";
import { attributeFlatter } from "@pos_self_order/app/utils";
import { constructFullProductName } from "@point_of_sale/utils";
export class ProductPage extends Component {
static template = "pos_self_order.ProductPage";
@@ -19,7 +16,7 @@ export class ProductPage extends Component {
useSubEnv({ selectedValues: {}, customValues: {}, editable: this.editableProductLine });
if (!this.props.product) {
this.router.navigate("productList");
this.router.navigate("product_list");
return;
}
@@ -80,75 +77,19 @@ export class ProductPage extends Component {
return increase ? this.state.qty++ : this.state.qty--;
}
orderlineCanBeMerged() {
if (this.props.product.pos_combo_ids.length) {
return false;
}
const editedLine = this.selfOrder.editedLine;
if (editedLine) {
return editedLine;
}
const line = this.selfOrder.currentOrder.lines.find(
(l) =>
JSON.stringify(l.attribute_value_ids.sort()) ===
JSON.stringify(attributeFlatter(this.env.selectedValues).sort()) &&
l.customer_note === this.state.customer_note &&
l.product_id === this.product.id
);
return line || false;
}
async addToCart() {
const lines = this.selfOrder.currentOrder.lines;
const lineToMerge = this.orderlineCanBeMerged();
if (lineToMerge) {
lineToMerge.attribute_value_ids = attributeFlatter(this.env.selectedValues);
lineToMerge.customer_note = this.state.customer_note;
lineToMerge.full_product_name = this.product.name;
if (this.selfOrder.editedLine) {
lineToMerge.qty = this.state.qty;
} else {
lineToMerge.qty += this.state.qty;
}
} else {
const mainLine = new Line({
id: lineToMerge ? lineToMerge.id : null,
uuid: lineToMerge ? lineToMerge.uuid : null,
qty: this.state.qty,
product_id: this.product.id,
customer_note: this.state.customer_note,
custom_attribute_value_ids: Object.values(this.env.customValues),
attribute_value_ids: attributeFlatter(this.env.selectedValues),
});
mainLine.full_product_name = constructFullProductName(
mainLine,
this.selfOrder.attributeValueById,
this.product.name
);
lines.push(mainLine);
}
// If a command line does not have a quantity greater than 0, we consider it deleted
await this.selfOrder.getPricesFromServer();
this.selfOrder.currentOrder.lines = lines.filter((o) => o.qty > 0);
if (this.selfOrder.currentOrder.lines.length === 0) {
this.router.navigate("productList");
} else {
this.router.back();
}
}
get showQtyButtons() {
return this.props.product.self_order_available;
}
addToCart() {
this.selfOrder.addToCart(
this.props.product,
this.state.qty,
this.state.customer_note,
this.env.selectedValues,
this.env.customValues
);
this.router.back();
}
isEveryValueSelected() {
return Object.values(this.state.selectedValues).find((value) => !value) == false;
@@ -7,7 +7,7 @@ import { Product } from "@pos_self_order/app/models/product";
import { Combo } from "@pos_self_order/app/models/combo";
import { session } from "@web/session";
import { getColor } from "@web/core/colors/colors";
import { categorySorter, attributeFormatter } from "@pos_self_order/app/utils";
import { categorySorter, attributeFormatter, attributeFlatter } from "@pos_self_order/app/utils";
import { Order } from "@pos_self_order/app/models/order";
import { batched } from "@web/core/utils/timing";
import { useState, markup } from "@odoo/owl";
@@ -16,7 +16,8 @@ import { registry } from "@web/core/registry";
import { cookie } from "@web/core/browser/cookie";
import { formatDateTime } from "@web/core/l10n/dates";
import { printerService } from "@point_of_sale/app/printer/printer_service";
import { qrCodeSrc } from "@point_of_sale/utils";
import { constructFullProductName, qrCodeSrc } from "@point_of_sale/utils";
import { Line } from "./models/line";
export class SelfOrder extends Reactive {
constructor(...args) {
@@ -24,13 +25,14 @@ export class SelfOrder extends Reactive {
this.ready = this.setup(...args).then(() => this);
}
async setup(env, { rpc, notification, router, printer }) {
async setup(env, { rpc, notification, router, printer, barcode }) {
// services
this.notification = notification;
this.router = router;
this.env = env;
this.rpc = rpc;
this.printer = printer;
this.barcode = barcode;
// data
Object.assign(this, {
@@ -60,6 +62,75 @@ export class SelfOrder extends Reactive {
} else {
await this.initMobileData();
}
barcode.bus.addEventListener("barcode_scanned", (ev) => {
if (!this.ordering) {
this.notification.add(_t("We're currently closed"), {
type: "danger",
});
return;
}
const product = Object.values(this.productByIds).filter(
(p) => p.barcode === ev.detail.barcode
)?.[0];
if (!product) {
this.notification.add(_t("Product not found"), {
type: "danger",
});
return;
}
if (!product.self_order_available) {
this.notification.add(_t("Product is not available"), {
type: "danger",
});
return;
}
if (product.attributes.length) {
this.router.navigate("product", { id: product.id });
return;
}
this.addToCart(product, 1, "", {}, {});
this.router.navigate("cart");
});
}
async addToCart(product, qty, customer_note, selectedValues, customValues) {
const lineToMerge =
!product.pos_combo_ids.length &&
(this.editedLine ||
this.currentOrder.lines.find(
(l) =>
JSON.stringify(l.attribute_value_ids.sort()) ===
JSON.stringify(attributeFlatter(selectedValues).sort()) &&
l.customer_note === customer_note &&
l.product_id === product.id
));
if (lineToMerge) {
lineToMerge.attribute_value_ids = attributeFlatter(selectedValues);
lineToMerge.customer_note = customer_note;
lineToMerge.full_product_name = product.name;
if (this.editedLine) {
lineToMerge.qty = qty;
} else {
lineToMerge.qty += qty;
}
} else {
const mainLine = new Line({
qty,
product_id: product.id,
customer_note,
custom_attribute_value_ids: Object.values(customValues),
attribute_value_ids: attributeFlatter(selectedValues),
});
mainLine.full_product_name = constructFullProductName(
mainLine,
this.attributeValueById,
product.name
);
this.currentOrder.lines.push(mainLine);
}
await this.getPricesFromServer();
// If a command line does not have a quantity greater than 0, we consider it deleted
this.currentOrder.lines = this.currentOrder.lines.filter((o) => o.qty > 0);
}
async confirmOrder() {
@@ -587,9 +658,9 @@ export class SelfOrder extends Reactive {
}
export const selfOrderService = {
dependencies: ["rpc", "notification", "router", "printer"],
async start(env, { rpc, notification, router, printer }) {
return new SelfOrder(env, { rpc, notification, router, printer }).ready;
dependencies: ["rpc", "notification", "router", "printer", "barcode"],
async start(env, { rpc, notification, router, printer, barcode }) {
return new SelfOrder(env, { rpc, notification, router, printer, barcode }).ready;
},
};