diff --git a/fiscal_epos_print/README.rst b/fiscal_epos_print/README.rst new file mode 100644 index 000000000000..66e7342ba751 --- /dev/null +++ b/fiscal_epos_print/README.rst @@ -0,0 +1,150 @@ +============================================================= +ITA - Driver per stampanti fiscali compatibili ePOS-Print XML +============================================================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:ba5b8a62de8bec7b038dcedd9f21af3df1eda34c66681317bb8ecd4426015490 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fl10n--italy-lightgray.png?logo=github + :target: https://github.com/OCA/l10n-italy/tree/16.0/fiscal_epos_print + :alt: OCA/l10n-italy +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/l10n-italy-16-0/l10n-italy-16-0-fiscal_epos_print + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/l10n-italy&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +**Italiano** + +Questo modulo permette di stampare gli scontrini del punto vendita, su +stampanti fiscali Epson, tramite protocollo EPos + +Stampanti supportate: + +- FP81II +- FP90III + +**English** + +This module allows to print receipt of point of sale, on fiscal printers +Epson, via EPos protocol. + +Supported printers: + +- FP81II +- FP90III + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +**Italiano** + +- stampare la lista dei reparti della vostra stampante fiscale +- mappare le imposta di vendita di odoo con i gruppi di imposte - + dipartimenti della stampante fiscale, per ogni imposta di vendita in + odoo, usando il campo "Reparto sulla stampante fiscale 1~99" +- in odoo, utilizzare imposte incluse nel prezzo +- connettere la vostra stampante fiscale alla rete locale e recuperare + l'IP +- aprire la configurazione POS e impostare l'indirizzo IP e il seriale + della stampante nella sezione "Stampante Fiscale" +- è tutto, alla validazione del pagamento nella sessione POS, il + sistema stamperà lo scontrino fiscale. + +**English** + +- print list departments of your fiscal printer +- map odoo sale taxes with taxes groups - departments of fiscal + printer, for each sale tax on odoo, using field "Department on fiscal + printer 1~99" +- in odoo, use taxes included in price +- connect your fiscal printer to local network and find IP +- open POS configuration and fill Printer IP Address field +- that's all, at validation of payment on POS session, system prints + fiscal receipt. + +Known issues / Roadmap +====================== + +- Resi: + + - Aggiungere controllo "rendibilità" + - Stampare sullo scontrino un barcode identificativo, in modo da + generare il reso facendone la scansione + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Agile Business Group +* Leonardo Donelli +* TAKOBI +* Level Prime Srl + +Contributors +------------ + +- Leonardo Donelli +- `TAKOBI `__: + + - Lorenzo Battistini + +- Alessio Gerace +- Roberto Fichera +- Giuseppe Borruso + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-eLBati| image:: https://github.com/eLBati.png?size=40px + :target: https://github.com/eLBati + :alt: eLBati + +Current `maintainer `__: + +|maintainer-eLBati| + +This module is part of the `OCA/l10n-italy `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/fiscal_epos_print/__init__.py b/fiscal_epos_print/__init__.py new file mode 100644 index 000000000000..0650744f6bc6 --- /dev/null +++ b/fiscal_epos_print/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/fiscal_epos_print/__manifest__.py b/fiscal_epos_print/__manifest__.py new file mode 100644 index 000000000000..197a3d1ddd3d --- /dev/null +++ b/fiscal_epos_print/__manifest__.py @@ -0,0 +1,64 @@ +# Leonardo Donelli - Creativi Quadrati +# © 2016 Alessio Gerace - Agile Business Group +# © 2018-2020 Lorenzo Battistini +# © 2019-2020 Roberto Fichera - Level Prime Srl +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "ITA - Driver per stampanti fiscali compatibili ePOS-Print XML", + "version": "16.0.1.0.0", + "category": "Point Of Sale", + "summary": "ePOS-Print XML Fiscal Printer Driver - Stampanti Epson compatibili: " + "FP81II, FP90III", + "author": ( + "Odoo Community Association (OCA), Agile Business Group, " + "Leonardo Donelli, TAKOBI, Level Prime Srl" + ), + "license": "AGPL-3", + "website": "https://github.com/OCA/l10n-italy", + "maintainers": ["eLBati"], + "depends": [ + "point_of_sale", + "hr", + "pos_hr", + # TODO is this necessary? + # 'pos_order_mgmt' + ], + "data": [ + "views/account.xml", + "views/point_of_sale.xml", + "views/employee_view.xml", + ], + "assets": { + "point_of_sale.assets": [ + "fiscal_epos_print/static/lib/pikaday/pikaday.min.css", + "fiscal_epos_print/static/src/css/pos.css", + "fiscal_epos_print/static/lib/fiscalprint/fiscalprint.js", + "fiscal_epos_print/static/lib/pikaday/pikaday.min.js", + "fiscal_epos_print/static/src/js/epson_epos_print.js", + "fiscal_epos_print/static/src/js/models.js", + # ChromeWidgets + "fiscal_epos_print/static/src/js/ChromeWidgets/EpsonEPOSButton.js", + "fiscal_epos_print/static/src/js/ChromeWidgets/EpsonFP81IIComponent.js", + "fiscal_epos_print/static/src/js/ChromeWidgets/SetLotteryCodeButton.js", + "fiscal_epos_print/static/src/js/ChromeWidgets/SetRefundInfoButton.js", + # Popups + "fiscal_epos_print/static/src/js/Popups/LotteryCodePopup.js", + "fiscal_epos_print/static/src/js/Popups/RefundInfoPopup.js", + # Screens + "fiscal_epos_print/static/src/js/Screens/PaymentScreen/PaymentScreen.js", + "fiscal_epos_print/static/src/js/Screens/ReceiptScreen/ReceiptScreen.js", + # Popups + "fiscal_epos_print/static/src/xml/Popups/LotteryCodePopup.xml", + "fiscal_epos_print/static/src/xml/Popups/RefundInfoPopup.xml", + # Others + "fiscal_epos_print/static/src/xml/Chrome.xml", + "fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonEPOSButton.xml", + "fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml", + "fiscal_epos_print/static/src/xml/ChromeWidgets/SetLotteryCodeButton.xml", + "fiscal_epos_print/static/src/xml/ChromeWidgets/SetRefundInfoButton.xml", + ], + }, + "installable": True, + "auto_install": False, +} diff --git a/fiscal_epos_print/i18n/fiscal_epos_print.pot b/fiscal_epos_print/i18n/fiscal_epos_print.pot new file mode 100644 index 000000000000..36442cafee1f --- /dev/null +++ b/fiscal_epos_print/i18n/fiscal_epos_print.pot @@ -0,0 +1,842 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * fiscal_epos_print +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/pos_order_mgmt.js:0 +#, python-format +msgid ": order already has a fiscal number, " +msgstr "" + +#. module: fiscal_epos_print +#: model_terms:ir.ui.view,arch_db:fiscal_epos_print.view_pos_config_printer_form +msgid "Fiscal Printer Serial" +msgstr "" + +#. module: fiscal_epos_print +#: model_terms:ir.ui.view,arch_db:fiscal_epos_print.view_pos_config_printer_form +msgid "Printer address" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "An error happened while sending data to the printer. Error code: " +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/Popups/RefundInfoPopup.xml:0 +#: code:addons/fiscal_epos_print/static/src/xml/lottery.xml:0 +#, python-format +msgid "Apply" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/lottery.xml:0 +#, python-format +msgid "Cancel" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields.selection,name:fiscal_epos_print.selection__pos_payment_method__fiscalprinter_payment_type__0 +msgid "Cash" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/ChromeWidgets/EpsonFP81IIComponent.js:0 +#, python-format +msgid "CashDrawer Opened" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields.selection,name:fiscal_epos_print.selection__pos_payment_method__fiscalprinter_payment_type__1 +msgid "Cheque" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/ChromeWidgets/EpsonFP81IIComponent.js:0 +#, python-format +msgid "Close" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/Popups/RefundInfoPopup.xml:0 +#, python-format +msgid "Closing Seq." +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml:0 +#, python-format +msgid "Closure Report Z (Fiscal)" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_order__refund_report +msgid "Closure reference" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/ChromeWidgets/EpsonFP81IIComponent.js:0 +#, python-format +msgid "Confirm Printer Daily Financial Report (Report X)?" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/ChromeWidgets/EpsonFP81IIComponent.js:0 +#, python-format +msgid "Confirm Printer Fiscal Closure (Report Z)?" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/pos_order_mgmt.js:0 +#, python-format +msgid "Connecting to the fiscal printer" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Connection to the printer failed" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields.selection,name:fiscal_epos_print.selection__pos_payment_method__fiscalprinter_payment_type__2 +msgid "Credit or Credit Card" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_order__fiscal_printer_debug_info +msgid "Debug info" +msgstr "" + +#. module: fiscal_epos_print +#: code:addons/fiscal_epos_print/models/account.py:0 +#, python-format +msgid "Department ID number range [1 - 99]" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_account_tax__fpdeptax +msgid "Department on fiscal printer 1~99" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Discount" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields.selection,name:fiscal_epos_print.selection__pos_payment_method__fiscalprinter_payment_type__6 +msgid "Discount on payment" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_account_tax__display_name +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_config__display_name +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_order__display_name +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_payment__display_name +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_payment_method__display_name +msgid "Display Name" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_order__refund_doc_num +msgid "Document Number" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_payment_method__fiscalprinter_payment_index +msgid "Electronic Payment / Ticket Index" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonEPOSButton.xml:0 +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml:0 +#, python-format +msgid "Epson ePOS" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonEPOSButton.xml:0 +#, python-format +msgid "Epson ePOS Operation" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/models.js:0 +#, python-format +msgid "Error" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Error Message: " +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Error on last ESC/POS command with Fiscal/Non fiscal closed" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Error on last ESC/POS command with Non fiscal open" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Files waiting to be sent: " +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml:0 +#, python-format +msgid "Financial Report X" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Finished" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_config__fiscal_cashdrawer +msgid "Fiscal Printer Open CashDrawer" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_config__fiscal_printer_serial +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_order__fiscal_printer_serial +msgid "Fiscal Printer Serial" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml:0 +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml:0 +#, python-format +msgid "Fiscal closure" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_order__fiscal_z_rep_number +msgid "Fiscal closure number" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Fiscal document open" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Fiscal open" +msgstr "" + +#. module: fiscal_epos_print +#: model_terms:ir.ui.view,arch_db:fiscal_epos_print.view_pos_config_printer_form +msgid "Fiscal printer" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_order__fiscal_receipt_amount +msgid "Fiscal receipt amount" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_order__fiscal_receipt_date +msgid "Fiscal receipt date" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_order__fiscal_receipt_number +msgid "Fiscal receipt number" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "From other measurement device" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_account_tax__id +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_config__id +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_order__id +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_payment__id +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_payment_method__id +msgid "ID" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "IRA files" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml:0 +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml:0 +#, python-format +msgid "IRA status" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/pos.xml:0 +#, python-format +msgid "Invoice recorded" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_account_tax____last_update +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_config____last_update +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_order____last_update +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_payment____last_update +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_payment_method____last_update +msgid "Last Modified on" +msgstr "" + +#. module: fiscal_epos_print +#: model_terms:ir.ui.view,arch_db:fiscal_epos_print.view_pos_pos_form_refund_info +msgid "Lottery" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/ChromeWidgets/SetLotteryCodeButton.js:0 +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/SetLotteryCodeButton.xml:0 +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_order__lottery_code +#, python-format +msgid "Lottery Code" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/models.js:0 +#, python-format +msgid "Manca iva su prodotto" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/Popups/RefundInfoPopup.xml:0 +#: code:addons/fiscal_epos_print/static/src/xml/Popups/RefundInfoPopup.xml:0 +#, python-format +msgid "Max 4 numbers" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Negative receipt" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#: code:addons/fiscal_epos_print/static/src/js/models.js:0 +#, python-format +msgid "Network error" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields.selection,name:fiscal_epos_print.selection__pos_payment_method__fiscalprinter_payment_type__5 +msgid "No Paid" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/models.js:0 +#, python-format +msgid "No taxes found" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Non fiscal open" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Offline (end of paper or open cover)" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Old files: " +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml:0 +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml:0 +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml:0 +#, python-format +msgid "Open CashDrawer" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml:0 +#, python-format +msgid "Operations" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/pos_order_mgmt.js:0 +#, python-format +msgid "Order already printed" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Paper running low" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Payment" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Payment in progress" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_payment_method__fiscalprinter_payment_type +msgid "Payment type" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/ChromeWidgets/EpsonFP81IIComponent.js:0 +#, python-format +msgid "Please confirm to execute the Printer Daily Financial Report" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/ChromeWidgets/EpsonFP81IIComponent.js:0 +#, python-format +msgid "Please confirm to execute the Printer Fiscal Closure" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/ChromeWidgets/EpsonFP81IIComponent.js:0 +#, python-format +msgid "Please confirm to reprint the last receipt" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model,name:fiscal_epos_print.model_pos_config +msgid "Point of Sale Configuration" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model,name:fiscal_epos_print.model_pos_order +msgid "Point of Sale Orders" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model,name:fiscal_epos_print.model_pos_payment_method +msgid "Point of Sale Payment Methods" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model,name:fiscal_epos_print.model_pos_payment +msgid "Point of Sale Payments" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Previous" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_config__printer_ip +msgid "Printer IP Address" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Printer can not be reached" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/pos.xml:0 +#, python-format +msgid "Printing error" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/Screens/PaymentScreen/PaymentScreen.js:0 +#, python-format +msgid "" +"Product prices on receipts must be set to 'Tax-Included Price' in POS " +"configuration" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/Popups/RefundInfoPopup.xml:0 +#: code:addons/fiscal_epos_print/static/src/xml/Popups/RefundInfoPopup.xml:0 +#, python-format +msgid "RT Serial" +msgstr "" + +#. module: fiscal_epos_print +#: model_terms:ir.ui.view,arch_db:fiscal_epos_print.view_pos_pos_form_refund_info +msgid "RT data" +msgstr "" + +#. module: fiscal_epos_print +#: model_terms:ir.ui.view,arch_db:fiscal_epos_print.view_pos_pos_form_refund_info +msgid "Receipt" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/Popups/RefundInfoPopup.xml:0 +#, python-format +msgid "Receipt Date" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/Popups/RefundInfoPopup.xml:0 +#, python-format +msgid "Receipt Num." +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/pos.xml:0 +#, python-format +msgid "Receipt sent to the printer" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Refund >>> " +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_order__refund_cash_fiscal_serial +msgid "Refund Cash Serial" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/SetRefundInfoButton.xml:0 +#, python-format +msgid "Refund Data" +msgstr "" + +#. module: fiscal_epos_print +#: model_terms:ir.ui.view,arch_db:fiscal_epos_print.view_pos_pos_form_refund_info +msgid "Refund Info" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/ChromeWidgets/SetRefundInfoButton.js:0 +#, python-format +msgid "Refund Information Details" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/Screens/PaymentScreen/PaymentScreen.js:0 +#, python-format +msgid "Refund Information Not Present" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Refund cash" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_order__refund_date +msgid "Refund date reference" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Rejected files: " +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml:0 +#, python-format +msgid "Reports" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml:0 +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml:0 +#, python-format +msgid "Reprint Last Receipt" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/ChromeWidgets/EpsonFP81IIComponent.js:0 +#, python-format +msgid "Reprint Last Receipt?" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml:0 +#, python-format +msgid "Reprint last receipt" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/pos.xml:0 +#, python-format +msgid "Retry" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Running low" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml:0 +#, python-format +msgid "Send fiscal X report to the fiscal printer" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml:0 +#, python-format +msgid "Send fiscal closure command to the fiscal printer" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,help:fiscal_epos_print.field_pos_payment_method__fiscalprinter_payment_index +msgid "Set the index of the given payment type to specify the detail." +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_config__show_receipt_when_printing +msgid "Show receipt on screen when printing" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/Popups/RefundInfoPopup.xml:0 +#: code:addons/fiscal_epos_print/static/src/xml/lottery.xml:0 +#, python-format +msgid "Some fields are empty." +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml:0 +#, python-format +msgid "Status of files to be sent to IRA" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/Screens/PaymentScreen/PaymentScreen.js:0 +#, python-format +msgid "SubTotal" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model,name:fiscal_epos_print.model_account_tax +msgid "Tax" +msgstr "" + +#. module: fiscal_epos_print +#: model_terms:ir.ui.view,arch_db:fiscal_epos_print.view_apos_payment_method_form_fp90 +msgid "Termini pagamento stampante" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,help:fiscal_epos_print.field_pos_config__printer_ip +#: model_terms:ir.ui.view,arch_db:fiscal_epos_print.view_pos_config_printer_form +msgid "The hostname or IP address of the fiscal printer" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,help:fiscal_epos_print.field_pos_payment_method__fiscalprinter_payment_type +msgid "The payment type to send to the Fiscal Printer." +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/Screens/PaymentScreen/PaymentScreen.js:0 +#, python-format +msgid "" +"The refund information aren't present. Please insert them before printing " +"the receipt" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields.selection,name:fiscal_epos_print.selection__pos_payment_method__fiscalprinter_payment_type__3 +msgid "Ticket" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields.selection,name:fiscal_epos_print.selection__pos_payment_method__fiscalprinter_payment_type__4 +msgid "Ticket with number" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Title closed" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Title open" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "To format" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_config__use_https +msgid "Use https" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Waiting for receipt closing in JAVAPOS mode" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#: code:addons/fiscal_epos_print/static/src/js/epson_epos_print.js:0 +#, python-format +msgid "Wrong answer" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/Screens/PaymentScreen/PaymentScreen.js:0 +#, python-format +msgid "Wrong tax configuration" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/lottery.xml:0 +#, python-format +msgid "XXXXXXXX" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/Popups/RefundInfoPopup.xml:0 +#, python-format +msgid "YYYY-MM-DD" +msgstr "" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/pos.xml:0 +#, python-format +msgid "display:none" +msgstr "" diff --git a/fiscal_epos_print/models/__init__.py b/fiscal_epos_print/models/__init__.py new file mode 100644 index 000000000000..fc90c29a14a3 --- /dev/null +++ b/fiscal_epos_print/models/__init__.py @@ -0,0 +1,11 @@ +from . import pos_payment +from . import pos_payment_method +from . import point_of_sale +from . import account +from . import account_journal +from . import pos_order +from . import pos_session +from . import res_config_settings +from . import res_users +from . import hr_employee_base +from . import hr_employee diff --git a/fiscal_epos_print/models/account.py b/fiscal_epos_print/models/account.py new file mode 100644 index 000000000000..b16ec1570bb7 --- /dev/null +++ b/fiscal_epos_print/models/account.py @@ -0,0 +1,18 @@ +import re + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + +regex = r"^[1-9][0-9]?$" # regular expression to match numbers between [1 - 99] + + +class AccountTax(models.Model): + _inherit = "account.tax" + + fpdeptax = fields.Char("Department on fiscal printer 1~99", size=2, default="1") + + @api.constrains("fpdeptax") + def _validate_fpdeptax(self): + for tax in self: + if not re.search(regex, tax.fpdeptax): + raise ValidationError(_("Department ID number range [1 - 99]")) diff --git a/fiscal_epos_print/models/account_journal.py b/fiscal_epos_print/models/account_journal.py new file mode 100644 index 000000000000..f0ddbd3ffa09 --- /dev/null +++ b/fiscal_epos_print/models/account_journal.py @@ -0,0 +1,25 @@ +from odoo import fields, models + + +class AccountJournal(models.Model): + _inherit = "account.journal" + + fiscalprinter_payment_type = fields.Selection( + [ + ("0", "Cash"), + ("1", "Cheque"), + ("2", "Credit or Credit Card"), + ("3", "Ticket"), + ("4", "Ticket with number"), + ("5", "No Paid"), + ("6", "Discount on payment"), + ], + "Payment type", + help="The payment type to send to the Fiscal Printer.", + default="0", + ) + + fiscalprinter_payment_index = fields.Integer( + string="Electronic Payment / Ticket Index", + help="Set the index of the given payment type to specify the detail.", + ) diff --git a/fiscal_epos_print/models/hr_employee.py b/fiscal_epos_print/models/hr_employee.py new file mode 100644 index 000000000000..efac145a2fd7 --- /dev/null +++ b/fiscal_epos_print/models/hr_employee.py @@ -0,0 +1,15 @@ +from odoo import api, fields, models + + +class HrEmployeeInherit(models.AbstractModel): + _inherit = "hr.employee" + + fiscal_operator_number = fields.Char( + string="Fiscal Printer Operator", size=1, default="1", tracking=True + ) + + @api.onchange("fiscal_operator_number") + def _onchange_fiscal_operator_number(self): + for rec in self: + if rec.user_id: + rec.user_id.fiscal_operator_number = rec.fiscal_operator_number diff --git a/fiscal_epos_print/models/hr_employee_base.py b/fiscal_epos_print/models/hr_employee_base.py new file mode 100644 index 000000000000..522400220d0c --- /dev/null +++ b/fiscal_epos_print/models/hr_employee_base.py @@ -0,0 +1,10 @@ +from odoo import fields, models + + +class HrEmployeeBaseInherit(models.AbstractModel): + _name = "hr.employee.base" + _inherit = ["hr.employee.base", "mail.thread"] + + fiscal_operator_number = fields.Char( + string="Fiscal Printer Operator", size=1, default="1", tracking=True + ) diff --git a/fiscal_epos_print/models/point_of_sale.py b/fiscal_epos_print/models/point_of_sale.py new file mode 100644 index 000000000000..cfbd0e8fe09d --- /dev/null +++ b/fiscal_epos_print/models/point_of_sale.py @@ -0,0 +1,21 @@ +from odoo import fields, models + + +class PosConfig(models.Model): + _inherit = "pos.config" + + printer_ip = fields.Char( + "Printer IP Address", + help="The hostname or IP address of the fiscal printer", + size=45, + ) + use_https = fields.Boolean( + string="Use https", + default=False, + ) + show_receipt_when_printing = fields.Boolean( + string="Show receipt on screen when printing", default=True + ) + fiscal_printer_serial = fields.Char() + + fiscal_cashdrawer = fields.Boolean(string="Fiscal Printer Open CashDrawer") diff --git a/fiscal_epos_print/models/pos_order.py b/fiscal_epos_print/models/pos_order.py new file mode 100644 index 000000000000..f1b7ed94a579 --- /dev/null +++ b/fiscal_epos_print/models/pos_order.py @@ -0,0 +1,70 @@ +import logging + +from odoo import api, fields, models + +_logger = logging.getLogger(__name__) + + +class PosOrder(models.Model): + _inherit = "pos.order" + + refund_date = fields.Date(string="Refund date reference") + refund_report = fields.Integer(string="Closure reference") + refund_doc_num = fields.Integer(string="Document Number") + refund_cash_fiscal_serial = fields.Char(string="Refund Cash Serial") + refund_full_refund = fields.Boolean(string="Full Refund", default=False) + fiscal_receipt_number = fields.Integer( + string="Fiscal receipt number", + ) + fiscal_receipt_amount = fields.Float("Fiscal receipt amount") + fiscal_receipt_date = fields.Date("Fiscal receipt date") + fiscal_z_rep_number = fields.Integer("Fiscal closure number") + fiscal_printer_serial = fields.Char() + fiscal_printer_debug_info = fields.Text("Debug info", readonly=True) + fiscal_operator_number = fields.Text("Fiscal Operator", readonly=True) + + # TODO allow to save code on customer and load customer, if present + lottery_code = fields.Char() + + @api.model + def _order_fields(self, ui_order): + res = super()._order_fields(ui_order) + res["lottery_code"] = ui_order.get("lottery_code", "") + res["refund_date"] = ui_order.get("refund_date", False) + res["refund_report"] = ui_order.get("refund_report", False) + res["refund_doc_num"] = ui_order.get("refund_doc_num", False) + res["refund_cash_fiscal_serial"] = ui_order.get( + "refund_cash_fiscal_serial", False + ) + res["refund_full_refund"] = ui_order.get("refund_full_refund", False) + res["fiscal_receipt_number"] = ui_order.get("fiscal_receipt_number", False) + res["fiscal_receipt_amount"] = ui_order.get("fiscal_receipt_amount", False) + res["fiscal_receipt_date"] = ui_order.get("fiscal_receipt_date", False) + res["fiscal_z_rep_number"] = ui_order.get("fiscal_z_rep_number", False) + res["fiscal_printer_serial"] = ui_order.get("fiscal_printer_serial", False) + res["fiscal_printer_debug_info"] = ui_order.get( + "fiscal_printer_debug_info", False + ) + res["fiscal_operator_number"] = ui_order.get("fiscal_operator_number", False) + return res + + def _export_for_ui(self, order): + result = super()._export_for_ui(order) + result.update( + { + "lottery_code": order.lottery_code, + "refund_date": order.refund_date, + "refund_report": order.refund_report, + "refund_doc_num": order.refund_doc_num, + "refund_cash_fiscal_serial": order.refund_cash_fiscal_serial, + "refund_full_refund": order.refund_full_refund, + "fiscal_receipt_number": order.fiscal_receipt_number, + "fiscal_receipt_amount": order.fiscal_receipt_amount, + "fiscal_receipt_date": order.fiscal_receipt_date, + "fiscal_z_rep_number": order.fiscal_z_rep_number, + "fiscal_printer_serial": order.fiscal_printer_serial, + "fiscal_printer_debug_info": order.fiscal_printer_debug_info, + "fiscal_operator_number": order.fiscal_operator_number, + } + ) + return result diff --git a/fiscal_epos_print/models/pos_payment.py b/fiscal_epos_print/models/pos_payment.py new file mode 100644 index 000000000000..22320ae09b80 --- /dev/null +++ b/fiscal_epos_print/models/pos_payment.py @@ -0,0 +1,15 @@ +from odoo import models + + +class PosPayment(models.Model): + _inherit = "pos.payment" + + def _export_for_ui(self, payment): + res = super()._export_for_ui(payment) + res[ + "fiscalprinter_payment_type" + ] = payment.payment_method_id.fiscalprinter_payment_type + res[ + "fiscalprinter_payment_index" + ] = payment.payment_method_id.fiscalprinter_payment_index + return res diff --git a/fiscal_epos_print/models/pos_payment_method.py b/fiscal_epos_print/models/pos_payment_method.py new file mode 100644 index 000000000000..d1667e724ae0 --- /dev/null +++ b/fiscal_epos_print/models/pos_payment_method.py @@ -0,0 +1,25 @@ +from odoo import fields, models + + +class PosPaymentMethod(models.Model): + _inherit = "pos.payment.method" + + fiscalprinter_payment_type = fields.Selection( + [ + ("0", "Cash"), + ("1", "Cheque"), + ("2", "Credit or Credit Card"), + ("3", "Ticket"), + ("4", "Ticket with number"), + ("5", "No Paid"), + ("6", "Discount on payment"), + ], + "Payment type", + help="The payment type to send to the Fiscal Printer.", + default="0", + ) + + fiscalprinter_payment_index = fields.Integer( + string="Electronic Payment / Ticket Index", + help="Set the index of the given payment type to specify the detail.", + ) diff --git a/fiscal_epos_print/models/pos_session.py b/fiscal_epos_print/models/pos_session.py new file mode 100644 index 000000000000..7d838354b698 --- /dev/null +++ b/fiscal_epos_print/models/pos_session.py @@ -0,0 +1,32 @@ +# Copyright 2022 Dinamiche Aziendali srl +# @author: Giuseppe Borruso +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import models + + +class PosSession(models.Model): + _inherit = "pos.session" + + def _loader_params_pos_payment_method(self): + result = super()._loader_params_pos_payment_method() + result["search_params"]["fields"].append("fiscalprinter_payment_type") + result["search_params"]["fields"].append("fiscalprinter_payment_index") + return result + + def _loader_params_account_tax(self): + result = super()._loader_params_account_tax() + result["search_params"]["fields"].append("fpdeptax") + return result + + def _loader_params_hr_employee(self): + result = super()._loader_params_hr_employee() + result["search_params"]["fields"].append("fiscal_operator_number") + return result + + def _get_pos_ui_hr_employee(self, params): + employees = super()._get_pos_ui_hr_employee(params) + for employee in employees: + emp = self.env["hr.employee"].browse(employee["id"]) + employee["fiscal_operator_number"] = emp.fiscal_operator_number + return employees diff --git a/fiscal_epos_print/models/res_config_settings.py b/fiscal_epos_print/models/res_config_settings.py new file mode 100644 index 000000000000..46945b7fa26e --- /dev/null +++ b/fiscal_epos_print/models/res_config_settings.py @@ -0,0 +1,17 @@ +from odoo import fields, models + + +class ResConfigSetting(models.TransientModel): + _inherit = "res.config.settings" + + printer_ip = fields.Char(related="pos_config_id.printer_ip", readonly=False) + use_https = fields.Boolean(related="pos_config_id.use_https", readonly=False) + show_receipt_when_printing = fields.Boolean( + related="pos_config_id.show_receipt_when_printing", readonly=False + ) + fiscal_printer_serial = fields.Char( + related="pos_config_id.fiscal_printer_serial", readonly=False + ) + fiscal_cashdrawer = fields.Boolean( + related="pos_config_id.fiscal_cashdrawer", readonly=False + ) diff --git a/fiscal_epos_print/models/res_users.py b/fiscal_epos_print/models/res_users.py new file mode 100644 index 000000000000..e813b05d7124 --- /dev/null +++ b/fiscal_epos_print/models/res_users.py @@ -0,0 +1,9 @@ +from odoo import fields, models + + +class ResUsersInherit(models.Model): + _inherit = "res.users" + + fiscal_operator_number = fields.Char( + string="Fiscal Printer Operator", size=1, default="1" + ) diff --git a/fiscal_epos_print/pyproject.toml b/fiscal_epos_print/pyproject.toml new file mode 100644 index 000000000000..4231d0cccb3d --- /dev/null +++ b/fiscal_epos_print/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/fiscal_epos_print/readme/CONFIGURE.md b/fiscal_epos_print/readme/CONFIGURE.md new file mode 100644 index 000000000000..052139dbbd8d --- /dev/null +++ b/fiscal_epos_print/readme/CONFIGURE.md @@ -0,0 +1,25 @@ +**Italiano** + +- stampare la lista dei reparti della vostra stampante fiscale +- mappare le imposta di vendita di odoo con i gruppi di imposte - + dipartimenti della stampante fiscale, per ogni imposta di vendita in + odoo, usando il campo "Reparto sulla stampante fiscale 1~99" +- in odoo, utilizzare imposte incluse nel prezzo +- connettere la vostra stampante fiscale alla rete locale e recuperare + l'IP +- aprire la configurazione POS e impostare l'indirizzo IP e il seriale + della stampante nella sezione "Stampante Fiscale" +- è tutto, alla validazione del pagamento nella sessione POS, il sistema + stamperà lo scontrino fiscale. + +**English** + +- print list departments of your fiscal printer +- map odoo sale taxes with taxes groups - departments of fiscal printer, + for each sale tax on odoo, using field "Department on fiscal printer + 1~99" +- in odoo, use taxes included in price +- connect your fiscal printer to local network and find IP +- open POS configuration and fill Printer IP Address field +- that's all, at validation of payment on POS session, system prints + fiscal receipt. diff --git a/fiscal_epos_print/readme/CONTRIBUTORS.md b/fiscal_epos_print/readme/CONTRIBUTORS.md new file mode 100644 index 000000000000..1575d667cd9e --- /dev/null +++ b/fiscal_epos_print/readme/CONTRIBUTORS.md @@ -0,0 +1,6 @@ +- Leonardo Donelli +- [TAKOBI](https://takobi.online): + - Lorenzo Battistini +- Alessio Gerace +- Roberto Fichera +- Giuseppe Borruso \<\> diff --git a/fiscal_epos_print/readme/DESCRIPTION.md b/fiscal_epos_print/readme/DESCRIPTION.md new file mode 100644 index 000000000000..f8fe98d89c16 --- /dev/null +++ b/fiscal_epos_print/readme/DESCRIPTION.md @@ -0,0 +1,19 @@ +**Italiano** + +Questo modulo permette di stampare gli scontrini del punto vendita, su +stampanti fiscali Epson, tramite protocollo EPos + +Stampanti supportate: + +- FP81II +- FP90III + +**English** + +This module allows to print receipt of point of sale, on fiscal printers +Epson, via EPos protocol. + +Supported printers: + +- FP81II +- FP90III diff --git a/fiscal_epos_print/readme/ROADMAP.md b/fiscal_epos_print/readme/ROADMAP.md new file mode 100644 index 000000000000..fec2b6652ba4 --- /dev/null +++ b/fiscal_epos_print/readme/ROADMAP.md @@ -0,0 +1,4 @@ +- Resi: + - Aggiungere controllo "rendibilità" + - Stampare sullo scontrino un barcode identificativo, in modo da + generare il reso facendone la scansione diff --git a/fiscal_epos_print/static/description/icon.png b/fiscal_epos_print/static/description/icon.png new file mode 100644 index 000000000000..3a0328b516c4 Binary files /dev/null and b/fiscal_epos_print/static/description/icon.png differ diff --git a/fiscal_epos_print/static/description/index.html b/fiscal_epos_print/static/description/index.html new file mode 100644 index 000000000000..38c172ce8fbf --- /dev/null +++ b/fiscal_epos_print/static/description/index.html @@ -0,0 +1,489 @@ + + + + + +ITA - Driver per stampanti fiscali compatibili ePOS-Print XML + + + +
+

