diff --git a/addons/l10n_co_pos/static/src/overrides/components/order_receipt/order_receipt.xml b/addons/l10n_co_pos/static/src/overrides/components/order_receipt/order_receipt.xml index 7cb7fd75825..96a2aaf7cab 100644 --- a/addons/l10n_co_pos/static/src/overrides/components/order_receipt/order_receipt.xml +++ b/addons/l10n_co_pos/static/src/overrides/components/order_receipt/order_receipt.xml @@ -3,8 +3,8 @@ - -
+ +
diff --git a/addons/l10n_es_pos/static/src/overrides/components/order_receipt/order_receipt.xml b/addons/l10n_es_pos/static/src/overrides/components/order_receipt/order_receipt.xml deleted file mode 100644 index af60dd4c78a..00000000000 --- a/addons/l10n_es_pos/static/src/overrides/components/order_receipt/order_receipt.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - -
Simplified invoice
-
- -
-
-
-
()
- - - - -
Customer:
-
- : - -
-
- - - - diff --git a/addons/l10n_es_pos/static/src/overrides/components/receipt_header/receipt_header.xml b/addons/l10n_es_pos/static/src/overrides/components/receipt_header/receipt_header.xml new file mode 100644 index 00000000000..7606245f86d --- /dev/null +++ b/addons/l10n_es_pos/static/src/overrides/components/receipt_header/receipt_header.xml @@ -0,0 +1,26 @@ + + + + + + + +
Simplified invoice
+
+ +
+
+
+
()
+ + + + + +
Customer:
+
:
+
+ + + + diff --git a/addons/l10n_es_pos/static/src/overrides/models/pos_store.js b/addons/l10n_es_pos/static/src/overrides/models/pos_store.js new file mode 100644 index 00000000000..ca8cadcc1c4 --- /dev/null +++ b/addons/l10n_es_pos/static/src/overrides/models/pos_store.js @@ -0,0 +1,16 @@ +/** @odoo-module */ +import { patch } from "@web/core/utils/patch"; +import { PosStore } from "@point_of_sale/app/store/pos_store"; + +patch(PosStore.prototype, { + getReceiptHeaderData() { + return { + ...super.getReceiptHeaderData(...arguments), + is_spanish: this.config.is_spanish, + simplified_partner_id: this.config.simplified_partner_id[0], + is_l10n_es_simplified_invoice: this.get_order().is_l10n_es_simplified_invoice, + partner: this.get_order().get_partner(), + invoice_name: this.get_order().invoice_name, + }; + }, +}); diff --git a/addons/l10n_fr_pos_cert/static/src/xml/OrderReceipt.xml b/addons/l10n_fr_pos_cert/static/src/xml/OrderReceipt.xml index 896c5e9fa3c..52a7f73e85c 100644 --- a/addons/l10n_fr_pos_cert/static/src/xml/OrderReceipt.xml +++ b/addons/l10n_fr_pos_cert/static/src/xml/OrderReceipt.xml @@ -2,21 +2,21 @@ - +
-
+
- - - + + +
Old unit price: - / Units + / Units
diff --git a/addons/l10n_gcc_pos/static/src/overrides/app/screens/receipt_screen/receipt/order_receipt.xml b/addons/l10n_gcc_pos/static/src/overrides/app/screens/receipt_screen/receipt/order_receipt.xml index dcbe9f38270..ebd64798cec 100644 --- a/addons/l10n_gcc_pos/static/src/overrides/app/screens/receipt_screen/receipt/order_receipt.xml +++ b/addons/l10n_gcc_pos/static/src/overrides/app/screens/receipt_screen/receipt/order_receipt.xml @@ -1,9 +1,8 @@ - - + - +