ITA - Driver per stampanti fiscali compatibili ePOS-Print XML

+ + +

Beta License: AGPL-3 OCA/l10n-italy Translate me on Weblate Try me on Runboat

+

Italiano

+

Questo modulo permette di stampare gli scontrini del punto vendita, su +stampanti fiscali Epson, tramite protocollo EPos

+

Stampanti supportate:

+
    +
  • FP81II
  • +
  • FP90III
  • +
+

English

+

This module allows to print receipt of point of sale, on fiscal printers +Epson, via EPos protocol.

+

Supported printers:

+
    +
  • FP81II
  • +
  • FP90III
  • +
+

Table of contents

+ +
+

Configuration

+

Italiano

+
    +
  • stampare la lista dei reparti della vostra stampante fiscale
  • +
  • mappare le imposta di vendita di odoo con i gruppi di imposte - +dipartimenti della stampante fiscale, per ogni imposta di vendita in +odoo, usando il campo “Reparto sulla stampante fiscale 1~99”
  • +
  • in odoo, utilizzare imposte incluse nel prezzo
  • +
  • connettere la vostra stampante fiscale alla rete locale e recuperare +l’IP
  • +
  • aprire la configurazione POS e impostare l’indirizzo IP e il seriale +della stampante nella sezione “Stampante Fiscale”
  • +
  • è tutto, alla validazione del pagamento nella sessione POS, il +sistema stamperà lo scontrino fiscale.
  • +
+

English

+
    +
  • print list departments of your fiscal printer
  • +
  • map odoo sale taxes with taxes groups - departments of fiscal +printer, for each sale tax on odoo, using field “Department on fiscal +printer 1~99”
  • +
  • in odoo, use taxes included in price
  • +
  • connect your fiscal printer to local network and find IP
  • +
  • open POS configuration and fill Printer IP Address field
  • +
  • that’s all, at validation of payment on POS session, system prints +fiscal receipt.
  • +
+
+
+

Known issues / Roadmap

+
    +
  • Resi:
      +
    • Aggiungere controllo “rendibilità”
    • +
    • Stampare sullo scontrino un barcode identificativo, in modo da +generare il reso facendone la scansione
    • +
    +
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Agile Business Group
  • +
  • Leonardo Donelli
  • +
  • TAKOBI
  • +
  • Level Prime Srl
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainer:

+

eLBati

+