@@ -15,99 +14,64 @@ - - !receiptData.receipt.is_gcc_country + + !props.data.is_gcc_country - -
-
Served by / خدم بواسطة
+ +
+
Served by / خدم بواسطة
+ - - !receiptData.receipt.is_gcc_country + + + !props.data.is_gcc_country - -
+ +
TOTAL / الإجمالي - +
- - !receiptData.receipt.is_gcc_country + + !props.data.is_gcc_country - -
+ +
Rounding / التقريب - + +
+
+ To Pay +
- - !receiptData.receipt.is_gcc_country + + !props.data.is_gcc_country - -
- To Pay / للسداد - -
-
- - - !receiptData.receipt.is_gcc_country - - -
+ +
CHANGE / الباقي - +
- - !receiptData.receipt.is_gcc_country + + !props.data.is_gcc_country - -
+ +
Discounts / الخصومات - +
- Total Taxes / إجمالي الضرائب + Total Taxes / إجمالي الضرائب Total - - - - !receiptData.receipt.is_gcc_country - - - -
- - - x - -
-
-
-
Taxes / الضرائب
: -
- -
-
-
- - - !receiptData.receipt.is_gcc_country - - -
-
Discount / الخصم
: % -
-
-
diff --git a/addons/l10n_gcc_pos/static/src/overrides/app/screens/receipt_screen/receipt_screen.js b/addons/l10n_gcc_pos/static/src/overrides/app/screens/receipt_screen/receipt_screen.js index a98c9330445..7f8d7df7bbc 100644 --- a/addons/l10n_gcc_pos/static/src/overrides/app/screens/receipt_screen/receipt_screen.js +++ b/addons/l10n_gcc_pos/static/src/overrides/app/screens/receipt_screen/receipt_screen.js @@ -4,13 +4,12 @@ import { Order } from "@point_of_sale/app/store/models"; import { patch } from "@web/core/utils/patch"; patch(Order.prototype, { - getOrderReceiptEnv() { - const receiptData = super.getOrderReceiptEnv(); - const receipt = receiptData.receipt; - const country = receiptData.order.pos.company.country; - receipt.is_gcc_country = country - ? ["SA", "AE", "BH", "OM", "QA", "KW"].includes(country && country.code) - : false; - return receiptData; + export_for_printing() { + return { + ...super.export_for_printing(), + is_gcc_country: ["SA", "AE", "BH", "OM", "QA", "KW"].includes( + this.pos.company.country?.code + ), + }; }, }); diff --git a/addons/l10n_in_pos/static/src/overrides/components/pos_receipt.xml b/addons/l10n_in_pos/static/src/overrides/components/pos_receipt.xml index 221a6fc849f..95f70834d7a 100644 --- a/addons/l10n_in_pos/static/src/overrides/components/pos_receipt.xml +++ b/addons/l10n_in_pos/static/src/overrides/components/pos_receipt.xml @@ -1,14 +1,14 @@ - - - + + +
-
- +
+
Phone: - +

@@ -17,8 +17,8 @@
- - + +
HSN Code: diff --git a/addons/l10n_in_pos/static/src/overrides/models/orderline.js b/addons/l10n_in_pos/static/src/overrides/models/orderline.js index 96acdb8984c..862c1778566 100644 --- a/addons/l10n_in_pos/static/src/overrides/models/orderline.js +++ b/addons/l10n_in_pos/static/src/overrides/models/orderline.js @@ -4,9 +4,10 @@ import { Orderline } from "@point_of_sale/app/store/models"; import { patch } from "@web/core/utils/patch"; patch(Orderline.prototype, { - export_for_printing() { - const line = super.export_for_printing(...arguments); - line.l10n_in_hsn_code = this.get_product().l10n_in_hsn_code; - return line; + getDisplayData() { + return { + ...super.getDisplayData(), + l10n_in_hsn_code: this.get_product().l10n_in_hsn_code, + }; }, }); diff --git a/addons/l10n_sa_pos/static/src/overrides/components/order_receipt/order_receipt.xml b/addons/l10n_sa_pos/static/src/overrides/components/order_receipt/order_receipt.xml index 25dd7760597..586e2a79715 100644 --- a/addons/l10n_sa_pos/static/src/overrides/components/order_receipt/order_receipt.xml +++ b/addons/l10n_sa_pos/static/src/overrides/components/order_receipt/order_receipt.xml @@ -1,21 +1,21 @@ - + - - + +
- + Simplified Tax Invoice - + فاتورة ضريبية مبسطة diff --git a/addons/point_of_sale/static/src/app/errors/popups/error_popup.js b/addons/point_of_sale/static/src/app/errors/popups/error_popup.js index ca5bc68ea22..d597adadd94 100644 --- a/addons/point_of_sale/static/src/app/errors/popups/error_popup.js +++ b/addons/point_of_sale/static/src/app/errors/popups/error_popup.js @@ -7,13 +7,6 @@ import { onMounted } from "@odoo/owl"; export class ErrorPopup extends AbstractAwaitablePopup { static template = "point_of_sale.ErrorPopup"; - static props = { - confirmText: String, - title: String, - body: String, - cancelKey: Boolean, - sound: Boolean, - }; static defaultProps = { confirmText: _t("Ok"), title: _t("Error"), diff --git a/addons/point_of_sale/static/src/app/generic_components/order_widget/order_widget.xml b/addons/point_of_sale/static/src/app/generic_components/order_widget/order_widget.xml index bd669bf7787..93f2f756a09 100644 --- a/addons/point_of_sale/static/src/app/generic_components/order_widget/order_widget.xml +++ b/addons/point_of_sale/static/src/app/generic_components/order_widget/order_widget.xml @@ -3,11 +3,8 @@
- - - - - + +
diff --git a/addons/point_of_sale/static/src/app/generic_components/orderline/orderline.js b/addons/point_of_sale/static/src/app/generic_components/orderline/orderline.js index 7f1e53b470a..8a89c293c89 100644 --- a/addons/point_of_sale/static/src/app/generic_components/orderline/orderline.js +++ b/addons/point_of_sale/static/src/app/generic_components/orderline/orderline.js @@ -19,6 +19,7 @@ export class Orderline extends Component { oldUnitPrice: { type: String, optional: true }, customerNote: { type: String, optional: true }, internalNote: { type: String, optional: true }, + "*": true, }, }, slots: { type: Object, optional: true }, diff --git a/addons/point_of_sale/static/src/app/generic_components/orderline/orderline.xml b/addons/point_of_sale/static/src/app/generic_components/orderline/orderline.xml index ffe5c3e4a2c..b184f57d631 100644 --- a/addons/point_of_sale/static/src/app/generic_components/orderline/orderline.xml +++ b/addons/point_of_sale/static/src/app/generic_components/orderline/orderline.xml @@ -5,7 +5,7 @@
  • - +
    @@ -16,15 +16,15 @@ x /
  • -
  • +
  • With a % discount
  • -
  • +
  • -
  • +
  • diff --git a/addons/point_of_sale/static/src/app/navbar/cash_move_popup/cash_move_popup.js b/addons/point_of_sale/static/src/app/navbar/cash_move_popup/cash_move_popup.js index d27e79dfe06..a03cbaa9985 100644 --- a/addons/point_of_sale/static/src/app/navbar/cash_move_popup/cash_move_popup.js +++ b/addons/point_of_sale/static/src/app/navbar/cash_move_popup/cash_move_popup.js @@ -1,14 +1,13 @@ /** @odoo-module */ import { _t } from "@web/core/l10n/translation"; -import { renderToElement } from "@web/core/utils/render"; import { useService } from "@web/core/utils/hooks"; import { parseFloat } from "@web/views/fields/parsers"; import { useState } from "@odoo/owl"; import { usePos } from "@point_of_sale/app/store/pos_hook"; import { AbstractAwaitablePopup } from "@point_of_sale/app/popup/abstract_awaitable_popup"; -import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup"; +import { CashMoveReceipt } from "@point_of_sale/app/navbar/cash_move_popup/cash_move_receipt/cash_move_receipt"; import { useAsyncLockedMethod } from "@point_of_sale/app/utils/hooks"; import { Input } from "@point_of_sale/app/generic_components/inputs/input/input"; @@ -22,6 +21,7 @@ export class CashMovePopup extends AbstractAwaitablePopup { this.orm = useService("orm"); this.pos = usePos(); this.hardwareProxy = useService("hardware_proxy"); + this.printer = useService("printer"); this.state = useState({ /** @type {'in'|'out'} */ type: "out", @@ -54,28 +54,13 @@ export class CashMovePopup extends AbstractAwaitablePopup { `${_t("Cash")} ${translatedType} - ${_t("Amount")}: ${formattedAmount}`, "CASH_DRAWER_ACTION" ); - - if (this.hardwareProxy.printer) { - const renderedReceipt = renderToElement("point_of_sale.CashMoveReceipt", { - _receipt: { - type, - reason, - amount, - translatedType, - formattedAmount, - cashier: this.pos.get_cashier(), - company: this.pos.company, - date: new Date().toLocaleString(), - }, - }); - const printResult = await this.hardwareProxy.printer.printReceipt(renderedReceipt); - if (!printResult.successful) { - this.popup.add(ErrorPopup, { - title: printResult.message.title, - body: printResult.message.body, - }); - } - } + await this.printer.print(CashMoveReceipt, { + reason, + translatedType, + formattedAmount, + headerData: this.pos.getReceiptHeaderData(), + date: new Date().toLocaleString(), + }); this.props.close(); this.notification.add( _t("Successfully made a cash %s of %s.", type, formattedAmount), diff --git a/addons/point_of_sale/static/src/app/navbar/cash_move_popup/cash_move_receipt/cash_move_receipt.js b/addons/point_of_sale/static/src/app/navbar/cash_move_popup/cash_move_receipt/cash_move_receipt.js new file mode 100644 index 00000000000..b942d6b60c5 --- /dev/null +++ b/addons/point_of_sale/static/src/app/navbar/cash_move_popup/cash_move_receipt/cash_move_receipt.js @@ -0,0 +1,16 @@ +/** @odoo-module **/ + +import { Component } from "@odoo/owl"; +import { ReceiptHeader } from "@point_of_sale/app/screens/receipt_screen/receipt/receipt_header/receipt_header"; + +export class CashMoveReceipt extends Component { + static template = "point_of_sale.CashMoveReceipt"; + static components = { ReceiptHeader }; + static props = { + reason: String, + translatedType: String, + formattedAmount: String, + headerData: Object, + date: String, + }; +} diff --git a/addons/point_of_sale/static/src/app/navbar/cash_move_popup/cash_move_receipt/cash_move_receipt.xml b/addons/point_of_sale/static/src/app/navbar/cash_move_popup/cash_move_receipt/cash_move_receipt.xml new file mode 100644 index 00000000000..ebd8149efee --- /dev/null +++ b/addons/point_of_sale/static/src/app/navbar/cash_move_popup/cash_move_receipt/cash_move_receipt.xml @@ -0,0 +1,27 @@ + + + + +
    + +
    + CASH + +
    +
    +
    + AMOUNT + +
    +
    + REASON + +
    +
    +
    +
    +
    +
    +
    + +
    diff --git a/addons/point_of_sale/static/src/app/navbar/cash_move_popup/cash_move_receipt_template.xml b/addons/point_of_sale/static/src/app/navbar/cash_move_popup/cash_move_receipt_template.xml deleted file mode 100644 index 7a08fa906d8..00000000000 --- a/addons/point_of_sale/static/src/app/navbar/cash_move_popup/cash_move_receipt_template.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - -
    - - -
    -
    - -

    - -

    -
    -
    -
    -
    - -
    - -
    Tel: - -
    -
    - -
    VAT: - -
    -
    - -
    - -
    -
    - -
    - -
    -
    - -
    -
    --------------------------------
    -
    Done by - -
    -
    -
    -
    -
    - -
    - CASH - -
    -
    -
    - AMOUNT - -
    -
    - REASON - -
    -
    -
    -
    -
    -
    -
    - -
    diff --git a/addons/point_of_sale/static/src/app/pos_app.xml b/addons/point_of_sale/static/src/app/pos_app.xml index d903d9fca44..3a4e1163316 100644 --- a/addons/point_of_sale/static/src/app/pos_app.xml +++ b/addons/point_of_sale/static/src/app/pos_app.xml @@ -3,7 +3,6 @@
    -
    diff --git a/addons/point_of_sale/static/src/app/printer/base_printer.js b/addons/point_of_sale/static/src/app/printer/base_printer.js index e0d7a3dccdb..d89fc6e7394 100644 --- a/addons/point_of_sale/static/src/app/printer/base_printer.js +++ b/addons/point_of_sale/static/src/app/printer/base_printer.js @@ -1,8 +1,7 @@ /* @odoo-module */ -/* global html2canvas */ import { _t } from "@web/core/l10n/translation"; - +import { htmlToCanvas } from "@point_of_sale/app/printer/render_service"; /** * Implements basic printer functions. */ @@ -28,7 +27,9 @@ export class BasePrinter { let image, printResult; while (this.receiptQueue.length > 0) { receipt = this.receiptQueue.shift(); - image = await this.htmlToImg(receipt); + image = this.processCanvas( + await htmlToCanvas(receipt, { addClass: "pos-receipt-print" }) + ); try { printResult = await this.sendPrintingJob(image); } catch { @@ -62,33 +63,6 @@ export class BasePrinter { return canvas.toDataURL("image/jpeg").replace("data:image/jpeg;base64,", ""); } - /** - * This function renders `el` as an image for printing assuming the following - * - a `div.pos-receipt-print` is present in the dom and will serve as the container - * - a `.pos-receipt` is the root element of `el`. - * @param {HTMLElement} el The element to be converted as image - * @returns {HTMLCanvasElement} - */ - async htmlToImg(el) { - const elClone = el.cloneNode(true); - const receiptContainer = document.querySelector(".pos-receipt-print"); - receiptContainer.appendChild(elClone); - const receipt = receiptContainer.querySelector(".pos-receipt"); - // Odoo RTL support automatically flip left into right but html2canvas - // won't work as expected if the receipt is aligned to the right of the - // screen so we need to flip it back. - const parent = receipt.parentElement; - parent.style.left = 0; - parent.style.right = "auto"; - const canvas = await html2canvas(receipt, { - height: Math.ceil(receipt.clientHeight), - width: Math.ceil(receipt.clientWidth), - scale: 1, - }); - receipt.remove(); - return this.processCanvas(canvas); - } - /** * Return value of this method will be the result of calling `printReceipt` * if it failed to connect to the IoT box. @@ -116,7 +90,7 @@ export class BasePrinter { "Please check if the printer is still connected. \n" + "Some browsers don't allow HTTP calls from websites to devices in the network (for security reasons). " + "If it is the case, you will need to follow Odoo's documentation for " + - "'Self-signed certificate for ePOS printers' and 'Secure connection (HTTPS)' to solve the issue" + "'Self-signed certificate for ePOS printers' and 'Secure connection (HTTPS)' to solve the issue. " ), }, }; diff --git a/addons/point_of_sale/static/src/app/printer/pos_printer_service.js b/addons/point_of_sale/static/src/app/printer/pos_printer_service.js new file mode 100644 index 00000000000..d53fdfa2bc0 --- /dev/null +++ b/addons/point_of_sale/static/src/app/printer/pos_printer_service.js @@ -0,0 +1,57 @@ +/** @odoo-module **/ + +import { _t } from "@web/core/l10n/translation"; +import { registry } from "@web/core/registry"; +import { ConfirmPopup } from "@point_of_sale/app/utils/confirm_popup/confirm_popup"; +import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup"; +import { PrinterService } from "@point_of_sale/app/printer/printer_service"; + +const posPrinterService = { + dependencies: ["hardware_proxy", "popup", "renderer"], + start(env, { hardware_proxy, popup, renderer }) { + return new PosPrinterService(env, { hardware_proxy, popup, renderer }); + }, +}; +class PosPrinterService extends PrinterService { + constructor(...args) { + super(...args); + this.setup(...args); + } + setup(env, { hardware_proxy, popup, renderer }) { + this.renderer = renderer; + this.hardware_proxy = hardware_proxy; + this.popup = popup; + this.device = hardware_proxy.printer; + } + printWeb() { + try { + return super.printWeb(...arguments); + } catch { + this.popup.add(ErrorPopup, { + title: _t("Printing is not supported on some browsers"), + body: _t("It is possible to print your tickets by making use of an IoT Box."), + }); + return false; + } + } + async printHtml() { + this.setPrinter(this.hardware_proxy.printer); + try { + return await super.printHtml(...arguments); + } catch (error) { + const { confirmed } = await this.popup.add(ConfirmPopup, { + title: error.title || _t("Printing error"), + body: error.body + _t("Do you want to print using the web printer? "), + }); + if (!confirmed) { + return false; + } + // We want to call the _printWeb when the popup is fully gone + // from the screen which happens after the next animation frame. + await new Promise(requestAnimationFrame); + return await this.printWeb(...arguments); + } + } +} + +registry.category("services").add("printer", posPrinterService); diff --git a/addons/point_of_sale/static/src/app/printer/printer_service.js b/addons/point_of_sale/static/src/app/printer/printer_service.js new file mode 100644 index 00000000000..79f93d3c9ed --- /dev/null +++ b/addons/point_of_sale/static/src/app/printer/printer_service.js @@ -0,0 +1,42 @@ +/** @odoo-module **/ + +export const printerService = { + dependencies: ["renderer"], + start(env, { renderer }) { + return new PrinterService(env, { renderer }); + }, +}; +export class PrinterService { + constructor(...args) { + this.setup(...args); + } + setup(env, { renderer }) { + this.renderer = renderer; + this.device = null; + } + setPrinter(newDevice) { + this.device = newDevice; + } + printWeb(el) { + this.renderer.whenMounted({ el, callback: window.print }); + return true; + } + async printHtml(el, { webPrintFallback = false } = {}) { + if (!this.device) { + return webPrintFallback && this.printWeb(el); + } + const printResult = await this.device.printReceipt(el); + if (printResult.successful) { + return true; + } + throw { + title: printResult.message.title || "Error", + body: printResult.message.body, + }; + } + async print(component, props, options) { + const el = await this.renderer.toHtml(component, props); + return await this.printHtml(el, options); + } + is = () => Boolean(this.device?.printReceipt); +} diff --git a/addons/point_of_sale/static/src/app/printer/render_service.js b/addons/point_of_sale/static/src/app/printer/render_service.js new file mode 100644 index 00000000000..7f00e8b225c --- /dev/null +++ b/addons/point_of_sale/static/src/app/printer/render_service.js @@ -0,0 +1,105 @@ +/** @odoo-module **/ +/* global html2canvas */ + +import { registry } from "@web/core/registry"; +import { Component, onRendered, reactive, useRef, xml } from "@odoo/owl"; + +export class RenderContainer extends Component { + static props = ["comp", "onRendered"]; + // the `.render-container` is used by other functions that need a + // place where to momentarily render some html code + // we should only intact with that div through the `whenMounted` function + static template = xml` +
    +
    + +
    +
    +
    `; + setup() { + this.ref = useRef("ref"); + onRendered(async () => { + // this timeout is needed in order to wait for the + // component to arrive in it's final state + await new Promise((r) => setTimeout(r, 100)); + this.props.onRendered(this.ref?.el?.firstChild); + }); + } +} +/** + * This service does for components what renderToElement does for templates. + * In order to obtain the html code that represents a component, we need to + * actually mount the respective component in the dom. + */ +const renderService = { + dependencies: [], + start() { + const toBeRenderedComponentData = reactive({}); + let elem, resolver; + registry.category("main_components").add("RenderContainer", { + Component: RenderContainer, + props: { + comp: toBeRenderedComponentData, + onRendered: (el) => { + elem = el; + resolver?.(); + // after obtaining the html code, we need to flush the + // contents of the RenderContainer component + toBeRenderedComponentData.component = null; + }, + }, + }); + const toHtml = async (component, props) => { + Object.assign(toBeRenderedComponentData, { component, props }); + // we wait for the RenderContainer component to actually + // render our component + await new Promise((r) => (resolver = r)); + return elem; + }; + const toCanvas = async (component, props, options) => { + return htmlToCanvas(await toHtml(component, props), options); + }; + const toJpeg = async (component, props, options) => { + const canvas = await toCanvas(component, props, options); + return canvas.toDataURL("image/jpeg").replace("data:image/jpeg;base64,", ""); + }; + const whenMounted = async ({ el, container, callback }) => { + container ||= document.querySelector(".render-container"); + return await applyWhenMounted({ el, container, callback }); + }; + return { toHtml, toCanvas, toJpeg, whenMounted }; + }, +}; +registry.category("services").add("renderer", renderService); + +/** + * This function is meant to be used for the cases where an + * action needs to be performed based on some html code, but + * that html code has to be in the dom for the action to be + * performed. ( for example calling html2canvas ) + */ +const applyWhenMounted = async ({ el, container, callback }) => { + const elClone = el.cloneNode(true); + container.appendChild(elClone); + const res = await callback(elClone); + elClone.remove(); + return res; +}; + +/** + * This function assumes that the `renderer` service is available. + */ +export const htmlToCanvas = async (el, options) => { + el.classList.add(options.addClass || ""); + // html2canvas expects the given element to be in the DOM + return await applyWhenMounted({ + el, + container: document.querySelector(".render-container"), + callback: async (el) => + await html2canvas(el, { + height: Math.ceil(el.clientHeight), + width: Math.ceil(el.clientWidth), + scale: 1, + }), + }); +}; diff --git a/addons/point_of_sale/static/src/app/screens/payment_screen/payment_screen.js b/addons/point_of_sale/static/src/app/screens/payment_screen/payment_screen.js index c05e9b81927..e02b66ad2b6 100644 --- a/addons/point_of_sale/static/src/app/screens/payment_screen/payment_screen.js +++ b/addons/point_of_sale/static/src/app/screens/payment_screen/payment_screen.js @@ -15,10 +15,10 @@ import { ConnectionLostError } from "@web/core/network/rpc_service"; import { PaymentScreenPaymentLines } from "@point_of_sale/app/screens/payment_screen/payment_lines/payment_lines"; import { PaymentScreenStatus } from "@point_of_sale/app/screens/payment_screen/payment_status/payment_status"; import { usePos } from "@point_of_sale/app/store/pos_hook"; -import { Component, useState, useRef, onMounted } from "@odoo/owl"; -import { renderToElement } from "@web/core/utils/render"; +import { Component, useState, onMounted } from "@odoo/owl"; import { Numpad } from "@point_of_sale/app/generic_components/numpad/numpad"; import { floatIsZero } from "@web/core/utils/numbers"; +import { OrderReceipt } from "@point_of_sale/app/screens/receipt_screen/receipt/order_receipt"; export class PaymentScreen extends Component { static template = "point_of_sale.PaymentScreen"; @@ -36,7 +36,7 @@ export class PaymentScreen extends Component { this.report = useService("report"); this.notification = useService("pos_notification"); this.hardwareProxy = useService("hardware_proxy"); - this.orderReceipt = useRef("order-receipt"); + this.printer = useService("printer"); this.payment_methods_from_config = this.pos.payment_methods.filter((method) => this.pos.config.payment_method_ids.includes(method.id) ); @@ -357,16 +357,12 @@ export class PaymentScreen extends Component { : true; if (this.hardwareProxy.printer && invoiced_finalized) { - const orderReceipt = renderToElement("point_of_sale.OrderReceipt", { - receiptData: this.pos.get_order().getOrderReceiptEnv(), - pos: this.pos, - env: this.env, + const printResult = await this.printer.print(OrderReceipt, { + data: this.pos.get_order().export_for_printing(), + formatCurrency: this.env.utils.formatCurrency, }); - // Need to await to have the result in case of automatic skip screen. - const printResult = await this.hardwareProxy.printer.printReceipt(orderReceipt); - - if (printResult.successful && this.pos.config.iface_print_skip_screen) { + if (printResult && this.pos.config.iface_print_skip_screen) { this.pos.removeOrder(this.currentOrder); this.pos.add_new_order(); nextScreen = "ProductScreen"; diff --git a/addons/point_of_sale/static/src/app/screens/receipt_screen/abstract_receipt_screen.js b/addons/point_of_sale/static/src/app/screens/receipt_screen/abstract_receipt_screen.js deleted file mode 100644 index 302a1146fc9..00000000000 --- a/addons/point_of_sale/static/src/app/screens/receipt_screen/abstract_receipt_screen.js +++ /dev/null @@ -1,64 +0,0 @@ -/** @odoo-module **/ - -import { _t } from "@web/core/l10n/translation"; -import { useService } from "@web/core/utils/hooks"; -import { ConfirmPopup } from "@point_of_sale/app/utils/confirm_popup/confirm_popup"; -import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup"; -import { Component, useRef } from "@odoo/owl"; - -/** - * This relies on the assumption that there is a reference to - * `order-receipt` so it is important to declare a `t-ref` to - * `order-receipt` in the template of the Component that extends - * this abstract component. - */ -export class AbstractReceiptScreen extends Component { - setup() { - this.orderReceipt = useRef("order-receipt"); - this.popup = useService("popup"); - this.hardwareProxy = useService("hardware_proxy"); - } - async _printReceipt() { - if (this.hardwareProxy.printer) { - const printResult = await this.hardwareProxy.printer.printReceipt( - this.orderReceipt.el.firstChild.firstChild - ); - if (printResult.successful) { - return true; - } else { - await this.popup.add(ErrorPopup, { - title: printResult.message.title, - body: printResult.message.body, - }); - const { confirmed } = await this.popup.add(ConfirmPopup, { - title: printResult.message.title, - body: _t("Do you want to print using the web printer?"), - }); - if (confirmed) { - // We want to call the _printWeb when the popup is fully gone - // from the screen which happens after the next animation frame. - await new Promise(requestAnimationFrame); - return await this._printWeb(); - } - return false; - } - } else { - return await this._printWeb(); - } - } - async _printWeb() { - try { - window.print(); - return true; - } catch { - await this.popup.add(ErrorPopup, { - title: _t("Printing is not supported on some browsers"), - body: _t( - "Printing is not supported on some browsers due to no default printing protocol " + - "is available. It is possible to print your tickets by making use of an IoT Box." - ), - }); - return false; - } - } -} diff --git a/addons/point_of_sale/static/src/app/screens/receipt_screen/receipt/order_receipt.js b/addons/point_of_sale/static/src/app/screens/receipt_screen/receipt/order_receipt.js new file mode 100644 index 00000000000..cc22d30a25f --- /dev/null +++ b/addons/point_of_sale/static/src/app/screens/receipt_screen/receipt/order_receipt.js @@ -0,0 +1,23 @@ +/** @odoo-module **/ + +import { Component } from "@odoo/owl"; +import { Orderline } from "@point_of_sale/app/generic_components/orderline/orderline"; +import { OrderWidget } from "@point_of_sale/app/generic_components/order_widget/order_widget"; +import { ReceiptHeader } from "@point_of_sale/app/screens/receipt_screen/receipt/receipt_header/receipt_header"; +import { omit } from "@web/core/utils/objects"; + +export class OrderReceipt extends Component { + static template = "point_of_sale.OrderReceipt"; + static components = { + Orderline, + OrderWidget, + ReceiptHeader, + }; + static props = { + data: Object, + formatCurrency: Function, + }; + omit(...args) { + return omit(...args); + } +} diff --git a/addons/point_of_sale/static/src/app/screens/receipt_screen/receipt/order_receipt.xml b/addons/point_of_sale/static/src/app/screens/receipt_screen/receipt/order_receipt.xml index 405e361dc9e..b6643500289 100644 --- a/addons/point_of_sale/static/src/app/screens/receipt_screen/receipt/order_receipt.xml +++ b/addons/point_of_sale/static/src/app/screens/receipt_screen/receipt/order_receipt.xml @@ -1,146 +1,114 @@ -
    - - -
    -
    - -

    - -

    -
    -
    -
    - -
    -
    - -
    Tel:
    -
    - -
    :
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    --------------------------------
    -
    Served by
    -
    -
    -
    -

    - - -
    - -
    + + + + +
  • + + +
  • +
      +
    • + SN +
    • +
    +
    +
    --------

    TOTAL - +
    - +
    Rounding - +
    To Pay - +


    - -
    - - -
    -
    -
    - -
    - CHANGE - +
    + + +
    + +
    + CHANGE +
    -
    - +
    Discounts - +
    -
    +
    VAT% VAT ExVAT Total - + % - - - + + + - + - - - + + +