This module is part of the OCA/l10n-italy project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/fiscal_epos_print/static/lib/fiscalprint/fiscalprint.js b/fiscal_epos_print/static/lib/fiscalprint/fiscalprint.js new file mode 100644 index 000000000000..bf7fbebc39c2 --- /dev/null +++ b/fiscal_epos_print/static/lib/fiscalprint/fiscalprint.js @@ -0,0 +1,1634 @@ +// +// ePOS-Print and Fiscal Print API +// +// Version 1.1.1 +// +// Copyright (C) SEIKO EPSON CORPORATION 2018. All rights reserved. +// + +// 06/12/2012 1.0.0 +// First release. + +// 01/09/2014 1.0.1 +// 1. Added send timeout parameter (argument). +// 2. Added onreceive res_add argument so that complete response can also be returned. +// 3. onerror response generates FP_NO_ANSWER_NETWORK fixed text instead of xhr response. +// 4. encodeBase64Binary object added for future use. + + +// 23/03/2018 1.1.0 +// 1. Added empty node exception handling. +// 2. Added "statusText" string field in result variable as "status" field is Integer which doesn't manage text +// present in certain replies. +// 3. Added callMode argument in send method so that either asynchronous or synchronous mode can be selected. +// Use "async" for asynchronous otherwise defaults to synchronous. +// If null, defaults to async. +// Timeout only valid for async mode. + +// 14/06/2018 1.1.1 +// 1. Added responseText and responseXML fields to onerror result object. + + +// 06/07/2018 1.1.2 +// 1. Added radix to parseInt for best practice. + +(function (window) +{ + + // + // Function: ePOSBuilder constructor + // Description: initialize an ePOS-Print XML Builder object + // Parameters: none + // Return: none + // + function ePOSBuilder() { + // properties + this.message = ''; + // constants + this.FONT_A = 'font_a'; + this.FONT_B = 'font_b'; + this.FONT_C = 'font_c'; + this.FONT_SPECIAL_A = 'special_a'; + this.FONT_SPECIAL_B = 'special_b'; + this.ALIGN_LEFT = 'left'; + this.ALIGN_CENTER = 'center'; + this.ALIGN_RIGHT = 'right'; + this.COLOR_NONE = 'none'; + this.COLOR_1 = 'color_1'; + this.COLOR_2 = 'color_2'; + this.COLOR_3 = 'color_3'; + this.COLOR_4 = 'color_4'; + this.BARCODE_UPC_A = 'upc_a'; + this.BARCODE_UPC_E = 'upc_e'; + this.BARCODE_EAN13 = 'ean13'; + this.BARCODE_JAN13 = 'jan13'; + this.BARCODE_EAN8 = 'ean8'; + this.BARCODE_JAN8 = 'jan8'; + this.BARCODE_CODE39 = 'code39'; + this.BARCODE_ITF = 'itf'; + this.BARCODE_CODABAR = 'codabar'; + this.BARCODE_CODE93 = 'code93'; + this.BARCODE_CODE128 = 'code128'; + this.BARCODE_GS1_128 = 'gs1_128'; + this.BARCODE_GS1_DATABAR_OMNIDIRECTIONAL = 'gs1_databar_omnidirectional'; + this.BARCODE_GS1_DATABAR_TRUNCATED = 'gs1_databar_truncated'; + this.BARCODE_GS1_DATABAR_LIMITED = 'gs1_databar_limited'; + this.BARCODE_GS1_DATABAR_EXPANDED = 'gs1_databar_expanded'; + this.HRI_NONE = 'none'; + this.HRI_ABOVE = 'above'; + this.HRI_BELOW = 'below'; + this.HRI_BOTH = 'both'; + this.SYMBOL_PDF417_STANDARD = 'pdf417_standard'; + this.SYMBOL_PDF417_TRUNCATED = 'pdf417_truncated'; + this.SYMBOL_QRCODE_MODEL_1 = 'qrcode_model_1'; + this.SYMBOL_QRCODE_MODEL_2 = 'qrcode_model_2'; + this.SYMBOL_MAXICODE_MODE_2 = 'maxicode_mode_2'; + this.SYMBOL_MAXICODE_MODE_3 = 'maxicode_mode_3'; + this.SYMBOL_MAXICODE_MODE_4 = 'maxicode_mode_4'; + this.SYMBOL_MAXICODE_MODE_5 = 'maxicode_mode_5'; + this.SYMBOL_MAXICODE_MODE_6 = 'maxicode_mode_6'; + this.SYMBOL_GS1_DATABAR_STACKED = 'gs1_databar_stacked'; + this.SYMBOL_GS1_DATABAR_STACKED_OMNIDIRECTIONAL = 'gs1_databar_stacked_omnidirectional'; + this.SYMBOL_GS1_DATABAR_EXPANDED_STACKED = 'gs1_databar_expanded_stacked'; + this.LEVEL_0 = 'level_0'; + this.LEVEL_1 = 'level_1'; + this.LEVEL_2 = 'level_2'; + this.LEVEL_3 = 'level_3'; + this.LEVEL_4 = 'level_4'; + this.LEVEL_5 = 'level_5'; + this.LEVEL_6 = 'level_6'; + this.LEVEL_7 = 'level_7'; + this.LEVEL_8 = 'level_8'; + this.LEVEL_L = 'level_l'; + this.LEVEL_M = 'level_m'; + this.LEVEL_Q = 'level_q'; + this.LEVEL_H = 'level_h'; + this.LEVEL_DEFAULT = 'default'; + this.LINE_THIN = 'thin'; + this.LINE_MEDIUM = 'medium'; + this.LINE_THICK = 'thick'; + this.LINE_THIN_DOUBLE = 'thin_double'; + this.LINE_MEDIUM_DOUBLE = 'medium_double'; + this.LINE_THICK_DOUBLE = 'thick_double'; + this.DIRECTION_LEFT_TO_RIGHT = 'left_to_right'; + this.DIRECTION_BOTTOM_TO_TOP = 'bottom_to_top'; + this.DIRECTION_RIGHT_TO_LEFT = 'right_to_left'; + this.DIRECTION_TOP_TO_BOTTOM = 'top_to_bottom'; + this.CUT_NO_FEED = 'no_feed'; + this.CUT_FEED = 'feed'; + this.CUT_RESERVE = 'reserve'; + this.DRAWER_1 = 'drawer_1'; + this.DRAWER_2 = 'drawer_2'; + this.PULSE_100 = 'pulse_100'; + this.PULSE_200 = 'pulse_200'; + this.PULSE_300 = 'pulse_300'; + this.PULSE_400 = 'pulse_400'; + this.PULSE_500 = 'pulse_500'; + this.PATTERN_NONE = 'none'; + this.PATTERN_A = 'pattern_a'; + this.PATTERN_B = 'pattern_b'; + this.PATTERN_C = 'pattern_c'; + this.PATTERN_D = 'pattern_d'; + this.PATTERN_E = 'pattern_e'; + this.PATTERN_ERROR = 'error'; + this.PATTERN_PAPER_END = 'paper_end'; + } + + // + // Function: addFeedUnit method + // Description: append the XML element to print and feed paper + // Parameters: + // unit unsignedbyte paper feed amount (units) + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addFeedUnit = function (unit) { + // create empty string + var s = ''; + // check parameter + s += getUByteAttr('unit', unit); + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addFeedLine method + // Description: append the XML element to print and feed n lines + // Parameters: + // line unsignedbyte paper feed amount (lines) + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addFeedLine = function (line) { + // create empty string + var s = ''; + // check parameter + s += getUByteAttr('line', line); + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addText method + // Description: append the XML element to print characters + // Parameters: + // data string characters + // Return: object ePOSBuilder object + // + ePOSBuilder.prototype.addText = function (data) { + // append element + this.message += '' + encodeXmlEntity(data) + ''; + // return builder object + return this; + } + + // + // Function: addTextLang method + // Description: append the XML element to select language + // Parameters: + // lang string language code and country code (en, en-US, ja, ja-JP, etc.) + // Return: object ePOSBuilder object + // + ePOSBuilder.prototype.addTextLang = function (lang) { + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addTextAlign method + // Description: append the XML element to set alignment + // Parameters: + // align string alignment (ALIGN_* constants) + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addTextAlign = function (align) { + // create empty string + var s = ''; + // check parameter + s += getEnumAttr('align', align, regexAlign); + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addTextRotate method + // Description: append the XML element to turn upside-down print mode on/off + // Parameters: + // rotate boolean when true, upside-down print mode is turned on + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addTextRotate = function (rotate) { + // create empty string + var s = ''; + // check parameter + s += getBoolAttr('rotate', rotate); + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addTextLineSpace method + // Description: append the XML element to set line spacing + // Parameters: + // linespc unsignedByte the amount of line spacing + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addTextLineSpace = function (linespc) { + // create empty string + var s = ''; + // check parameter + s += getUByteAttr('linespc', linespc); + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addTextFont method + // Description: append the XML element to select character font + // Parameters: + // font string font (FONT_* constants) + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addTextFont = function (font) { + // create empty string + var s = ''; + // check parameter + s += getEnumAttr('font', font, regexFont); + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addTextSmooth method + // Description: append the XML element to turn smoothing mode on/off + // Parameters: + // smooth boolean when true, smoothing mode is turned on + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addTextSmooth = function (smooth) { + // create empty string + var s = ''; + // check parameter + s += getBoolAttr('smooth', smooth); + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addTextDouble method + // Description: append the XML element to turn double-wide/double-high mode on/off + // Parameters: + // dw boolean when true, double-wide mode is turned on [option] + // dh boolean when true, double-high mode is turned on [option] + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addTextDouble = function (dw, dh) { + // create empty string + var s = ''; + // check parameter (option) + if (dw !== undefined) { + s += getBoolAttr('dw', dw); + } + // check parameter (option) + if (dh !== undefined) { + s += getBoolAttr('dh', dh); + } + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addTextSize method + // Description: append the XML element to select character size + // Parameters: + // width unsignedByte character width (1 to 8) [option] + // height unsignedByte character height (1 to 8) [option] + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addTextSize = function (width, height) { + // create empty string + var s = ''; + // check parameter (option) + if (width !== undefined) { + s += getIntAttr('width', width, 1, 8); + } + // check parameter (option) + if (height !== undefined) { + s += getIntAttr('height', height, 1, 8); + } + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addTextStyle method + // Description: append the XML element to select character style + // Parameters: + // reverse boolean when true, black/white reverse print mode is turned on [option] + // ul boolean when true, underline mode is turned on [option] + // em boolean when true, emphasized mode is turned on [option] + // color string color (COLOR_* constants) [option] + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addTextStyle = function (reverse, ul, em, color) { + // create empty string + var s = ''; + // check parameter (option) + if (reverse !== undefined) { + s += getBoolAttr('reverse', reverse); + } + // check parameter (option) + if (ul !== undefined) { + s += getBoolAttr('ul', ul); + } + // check parameter (option) + if (em !== undefined) { + s += getBoolAttr('em', em); + } + // check parameter (option) + if (color !== undefined) { + s += getEnumAttr('color', color, regexColor); + } + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addTextPosition method + // Description: append the XML element to set absolute print position + // Parameters: + // x unsignedShort X start position + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addTextPosition = function (x) { + // create empty string + var s = ''; + // check parameter + s += getUShortAttr('x', x); + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addImage method + // Description: append the XML element to print the graphics data (raster format) + // Parameters: + // context object the 2-D context of HTML 5 Canvas + // x unsignedShort X start position + // y unsignedShort Y start position + // width unsignedShort horizontal size + // height unsignedShort vertical size + // color string color (COLOR_* constants) [option] + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addImage = function (context, x, y, width, height, color) { + // create empty string + var s = ''; + // check parameter + getUShortAttr('x', x); + // check parameter + getUShortAttr('y', y); + // check parameter + s += getUShortAttr('width', width); + // check parameter + s += getUShortAttr('height', height); + // check parameter (option) + if (color !== undefined) { + s += getEnumAttr('color', color, regexColor); + } + // create image data + var image = encodeRasterImage(context.getImageData(x, y, width, height).data, width, height); + // append element + this.message += '' + encodeBase64Binary(image) + ''; + // return builder object + return this; + } + + // + // Function: addLogo method + // Description: append the XML element to print specified NV graphics data + // Parameters: + // key1 unsignedShort key code 1 + // key2 unsignedShort key code 2 + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addLogo = function (key1, key2) { + // create empty string + var s = ''; + // check parameter + s += getUByteAttr('key1', key1); + // check parameter + s += getUByteAttr('key2', key2); + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addBarcode method + // Description: append the XML element to print bar code + // Parameters: + // data object bar code data (characters, escape sequences '\xnn', '\\') + // type string bar code type (BARCODE_* constants) + // hri string print position of HRI characters (HRI_* constants) [option] + // font string font for HRI characters (FONT_* constants) [option] + // width unsignedByte bar code module width [option] + // height unsignedByte bar code module height [option] + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addBarcode = function (data, type, hri, font, width, height) { + // create empty string + var s = ''; + // check parameter + s += getEnumAttr('type', type, regexBarcode); + // check parameter (option) + if (hri !== undefined) { + s += getEnumAttr('hri', hri, regexHri); + } + // check parameter (option) + if (font !== undefined) { + s += getEnumAttr('font', font, regexFont); + } + // check parameter (option) + if (width !== undefined) { + s += getUByteAttr('width', width); + } + // check parameter (option) + if (height !== undefined) { + s += getUByteAttr('height', height); + } + // append element + this.message += '' + escapeText(encodeXmlEntity(data)) + ''; + // return builder object + return this; + } + + // + // Function: addSymbol method + // Description: append the XML element to print two dimension code + // Parameters: + // data object symbol data (characters, escape sequences '\xnn', '\\') + // type string symbol type (SYMBOL_* constants) + // level string error correction level (LEVEL_* constants) [option] + // width unsignedByte module width (PDF417, QR Code, GS1 DataBar) [option] + // height unsignedByte module height (PDF417) [option] + // size unsignedShort the number of columns (PDF417), maximum width (GS1 DataBar) [option] + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addSymbol = function (data, type, level, width, height, size) { + // create empty string + var s = ''; + // check parameter + s += getEnumAttr('type', type, regexSymbol); + // check parameter (option) + if (level !== undefined) { + s += getEnumAttr('level', level, regexLevel); + } + // check parameter (option) + if (width !== undefined) { + s += getUByteAttr('width', width); + } + // check parameter (option) + if (height !== undefined) { + s += getUByteAttr('height', height); + } + // check parameter (option) + if (size !== undefined) { + s += getUShortAttr('size', size); + } + // append element + this.message += '' + escapeText(encodeXmlEntity(data)) + ''; + // return builder object + return this; + } + + // + // Function: addCommand method + // Description: append the XML element to send commands + // Parameters: + // data string commands + // Return: object ePOSBuilder object + // + ePOSBuilder.prototype.addCommand = function (data) { + // append element + this.message += '' + encodeHexBinary(data) + ''; + // return builder object + return this; + } + + // + // Function: addHLine method + // Description: append the XML element to draw horizontal line + // Parameters: + // x1 unsignedShort X start position + // x2 unsignedShort X end position + // style string the style of line (LINE_* constants) [option] + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addHLine = function (x1, x2, style) { + // create empty string + var s = ''; + // check parameter + s += getUShortAttr('x1', x1); + // check parameter + s += getUShortAttr('x2', x2); + // check parameter (option) + if (style !== undefined) { + s += getEnumAttr('style', style, regexLine); + } + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addVLineBegin method + // Description: append the XML element to draw vertical line + // Parameters: + // x unsignedShort X start position + // style string the style of line (LINE_* constants) [option] + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addVLineBegin = function (x, style) { + // create empty string + var s = ''; + // check parameter + s += getUShortAttr('x', x); + // check parameter (option) + if (style !== undefined) { + s += getEnumAttr('style', style, regexLine); + } + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addVLineEnd method + // Description: append the XML element to draw vertical line + // Parameters: + // x unsignedShort X end position + // style string the style of line (LINE_* constants) [option] + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addVLineEnd = function (x, style) { + // create empty string + var s = ''; + // check parameter + s += getUShortAttr('x', x); + // check parameter (option) + if (style !== undefined) { + s += getEnumAttr('style', style, regexLine); + } + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addPageBegin method + // Description: append the XML element to select page mode + // Parameters: none + // Return: object ePOSBuilder object + // + ePOSBuilder.prototype.addPageBegin = function () { + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addPageEnd method + // Description: append the XML element to print and return to standard mode (in page mode) + // Parameters: none + // Return: object ePOSBuilder object + // + ePOSBuilder.prototype.addPageEnd = function () { + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addPageArea method + // Description: append the XML element to set print area in page mode + // Parameters: + // x unsignedShort horizontal logical origin + // y unsignedShort vertical logical origin + // width unsignedShort print area width + // height unsignedShort print area height + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addPageArea = function (x, y, width, height) { + // create empty string + var s = ''; + // check parameter + s += getUShortAttr('x', x); + // check parameter + s += getUShortAttr('y', y); + // check parameter + s += getUShortAttr('width', width); + // check parameter + s += getUShortAttr('height', height); + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addPageDirection method + // Description: append the XML element to select print direction in page mode + // Parameters: + // dir string direction (DIRECTION_* constants) + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addPageDirection = function (dir) { + // create empty string + var s = ''; + // check parameter + s += getEnumAttr('dir', dir, regexDirection); + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addPagePosition method + // Description: append the XML element to set absolute print position in page mode + // Parameters: + // x unsignedShort horizontal position + // y unsignedShort vertical position + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addPagePosition = function (x, y) { + // create empty string + var s = ''; + // check parameter + s += getUShortAttr('x', x); + // check parameter + s += getUShortAttr('y', y); + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addPageLine method + // Description: append the XML element to draw line in page mode + // Parameters: + // x1 unsignedShort X start position + // y1 unsignedShort Y start position + // x2 unsignedShort X end position + // y2 unsignedShort Y end position + // style string the style of line (LINE_* constants) [option] + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addPageLine = function (x1, y1, x2, y2, style) { + // create empty string + var s = ''; + // check parameter + s += getUShortAttr('x1', x1); + // check parameter + s += getUShortAttr('y1', y1); + // check parameter + s += getUShortAttr('x2', x2); + // check parameter + s += getUShortAttr('y2', y2); + // check parameter (option) + if (style !== undefined) { + s += getEnumAttr('style', style, regexLine); + } + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addPageRectangle method + // Description: append the XML element to draw rectangle in page mode + // Parameters: + // x1 unsignedShort X start position + // y1 unsignedShort Y start position + // x2 unsignedShort X end position + // y2 unsignedShort Y end position + // style string the style of line (LINE_* constants) [option] + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addPageRectangle = function (x1, y1, x2, y2, style) { + // create empty string + var s = ''; + // check parameter + s += getUShortAttr('x1', x1); + // check parameter + s += getUShortAttr('y1', y1); + // check parameter + s += getUShortAttr('x2', x2); + // check parameter + s += getUShortAttr('y2', y2); + // check parameter (option) + if (style !== undefined) { + s += getEnumAttr('style', style, regexLine); + } + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addCut method + // Description: append the XML element to cut paper + // Parameters: + // type string cut mode (CUT_* constants) [option] + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addCut = function (type) { + // create empty string + var s = ''; + // check parameter (option) + if (type !== undefined) { + s += getEnumAttr('type', type, regexCut); + } + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addPulse method + // Description: append the XML element to generate pulse + // Parameters: + // drawer string drawer kick-out connector pin (DRAWER_* constants) [option] + // time string the pulse on/off time [option] + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addPulse = function (drawer, time) { + // create empty string + var s = ''; + // check parameter (option) + if (drawer !== undefined) { + s += getEnumAttr('drawer', drawer, regexDrawer); + } + // check parameter (option) + if (time !== undefined) { + s += getEnumAttr('time', time, regexPulse); + } + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Function: addSound method + // Description: append the XML element to sound buzzer + // Parameters: + // pattern string a pattern (PATTERN_* constants) [option] + // repeat unsignedByte the number of times [option] + // Return: object ePOSBuilder object + // Throws: object invalid parameter + // + ePOSBuilder.prototype.addSound = function (pattern, repeat) { + // create empty string + var s = ''; + // check parameter (option) + if (pattern !== undefined) { + s += getEnumAttr('pattern', pattern, regexPattern); + } + // check parameter (option) + if (repeat !== undefined) { + s += getUByteAttr('repeat', repeat); + } + // append element + this.message += ''; + // return builder object + return this; + } + + // + // Method: toString + // Description: get the ePOS-Print XML message + // Parameters: none + // Return: string XML message + // + ePOSBuilder.prototype.toString = function () { + // append root element + var epos = '' + + this.message + ''; + // return message + return epos; + } + + // + // Function: encodeHexBinary method + // Description: encode binary data to hex binary data + // Parameters: + // s string binary data + // Return: string hex binary data + // + function encodeHexBinary(s) { + var r = ''; + for (i = 0; i < s.length; i++) { + r += ('0' + s.charCodeAt(i).toString(16)).slice(-2); + } + return r; + } + + // + // Function: encodeBase64Binary method + // Description: encode binary data to base64 binary data + // Parameters: + // s string binary data + // Return: string base64 binary data + // + function encodeBase64Binary(s) { + var r = ''; + var l = s.length; + var t = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + s += '\x00\x00'; + for (var i = 0; i < l; i += 3) { + var n = (s.charCodeAt(i) << 16) | (s.charCodeAt(i + 1) << 8) | s.charCodeAt(i + 2); + r += t.charAt((n >> 18) & 63) + t.charAt((n >> 12) & 63) + t.charAt((n >> 6) & 63) + t.charAt(n & 63); + } + var p = (3 - l % 3) % 3; + return r.slice(0, r.length - p) + '=='.slice(0, p); + } + + // + // Function: encodeRasterImage method + // Description: encode image data to raster bit image data + // Parameters: + // data byte[] RGBA image data + // w int image width + // h int image height + // Return: string raster bit image data + // + function encodeRasterImage(data, w, h) { + var d8 = [ + [0, 32, 8, 40, 2, 34, 10, 42], + [48, 16, 56, 24, 50, 18, 58, 26], + [12, 44, 4, 36, 14, 46, 6, 38], + [60, 28, 52, 20, 62, 30, 54, 22], + [3, 35, 11, 43, 1, 33, 9, 41], + [51, 19, 59, 27, 49, 17, 57, 25], + [15, 47, 7, 39, 13, 45, 5, 37], + [63, 31, 55, 23, 61, 29, 53, 21] + ]; + var s = '', n = 0, p = 0; + for (var y = 0; y < h; y++) { + for (var x = 0; x < w; x++) { + var r = data[p++], g = data[p++], b = data[p++], a = data[p++]; + var v = 255 - a + ((r * 29891 + g * 58661 + b * 11448) * a + 12750000) / 25500000; + var d = (d8[y & 7][x & 7] << 2) + 2; + if (v < d) { + n |= 0x80 >> (x & 7); + } + if ((x & 7) == 7 || x == w - 1) { + s += String.fromCharCode(n == 16 ? 32 : n); + n = 0; + } + } + } + return s; + } + + // + // Function: encodeXmlEntity method + // Description: encode markup character to XML entity + // Parameters: + // s string text data + // Return: string text data with XML entity + // + function encodeXmlEntity(s) { + var markup = /[<>&'"\t\n\r]/g; + if (markup.test(s)) { + s = s.replace(markup, function (c) { + var r = ''; + switch (c) { + case '<': + r = '<'; + break; + case '>': + r = '>'; + break; + case '&': + r = '&'; + break; + case "'": + r = '''; + break; + case '"': + r = '"'; + break; + case '\t': + r = ' '; + break; + case '\n': + r = ' '; + break; + case '\r': + r = ' '; + break; + default: + break; + } + return r; + }); + } + return s; + } + + // + // Function: escapeText method + // Description: escape sequence for bar code and symbol + // Parameters: + // s string text data + // Return: string text data with escape sequence + // + function escapeText(s) { + var escape = /[\\\x00-\x1f\x7f-\xff]/g; + if (escape.test(s)) { + s = s.replace(escape, function (c) { + return (c == '\\') ? '\\\\' : '\\x' + ('0' + c.charCodeAt(0).toString(16)).slice(-2); + }); + } + return s; + } + + // + // Function: regular expressions + // Description: enumeration check pattern + // + var regexFont = /^(font_[abc]|special_[ab])$/; + var regexAlign = /^(left|center|right)$/; + var regexColor = /^(none|color_[1-4])$/; + var regexBarcode = /^(upc_[ae]|[ej]an13|[ej]an8|code(39|93|128)|itf|codabar|gs1_128|gs1_databar_(omnidirectional|truncated|limited|expanded))$/; + var regexHri = /^(none|above|below|both)$/; + var regexSymbol = /^(pdf417_(standard|truncated)|qrcode_model_[12]|maxicode_mode_[2-6]|gs1_databar_(stacked(_omnidirectional)?|expanded_stacked))$/; + var regexLevel = /^(level_[0-8lmqh]|default)$/; + var regexLine = /^(thin|medium|thick)(_double)?$/; + var regexDirection = /^(left_to_right|bottom_to_top|right_to_left|top_to_bottom)$/; + var regexCut = /^(no_feed|feed|reserve)$/; + var regexDrawer = /^(drawer_1|drawer_2)$/; + var regexPulse = /^pulse_[1-5]00$/; + var regexPattern = /^(none|pattern_[a-e]|error|paper_end)$/; + + // + // Function: getEnumAttr method + // Description: get a XML attribute from a parameter (enumration) + // Parameters: + // name string parameter name + // value string parameter value + // regex regex check pattern + // Return: string XML attribute (' name="value"') + // Throws: object invalid parameter + // + function getEnumAttr(name, value, regex) { + if (!regex.test(value)) { + throw new Error('Parameter "' + name + '" is invalid'); + } + return ' ' + name + '="' + value + '"'; + } + + // + // Function: getBoolAttr method + // Description: get a XML attribute from a parameter (boolean) + // Parameters: + // name string parameter name + // value boolean parameter value + // Return: string XML attribute (' name="value"') + // + function getBoolAttr(name, value) { + return ' ' + name + '="' + !!value + '"'; + } + + // + // Function: getIntAttr method + // Description: get a XML attribute from a parameter (integer) + // Parameters: + // name string parameter name + // value integer parameter value + // min integer minumum value + // max integer maximum value + // Return: string XML attribute (' name="value"') + // Throws: object invalid parameter + // + function getIntAttr(name, value, min, max) { + if (isNaN(value) || value < min || value > max) { + throw new Error('Parameter "' + name + '" is invalid'); + } + return ' ' + name + '="' + value + '"'; + } + + // + // Function: getUByteAttr method + // Description: get a XML attribute from a parameter (unsigned byte) + // Parameters: + // name string parameter name + // value integer parameter value + // Return: string XML attribute (' name="value"') + // Throws: object invalid parameter + // + function getUByteAttr(name, value) { + return getIntAttr(name, value, 0, 255); + } + + // + // Function: getUShortAttr method + // Description: get a XML attribute from a parameter (unsigned short) + // Parameters: + // name string parameter name + // value integer parameter value + // Return: string XML attribute (' name="value"') + // Throws: object invalid parameter + // + function getUShortAttr(name, value) { + return getIntAttr(name, value, 0, 65535); + } + + // + // Function: ePOSPrint constructor + // Description: initialize an ePOS-Print object + // Parameters: none + // Return: none + // + function ePOSPrint() { + // events + this.onreceive = null; + this.onerror = null; + // constants + this.ASB_NO_RESPONSE = 0x00000001; + this.ASB_PRINT_SUCCESS = 0x00000002; + this.ASB_DRAWER_KICK = 0x00000004; + this.ASB_OFF_LINE = 0x00000008; + this.ASB_COVER_OPEN = 0x00000020; + this.ASB_PAPER_FEED = 0x00000040; + this.ASB_WAIT_ON_LINE = 0x00000100; + this.ASB_PANEL_SWITCH = 0x00000200; + this.ASB_MECHANICAL_ERR = 0x00000400; + this.ASB_AUTOCUTTER_ERR = 0x00000800; + this.ASB_UNRECOVER_ERR = 0x00002000; + this.ASB_AUTORECOVER_ERR = 0x00004000; + this.ASB_RECEIPT_NEAR_END = 0x00020000; + this.ASB_RECEIPT_END = 0x00080000; + this.ASB_BUZZER = 0x01000000; + this.ASB_SPOOLER_IS_STOPPED = 0x80000000; + } + + // + // Function: ePOSprint send method + // Description: send the ePOS-Print XML message + // Parameters: + // address string the address of ePOS-Print service + // request string request message + // Return: none + // Throws: object the browser does not equip XMLHttpRequest + // + ePOSPrint.prototype.send = function (address, request) { + // create SOAP envelope + var soap = '' + + '' + + request + ''; + // create XMLHttpRequest object + var xhr = createXMLHttpRequest(); + xhr.open('POST', address, true); + // set headers + xhr.setRequestHeader('Content-Type', 'text/xml; charset=UTF-8'); + xhr.setRequestHeader('If-Modified-Since', 'Thu, 01 Jan 1970 00:00:00 GMT'); + xhr.setRequestHeader('SOAPAction', '""'); + // receive event + var epos = this; + xhr.onreadystatechange = function () { + // receive response message + if (xhr.readyState == 4) { + if (xhr.status == 200) { + fireReceiveEvent(epos, xhr); + } + else { + fireErrorEvent(epos, xhr); + } + } + } + // send request message + xhr.send(soap); + } + + + /* + Function: createXMLHttpRequest method + Description: create an XMLHttpRequest object + Parameters: none + Return: object XMLHttpRequest object + Throws: object the browser does not equip XMLHttpRequest + */ + + function createXMLHttpRequest() + { + var xhr = null; + if (window.XMLHttpRequest) + { + xhr = new XMLHttpRequest(); + } + else if (window.ActiveXObject) + { + xhr = new ActiveXObject('Msxml2.XMLHTTP'); + } + else + { + throw new Error('XMLHttpRequest is not supported'); + } + return xhr; + } + + + // + // Function: fireReceiveEvent method + // Description: generate the onreceive event + // Parameters: + // epos object ePOSPrint object + // xhr object XMLHttpRequest object + // Return: none + // + function fireReceiveEvent(epos, xhr) { + if (epos.onreceive) { + var res = xhr.responseXML.getElementsByTagName('response'); + if (res.length > 0) { + // fire onreceive event + epos.onreceive({ + success: /^(1|true)$/.test(res[0].getAttribute('success')), + code: res[0].getAttribute('code'), + status: parseInt(res[0].getAttribute('status')) + }); + } + else { + fireErrorEvent(epos, xhr); + } + } + } + + // + // Function: fireErrorEvent method + // Description: generate the onerror event + // Parameters: + // epos object ePOSPrint object + // xhr object XMLHttpRequest object + // Return: none + // + function fireErrorEvent(epos, xhr) { + // fire onerror event + if (epos.onerror) { + epos.onerror({ + status: xhr.status, + responseText: xhr.responseText + }); + } + } + + +// F I S C A L --- F I S C A L --- F I S C A L --- F I S C A L --- F I S C A L --- F I S C A L --- F I S C A L --- F I S C A L + + + /* + Function: fiscalPrint constructor + Description: initialize a fiscalPrint object + Parameters: none + Return: none + */ + + function fiscalPrint() + { + // events + this.onreceive = null; + this.onerror = null; + + // constants + this.ASB_NO_RESPONSE = 0x00000001; + this.ASB_PRINT_SUCCESS = 0x00000002; + this.ASB_DRAWER_KICK = 0x00000004; + this.ASB_OFF_LINE = 0x00000008; + this.ASB_COVER_OPEN = 0x00000020; + this.ASB_PAPER_FEED = 0x00000040; + this.ASB_WAIT_ON_LINE = 0x00000100; + this.ASB_PANEL_SWITCH = 0x00000200; + this.ASB_MECHANICAL_ERR = 0x00000400; + this.ASB_AUTOCUTTER_ERR = 0x00000800; + this.ASB_UNRECOVER_ERR = 0x00002000; + this.ASB_AUTORECOVER_ERR = 0x00004000; + this.ASB_RECEIPT_NEAR_END = 0x00020000; + this.ASB_RECEIPT_END = 0x00080000; + this.ASB_BUZZER = 0x01000000; + this.ASB_SPOOLER_IS_STOPPED = 0x80000000; + } + + + /* + Function: fiscalPrint send method + Description: send the fiscal ePOS-Print XML message + Parameters: + address string The address where fpmate.cgi resides + request string Request message + Return: none + Throws: object The browser does not equip XMLHttpRequest + */ + + fiscalPrint.prototype.send = function (address, request, timeout, callMode) + { + timeout = timeout || 0; + callMode = callMode || "async"; + + // create SOAP envelope + var soap = '\n' + + '\n' + + '\n' + + request + + '\n' + + '\n'; + // create XMLHttpRequest object + var xhr = createXMLHttpRequest(); + if (callMode == "async") + { + xhr.open('POST', address, true); + } + else + { + xhr.open('POST', address, false); // PHIL false = sincrono + } + // set headers + xhr.setRequestHeader('Content-Type', 'text/xml; charset=UTF-8'); + xhr.setRequestHeader('If-Modified-Since', 'Thu, 01 Jan 1970 00:00:00 GMT'); + xhr.setRequestHeader('SOAPAction', '""'); + // receive event + var epos = this; + + // PHIL timeout non va con le richieste sincrone + if (callMode == "async") + { + xhr.timeout = timeout; + xhr.ontimeout = function () { + console.log("Timed out!!!"); + fireFiscalErrorEvent(epos, xhr); + } + } + + xhr.onreadystatechange = function () + { + // receive response message + // alert("xhr.readyState = " + xhr.readyState + "\n" + "xhr.status = " + xhr.status); + if (xhr.readyState == 4) + { + if (xhr.status == 200) + { + fireFiscalReceiveEvent(epos, xhr); + } + else + { + fireFiscalErrorEvent(epos, xhr); + } + } + } + + // send request message + xhr.send(soap); + } + + /* + Function: fireFiscalReceiveEvent method + Description: generate the onreceive event + Parameters: + epos object ePOSPrint object + xhr object XMLHttpRequest object + Return: none + */ + + function fireFiscalReceiveEvent(epos, xhr) + { + if (epos.onreceive) + { + // alert ("xhr.responseXML.xml = " + xhr.responseXML.xml); + var res = xhr.responseXML.getElementsByTagName('response'); + if (res.length > 0) + { + // fire onreceive event + var result = + { + success: /^(1|true)$/.test(res[0].getAttribute('success')), + code: res[0].getAttribute('code'), + status: parseInt(res[0].getAttribute('status'), 10), + statusText: res[0].getAttribute('status') + }; + + // look for additional info + var res_add = res[0].getElementsByTagName('addInfo'); + if (res_add.length > 0) + { + var list = res_add[0].getElementsByTagName('elementList'); + var list_len = list.length; + var tag_names_list = list[0].childNodes[0].nodeValue; + var tag_names_array = tag_names_list.split(','); + var add_info = {}; + + for (var tnai = 0; tnai < tag_names_array.length; tnai++) + { + var node = res_add[0].getElementsByTagName(tag_names_array[tnai])[0]; + var node_child = node.childNodes[0]; + var node_val = ""; + // 21/02/2018 Philip Barnett. Alcuni comandi tornano con responseData vuoto. Senza la verifica, possono vericarsi i null Exception. + // Questa riga non ha risolto il problema - if(node_child.nodeValue != null && node_child.nodeValue != "") + try + { + node_val = node_child.nodeValue; + } + catch(err) // Blank lines generate exceptions + { + // node_val = "Elemento " + node.childNodes[0] + " vuoto"; + } + add_info[tag_names_array[tnai]] = node_val; + } + } + else { + var tag_names_array = ""; + var add_info = ""; + } + + epos.onreceive(result, tag_names_array, add_info, res_add) + } + else // res.length <= 0 + { + // alert ("res.length = " + res.length); + } // end if (res.length > 0) + } // end if (epos.onreceive) + } // end function fireFiscalReceiveEvent(epos, xhr) + + + /* + Function: fireFiscalErrorEvent method + Description: generate the onerror event + Parameters: + epos object ePOSPrint object + xhr object XMLHttpRequest object + Return: none + */ + + function fireFiscalErrorEvent(epos, xhr) + { + // fire onerror event + // alert("Error event called"); + if (epos.onerror) + { + var result = + { + success: 'false', + code: "FP_NO_ANSWER_NETWORK", + status: 0, + responseXML: xhr.responseXML, + responseText: xhr.responseText + }; + + epos.onerror(result) + } + } + + + // + // Function: CanvasPrint constructor + // Description: initialize a Canvas-Print object + // Parameters: none + // Return: none + // + + function CanvasPrint() { + } + // inherit from ePOSPrint object + CanvasPrint.prototype = new ePOSPrint(); + CanvasPrint.prototype.constructor = CanvasPrint; + + // + // Function: print method + // Description: print the HTML 5 Canvas + // Parameters: + // address string the address of ePOS-Print service + // canvas object HTML 5 Canvas object + // cut boolean when true, cut paper [option] + // Return: none + // Throws: object the browser does not equip Canvas + // + CanvasPrint.prototype.print = function (address, canvas, cut) { + // check parameter + if (!canvas.getContext) { + throw new Error('Canvas is not supported'); + } + // get HTML 5 Canvas + var context = canvas.getContext('2d'); + var width = canvas.getAttribute('width'); + var height = canvas.getAttribute('height'); + // create ePOS-Print XML message + var builder = new ePOSBuilder(); + builder.addImage(context, 0, 0, width, height); + if (cut) { + builder.addCut(builder.CUT_FEED); + } + // send request message + this.send(address, builder.toString()); + }; + + + /* + Function: epson object + Description: append constructors to window object + */ + + // Core module 'pos_epson_printer' appends constructors to the same window object so + // this implementation avoid to override the epson object. + if (!window.epson) { + window.epson = {} + } + window.epson.ePOSBuilder = window.epson.ePOSBuilder || ePOSBuilder + window.epson.ePOSPrint = window.epson.ePOSPrint || ePOSPrint + window.epson.CanvasPrint = window.epson.CanvasPrint || CanvasPrint + window.epson.fiscalPrint = fiscalPrint + window.epson.encodeBase64Binary = encodeBase64Binary + + +})(window); + + +/* +Function: decodeFpStatus +Description: Decodes the five printer status bytes +Parameters: add_info.fpstatus +Return: printer, ej, receipt, cash_drawer and mode +*/ + + +/* +function decodeFpStatus(add_info.fpStatus) +{ + + var printer = ""; + var ej = ""; + var cash_drawer = ""; + var receipt = ""; + var mode = ""; + + switch(add_info.fpStatus.substring(0,1)) { + case "0": + printer = "OK"; + break; + case "2": + printer = "Carta in esaurimento"; + break; + case "3": + printer = "Offline (fine carta o coperchio aperto)"; + break; + default: + printer = "Risposta errata"; + } + + switch(add_info.fpStatus.substring(1,2)) { + case "0": + ej = "OK"; + break; + case "1": + ej = "Prossimo ad Esaurimento"; + break; + case "2": + ej = "Da formattare"; + break; + case "3": + ej = "Precedente"; + break; + case "4": + ej = "Di altro misuratore"; + break; + case "5": + ej = "Esaurito"; + break; + default: + ej = "Risposta errata"; + } + + switch(add_info.fpStatus.substring(2,3)) { + case "0": + cash_drawer = "Aperto"; + break; + case "1": + cash_drawer = "Chiuso"; + break; + default: + cash_drawer = "Risposta errata"; + } + + switch(add_info.fpStatus.substring(3,4)) { + case "0": + receipt = "Fiscale aperto"; + break; + case "1": + receipt = "Fiscale/Non fiscale chiuso"; + break; + case "2": + receipt = "Non fiscale aperto"; + break; + case "3": + receipt = "Pagamento in corso"; + break; + case "4": + receipt = "Errore ultimo comando ESC/POS con Fiscale/Non fiscale chiuso"; + break; + case "5": + receipt = "Scontrino in negativo"; + break; + case "6": + receipt = "Errore ultimo comando ESC/POS con Non fiscale aperto"; + break; + case "7": + receipt = "Attesa chiusura scontrino modalità JAVAPOS"; + break; + case "8": + receipt = "Documento fiscale aperto"; + break; + case "A": + receipt = "Titolo aperto"; + break; + case "2": + receipt = "Titolo chiuso"; + break; + default: + receipt = "Risposta errata"; + } + + switch(add_info.fpStatus.substring(4,5)) { + case "0": + mode = "Stato registrazione"; + break; + case "1": + mode = "Stato X"; + break; + case "2": + mode = "Stato Z"; + break; + case "3": + mode = "Stato Set"; + break; + default: + mode = "Risposta errata"; + } +} +*/ diff --git a/fiscal_epos_print/static/lib/pikaday/pikaday.min.css b/fiscal_epos_print/static/lib/pikaday/pikaday.min.css new file mode 100644 index 000000000000..92f41e33c90b --- /dev/null +++ b/fiscal_epos_print/static/lib/pikaday/pikaday.min.css @@ -0,0 +1,5 @@ +@charset "UTF-8";/*! + * Pikaday + * Copyright © 2014 David Bushell | BSD & MIT license | https://dbushell.com/ + */.pika-single{z-index:9999;display:block;position:relative;color:#333;background:#fff;border:1px solid #ccc;border-bottom-color:#bbb;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}.pika-single:after,.pika-single:before{content:" ";display:table}.pika-single:after{clear:both}.pika-single.is-hidden{display:none}.pika-single.is-bound{position:absolute;box-shadow:0 5px 15px -5px rgba(0,0,0,.5)}.pika-lendar{float:left;width:240px;margin:8px}.pika-title{position:relative;text-align:center}.pika-label{display:inline-block;position:relative;z-index:9999;overflow:hidden;margin:0;padding:5px 3px;font-size:14px;line-height:20px;font-weight:700;background-color:#fff}.pika-title select{cursor:pointer;position:absolute;z-index:9998;margin:0;left:0;top:5px;opacity:0}.pika-next,.pika-prev{display:block;cursor:pointer;position:relative;outline:0;border:0;padding:0;width:20px;height:30px;text-indent:20px;white-space:nowrap;overflow:hidden;background-color:transparent;background-position:center center;background-repeat:no-repeat;background-size:75% 75%;opacity:.5}.pika-next:hover,.pika-prev:hover{opacity:1}.is-rtl .pika-next,.pika-prev{float:left;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==)}.is-rtl .pika-prev,.pika-next{float:right;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=)}.pika-next.is-disabled,.pika-prev.is-disabled{cursor:default;opacity:.2}.pika-select{display:inline-block}.pika-table{width:100%;border-collapse:collapse;border-spacing:0;border:0}.pika-table td,.pika-table th{width:14.285714285714286%;padding:0}.pika-table th{color:#999;font-size:12px;line-height:25px;font-weight:700;text-align:center}.pika-button{cursor:pointer;display:block;box-sizing:border-box;-moz-box-sizing:border-box;outline:0;border:0;margin:0;width:100%;padding:5px;color:#666;font-size:12px;line-height:15px;text-align:right;background:#f5f5f5}.pika-week{font-size:11px;color:#999}.is-today .pika-button{color:#3af;font-weight:700}.has-event .pika-button,.is-selected .pika-button{color:#fff;font-weight:700;background:#3af;box-shadow:inset 0 1px 3px #178fe5;border-radius:3px}.has-event .pika-button{background:#005da9;box-shadow:inset 0 1px 3px #0076c9}.is-disabled .pika-button,.is-inrange .pika-button{background:#d5e9f7}.is-startrange .pika-button{color:#fff;background:#6cb31d;box-shadow:none;border-radius:3px}.is-endrange .pika-button{color:#fff;background:#3af;box-shadow:none;border-radius:3px}.is-disabled .pika-button{pointer-events:none;cursor:default;color:#999;opacity:.3}.is-outside-current-month .pika-button{color:#999;opacity:.3}.is-selection-disabled{pointer-events:none;cursor:default}.pika-button:hover,.pika-row.pick-whole-week:hover .pika-button{color:#fff;background:#ff8000;box-shadow:none;border-radius:3px}.pika-table abbr{border-bottom:none;cursor:help} +/*# sourceMappingURL=pikaday.min.css.map */ \ No newline at end of file diff --git a/fiscal_epos_print/static/lib/pikaday/pikaday.min.js b/fiscal_epos_print/static/lib/pikaday/pikaday.min.js new file mode 100644 index 000000000000..078c1b5d6238 --- /dev/null +++ b/fiscal_epos_print/static/lib/pikaday/pikaday.min.js @@ -0,0 +1 @@ +!function(e,t){"use strict";var n;if("object"==typeof exports){try{n=require("moment")}catch(e){}module.exports=t(n)}else"function"==typeof define&&define.amd?define(function(e){try{n=e("moment")}catch(e){}return t(n)}):e.Pikaday=t(e.moment)}(this,function(n){"use strict";var s="function"==typeof n,o=!!window.addEventListener,c=window.document,h=window.setTimeout,r=function(e,t,n,a){o?e.addEventListener(t,n,!!a):e.attachEvent("on"+t,n)},t=function(e,t,n,a){o?e.removeEventListener(t,n,!!a):e.detachEvent("on"+t,n)},l=function(e,t){return-1!==(" "+e.className+" ").indexOf(" "+t+" ")},f=function(e,t){l(e,t)||(e.className=""===e.className?t:e.className+" "+t)},g=function(e,t){var n;e.className=(n=(" "+e.className+" ").replace(" "+t+" "," ")).trim?n.trim():n.replace(/^\s+|\s+$/g,"")},y=function(e){return/Array/.test(Object.prototype.toString.call(e))},F=function(e){return/Date/.test(Object.prototype.toString.call(e))&&!isNaN(e.getTime())},L=function(e,t){return[31,(n=e,n%4==0&&n%100!=0||n%400==0?29:28),31,30,31,30,31,31,30,31,30,31][t];var n},P=function(e){F(e)&&e.setHours(0,0,0,0)},B=function(e,t){return e.getTime()===t.getTime()},d=function(e,t,n){var a,i;for(a in t)(i=void 0!==e[a])&&"object"==typeof t[a]&&null!==t[a]&&void 0===t[a].nodeName?F(t[a])?n&&(e[a]=new Date(t[a].getTime())):y(t[a])?n&&(e[a]=t[a].slice(0)):e[a]=d({},t[a],n):!n&&i||(e[a]=t[a]);return e},i=function(e,t,n){var a;c.createEvent?((a=c.createEvent("HTMLEvents")).initEvent(t,!0,!1),a=d(a,n),e.dispatchEvent(a)):c.createEventObject&&(a=c.createEventObject(),a=d(a,n),e.fireEvent("on"+t,a))},a=function(e){return e.month<0&&(e.year-=Math.ceil(Math.abs(e.month)/12),e.month+=12),11';t.push("is-outside-current-month"),e.enableSelectionDaysInNextAndPreviousMonths||t.push("is-selection-disabled")}return e.isDisabled&&t.push("is-disabled"),e.isToday&&t.push("is-today"),e.isSelected&&(t.push("is-selected"),n="true"),e.hasEvent&&t.push("has-event"),e.isInRange&&t.push("is-inrange"),e.isStartRange&&t.push("is-startrange"),e.isEndRange&&t.push("is-endrange"),'"},p=function(e,t,n,a,i,s){var o,r,l,h,d,u=e._o,c=n===u.minYear,f=n===u.maxYear,g='
',m=!0,p=!0;for(l=[],o=0;o<12;o++)l.push('");for(h='
'+u.i18n.months[a]+'
",y(u.yearRange)?(o=u.yearRange[0],r=u.yearRange[1]+1):(o=n-u.yearRange,r=1+n+u.yearRange),l=[];o=u.minYear&&l.push('");return d='
'+n+u.yearSuffix+'
",u.showMonthAfterYear?g+=d+h:g+=h+d,c&&(0===a||u.minMonth>=a)&&(m=!1),f&&(11===a||u.maxMonth<=a)&&(p=!1),0===t&&(g+='"),t===e._o.numberOfMonths-1&&(g+='"),g+"
"},V=function(e,t,n){return''+function(e){var t,n=[];for(e.showWeekNumber&&n.push(""),t=0;t<7;t++)n.push('");return""+(e.isRTL?n.reverse():n).join("")+""}(e)+(""+t.join("")+"")+"
'+m(e,t,!0)+"
"},e=function(e){var a=this,i=a.config(e);a._onMouseDown=function(e){if(a._v){var t=(e=e||window.event).target||e.srcElement;if(t)if(l(t,"is-disabled")||(!l(t,"pika-button")||l(t,"is-empty")||l(t.parentNode,"is-disabled")?l(t,"pika-prev")?a.prevMonth():l(t,"pika-next")&&a.nextMonth():(a.setDate(new Date(t.getAttribute("data-pika-year"),t.getAttribute("data-pika-month"),t.getAttribute("data-pika-day"))),i.bound&&h(function(){a.hide(),i.blurFieldOnSelect&&i.field&&i.field.blur()},100))),l(t,"pika-select"))a._c=!0;else{if(!e.preventDefault)return e.returnValue=!1;e.preventDefault()}}},a._onChange=function(e){var t=(e=e||window.event).target||e.srcElement;t&&(l(t,"pika-select-month")?a.gotoMonth(t.value):l(t,"pika-select-year")&&a.gotoYear(t.value))},a._onKeyChange=function(e){if(e=e||window.event,a.isVisible())switch(e.keyCode){case 13:case 27:i.field&&i.field.blur();break;case 37:e.preventDefault(),a.adjustDate("subtract",1);break;case 38:a.adjustDate("subtract",7);break;case 39:a.adjustDate("add",1);break;case 40:a.adjustDate("add",7)}},a._onInputChange=function(e){var t;e.firedBy!==a&&(t=i.parse?i.parse(i.field.value,i.format):s?(t=n(i.field.value,i.format,i.formatStrict))&&t.isValid()?t.toDate():null:new Date(Date.parse(i.field.value)),F(t)&&a.setDate(t),a._v||a.show())},a._onInputFocus=function(){a.show()},a._onInputClick=function(){a.show()},a._onInputBlur=function(){var e=c.activeElement;do{if(l(e,"pika-single"))return}while(e=e.parentNode);a._c||(a._b=h(function(){a.hide()},50)),a._c=!1},a._onClick=function(e){var t=(e=e||window.event).target||e.srcElement,n=t;if(t){!o&&l(t,"pika-select")&&(t.onchange||(t.setAttribute("onchange","return;"),r(t,"change",a._onChange)));do{if(l(n,"pika-single")||n===i.trigger)return}while(n=n.parentNode);a._v&&t!==i.trigger&&n!==i.trigger&&a.hide()}},a.el=c.createElement("div"),a.el.className="pika-single"+(i.isRTL?" is-rtl":"")+(i.theme?" "+i.theme:""),r(a.el,"mousedown",a._onMouseDown,!0),r(a.el,"touchend",a._onMouseDown,!0),r(a.el,"change",a._onChange),i.keyboardInput&&r(c,"keydown",a._onKeyChange),i.field&&(i.container?i.container.appendChild(a.el):i.bound?c.body.appendChild(a.el):i.field.parentNode.insertBefore(a.el,i.field.nextSibling),r(i.field,"change",a._onInputChange),i.defaultDate||(s&&i.field.value?i.defaultDate=n(i.field.value,i.format).toDate():i.defaultDate=new Date(Date.parse(i.field.value)),i.setDefaultDate=!0));var t=i.defaultDate;F(t)?i.setDefaultDate?a.setDate(t,!0):a.gotoDate(t):a.gotoDate(new Date),i.bound?(this.hide(),a.el.className+=" is-bound",r(i.trigger,"click",a._onInputClick),r(i.trigger,"focus",a._onInputFocus),r(i.trigger,"blur",a._onInputBlur)):this.show()};return e.prototype={config:function(e){this._o||(this._o=d({},u,!0));var t=d(this._o,e,!0);t.isRTL=!!t.isRTL,t.field=t.field&&t.field.nodeName?t.field:null,t.theme="string"==typeof t.theme&&t.theme?t.theme:null,t.bound=!!(void 0!==t.bound?t.field&&t.bound:t.field),t.trigger=t.trigger&&t.trigger.nodeName?t.trigger:t.field,t.disableWeekends=!!t.disableWeekends,t.disableDayFn="function"==typeof t.disableDayFn?t.disableDayFn:null;var n=parseInt(t.numberOfMonths,10)||1;if(t.numberOfMonths=4=i&&(this._y=i,!isNaN(o)&&this._m>o&&(this._m=o)),t="pika-title-"+Math.random().toString(36).replace(/[^a-z]+/g,"").substr(0,2);for(var l=0;l'+p(this,l,this.calendars[l].year,this.calendars[l].month,this.calendars[0].year,t)+this.render(this.calendars[l].year,this.calendars[l].month,t)+"";this.el.innerHTML=r,n.bound&&"hidden"!==n.field.type&&h(function(){n.trigger.focus()},1),"function"==typeof this._o.onDraw&&this._o.onDraw(this),n.bound&&n.field.setAttribute("aria-label",n.ariaLabel)}},adjustPosition:function(){var e,t,n,a,i,s,o,r,l,h,d,u;if(!this._o.container){if(this.el.style.position="absolute",t=e=this._o.trigger,n=this.el.offsetWidth,a=this.el.offsetHeight,i=window.innerWidth||c.documentElement.clientWidth,s=window.innerHeight||c.documentElement.clientHeight,o=window.pageYOffset||c.body.scrollTop||c.documentElement.scrollTop,u=d=!0,"function"==typeof e.getBoundingClientRect)r=(h=e.getBoundingClientRect()).left+window.pageXOffset,l=h.bottom+window.pageYOffset;else for(r=t.offsetLeft,l=t.offsetTop+t.offsetHeight;t=t.offsetParent;)r+=t.offsetLeft,l+=t.offsetTop;(this._o.reposition&&ia.maxDate||a.disableWeekends&&(void 0,0===(w=R.getDay())||6===w)||a.disableDayFn&&a.disableDayFn(R),isEmpty:I,isStartRange:O,isEndRange:j,isInRange:W,showDaysInNextAndPreviousMonths:a.showDaysInNextAndPreviousMonths,enableSelectionDaysInNextAndPreviousMonths:a.enableSelectionDaysInNextAndPreviousMonths};a.pickWholeWeek&&N&&(M=!0),l.push(H(A)),7==++x&&(a.showWeekNumber&&l.unshift((D=k-o,v=t,b=e,_=void 0,_=new Date(b,0,1),''+Math.ceil(((new Date(b,v,D)-_)/864e5+_.getDay()+1)/7)+"")),r.push((p=l,y=a.isRTL,''+(y?p.reverse():p).join("")+"")),x=0,M=!(l=[]))}return V(a,r,n)},isVisible:function(){return this._v},show:function(){this.isVisible()||(this._v=!0,this.draw(),g(this.el,"is-hidden"),this._o.bound&&(r(c,"click",this._onClick),this.adjustPosition()),"function"==typeof this._o.onOpen&&this._o.onOpen.call(this))},hide:function(){var e=this._v;!1!==e&&(this._o.bound&&t(c,"click",this._onClick),this.el.style.position="static",this.el.style.left="auto",this.el.style.top="auto",f(this.el,"is-hidden"),this._v=!1,void 0!==e&&"function"==typeof this._o.onClose&&this._o.onClose.call(this))},destroy:function(){var e=this._o;this.hide(),t(this.el,"mousedown",this._onMouseDown,!0),t(this.el,"touchend",this._onMouseDown,!0),t(this.el,"change",this._onChange),e.keyboardInput&&t(c,"keydown",this._onKeyChange),e.field&&(t(e.field,"change",this._onInputChange),e.bound&&(t(e.trigger,"click",this._onInputClick),t(e.trigger,"focus",this._onInputFocus),t(e.trigger,"blur",this._onInputBlur))),this.el.parentNode&&this.el.parentNode.removeChild(this.el)}},e}); \ No newline at end of file diff --git a/fiscal_epos_print/static/src/css/pos.css b/fiscal_epos_print/static/src/css/pos.css new file mode 100644 index 000000000000..e92003755081 --- /dev/null +++ b/fiscal_epos_print/static/src/css/pos.css @@ -0,0 +1,83 @@ +/* ********* The Epson ePOS FP81II Widget ********* */ + +.pos .epson-fp81ii-widget { + z-index: 100000; + position: absolute; + right: 10px; + top: 10px; + width: 236px; + font-size: 14px; + + background: rgba(0, 0, 0, 0.82) !important; + color: white; + padding-bottom: 10px; + cursor: move; + -webkit-transform: translate3d(0, 0, 0); +} +.pos .pos-rightheader .header-button > img { + vertical-align: middle; +} +.pos .epson-fp81ii-widget .toggle { + position: absolute; + font-size: 16px; + cursor: pointer; + top: 0px; + right: 0px; + padding: 10px; + padding-right: 15px; +} +.pos .epson-fp81ii-widget .content { + overflow: hidden; +} +.pos .epson-fp81ii-widget h1 { + background: black; + padding-top: 10px; + padding-left: 10px; + margin-top: 0; + margin-bottom: 0; +} +.pos .epson-fp81ii-widget .category { + background: black; + padding-left: 10px; + margin: 0px; + font-weight: bold; + font-size: 16px; + padding-top: 3px; + padding-bottom: 3px; +} +.pos .epson-fp81ii-widget .button { + padding: 5px; + padding-left: 15px; + display: block; + cursor: pointer; +} +.pos .epson-fp81ii-widget .button:hover { + background: #546e7a; +} +.pos .epson-fp81ii-widget .category-label { + position: relative; + padding: 4px; + top: -12px; +} +.pos .epson-fp81ii-widget li img { + min-width: 32px; +} +.pos .epson-fp81ii-widget .button:active { + background: rgba(96, 21, 177, 0.45); +} +.pos .epson-fp81ii-widget .status { + padding: 5px; + padding-left: 15px; + display: block; + cursor: default; +} +.pos .epson-fp81ii-widget .status.on { + background-color: #6cd11d; +} +.pos .epson-fp81ii-widget .event { + padding: 5px; + padding-left: 15px; + display: block; + cursor: default; + background-color: #1e1e1e; +} diff --git a/fiscal_epos_print/static/src/css/pos_reprint.css b/fiscal_epos_print/static/src/css/pos_reprint.css new file mode 100644 index 000000000000..e882fa7fd372 --- /dev/null +++ b/fiscal_epos_print/static/src/css/pos_reprint.css @@ -0,0 +1,20 @@ +.receipt-screen .default-view .actions .reprint_buttons .button { + flex: 1; + border: solid 1px rgb(209, 209, 209); + padding: 1rem; + text-align: center; + border-radius: 3px; + cursor: pointer; + background: rgb(230, 230, 230); +} + +.receipt-screen .default-view .actions .reprint_buttons { + display: flex; + margin: 1rem 0px; +} + +.receipt-screen .default-view .actions .reprint_buttons .printing-error { + background: rgb(255, 0, 0); + border: solid 1px #648b0000 !important; + color: white !important; +} diff --git a/fiscal_epos_print/static/src/img/X.svg b/fiscal_epos_print/static/src/img/X.svg new file mode 100644 index 000000000000..a8b9a34b3358 --- /dev/null +++ b/fiscal_epos_print/static/src/img/X.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/fiscal_epos_print/static/src/img/Z.svg b/fiscal_epos_print/static/src/img/Z.svg new file mode 100644 index 000000000000..4eb71e9c0886 --- /dev/null +++ b/fiscal_epos_print/static/src/img/Z.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/fiscal_epos_print/static/src/img/ade-logo.svg b/fiscal_epos_print/static/src/img/ade-logo.svg new file mode 100644 index 000000000000..fd785b17addc --- /dev/null +++ b/fiscal_epos_print/static/src/img/ade-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/fiscal_epos_print/static/src/img/cashdrawer.svg b/fiscal_epos_print/static/src/img/cashdrawer.svg new file mode 100644 index 000000000000..8f5cd95f3257 --- /dev/null +++ b/fiscal_epos_print/static/src/img/cashdrawer.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/fiscal_epos_print/static/src/img/epos.svg b/fiscal_epos_print/static/src/img/epos.svg new file mode 100644 index 000000000000..1223ef920660 --- /dev/null +++ b/fiscal_epos_print/static/src/img/epos.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/fiscal_epos_print/static/src/img/reprint.svg b/fiscal_epos_print/static/src/img/reprint.svg new file mode 100644 index 000000000000..85037e2220fc --- /dev/null +++ b/fiscal_epos_print/static/src/img/reprint.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/fiscal_epos_print/static/src/js/ChromeWidgets/EpsonEPOSButton.js b/fiscal_epos_print/static/src/js/ChromeWidgets/EpsonEPOSButton.js new file mode 100644 index 000000000000..a92158689f68 --- /dev/null +++ b/fiscal_epos_print/static/src/js/ChromeWidgets/EpsonEPOSButton.js @@ -0,0 +1,27 @@ +odoo.define("fiscal_epos_print.EpsonEPOSButton", function (require) { + "use strict"; + + const PosComponent = require("point_of_sale.PosComponent"); + const Registries = require("point_of_sale.Registries"); + + class EpsonEPOSButton extends PosComponent { + /** + * Method that manage EpsonFP81IIComponent visibility through onClick + * Handler + */ + async onClick() { + var epsonFP81IIComponent = $(".status-buttons .epson-fp81ii-widget"); + if (epsonFP81IIComponent.hasClass("oe_hidden")) { + epsonFP81IIComponent.removeClass("oe_hidden"); + } else { + epsonFP81IIComponent.addClass("oe_hidden"); + } + } + } + + EpsonEPOSButton.template = "EpsonEPOSButton"; + + Registries.Component.add(EpsonEPOSButton); + + return EpsonEPOSButton; +}); diff --git a/fiscal_epos_print/static/src/js/ChromeWidgets/EpsonFP81IIComponent.js b/fiscal_epos_print/static/src/js/ChromeWidgets/EpsonFP81IIComponent.js new file mode 100644 index 000000000000..7128bf48459f --- /dev/null +++ b/fiscal_epos_print/static/src/js/ChromeWidgets/EpsonFP81IIComponent.js @@ -0,0 +1,208 @@ +odoo.define("fiscal_epos_print.EpsonFP81IIComponent", function (require) { + "use strict"; + + var core = require("web.core"); + var epson_epos_print = require("fiscal_epos_print.epson_epos_print"); + var _t = core._t; + var eposDriver = epson_epos_print.eposDriver; + + const {Gui} = require("point_of_sale.Gui"); + const PosComponent = require("point_of_sale.PosComponent"); + const Registries = require("point_of_sale.Registries"); + + class EpsonFP81IIComponent extends PosComponent { + constructor() { + super(...arguments); + var self = this; + + // For dragging the debug widget around + this.dragging = false; + this.dragpos = { + x: 0, + y: 0, + }; + + function eventpos(event) { + if (event.touches && event.touches[0]) { + return { + x: event.touches[0].screenX, + y: event.touches[0].screenY, + }; + } + return { + x: event.screenX, + y: event.screenY, + }; + } + + this.dragend_handler = function () { + self.dragging = false; + }; + this.dragstart_handler = function (event) { + self.dragging = true; + self.dragpos = eventpos(event); + }; + this.dragmove_handler = function (event) { + if (self.dragging) { + var top = this.offsetTop; + var left = this.offsetLeft; + var pos = eventpos(event); + var dx = pos.x - self.dragpos.x; + var dy = pos.y - self.dragpos.y; + + self.dragpos = pos; + + this.style.right = "auto"; + this.style.bottom = "auto"; + this.style.left = left + dx + "px"; + this.style.top = top + dy + "px"; + } + event.preventDefault(); + event.stopPropagation(); + }; + } + + OnMounted() { + // Jquery reference of the component + this.$el = $(this.el); + // Drag listeners + this.el.addEventListener("mouseleave", this.dragend_handler); + this.el.addEventListener("mouseup", this.dragend_handler); + this.el.addEventListener("touchend", this.dragend_handler); + this.el.addEventListener("touchcancel", this.dragend_handler); + this.el.addEventListener("mousedown", this.dragstart_handler); + this.el.addEventListener("touchstart", this.dragstart_handler); + this.el.addEventListener("mousemove", this.dragmove_handler); + this.el.addEventListener("touchmove", this.dragmove_handler); + } + + do_show() { + var epsonFP81IIComponent = $(".status-buttons .epson-fp81ii-widget"); + epsonFP81IIComponent.removeClass("oe_hidden"); + } + + do_hide() { + var epsonFP81IIComponent = $(".status-buttons .epson-fp81ii-widget"); + epsonFP81IIComponent.addClass("oe_hidden"); + } + + getPrinterOptions() { + var protocol = this.env.pos.config.use_https ? "https://" : "http://"; + var printer_url = + protocol + this.env.pos.config.printer_ip + "/cgi-bin/fpmate.cgi"; + return {url: printer_url}; + } + + onToggleComponent() { + this.do_hide(); + } + + async openCashDrawer() { + this.do_hide(); + // TODO find the same Component method that show loading_* + // this.chrome.loading_show(); + // this.chrome.loading_message(_t('Connecting to the fiscal printer')); + var printer_options = this.getPrinterOptions(); + var fp90 = new eposDriver(printer_options, this); + fp90.printOpenCashDrawer(); + const {confirmed} = await Gui.showPopup("ConfirmPopup", { + title: _t("CashDrawer Opened"), + body: _t("Close"), + }); + if (confirmed) { + fp90.resetPrinter(); + } else { + // TODO not exist + // this.chrome.loading_hide(); + } + } + + async reprintLastReceipt() { + this.do_hide(); + // Var self = this; + // this._super(); + + // TODO find the same Component method that show loading_* + // this.chrome.loading_show(); + // this.chrome.loading_message(_t('Connecting to the fiscal printer')); + var printer_options = this.getPrinterOptions(); + var fp90 = new eposDriver(printer_options, this); + // ConfirmPopup + const {confirmed} = await Gui.showPopup("ConfirmPopup", { + title: _t("Reprint Last Receipt?"), + body: _t("Please confirm to reprint the last receipt"), + }); + if (confirmed) { + fp90.printFiscalReprintLast( + this.env.pos.cashier.fiscal_operator_number || "1" + ); + } else { + // TODO not exist + // this.chrome.loading_hide(); + } + } + + showAdeStatus() { + this.do_hide(); + // TODO find the same Component method that show loading_* + // this.chrome.loading_show(); + // this.chrome.loading_message(_t('Connecting to the fiscal printer')); + var printer_options = this.getPrinterOptions(); + var fp90 = new eposDriver(printer_options, this); + fp90.getStatusOfFilesForADE(); + } + + async fiscalClosing() { + this.do_hide(); + // TODO find the same Component method that show loading_* + // this.chrome.loading_show(); + // this.chrome.loading_message(_t('Connecting to the fiscal printer')); + var printer_options = this.getPrinterOptions(); + var fp90 = new eposDriver(printer_options, this); + // ConfirmPopup + const {confirmed} = await this.showPopup("ConfirmPopup", { + title: _t("Confirm Printer Fiscal Closure (Report Z)?"), + body: _t("Please confirm to execute the Printer Fiscal Closure"), + }); + if (confirmed) { + // Fp90.printFiscalReport(); + fp90.printFiscalXZReport( + this.env.pos.cashier.fiscal_operator_number || "1" + ); + } else { + // TODO not exist + // this.chrome.loading_hide(); + } + } + + async fiscalXreport() { + this.do_hide(); + // TODO find the same Component method that show loading_* + // this.chrome.loading_show(); + // this.chrome.loading_message(_t('Connecting to the fiscal printer')); + var printer_options = this.getPrinterOptions(); + var fp90 = new eposDriver(printer_options, this); + // ConfirmPopup + const {confirmed} = await this.showPopup("ConfirmPopup", { + title: _t("Confirm Printer Daily Financial Report (Report X)?"), + body: _t( + "Please confirm to execute the Printer Daily Financial Report" + ), + }); + if (confirmed) { + fp90.printFiscalXReport( + this.env.pos.cashier.fiscal_operator_number || "1" + ); + } else { + // TODO not exist + // this.chrome.loading_hide(); + } + } + } + + EpsonFP81IIComponent.template = "EpsonFP81IIComponent"; + + Registries.Component.add(EpsonFP81IIComponent); + + return EpsonFP81IIComponent; +}); diff --git a/fiscal_epos_print/static/src/js/ChromeWidgets/SetLotteryCodeButton.js b/fiscal_epos_print/static/src/js/ChromeWidgets/SetLotteryCodeButton.js new file mode 100644 index 000000000000..3c4fbfd73b67 --- /dev/null +++ b/fiscal_epos_print/static/src/js/ChromeWidgets/SetLotteryCodeButton.js @@ -0,0 +1,50 @@ +odoo.define("fiscal_epos_print.SetLotteryCodeButton", function (require) { + "use strict"; + + const PosComponent = require("point_of_sale.PosComponent"); + const ProductScreen = require("point_of_sale.ProductScreen"); + const Registries = require("point_of_sale.Registries"); + const core = require("web.core"); + const _t = core._t; + + class SetLotteryCodeButton extends PosComponent { + render() { + var color = this.lottery_get_button_color(); + $(this.el).css("background", color); + } + + async onClickLotteryCode() { + var self = this; + var current_order = this.env.pos.get_order(); + this.showPopup("LotteryCodePopup", { + title: _t("Lottery Code"), + lottery_code: current_order.lottery_code, + update_lottery_info_button: function () { + self.render(); + }, + }); + } + + lottery_get_button_color() { + var order = this.env.pos.get_order(); + var color = "#e2e2e2"; + if (order.lottery_code) { + color = "lightgreen"; + } + return color; + } + } + + SetLotteryCodeButton.template = "SetLotteryCodeButton"; + + ProductScreen.addControlButton({ + component: SetLotteryCodeButton, + condition: function () { + return this.env.pos; + }, + }); + + Registries.Component.add(SetLotteryCodeButton); + + return SetLotteryCodeButton; +}); diff --git a/fiscal_epos_print/static/src/js/ChromeWidgets/SetRefundInfoButton.js b/fiscal_epos_print/static/src/js/ChromeWidgets/SetRefundInfoButton.js new file mode 100644 index 000000000000..ffef84e1ba9c --- /dev/null +++ b/fiscal_epos_print/static/src/js/ChromeWidgets/SetRefundInfoButton.js @@ -0,0 +1,133 @@ +odoo.define("fiscal_epos_print.SetRefundInfoButton", function (require) { + "use strict"; + + const PosComponent = require("point_of_sale.PosComponent"); + const ProductScreen = require("point_of_sale.ProductScreen"); + const Registries = require("point_of_sale.Registries"); + const core = require("web.core"); + const _t = core._t; + + class SetRefundInfoButton extends PosComponent { + onMounted() { + this.bind_order_events(); + this.orderline_change(); + } + + is_available() { + const order = this.env.pos.get_order(); + return order; + } + + render() { + var color = this.refund_get_button_color(); + $(this.el).css("background", color); + } + + async onClickRefund() { + var self = this; + var current_order = this.env.pos.get_order(); + if ( + current_order.refund_date === null || + current_order.refund_date === "" + ) { + this.showPopup("ErrorPopup", { + title: _t("Error"), + body: _t( + "Must select a refund order before clicking on this button!" + ), + }); + } + var dd = ("0" + current_order.refund_date.getDate()).slice(-2); + var mm = ("0" + (current_order.refund_date.getMonth() + 1)).slice(-2); + var yyyy = current_order.refund_date.getFullYear(); + this.showPopup("RefundInfoPopup", { + title: _t("Refund Information Details"), + refund_date: yyyy + "-" + mm + "-" + dd, + refund_report: current_order.refund_report, + refund_doc_num: current_order.refund_doc_num, + refund_cash_fiscal_serial: current_order.refund_cash_fiscal_serial, + update_refund_info_button: function () { + self.render(); + }, + }); + } + + bind_order_events() { + var order = this.env.pos.get_order(); + + if (!order) { + return; + } + + if (this.old_order) { + this.old_order.unbind(null, null, this); + } + + this.env.pos.bind("change:selectedOrder", this.orderline_change, this); + + var lines = order.orderlines; + lines.unbind("add", this.orderline_change, this); + lines.bind("add", this.orderline_change, this); + lines.unbind("remove", this.orderline_change, this); + lines.bind("remove", this.orderline_change, this); + lines.unbind("change", this.orderline_change, this); + lines.bind("change", this.orderline_change, this); + + this.old_order = order; + } + + refund_get_button_color() { + var order = this.env.pos.get_order(); + var color = "#e2e2e2"; + if (order) { + var lines = order.orderlines; + var has_refund = + lines.find(function (line) { + return line.quantity < 0.0; + }) !== undefined; + if (has_refund === true) { + if ( + order.refund_date && + order.refund_date !== "" && + order.refund_doc_num && + order.refund_doc_num !== "" && + order.refund_cash_fiscal_serial && + order.refund_cash_fiscal_serial !== "" && + order.refund_report && + order.refund_report !== "" + ) { + color = "lightgreen"; + } else { + color = "red"; + } + } + } + return color; + } + + orderline_change() { + var order = this.env.pos.get_order(); + if (order) { + var lines = order.orderlines; + order.has_refund = + lines.find(function (line) { + return line.quantity < 0.0; + }) !== undefined; + } + this.render(); + } + } + + SetRefundInfoButton.template = "SetRefundInfoButton"; + + ProductScreen.addControlButton({ + component: SetRefundInfoButton, + condition: function () { + return this.env.pos; + }, + }); + + Registries.Component.add(SetRefundInfoButton); + + return SetRefundInfoButton; +}); diff --git a/fiscal_epos_print/static/src/js/ChromeWidgets/SetReprintButton.js b/fiscal_epos_print/static/src/js/ChromeWidgets/SetReprintButton.js new file mode 100644 index 000000000000..87cea8fbd8f4 --- /dev/null +++ b/fiscal_epos_print/static/src/js/ChromeWidgets/SetReprintButton.js @@ -0,0 +1,57 @@ +odoo.define("fiscal_epos_print.SetReprintButton", function (require) { + "use strict"; + + var epson_epos_print = require("fiscal_epos_print.epson_epos_print"); + var eposDriver = epson_epos_print.eposDriver; + const ReceiptScreen = require("point_of_sale.ReceiptScreen"); + const Registries = require("point_of_sale.Registries"); + const {Gui} = require("point_of_sale.Gui"); + const core = require("web.core"); + const _t = core._t; + + const SetReprintButton = (ReceiptScreen) => + class extends ReceiptScreen { + constructor() { + super(...arguments); + if (this.env.pos.get_order()._printed === true) { + var reprintButton = $(".reprint_buttons"); + reprintButton.addClass("oe_hidden"); + } + } + + async rePrintReceipt() { + var currentOrder = this.env.pos.get_order(); + + const {confirmed} = await Gui.showPopup("ConfirmPopup", { + title: _t("Ristampa ultimo scontrino?"), + confirmText: "Conferma", + cancelText: "Annulla", + body: + "Usare la ristampa ultimo scontrino solo in caso di errori di comunicazione con la" + + "stampante e lo scontrino NON è stato stampato. Viene ristampanto uno scontrino FISCALE" + + "con gli stessi articoli del precedente", + }); + if (confirmed) { + var printer_options = this.currentOrder.getPrinterOptions(); + printer_options.order = currentOrder; + var receipt = currentOrder.export_for_printing(); + this.sendToFP90Printer(receipt, printer_options); + } else { + // TODO not exist + } + } + + async sendToFP90Printer(receipt, printer_options) { + var fp90 = new eposDriver(printer_options, this); + const isPrinted = await fp90.printFiscalReceipt(receipt); + if (isPrinted) { + this.currentOrder._printed = true; + } else { + this.currentOrder._printed = false; + } + } + }; + + Registries.Component.extend(ReceiptScreen, SetReprintButton); + return ReceiptScreen; +}); diff --git a/fiscal_epos_print/static/src/js/Popups/LotteryCodePopup.js b/fiscal_epos_print/static/src/js/Popups/LotteryCodePopup.js new file mode 100644 index 000000000000..703de9b7b6f6 --- /dev/null +++ b/fiscal_epos_print/static/src/js/Popups/LotteryCodePopup.js @@ -0,0 +1,62 @@ +odoo.define("fiscal_epos_print.LotteryCodePopup", function (require) { + "use strict"; + + const {useRef, useState} = owl; + const AbstractAwaitablePopup = require("point_of_sale.AbstractAwaitablePopup"); + const Registries = require("point_of_sale.Registries"); + const {_lt} = require("@web/core/l10n/translation"); + + class LotteryCodePopup extends AbstractAwaitablePopup { + constructor() { + super(...arguments); + + this.state = useState({inputValue: this.props.startingValue}); + this.inputLotteryCode = useRef("inputLotteryCode"); + } + + clickConfirmLotteryCode() { + this.$el = $(this.el); + var self = this; + function allValid() { + return self.$el + .find("input") + .toArray() + .every(function (element) { + return element.value && element.value !== ""; + }); + } + + if (allValid()) { + this.$el.find("#error-message-dialog").hide(); + + var lottery_code = this.inputLotteryCode.el.value; + this.env.pos.context = { + lottery_code: lottery_code, + }; + this.env.pos.set_lottery_code_data(lottery_code); + if ( + this.props.update_lottery_info_button && + this.props.update_lottery_info_button instanceof Function + ) { + this.props.update_lottery_info_button(); + } + this.env.posbus.trigger("close-popup", {popupId: this.props.id}); + } else { + this.$el.find("#error-message-dialog").show(); + } + } + } + + LotteryCodePopup.template = "LotteryCodePopup"; + + LotteryCodePopup.defaultProps = { + confirmText: _lt("Ok"), + cancelText: _lt("Cancel"), + title: _lt("Confirm ?"), + body: "", + }; + + Registries.Component.add(LotteryCodePopup); + + return LotteryCodePopup; +}); diff --git a/fiscal_epos_print/static/src/js/Popups/RefundInfoPopup.js b/fiscal_epos_print/static/src/js/Popups/RefundInfoPopup.js new file mode 100644 index 000000000000..c695302cc247 --- /dev/null +++ b/fiscal_epos_print/static/src/js/Popups/RefundInfoPopup.js @@ -0,0 +1,94 @@ +odoo.define("fiscal_epos_print.RefundInfoPopup", function (require) { + "use strict"; + + const {useRef, useState} = owl; + const AbstractAwaitablePopup = require("point_of_sale.AbstractAwaitablePopup"); + const Registries = require("point_of_sale.Registries"); + const {_lt} = require("@web/core/l10n/translation"); + + class RefundInfoPopup extends AbstractAwaitablePopup { + constructor() { + super(...arguments); + + this.state = useState({inputValue: this.props.startingValue}); + this.inputRefundReport = useRef("inputRefundReport"); + this.inputRefundDate = useRef("inputRefundDate"); + this.inputRefundDocNum = useRef("inputRefundDocNum"); + this.inputRefundCashFiscalSerial = useRef("inputRefundCashFiscalSerial"); + this.inputRefundFullRefund = useRef("inputRefundFullRefund"); + this.inputDatePicker = this.initializeDatePicker(); + } + + clickConfirmRefund() { + this.$el = $(this.el); + var self = this; + function allValid() { + return self.$el + .find("input") + .not("#refund_full_refund") + .toArray() + .every(function (element) { + return element.value && element.value != ""; + }); + } + + if (allValid()) { + this.$el.find("#error-message-dialog").hide(); + + var refund_date = this.inputRefundDate.el.value; + var refund_report = this.inputRefundReport.el.value; + var refund_doc_num = this.inputRefundDocNum.el.value; + var refund_cash_fiscal_serial = + this.inputRefundCashFiscalSerial.el.value; + var refund_full_refund = this.inputRefundFullRefund.el.checked; + this.env.pos.context = { + refund_details: true, + refund_date: refund_date, + refund_report: refund_report, + refund_doc_num: refund_doc_num, + refund_cash_fiscal_serial: refund_cash_fiscal_serial, + refund_full_refund: refund_full_refund, + }; + this.env.pos.set_refund_data( + refund_date, + refund_report, + refund_doc_num, + refund_cash_fiscal_serial, + refund_full_refund + ); + if ( + this.props.update_refund_info_button && + this.props.update_refund_info_button instanceof Function + ) { + this.props.update_refund_info_button(); + } + this.env.posbus.trigger("close-popup", {popupId: this.props.id}); + } else { + this.$el.find("#error-message-dialog").show(); + } + } + + initializeDatePicker() { + this.$el = $(this.el); + var element = this.$el.find("#refund_date").get(0); + if (element && !this.datepicker) { + // eslint-disable-next-line + this.datepicker = new Pikaday({ + field: element, + }); + } + } + } + + RefundInfoPopup.template = "RefundInfoPopup"; + + RefundInfoPopup.defaultProps = { + confirmText: _lt("Ok"), + cancelText: _lt("Cancel"), + body: "", + }; + + Registries.Component.add(RefundInfoPopup); + + return RefundInfoPopup; +}); diff --git a/fiscal_epos_print/static/src/js/Screens/PaymentScreen/PaymentScreen.js b/fiscal_epos_print/static/src/js/Screens/PaymentScreen/PaymentScreen.js new file mode 100644 index 000000000000..ea3124867728 --- /dev/null +++ b/fiscal_epos_print/static/src/js/Screens/PaymentScreen/PaymentScreen.js @@ -0,0 +1,99 @@ +odoo.define("fiscal_epos_print.PaymentScreen", function (require) { + "use strict"; + + var core = require("web.core"); + var epson_epos_print = require("fiscal_epos_print.epson_epos_print"); + var _t = core._t; + var eposDriver = epson_epos_print.eposDriver; + const Registries = require("point_of_sale.Registries"); + const PaymentScreen = require("point_of_sale.PaymentScreen"); + + // eslint-disable-next-line + const MyPaymentScreen = (PaymentScreen) => + class extends PaymentScreen { + setup() { + super.setup(); + if (this.env.pos.config.printer_ip) { + var currentOrder = this.env.pos.get_order(); + var printer_options = currentOrder.getPrinterOptions(); + var fp90 = new eposDriver(printer_options, this); + var amount = this.env.pos.format_currency( + currentOrder.get_total_with_tax() + ); + fp90.printDisplayText(_t("SubTotal") + " " + amount); + } + } + + async sendToFP90Printer(order) { + if (this.env.pos.config.printer_ip && !order.is_to_invoice()) { + // TODO self.chrome does not exists + // this.chrome.loading_show(); + // this.chrome.loading_message(_t('Connecting to the fiscal printer')); + if ( + order.has_refund && + this.env.pos.context && + this.env.pos.context.refund_details + ) { + order.refund_date = this.env.pos.context.refund_date; + order.refund_report = this.env.pos.context.refund_report; + order.refund_doc_num = this.env.pos.context.refund_doc_num; + order.refund_cash_fiscal_serial = + this.env.pos.context.refund_cash_fiscal_serial; + } + + var printer_options = order.getPrinterOptions(); + printer_options.order = order; + var receipt = order.export_for_printing(); + var fp90 = new eposDriver(printer_options, this); + await fp90.printFiscalReceipt(receipt); + await new Promise((resolve) => setTimeout(resolve, 2000)); + // This line causes problems on bill split. What's the sense of deleting the actual pos context?! + // It regenerates orders which are already partly paid using split function... + // this.env.pos.context = {}; + } + } + + async _finalizeValidation() { + var currentOrder = this.currentOrder; + await this.sendToFP90Printer(currentOrder); + await super._finalizeValidation(); + } + + _isOrderValid(isForceValidate) { + if (this.env.pos.config.iface_tax_included === "subtotal") { + this.showPopup("ErrorPopup", { + title: _t("Wrong tax configuration"), + body: _t( + "Product prices on receipts must be set to 'Tax-Included Price' in POS configuration" + ), + }); + return false; + } + var receipt = this.env.pos.get_order(); + if ( + receipt.has_refund && + (receipt.refund_date === null || + receipt.refund_date === "" || + receipt.refund_doc_num === null || + receipt.refund_doc_num === "" || + receipt.refund_cash_fiscal_serial === null || + receipt.refund_cash_fiscal_serial === "" || + receipt.refund_report === null || + receipt.refund_report === "") + ) { + this.showPopup("ErrorPopup", { + title: _t("Refund Information Not Present"), + body: _t( + "The refund information aren't present. Please insert them before printing the receipt" + ), + }); + return false; + } + return super._isOrderValid(isForceValidate); + } + }; + + Registries.Component.extend(PaymentScreen, MyPaymentScreen); + + return PaymentScreen; +}); diff --git a/fiscal_epos_print/static/src/js/Screens/ReceiptScreen/ReceiptScreen.js b/fiscal_epos_print/static/src/js/Screens/ReceiptScreen/ReceiptScreen.js new file mode 100644 index 000000000000..ebf694c75fb1 --- /dev/null +++ b/fiscal_epos_print/static/src/js/Screens/ReceiptScreen/ReceiptScreen.js @@ -0,0 +1,51 @@ +odoo.define("fiscal_epos_print.ReceiptScreen", function (require) { + "use strict"; + + var epson_epos_print = require("fiscal_epos_print.epson_epos_print"); + var eposDriver = epson_epos_print.eposDriver; + const Registries = require("point_of_sale.Registries"); + const ReceiptScreen = require("point_of_sale.ReceiptScreen"); + + // eslint-disable-next-line + const MyReceiptScreen = (ReceiptScreen) => + class extends ReceiptScreen { + lock_screen(locked) { + super.lock_screen(...arguments); + if (locked) { + this.$(".receipt-sent").hide(); + this.$(".printing-error").show(); + this.$(".printing-retry").show(); + } else { + this.$(".receipt-sent").show(); + this.$(".printing-error").hide(); + this.$(".printing-retry").hide(); + } + } + + sendToFP90Printer(receipt, printer_options) { + var fp90 = new eposDriver(printer_options, this); + fp90.printFiscalReceipt(receipt); + } + + render_receipt() { + var self = this; + this._super(); + this.$(".printing-retry").click(function () { + if (self._locked) { + var currentOrder = self.env.pos.get_order(); + // TODO self.chrome does not exists + // self.chrome.loading_show(); + // self.chrome.loading_message(_t('Connecting to the fiscal printer')); + var printer_options = currentOrder.getPrinterOptions(); + printer_options.order = currentOrder; + var receipt = currentOrder.export_for_printing(); + self.sendToFP90Printer(receipt, printer_options); + } + }); + } + }; + + Registries.Component.extend(ReceiptScreen, MyReceiptScreen); + + return ReceiptScreen; +}); diff --git a/fiscal_epos_print/static/src/js/epson_epos_print.js b/fiscal_epos_print/static/src/js/epson_epos_print.js new file mode 100644 index 000000000000..c750a39ff3db --- /dev/null +++ b/fiscal_epos_print/static/src/js/epson_epos_print.js @@ -0,0 +1,828 @@ +odoo.define("fiscal_epos_print.epson_epos_print", function (require) { + "use strict"; + + var core = require("web.core"); + var utils = require("web.utils"); + // Var PosDB = require('point_of_sale.DB'); + // var rpc = require('web.rpc'); + var _t = core._t; + var round_pr = utils.round_precision; + + const {Gui} = require("point_of_sale.Gui"); + + function addPadding(str, padding = 4) { + var pad = new Array(padding).fill(0).join("") + str; + return pad.substr(pad.length - padding, padding); + } + + function isErrorStatus(printerStatus) { + var error = false; + switch (printerStatus.substring(0, 2)) { + case "00": + case "01": + case "20": + case "21": + error = false; + break; + default: + error = true; + } + return error; + } + + // eslint-disable-next-line + function decodeFpStatus(printerStatus) { + var printer = ""; + var ej = ""; + var receipt = ""; + + switch (printerStatus.substring(0, 1)) { + case "0": + printer = false; + break; + case "2": + printer = _t("Paper running low"); + break; + case "3": + printer = _t("Offline (end of paper or open cover)"); + break; + default: + printer = _t("Wrong answer"); + } + + switch (printerStatus.substring(1, 2)) { + case "0": + ej = false; + break; + case "1": + ej = _t("Running low"); + break; + case "2": + ej = _t("To format"); + break; + case "3": + ej = _t("Previous"); + break; + case "4": + ej = _t("From other measurement device"); + break; + case "5": + ej = _t("Finished"); + break; + default: + ej = _t("Wrong answer"); + } + + switch (printerStatus.substring(3, 4)) { + case "0": + receipt = _t("Fiscal open"); + break; + case "1": + receipt = false; + // Receipt = "Fiscale/Non fiscale chiuso"; + break; + case "2": + receipt = _t("Non fiscal open"); + break; + case "3": + receipt = _t("Payment in progress"); + break; + case "4": + receipt = _t( + "Error on last ESC/POS command with Fiscal/Non fiscal closed" + ); + break; + case "5": + receipt = _t("Negative receipt"); + break; + case "6": + receipt = _t("Error on last ESC/POS command with Non fiscal open"); + break; + case "7": + receipt = _t("Waiting for receipt closing in JAVAPOS mode"); + break; + case "8": + receipt = _t("Fiscal document open"); + break; + case "A": + receipt = _t("Title open"); + break; + case "B": + receipt = _t("Title closed"); + break; + default: + receipt = _t("Wrong answer"); + } + + return printer || ej || receipt; + } + + function getStatusField(tag) { + return tag === "printerStatus" || tag === "fsStatus"; + } + + var eposDriver = core.Class.extend({ + init: function (options, sender) { + var self = this; + var opts = options || {}; + this.url = opts.url || "http://192.168.1.1/cgi-bin/fpmate.cgi"; + // eslint-disable-next-line + this.fiscalPrinter = new epson.fiscalPrint(); + this.sender = sender; + this.order = opts.order || null; + // eslint-disable-next-line + this.fiscalPrinter.onreceive = function (res, tag_list_names, add_info) { + // TODO not exist + // sender.chrome.loading_hide(); + var tagStatus = tag_list_names + ? tag_list_names.filter(getStatusField) + : []; + var msgPrinter = ""; + var info = ""; + + if (tagStatus.length > 0 && res.success) { + info = add_info[tagStatus[0]]; + res.success = !isErrorStatus(info); + } + + var order = ""; + if (!res.success) { + if (self.order !== null) { + order = self.order; + order.fiscal_printer_debug_info = + JSON.stringify(res) + + "\n" + + JSON.stringify(tag_list_names) + + "\n" + + JSON.stringify(add_info); + // Sender.env.pos.push_single_order(order); + } + if (tagStatus.length > 0) { + info = add_info[tagStatus[0]]; + msgPrinter = decodeFpStatus(info); + } + // TODO + // sender.chrome.screens['receipt'].lock_screen(true); + // TODO is this correct? + Gui.showPopup("ErrorPopup", { + title: _t("Connection to the printer failed"), + body: + _t( + "An error happened while sending data to the printer. Error code: " + ) + + (res.code || "") + + "\n" + + _t("Error Message: ") + + msgPrinter, + }); + return; + } + + if (add_info.responseCommand === "1138") { + // Coming from FiscalPrinterADEFilesButtonWidget + var to_be_sent = + add_info.responseData[9] + + add_info.responseData[10] + + add_info.responseData[11] + + add_info.responseData[12]; + var old = + add_info.responseData[13] + + add_info.responseData[14] + + add_info.responseData[15] + + add_info.responseData[16]; + var rejected = + add_info.responseData[17] + + add_info.responseData[18] + + add_info.responseData[19] + + add_info.responseData[20]; + var msg = + _t("Files waiting to be sent: ") + + to_be_sent + + "; " + + _t("Old files: ") + + old + + "; " + + _t("Rejected files: ") + + rejected; + // TODO is this correct? + Gui.showPopup("ErrorPopup", { + title: _t("IRA files"), + body: msg, + }); + return; + } + + // Is it a receipt data? + if ( + add_info.fiscalReceiptNumber && + add_info.fiscalReceiptAmount && + add_info.fiscalReceiptDate && + add_info.zRepNumber + ) { + // TODO + // sender.chrome.screens['receipt'].lock_screen(false); + order = self.order; + order._printed = true; + if (!order.fiscal_receipt_number) { + order.fiscal_receipt_number = parseInt( + add_info.fiscalReceiptNumber, + 10 + ); + order.fiscal_receipt_amount = parseFloat( + add_info.fiscalReceiptAmount.replace(",", ".") + ); + var fiscalReceiptDate = new Date( + add_info.fiscalReceiptDate.replace( + /(\d{1,2})\/(\d{1,2})\/(\d{4})/, + "$3/$2/$1" + ) + ); + order.fiscal_receipt_date = + moment(fiscalReceiptDate).format("YYYY-MM-DD"); + order.fiscal_z_rep_number = add_info.zRepNumber; + order.fiscal_printer_serial = + sender.env.pos.config.fiscal_printer_serial; + // Sender.env.pos.db.add_order(order.export_as_JSON()); + // Try to save the order + // sender.env.pos.push_single_order(order); + } + if (sender.env.pos.config.fiscal_cashdrawer) { + self.printOpenCashDrawer(); + self.resetPrinter(); + } + if (!sender.env.pos.config.show_receipt_when_printing) { + // TODO + // sender.chrome.screens['receipt'].click_next(); + } + return; + } + }; + this.fiscalPrinter.onerror = function () { + // TODO not exist + // sender.chrome.loading_hide(); + // sender.chrome.screens['receipt'].lock_screen(true); + // TODO is this correct? + Gui.showPopup("ErrorPopup", { + title: _t("Network error"), + body: _t("Printer can not be reached"), + }); + }; + }, + + encodeXml: function (string) { + var xml_special_to_escaped_one_map = { + "&": "&", + '"': """, + "<": "<", + ">": ">", + }; + + // eslint-disable-next-line + return string.replace(/([\&"<>])/g, function (str, item) { + return xml_special_to_escaped_one_map[item]; + }); + }, + + /* + Prints a sale item line. + */ + printRecItem: function (args) { + var tag = + ""; + return tag; + }, + + /* + Prints a sale refund item line. + */ + printFiscalRefundDetails: function (args) { + var message = + "REFUND " + + addPadding(args.refund_report) + + " " + + addPadding(args.refund_doc_num) + + " " + + // Day + args.refund_date.substr(8, 2) + + // Month + args.refund_date.substr(5, 2) + + // Year + args.refund_date.substr(0, 4) + + " " + + args.refund_cash_fiscal_serial; + + var tag = + ""; + return tag; + }, + + printFiscalVoidDetails: function (args) { + var message = + "VOID " + + addPadding(args.refund_report) + + " " + + addPadding(args.refund_doc_num) + + " " + + // Day + args.refund_date.substr(8, 2) + + // Month + args.refund_date.substr(5, 2) + + // Year + args.refund_date.substr(0, 4) + + " " + + args.refund_cash_fiscal_serial; + + var tag = + ""; + return tag; + }, + + /* + Prints a sale refund item line. + Prints refund items on a commercial refund document if flag SET 14/58 = 1 (from display 3333 > 14 > 58 > X). + */ + printRecRefund: function (args) { + var tag = + ""; + return tag; + }, + + /* + Adds a discount to the last line. + */ + printRecItemAdjustment: function (args) { + var tag = + ""; + return tag; + }, + + /* + Prints a payment. + */ + printRecTotal: function (args) { + var tag = + ""; + return tag; + }, + + printRecTotalRefund: function (args) { + var tag = + ""; + return tag; + }, + + /* + Prints a rounding + */ + printRounding: function (args) { + var tag = + "'; + return tag; + }, + + // Remember that the header goes after + // but before otherwise it will not be printed + // as additional header messageType=1 + printFiscalReceiptHeader: function (receipt) { + var self = this; + var msg = ""; + if (receipt.header !== "" && receipt.header.length > 0) { + var hdr = receipt.header.split(/\r\n|\r|\n/); + _.each(hdr, function (m, i) { + msg += + "'; + }); + } + return msg; + }, + + // Remember that the footer goes within + // as PROMO code messageType=3 + printFiscalReceiptFooter: function (receipt) { + var self = this; + var msg = ""; + if (receipt.footer !== "" && receipt.footer.length > 0) { + var hdr = receipt.footer.split(/\r\n|\r|\n/); + _.each(hdr, function (m, i) { + msg += + "'; + }); + } + return msg; + }, + + printDisplayText: function (msg) { + var xml = + "" + + ""; + this.fiscalPrinter.send(this.url, xml); + }, + + /* + Print the order.id into fiscal receipt for the refund + */ + printOrderId: function (receipt) { + var message = receipt.name; + var tag = + ""; + return tag; + }, + + /* + Prints info payment customer + */ + printInfoPaymentCustomer: function (receipt) { + var tag = + ''; + var index = 5; + _.each(receipt.ticket.split("
"), function (msg) { + index += 1; + tag += + "'; + }); + return tag; + }, + + /* + Prints a receipt + */ + printFiscalReceipt: function (receipt) { + var self = this; + var has_refund = _.every(receipt.orderlines, function (line) { + return line.quantity < 0; + }); + var xml = ""; + var fiscal_operator = receipt.fiscal_operator_number || "1"; + // Header must be printed before beginning a fiscal receipt + if (!receipt.refund_full_refund) { + xml += this.printFiscalReceiptHeader(receipt); + } + if (has_refund) { + if (receipt.refund_full_refund) { + xml += this.printFiscalVoidDetails({ + refund_date: receipt.refund_date, + refund_report: receipt.refund_report, + refund_doc_num: receipt.refund_doc_num, + refund_cash_fiscal_serial: receipt.refund_cash_fiscal_serial, + operator: fiscal_operator, + }); + } else { + xml += this.printFiscalRefundDetails({ + refund_date: receipt.refund_date, + refund_report: receipt.refund_report, + refund_doc_num: receipt.refund_doc_num, + refund_cash_fiscal_serial: receipt.refund_cash_fiscal_serial, + operator: fiscal_operator, + }); + } + } + xml += ''; + + _.each(receipt.orderlines, function (l) { + if (l.price >= 0) { + if (l.quantity >= 0) { + if (l.discount < 100) { + xml += self.printRecItem({ + description: l.product_name, + quantity: l.quantity, + unitPrice: round_pr( + l.full_price, + self.sender.env.pos.currency.rounding + ), + department: l.tax_department.code, + operator: fiscal_operator, + }); + if (l.discount) { + xml += self.printRecItemAdjustment({ + adjustmentType: 0, + description: + _t("Discount") + " " + l.discount + "%", + amount: round_pr( + l.quantity * l.full_price - l.price_display, + self.sender.env.pos.currency.rounding + ), + operator: fiscal_operator, + }); + } + } + } else { + xml += self.printRecRefund({ + description: _t("Refund: ") + l.product_name, + quantity: l.quantity * -1.0, + unitPrice: round_pr( + l.price, + self.sender.env.pos.currency.rounding + ), + department: l.tax_department.code, + operator: fiscal_operator, + }); + + // TODO This line of code is added by us, check if it's right + // xml += self.printRecItem({ + // description: _t("Refund cash"), + // quantity: l.quantity, + // unitPrice: round_pr( + // l.price, + // self.sender.env.pos.currency.rounding + // ), + // department: l.tax_department.code, + // operator: fiscal_operator, + // }); + } + } else { + xml += self.printRecItemAdjustment({ + adjustmentType: 3, + description: l.product_name, + department: l.tax_department.code, + amount: -l.price, + operator: fiscal_operator, + }); + } + }); + // Footer can go only as promo code so within a fiscal receipt body + xml += this.printFiscalReceiptFooter(receipt); + if (receipt.lottery_code) { + // TX + // 1 135 OP ID CODE NU + // Example: 113501ABCDEFGN 0000 + // Pad with spaces to make the code field always 16 characters. + xml += + ''; + } + if (receipt.rounding_applied !== 0 && !has_refund) { + xml += self.printRounding({ + amount: Math.abs( + round_pr( + receipt.rounding_applied, + self.sender.env.pos.currency.rounding + ) + ), + operator: fiscal_operator, + }); + xml += + ''; + } + // TODO is always the same Total for refund and payments? + receipt.ticket = ""; + _.each(receipt.paymentlines, function (l) { + // Set ticket + receipt.ticket += l.ticket; + // Amount always positive because it's used for refund too + if (has_refund) { + xml += self.printRecTotalRefund({ + payment: Math.abs(l.amount), + paymentType: l.type, + paymentIndex: l.type_index, + description: l.journal, + operator: fiscal_operator, + }); + } else { + xml += self.printRecTotal({ + payment: Math.abs(l.amount), + paymentType: l.type, + paymentIndex: l.type_index, + description: l.journal, + operator: fiscal_operator, + }); + } + }); + xml += this.printOrderId(receipt); + if (receipt.ticket) { + xml += this.printInfoPaymentCustomer(receipt); + } + xml += + ''; + this.fiscalPrinter.send(this.url, xml); + console.log(xml); + }, + + /* + DON'T USE, this fiscal closure is forbid by Epson by default + */ + printFiscalReport: function (f_op) { + var xml = ""; + xml += ''; + xml += ""; + this.fiscalPrinter.send(this.url, xml); + }, + + /* + It prints report and fiscal closure both + */ + printFiscalXZReport: function (f_op) { + var xml = ""; + xml += + ''; + xml += ''; + xml += ""; + this.fiscalPrinter.send(this.url, xml); + }, + + printFiscalXReport: function (f_op) { + var xml = ""; + xml += ''; + xml += ""; + this.fiscalPrinter.send(this.url, xml); + }, + + getStatusOfFilesForADE: function () { + var xml = ""; + xml += ''; + xml += ""; + this.fiscalPrinter.send(this.url, xml); + }, + + /* + It need to be logged in to print the duplicate, the pw in data is 0212345 plus 93 spaces, total 100 chars + */ + printFiscalReprintLast: function (f_op) { + var xml = ""; + xml += + ''; + xml += ''; + xml += ""; + this.fiscalPrinter.send(this.url, xml); + }, + + printOpenCashDrawer: function () { + var xml = ""; + xml += ''; + xml += ""; + this.fiscalPrinter.send(this.url, xml); + }, + + resetPrinter: function () { + var xml = ""; + xml += ''; + xml += ''; + xml += ""; + this.fiscalPrinter.send(this.url, xml); + }, + }); + + return { + eposDriver: eposDriver, + }; +}); diff --git a/fiscal_epos_print/static/src/js/models.js b/fiscal_epos_print/static/src/js/models.js new file mode 100644 index 000000000000..fe22148d2f70 --- /dev/null +++ b/fiscal_epos_print/static/src/js/models.js @@ -0,0 +1,285 @@ +odoo.define("fiscal_epos_print.models", function (require) { + "use strict"; + + var {PosGlobalState, Order, Orderline, Payment} = require("point_of_sale.models"); + const {Gui} = require("point_of_sale.Gui"); + var core = require("web.core"); + var _t = core._t; + const Registries = require("point_of_sale.Registries"); + + const FiscalEposPrintPosGlobalState = (PosGlobalState) => + class FiscalEposPrintPosGlobalState extends PosGlobalState { + set_refund_data( + refund_date, + refund_report, + refund_doc_num, + refund_cash_fiscal_serial, + refund_full_refund + ) { + const selectedOrder = this.get_order(); + selectedOrder.refund_date = refund_date; + selectedOrder.refund_report = refund_report; + selectedOrder.refund_doc_num = refund_doc_num; + selectedOrder.refund_cash_fiscal_serial = refund_cash_fiscal_serial; + selectedOrder.refund_full_refund = refund_full_refund; + } + + set_lottery_code_data(lottery_code) { + const selectedOrder = this.get_order(); + selectedOrder.lottery_code = lottery_code; + } + + reset_cashier() { + this.cashier = { + name: null, + id: null, + barcode: null, + user_id: null, + pin: null, + role: null, + fiscal_operator_number: null, + }; + } + }; + Registries.Model.extend(PosGlobalState, FiscalEposPrintPosGlobalState); + + const FiscalEposPrintOrder = (Order) => + class FiscalEposPrintOrder extends Order { + constructor() { + super(...arguments); + this.lottery_code = null; + this.refund_report = null; + this.refund_date = null; + this.refund_doc_num = null; + this.refund_cash_fiscal_serial = null; + this.refund_full_refund = false; + this.has_refund = false; + this.fiscal_receipt_number = null; + this.fiscal_receipt_amount = null; + this.fiscal_receipt_date = null; + this.fiscal_z_rep_number = null; + this.fiscal_printer_serial = + this.pos.config.fiscal_printer_serial || null; + this.fiscal_printer_debug_info = null; + try { + if (this.pos.config.module_pos_hr) { + this.fiscal_operator_number = + this.pos.cashier.fiscal_operator_number || null; + } else { + this.fiscal_operator_number = "1"; + } + } catch (error) { + this.fiscal_operator_number = "1"; + } + } + + // Manages the case in which after printing an invoice + // you pass a barcode in the mask of the registered invoice + add_product(product, options) { + if (this._printed || this.finalized === true) { + this.destroy(); + return this.pos.get_order().add_product(product, options); + } + return super.add_product(...arguments); + } + + check_order_has_refund() { + var order = this.pos.get_order(); + if (order) { + var lines = order.orderlines; + order.has_refund = + lines.find(function (line) { + return line.quantity < 0.0; + }) !== undefined; + if (order.has_refund) { + order.refund_report = this.name.substr(-4); + order.refund_doc_num = this.name.substr(-4); + order.refund_date = new Date(); + order.refund_cash_fiscal_serial = + this.pos.config.fiscal_printer_serial; + } + } + } + + init_from_JSON(json) { + super.init_from_JSON(...arguments); + this.check_order_has_refund(); + this.lottery_code = json.lottery_code; + this.refund_report = json.refund_report; + this.refund_date = json.refund_date; + this.refund_doc_num = json.refund_doc_num; + this.refund_cash_fiscal_serial = json.refund_cash_fiscal_serial; + this.refund_full_refund = json.refund_full_refund; + this.fiscal_receipt_number = json.fiscal_receipt_number; + this.fiscal_receipt_amount = json.fiscal_receipt_amount; + this.fiscal_receipt_date = json.fiscal_receipt_date; + this.fiscal_z_rep_number = json.fiscal_z_rep_number; + this.fiscal_printer_serial = this.pos.config.fiscal_printer_serial; + this.fiscal_printer_debug_info = json.fiscal_printer_debug_info; + try { + if (this.pos.config.module_pos_hr && json.employee_id) { + this.fiscal_operator_number = + this.pos.employee_by_id[json.employee_id] + .fiscal_operator_number || null; + } else { + this.fiscal_operator_number = "1"; + } + } catch (error) {} + } + + export_as_JSON() { + const json = super.export_as_JSON(...arguments); + this.check_order_has_refund(); + json.lottery_code = this.lottery_code || null; + json.refund_report = this.refund_report || null; + json.refund_date = this.refund_date || null; + json.refund_doc_num = this.refund_doc_num || null; + json.refund_cash_fiscal_serial = this.refund_cash_fiscal_serial || null; + json.refund_full_refund = this.refund_full_refund || false; + json.fiscal_receipt_number = this.fiscal_receipt_number || null; + json.fiscal_receipt_amount = this.fiscal_receipt_amount || null; + // Parsed by backend + json.fiscal_receipt_date = this.fiscal_receipt_date || null; + json.fiscal_z_rep_number = this.fiscal_z_rep_number || null; + json.fiscal_printer_serial = this.fiscal_printer_serial || null; + json.fiscal_printer_debug_info = this.fiscal_printer_debug_info || null; + try { + if (this.pos.config.module_pos_hr) { + json.fiscal_operator_number = + this.pos.cashier.fiscal_operator_number || null; + } else { + json.fiscal_operator_number = "1"; + } + } catch (error) {} + return json; + } + + export_for_printing() { + var json = super.export_for_printing(...arguments); + json.lottery_code = this.lottery_code; + json.refund_date = this.refund_date; + json.refund_report = this.refund_report; + json.refund_doc_num = this.refund_doc_num; + json.refund_cash_fiscal_serial = this.refund_cash_fiscal_serial; + json.refund_full_refund = this.refund_full_refund; + json.fiscal_receipt_number = this.fiscal_receipt_number; + json.fiscal_receipt_amount = this.fiscal_receipt_amount; + json.fiscal_receipt_date = this.fiscal_receipt_date; + json.fiscal_z_rep_number = this.fiscal_z_rep_number; + json.fiscal_printer_serial = this.fiscal_printer_serial; + json.fiscal_printer_debug_info = this.fiscal_printer_debug_info; + try { + json.fiscal_operator_number = + this.pos.cashier.fiscal_operator_number || null; + } catch (error) {} + return json; + } + + getPrinterOptions() { + var protocol = this.pos.config.use_https ? "https://" : "http://"; + var printer_url = + protocol + this.pos.config.printer_ip + "/cgi-bin/fpmate.cgi"; + return {url: printer_url}; + } + }; + Registries.Model.extend(Order, FiscalEposPrintOrder); + + const FiscalEposPrintOrderline = (Orderline) => + class FiscalEposPrintOrderline extends Orderline { + export_for_printing() { + var receipt = super.export_for_printing(...arguments); + + receipt.tax_department = this.get_tax_details_r(); + if (!receipt.tax_department) { + Gui.showPopup("ErrorPopup", { + title: _t("Network error"), + body: _t("Manca iva su prodotto"), + }); + } + if (receipt.tax_department) { + if (receipt.tax_department.included_in_price === true) { + receipt.full_price = this.price; + } else { + // This strategy was used because JavaScript's Math.round rounds to the nearest integer + const dec_precision = this.pos.currency.decimal_places; + const full_price = Number( + ( + this.price * + (1 + receipt.tax_department.tax_amount / 100) + ).toFixed(dec_precision) + ); + const rounding_factor = Math.pow(10, dec_precision); + receipt.full_price = + Math.trunc(full_price * rounding_factor) / rounding_factor; + } + } + + return receipt; + } + + get_tax_details_r() { + var detail = this.get_all_prices().taxDetails; + for (var i in detail) { + return { + code: this.pos.taxes_by_id[i].fpdeptax, + taxname: this.pos.taxes_by_id[i].name, + included_in_price: this.pos.taxes_by_id[i].price_include, + tax_amount: this.pos.taxes_by_id[i].amount, + }; + } + // TODO is this correct? + Gui.showPopup("ErrorPopup", { + title: _t("Error"), + body: _t("No taxes found"), + }); + } + + set_quantity(quantity) { + if (quantity === "0") { + // Epson FP doesn't allow lines with quantity 0 + quantity = "remove"; + } + return super.set_quantity(...arguments); + } + }; + Registries.Model.extend(Orderline, FiscalEposPrintOrderline); + + /* + Overwrite Payment.export_for_printing() in order + to make it export the payment type that must be passed + to the fiscal printer. + */ + const FiscalEposPrintPayment = (Payment) => + class FiscalEposPrintPayment extends Payment { + constructor() { + super(...arguments); + this.type = this.payment_method.fiscalprinter_payment_type || null; + this.type_index = + this.payment_method.fiscalprinter_payment_index || null; + } + export_as_JSON() { + const json = super.export_as_JSON(...arguments); + json.type = this.payment_method.fiscalprinter_payment_type; + json.type_index = this.payment_method.fiscalprinter_payment_index; + return json; + } + init_from_JSON(json) { + super.init_from_JSON(...arguments); + this.type = json.type; + this.type_index = json.type_index; + } + setFiscalprinterType(value) { + this.type = value; + } + setFiscalprinterIdex(value) { + this.type_index = value; + } + export_for_printing() { + const res = super.export_for_printing(...arguments); + res.type = this.type; + res.type_index = this.type_index; + return res; + } + }; + Registries.Model.extend(Payment, FiscalEposPrintPayment); +}); diff --git a/fiscal_epos_print/static/src/js/popups.js b/fiscal_epos_print/static/src/js/popups.js new file mode 100644 index 000000000000..7495b813896f --- /dev/null +++ b/fiscal_epos_print/static/src/js/popups.js @@ -0,0 +1,120 @@ +odoo.define("fiscal_epos_print.popups", function (require) { + "use strict"; + + var popups = require("point_of_sale.popups"); + var gui = require("point_of_sale.Gui"); + + // Function addPadding(str, padding = 4) { + // var pad = new Array(padding).fill(0).join("") + str; + // return pad.substr(pad.length - padding, padding); + // } + + var RefundInfoPopupWidget = popups.extend({ + template: "RefundInfoPopupWidget", + init: function (parent) { + this.refund_report = null; + this.refund_date = null; + this.refund_doc_num = null; + this.refund_cash_fiscal_serial = null; + this.datepicker = null; + return this._super(parent); + }, + show: function (options) { + var opts = options || {}; + this._super(opts); + this.update_refund_info_button = opts.update_refund_info_button; + this.renderElement(); + this.datepicker = null; + this.$("refund_report").focus(); + this.initializeDatePicker(); + }, + click_confirm: function () { + var self = this; + function allValid() { + return self + .$("input") + .toArray() + .every(function (element) { + return element.value && element.value !== ""; + }); + } + + if (allValid()) { + this.$("#error-message-dialog").hide(); + + var order = this.pos.get_order(); + order.refund_report = this.$("#refund_report").val(); + order.refund_date = this.$("#refund_date").val(); + order.refund_doc_num = this.$("#refund_doc_num").val(); + order.refund_cash_fiscal_serial = this.$( + "#refund_cash_fiscal_serial" + ).val(); + this.gui.close_popup(); + if ( + this.update_refund_info_button && + this.update_refund_info_button instanceof Function + ) { + this.update_refund_info_button(); + } + } else { + this.$("#error-message-dialog").show(); + } + }, + initializeDatePicker: function () { + var element = this.$("#refund_date").get(0); + + if (element && !this.datepicker) { + // eslint-disable-next-line + this.datepicker = new Pikaday({ + field: element, + }); + } + }, + }); + + var LotteryCodePopupWidget = popups.extend({ + template: "LotteryCodePopupWidget", + init: function (parent) { + this.lottery_code = null; + return this._super(parent); + }, + show: function (options) { + var opts = options || {}; + this._super(opts); + this.update_lottery_info_button = opts.update_lottery_info_button; + this.renderElement(); + this.$("#lottery_code").focus(); + }, + // TODO automatically close popup on barcode scanned + click_confirm: function () { + var self = this; + function allValid() { + return self + .$("input") + .toArray() + .every(function (element) { + return element.value && element.value !== ""; + }); + } + + if (allValid()) { + this.$("#lottery-error-message-dialog").hide(); + + var order = this.pos.get_order(); + order.lottery_code = this.$("#lottery_code").val(); + this.gui.close_popup(); + if ( + this.update_lottery_info_button && + this.update_lottery_info_button instanceof Function + ) { + this.update_lottery_info_button(); + } + } else { + this.$("#lottery-error-message-dialog").show(); + } + }, + }); + + gui.define_popup({name: "refundinfo", widget: RefundInfoPopupWidget}); + gui.define_popup({name: "lotterycode", widget: LotteryCodePopupWidget}); +}); diff --git a/fiscal_epos_print/static/src/js/pos_order_mgmt.js b/fiscal_epos_print/static/src/js/pos_order_mgmt.js new file mode 100644 index 000000000000..edfc1154eab0 --- /dev/null +++ b/fiscal_epos_print/static/src/js/pos_order_mgmt.js @@ -0,0 +1,60 @@ +// TODO is this necessary? +odoo.define("fiscal_epos_print.pos_order_mgmt", function (require) { + "use strict"; + + var core = require("web.core"); + var pos_order_mgmt = require("pos_order_mgmt.widgets"); + var epson_epos_print = require("fiscal_epos_print.epson_epos_print"); + var _t = core._t; + var OrderListScreenWidget = pos_order_mgmt.OrderListScreenWidget; + var eposDriver = epson_epos_print.eposDriver; + + OrderListScreenWidget.include({ + _prepare_order_from_order_data: function (order_data, action) { + var order = this._super(order_data, action); + if (action === "print") { + order.lottery_code = order_data.lottery_code; + order.refund_report = order_data.refund_report; + order.refund_date = order_data.refund_date; + order.refund_doc_num = order_data.refund_doc_num; + order.refund_cash_fiscal_serial = order_data.refund_cash_fiscal_serial; + order.refund_full_refund = order_data.refund_full_refund; + } else if (action === "return") { + order.lottery_code = order_data.lottery_code; + order.refund_report = order_data.fiscal_z_rep_number; + order.refund_date = order_data.fiscal_receipt_date; + order.refund_doc_num = order_data.fiscal_receipt_number; + order.refund_cash_fiscal_serial = order_data.fiscal_printer_serial; + order.refund_full_refund = order_data.refund_full_refund; + } + // For action === 'copy' we don't need to do anything + return order; + }, + // Copiato da screens.PaymentScreenWidget + sendToFP90Printer: function (receipt, printer_options) { + var fp90 = new eposDriver(printer_options, this); + fp90.printFiscalReceipt(receipt); + }, + action_print: function (order_data, order) { + if (this.pos.config.printer_ip) { + if (order_data.fiscal_receipt_number) { + this.pos.gui.show_popup("error", { + title: _t("Order already printed"), + body: + order_data.pos_reference + + _t(": order already has a fiscal number, ") + + order_data.fiscal_receipt_number, + }); + return; + } + this.chrome.loading_show(); + this.chrome.loading_message(_t("Connecting to the fiscal printer")); + var receipt = order.export_for_printing(); + var printer_options = order.getPrinterOptions(); + printer_options.order = order; + this.sendToFP90Printer(receipt, printer_options); + } + return this._super(order_data, order); + }, + }); +}); diff --git a/fiscal_epos_print/static/src/xml/Chrome.xml b/fiscal_epos_print/static/src/xml/Chrome.xml new file mode 100644 index 000000000000..1a27399f1fd5 --- /dev/null +++ b/fiscal_epos_print/static/src/xml/Chrome.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + diff --git a/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonEPOSButton.xml b/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonEPOSButton.xml new file mode 100644 index 000000000000..7b7c5db2b60d --- /dev/null +++ b/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonEPOSButton.xml @@ -0,0 +1,14 @@ + + + + +
+ Epson ePOS +
+
+ +
diff --git a/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml b/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml new file mode 100644 index 000000000000..062c54008b28 --- /dev/null +++ b/fiscal_epos_print/static/src/xml/ChromeWidgets/EpsonFP81IIComponent.xml @@ -0,0 +1,113 @@ + + + + +
+

Epson ePOS

+ +
+ +
+ +
+

Operations

+
    +
  • +
    + + Open CashDrawer + + + Open CashDrawer + +
    +
  • +
  • +
    + + Reprint Last Receipt + + + Reprint Last Receipt + +
    +
  • +
+

Reports

+
    +
  • +
    + + IRA status + + + IRA status + +
    +
  • +
  • +
    + + Fiscal closure + + + Closure Report Z (Fiscal) + +
    +
  • +
  • +
    + + Fiscal closure + + + Financial Report X + +
    +
  • +
+
+
+
+ +
diff --git a/fiscal_epos_print/static/src/xml/ChromeWidgets/SetLotteryCodeButton.xml b/fiscal_epos_print/static/src/xml/ChromeWidgets/SetLotteryCodeButton.xml new file mode 100644 index 000000000000..1998551cee6c --- /dev/null +++ b/fiscal_epos_print/static/src/xml/ChromeWidgets/SetLotteryCodeButton.xml @@ -0,0 +1,15 @@ + + + + +
+ + Lottery Code +
+
+ +
diff --git a/fiscal_epos_print/static/src/xml/ChromeWidgets/SetRefundInfoButton.xml b/fiscal_epos_print/static/src/xml/ChromeWidgets/SetRefundInfoButton.xml new file mode 100644 index 000000000000..fded09a0246c --- /dev/null +++ b/fiscal_epos_print/static/src/xml/ChromeWidgets/SetRefundInfoButton.xml @@ -0,0 +1,11 @@ + + + + +
+ + Refund Data +
+
+ +
diff --git a/fiscal_epos_print/static/src/xml/ChromeWidgets/SetReprintButton.xml b/fiscal_epos_print/static/src/xml/ChromeWidgets/SetReprintButton.xml new file mode 100644 index 000000000000..7f3f472749e8 --- /dev/null +++ b/fiscal_epos_print/static/src/xml/ChromeWidgets/SetReprintButton.xml @@ -0,0 +1,23 @@ + + + + + +
+
+ + Ristampa Ultimo Scontrino +
+
+
+
+ +
diff --git a/fiscal_epos_print/static/src/xml/Popups/LotteryCodePopup.xml b/fiscal_epos_print/static/src/xml/Popups/LotteryCodePopup.xml new file mode 100644 index 000000000000..fbba2ca6e392 --- /dev/null +++ b/fiscal_epos_print/static/src/xml/Popups/LotteryCodePopup.xml @@ -0,0 +1,42 @@ + + + + + + + + + + diff --git a/fiscal_epos_print/static/src/xml/Popups/RefundInfoPopup.xml b/fiscal_epos_print/static/src/xml/Popups/RefundInfoPopup.xml new file mode 100644 index 000000000000..fe10d84ceee9 --- /dev/null +++ b/fiscal_epos_print/static/src/xml/Popups/RefundInfoPopup.xml @@ -0,0 +1,87 @@ + + + + + + + + + + diff --git a/fiscal_epos_print/static/src/xml/lottery.xml b/fiscal_epos_print/static/src/xml/lottery.xml new file mode 100644 index 000000000000..9dd3499334e2 --- /dev/null +++ b/fiscal_epos_print/static/src/xml/lottery.xml @@ -0,0 +1,39 @@ + + + + + + + diff --git a/fiscal_epos_print/static/src/xml/pos.xml b/fiscal_epos_print/static/src/xml/pos.xml new file mode 100644 index 000000000000..5512c12933f8 --- /dev/null +++ b/fiscal_epos_print/static/src/xml/pos.xml @@ -0,0 +1,27 @@ + + + + + +
+ Receipt sent to the printer +
+
+ Invoice recorded +
+ + +
+
+ + + + display:none + + + +
diff --git a/fiscal_epos_print/views/account.xml b/fiscal_epos_print/views/account.xml new file mode 100644 index 000000000000..4a884b74dfad --- /dev/null +++ b/fiscal_epos_print/views/account.xml @@ -0,0 +1,32 @@ + + + + + POS Journal - Fiscal Printer field + pos.payment.method + + + + + + + + + + + + + account.tax.printer.form.view + account.tax + + + + + + + + + diff --git a/fiscal_epos_print/views/employee_view.xml b/fiscal_epos_print/views/employee_view.xml new file mode 100644 index 000000000000..b6d4bed75c49 --- /dev/null +++ b/fiscal_epos_print/views/employee_view.xml @@ -0,0 +1,14 @@ + + + + HR Employee - Fiscal Printer field + hr.employee + + + + + + + + + diff --git a/fiscal_epos_print/views/point_of_sale.xml b/fiscal_epos_print/views/point_of_sale.xml new file mode 100644 index 000000000000..e19b912918b5 --- /dev/null +++ b/fiscal_epos_print/views/point_of_sale.xml @@ -0,0 +1,161 @@ + + + + + pos.config.form.view + pos.config + + + +

Fiscal printer

+
+
+
+ Printer address +
+ The hostname or IP address of the fiscal printer +
+
+ +
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ Fiscal Printer Serial +
+ +
+
+
+
+
+
+
+ + + res.config.settings.form.printer.view + res.config.settings + + +

+

Fiscal printer

+
+
+
+ Printer address +
+ The hostname or IP address of the fiscal printer +
+
+ +
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ Fiscal Printer Serial +
+ +
+
+
+
+ +
+
+ + + pos.order.form + pos.order + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/setup/fiscal_epos_print/odoo/addons/fiscal_epos_print b/setup/fiscal_epos_print/odoo/addons/fiscal_epos_print new file mode 120000 index 000000000000..6a4664d218ed --- /dev/null +++ b/setup/fiscal_epos_print/odoo/addons/fiscal_epos_print @@ -0,0 +1 @@ +../../../../fiscal_epos_print \ No newline at end of file diff --git a/setup/fiscal_epos_print/setup.py b/setup/fiscal_epos_print/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/fiscal_epos_print/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)