From c4db591109fc4aa6c1f898df50c3ce43aa03f1df Mon Sep 17 00:00:00 2001 From: eLBati Date: Tue, 10 Dec 2019 14:46:09 +0100 Subject: [PATCH 01/65] ADD fiscal_epos_print --- fiscal_epos_print/__init__.py | 1 + fiscal_epos_print/__manifest__.py | 33 + fiscal_epos_print/i18n/it.po | 152 ++ fiscal_epos_print/models/__init__.py | 4 + fiscal_epos_print/models/account.py | 10 + fiscal_epos_print/models/account_journal.py | 16 + fiscal_epos_print/models/point_of_sale.py | 16 + fiscal_epos_print/models/product.py | 17 + fiscal_epos_print/readme/CONFIGURE.rst | 6 + fiscal_epos_print/readme/CONTRIBUTORS.rst | 4 + fiscal_epos_print/readme/DESCRIPTION.rst | 7 + .../static/lib/fiscalprint/fiscalprint.js | 1435 +++++++++++++++++ fiscal_epos_print/static/src/css/pos.css | 5 + fiscal_epos_print/static/src/img/ade-logo.png | Bin 0 -> 1323 bytes fiscal_epos_print/static/src/js/fp90iii.js | 318 ++++ fiscal_epos_print/static/src/xml/pos.xml | 29 + .../views/account_statement_view.xml | 37 + fiscal_epos_print/views/assets.xml | 11 + fiscal_epos_print/views/point_of_sale.xml | 25 + 19 files changed, 2126 insertions(+) create mode 100644 fiscal_epos_print/__init__.py create mode 100644 fiscal_epos_print/__manifest__.py create mode 100644 fiscal_epos_print/i18n/it.po create mode 100644 fiscal_epos_print/models/__init__.py create mode 100644 fiscal_epos_print/models/account.py create mode 100644 fiscal_epos_print/models/account_journal.py create mode 100644 fiscal_epos_print/models/point_of_sale.py create mode 100644 fiscal_epos_print/models/product.py create mode 100644 fiscal_epos_print/readme/CONFIGURE.rst create mode 100644 fiscal_epos_print/readme/CONTRIBUTORS.rst create mode 100644 fiscal_epos_print/readme/DESCRIPTION.rst create mode 100644 fiscal_epos_print/static/lib/fiscalprint/fiscalprint.js create mode 100644 fiscal_epos_print/static/src/css/pos.css create mode 100644 fiscal_epos_print/static/src/img/ade-logo.png create mode 100644 fiscal_epos_print/static/src/js/fp90iii.js create mode 100644 fiscal_epos_print/static/src/xml/pos.xml create mode 100644 fiscal_epos_print/views/account_statement_view.xml create mode 100644 fiscal_epos_print/views/assets.xml create mode 100644 fiscal_epos_print/views/point_of_sale.xml 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..b00f450a1b86 --- /dev/null +++ b/fiscal_epos_print/__manifest__.py @@ -0,0 +1,33 @@ +# Leonardo Donelli - Creativi Quadrati +# © 2016 Alessio Gerace - Agile Business Group +# © 2018-2019 Lorenzo Battistini +# License GPL-3.0 or later (http://www.gnu.org/licenses/gpl.html). + +{ + 'name': 'Driver for ePOS-Print XML compatible fiscal printers', + 'version': '12.0.1.0.0', + 'category': 'POS, Fiscal, Hardware, Driver', + 'summary': 'ePOS-Print XML Fiscal Printer Driver - Compatible Epson printers: ' + 'FP81II, FP90III', + 'author': ( + 'Odoo Community Association (OCA), Agile Business Group, ' + 'Leonardo Donelli @ Creativi Quadrati, TAKOBI' + ), + 'license': 'GPL-3', + 'website': 'https://github.com/OCA/l10n-italy', + 'depends': ['point_of_sale', 'pos_order_mgmt'], + 'data': [ + 'views/account_statement_view.xml', + 'views/point_of_sale.xml', + 'views/assets.xml', + ], + 'js': [ + 'static/src/js/fp90iii.js', + 'static/lib/fiscalprint/fiscalprint.js' + ], + 'qweb': [ + 'static/src/xml/pos.xml' + ], + 'installable': True, + 'auto_install': False, +} diff --git a/fiscal_epos_print/i18n/it.po b/fiscal_epos_print/i18n/it.po new file mode 100644 index 000000000000..177c792d61f9 --- /dev/null +++ b/fiscal_epos_print/i18n/it.po @@ -0,0 +1,152 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * fiscal_epos_print +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-10-29 10:20+0000\n" +"PO-Revision-Date: 2019-10-29 10:20+0000\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/fp90iii.js:32 +#, python-format +msgid "\n" +"Old files: " +msgstr "\nFile vecchi: " + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/fp90iii.js:32 +#, python-format +msgid "\n" +"Rejected files: " +msgstr "\nFile rifiutati: " + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/pos.xml:6 +#, python-format +msgid "ADE status" +msgstr "Stato ADE" + +#. module: fiscal_epos_print +#: selection:account.journal,fiscalprinter_payment_type:0 +msgid "Cash" +msgstr "Contanti" + +#. module: fiscal_epos_print +#: selection:account.journal,fiscalprinter_payment_type:0 +msgid "Cheque" +msgstr "Assegni" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/fp90iii.js:260 +#, python-format +msgid "Connecting to the fiscal printer" +msgstr "Connessione alla stampante fiscale" + +#. module: fiscal_epos_print +#: selection:account.journal,fiscalprinter_payment_type:0 +msgid "Credit/Credit Card" +msgstr "Carta di credito" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_account_tax__fpdeptax +msgid "Department group tax on fiscal printer 1~99" +msgstr "Reparto sulla stampante fiscale 1~99" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/fp90iii.js:24 +#, python-format +msgid "Error missing paper." +msgstr "Errore Manca La carta." + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/fp90iii.js:32 +#, python-format +msgid "Files waiting to be sent: " +msgstr "File in attesa di essere inviati: " + +#. module: fiscal_epos_print +#: model_terms:ir.ui.view,arch_db:fiscal_epos_print.view_pos_config_printer_form +msgid "Fiscal printer" +msgstr "Stampante fiscale" + +#. module: fiscal_epos_print +#: model:ir.model,name:fiscal_epos_print.model_account_journal +msgid "Journal" +msgstr "Registro" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/fp90iii.js:39 +#, python-format +msgid "Network error. Printer can not be reached" +msgstr "Errore di rete. La stampante non può essere raggiunta" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/js/fp90iii.js:162 +#, python-format +msgid "No taxes found" +msgstr "Nessuna imposta trovata" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_account_journal__fiscalprinter_payment_type +msgid "Payment type" +msgstr "Tipo di pagamento" + +#. module: fiscal_epos_print +#: model:ir.model,name:fiscal_epos_print.model_pos_config +msgid "Point of Sale Configuration" +msgstr "Configurazione punto vendita" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_config__printer_ip +msgid "Printer IP Address" +msgstr "Indirizzo IP stampante" + +#. module: fiscal_epos_print +#. openerp-web +#: code:addons/fiscal_epos_print/static/src/xml/pos.xml:6 +#, python-format +msgid "Status of files to be sent to ADE" +msgstr "Stato dei file da inviare all'ADE" + +#. module: fiscal_epos_print +#: model:ir.model,name:fiscal_epos_print.model_account_tax +msgid "Tax" +msgstr "Imposta" + +#. module: fiscal_epos_print +#: model:ir.model.fields,help:fiscal_epos_print.field_pos_config__printer_ip +msgid "The hostname or ip address of the hardware printer, please fill this field if you want use you receipts printer" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,help:fiscal_epos_print.field_account_journal__fiscalprinter_payment_type +msgid "The payment type to send to the Fiscal Printer." +msgstr "" + +#. module: fiscal_epos_print +#: selection:account.journal,fiscalprinter_payment_type:0 +msgid "Ticket" +msgstr "" + +#. module: fiscal_epos_print +#: model:ir.model.fields,field_description:fiscal_epos_print.field_pos_config__use_https +msgid "Use https" +msgstr "Usa HTTPS" + diff --git a/fiscal_epos_print/models/__init__.py b/fiscal_epos_print/models/__init__.py new file mode 100644 index 000000000000..e58ce761f35d --- /dev/null +++ b/fiscal_epos_print/models/__init__.py @@ -0,0 +1,4 @@ +from . import account_journal +from . import point_of_sale +from . import account +from . import product diff --git a/fiscal_epos_print/models/account.py b/fiscal_epos_print/models/account.py new file mode 100644 index 000000000000..1833ddd6de93 --- /dev/null +++ b/fiscal_epos_print/models/account.py @@ -0,0 +1,10 @@ +from odoo import fields, models + + +class AccountTax(models.Model): + _inherit = 'account.tax' + + fpdeptax = fields.Char( + 'Department group tax on fiscal printer 1~99', + size=1, default="1" + ) diff --git a/fiscal_epos_print/models/account_journal.py b/fiscal_epos_print/models/account_journal.py new file mode 100644 index 000000000000..1403b7ad5c2a --- /dev/null +++ b/fiscal_epos_print/models/account_journal.py @@ -0,0 +1,16 @@ +from odoo import fields, models + + +class AccountJournal(models.Model): + _inherit = 'account.journal' + fiscalprinter_payment_type = fields.Selection( + [ + ('0', 'Cash'), + ('1', 'Cheque'), + ('2', 'Credit/Credit Card'), + ('3', 'Ticket') + ], + 'Payment type', + help='The payment type to send to the Fiscal Printer.', + default='0' + ) 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..8a48ebfe3fa9 --- /dev/null +++ b/fiscal_epos_print/models/point_of_sale.py @@ -0,0 +1,16 @@ +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 hardware printer, ' + 'please fill this field if you want use you receipts printer', + size=45 + ) + use_https = fields.Boolean( + string='Use https', + default=False, + ) diff --git a/fiscal_epos_print/models/product.py b/fiscal_epos_print/models/product.py new file mode 100644 index 000000000000..d6472dec9972 --- /dev/null +++ b/fiscal_epos_print/models/product.py @@ -0,0 +1,17 @@ +from odoo import models, api, _ +from odoo.exceptions import ValidationError + + +class Product(models.Model): + _inherit = 'product.template' + + @api.constrains('available_in_pos', 'taxes_id') + def _check_pos_prod_taxes(self): + if self.available_in_pos: + if len(self.taxes_id) != 1: + raise ValidationError( + _("Product %s must have 1 tax") % self.display_name) + if not self.taxes_id[0].price_include: + raise ValidationError( + _("Tax %s must be included in price") + % self.taxes_id[0].display_name) diff --git a/fiscal_epos_print/readme/CONFIGURE.rst b/fiscal_epos_print/readme/CONFIGURE.rst new file mode 100644 index 000000000000..9ed99d8d874e --- /dev/null +++ b/fiscal_epos_print/readme/CONFIGURE.rst @@ -0,0 +1,6 @@ +- print list departments of your fiscal printer +- mapping odoo sale taxes with group taxes - departments of fiscal printer, for each sale tax on odoo, using field "Department group tax on fiscal printer 1~99" +- In odoo, use taxes included in price +- connect your fiscal printer on network and find IP +- open point_of_sale configuration and fill Printer IP Address field, with printer IP +- that's all, at validation of payment on POS session system print fiscal receipt. diff --git a/fiscal_epos_print/readme/CONTRIBUTORS.rst b/fiscal_epos_print/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000000..bfba346293a9 --- /dev/null +++ b/fiscal_epos_print/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* Leonardo Donelli +* Lorenzo Battistini +* Alessio Gerace +* Roberto Fichera diff --git a/fiscal_epos_print/readme/DESCRIPTION.rst b/fiscal_epos_print/readme/DESCRIPTION.rst new file mode 100644 index 000000000000..6eafa67be6fb --- /dev/null +++ b/fiscal_epos_print/readme/DESCRIPTION.rst @@ -0,0 +1,7 @@ +This module allows to print receipt of point_of_sale, +on fiscal printer Epson via EPos protocol. + +Supported printers: + +- FP81II +- FP90III 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..5fc128e5ca6b --- /dev/null +++ b/fiscal_epos_print/static/lib/fiscalprint/fiscalprint.js @@ -0,0 +1,1435 @@ +// +// ePOS-Print and Fiscal Print API +// +// Version 1.0.0 +// +// Copyright (C) SEIKO EPSON CORPORATION 2012. All rights reserved. +// + +(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 + + /* + 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) + { + // create SOAP envelope + var soap = '\n' + + '\n' + + '\n' + + request + + '\n' + + '\n'; + // 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 + // 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) + { + var res = xhr.responseXML.getElementsByTagName('response'); + // alert (xhr.responseXML.xml); + // document.write(xhr.responseXML.xml); + + 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')) + }; + + // 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 i = 0; i < tag_names_array.length; i++) + { + var node = res_add[0].getElementsByTagName(tag_names_array[i])[0]; + var node_child = node.childNodes[0]; + var node_val = node_child.nodeValue; + add_info[tag_names_array[i]] = node_val; + } + } + epos.onreceive(result, tag_names_array, add_info) + } + else // res.length <= 0 + { + // alert ("res.length = " + res.length); + fireFiscalErrorEvent(epos, xhr); + } // 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) + { + epos.onerror({ + status: xhr.status, + responseText: xhr.responseText + }); + } + } + + + // + // 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 + */ + window.epson = { + ePOSBuilder: ePOSBuilder, + ePOSPrint: ePOSPrint, + fiscalPrint: fiscalPrint, + CanvasPrint: CanvasPrint + }; + +})(window); 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..7ade28c334e5 --- /dev/null +++ b/fiscal_epos_print/static/src/css/pos.css @@ -0,0 +1,5 @@ +/* Disabilitato finchè non gestiamo i resi */ + +.orderlist-screen .order-line .order-list-return { + display:none; +} diff --git a/fiscal_epos_print/static/src/img/ade-logo.png b/fiscal_epos_print/static/src/img/ade-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2a01e0fa97518955eaba3ac142c1f70a4b4f95ba GIT binary patch literal 1323 zcmV+`1=RY9P)Al>qT9o?9-vL^tlJA=}BQ4BNyRyss`K~91S0b_xCfjpouurL-+ zZ0I$BG{kfeGZsHpw{F{k{@QLH;dy>xYtkWU+n3L|*|pnN&b)N8V))$}V?`{Uc%s(; zzW(JD$4{IwN=piN*^c*s<+wSP?Wphe9mcUeCXFqn)pEqLUCXkZJv-iiWk%)F#Vo5@ zf6F-lK%a5X0$?@&^X`O^gMGtLk10hIP+AM$|9phK2aln(q&K6Y%21C#GJlQ!vOd2O zAUyi%TL*BDZGn_~X_}a{L7rIk5&oBF+@b8AkivbB7<;UPVi*jpew$<9JO;$1yD1CF!_WmV;%xVmU6B?P58OSeA$7 zI3#UXw^~ktWjTFq*QNG!Jz8rM7!#N^(tf>gCn7_szz>Ff@!oQ@zIybBwa5SwK|~NO zXsr z;i=tv$%`{|C}hxkrYOjZHU+6vr`t5#d9cOeIWs=D9q)zq#V;beMToR1dre-sP9{NT zB7hRDlxP%M6e^T;n7XgDSy7g`@;d`$Iz7*?5|P>0BvUMUvwB%3Q47>pTyX*##lVD_NG-}R+Kfx;)$X&7h2BQt~VrU zxorPtA8V?&5Q=2I8kjYep;$RLJKZw2^SQBd(#ny65pVzfg;#QHZA=8RWATJ@6`;K& zW<}Xh;4JV~w7Tw{z^B9vUUEDHczBmQh@p6=lW1381LMVLr@TzP~u-`3HSJz_wkIjzfJz z6H8WY#Pw1=&BVV>PCfKsN>p9u)dh?ZYVw`aCY9y~fnF(s3xc7#U7}Zj4Fa*m hw1cMwUhDYe_!p8yX-584&fow5002ovPDHLkV1f+'; + 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; + }, + + /* + Prints a receipt + */ + printFiscalReceipt: function(receipt) { + var self = this; + var xml = ''; + _.each(receipt.orderlines, function(l, i, list) { + if (l.price >= 0) { + xml += self.printRecItem({ + description: l.product_name, + quantity: l.quantity, + unitPrice: l.price, + department: l.tax_department.code + }); + if (l.discount) { + xml += self.printRecItemAdjustment({ + adjustmentType: 0, + description: 'Sconto ' + l.discount + '%', + amount: round_pr(l.quantity * l.price - l.price_display, self.sender.pos.currency.rounding), + }); + } + } + else { + xml += self.printRecItemAdjustment({ + adjustmentType: 3, + description: l.product_name, + department: l.tax_department.code, + amount: -l.price, + }); + } + }); + _.each(receipt.paymentlines, function(l, i, list) { + xml += self.printRecTotal({ + payment: l.amount, + paymentType: l.type, + description: l.journal, + }); + }); + xml += ''; + this.fiscalPrinter.send(this.url, xml); + console.log(xml); + }, + + printFiscalReport: function() { + var xml = ''; + xml += ''; + xml += ''; + xml += ''; + this.fiscalPrinter.send(this.url, xml); + }, + + getStatusOfFilesForADE: function() { + var xml = ''; + xml += ''; + xml += ''; + this.fiscalPrinter.send(this.url, xml); + }, + + }); + + var _orderline_super = models.Orderline.prototype; + models.Orderline = models.Orderline.extend({ + export_for_printing: function(){ + var res = _orderline_super.export_for_printing.call(this, arguments); + res['tax_department'] = this.get_tax_details_r(); + return res; + }, + get_tax_details_r: function(){ + var details = this.get_all_prices(); + for (var i in details.taxDetails){ + return { + code: this.pos.taxes_by_id[i].fpdeptax, + taxname: this.pos.taxes_by_id[i].name, + } + } + alert(_t("No taxes found")); + }, + compute_all: function(taxes, price_unit, quantity, currency_rounding, no_map_tax) { + var res = _orderline_super.compute_all.call(this, taxes, price_unit, quantity, currency_rounding, no_map_tax); + var self = this; + + var total_excluded = round_pr(price_unit * quantity, currency_rounding); + var total_included = total_excluded; + var base = total_excluded; + var list_taxes = res.taxes; + // amount_type 'group' not handled (used only for purchases, in Italy) + _(taxes).each(function(tax) { + if (!no_map_tax){ + tax = self._map_tax_fiscal_position(tax); + } + if (!tax){ + return; + } + var tax_amount = self._compute_all(tax, base, quantity); + tax_amount = round_pr(tax_amount, currency_rounding); + if (!tax_amount){ + // Intervene here: also add taxes with 0 amount + if (tax.price_include) { + total_excluded -= tax_amount; + base -= tax_amount; + } + else { + total_included += tax_amount; + } + if (tax.include_base_amount) { + base += tax_amount; + } + var data = { + id: tax.id, + amount: tax_amount, + name: tax.name, + }; + list_taxes.push(data); + } + }); + res.taxes = list_taxes; + + return res; + }, + }); + + /* + Overwrite Paymentline.export_for_printing() in order + to make it export the payment type that must be passed + to the fiscal printer. + */ + var original = models.Paymentline.prototype.export_for_printing; + models.Paymentline = models.Paymentline.extend({ + export_for_printing: function() { + var res = original.apply(this, arguments); + res.type = this.cashregister.journal.fiscalprinter_payment_type; + return res; + } + }); + + // when print order i validate call print receipt + screens.PaymentScreenWidget.include({ + getPrinterOptions: function (){ + 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}; + }, + sendToPrinter: function(receipt, printer_options) { + var fp90 = new eposDriver(printer_options, this); + fp90.printFiscalReceipt(receipt); + }, + finalize_validation: function() { + this._super.apply(this, arguments); + var currentOrder = this.pos.get('selectedOrder'); + if (!currentOrder.is_to_invoice()) { + var printer_options = this.getPrinterOptions(); + var receipt = currentOrder.export_for_printing(); + this.sendToPrinter(receipt, printer_options); + this.pos.get('selectedOrder')._printed = true; + } + } + + }); + + var _super_posmodel = models.PosModel.prototype; + models.PosModel = models.PosModel.extend({ + initialize: function (session, attributes) { + var tax_model = _.find(this.models, function(model){ return model.model === 'account.tax'; }); + tax_model.fields.push('fpdeptax'); + return _super_posmodel.initialize.call(this, session, attributes); + }, + }); + + OrderListScreenWidget.include({ + // copiato da screens.PaymentScreenWidget + getPrinterOptions: function (){ + 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}]; + }, + // copiato da screens.PaymentScreenWidget + sendToPrinter: function(receipt, printer_options) { + for (var i = 0; i < printer_options.length; i++){ + var printer_option = printer_options[i]; + var fp90 = new eposDriver(printer_option, this); + fp90.printFiscalReceipt(receipt); + } + }, + action_print: function (order_data, order) { + var receipt = order.export_for_printing(); + var printer_options = this.getPrinterOptions(); + this.sendToPrinter(receipt, printer_options); + return this._super(order_data, order); + }, + }); + + var FiscalPrinterADEFilesButtonWidget = PosBaseWidget.extend({ + template: 'FiscalPrinterADEFilesButtonWidget', + + button_click: function () { + this.chrome.loading_show(); + this.chrome.loading_message(_t('Connecting to the fiscal printer')); + var protocol = ((this.pos.config.use_https) ? 'https://' : 'http://'); + var printer_url = protocol + this.pos.config.printer_ip + '/cgi-bin/fpmate.cgi'; + var printer_options = {url: printer_url}; + var fp90 = new eposDriver(printer_options, this); + fp90.getStatusOfFilesForADE(); + }, + + renderElement: function () { + var self = this; + this._super(); + this.$el.click(function () { + self.button_click(); + }); + }, + + }); + + var widgets = chrome.Chrome.prototype.widgets; + widgets.push({ + 'name': 'ADE files status', + 'widget': FiscalPrinterADEFilesButtonWidget, + 'append': '.pos-rightheader', + 'args': { + 'label': 'ADE files status', + }, + }); + + return { + eposDriver: eposDriver, + FiscalPrinterADEFilesButtonWidget: FiscalPrinterADEFilesButtonWidget, + }; + +}); 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..4b314984f7eb --- /dev/null +++ b/fiscal_epos_print/static/src/xml/pos.xml @@ -0,0 +1,29 @@ + + + + +
+ ADE status +
+
+ + + + display:none + + +
+ Scontrino inviato alla stampante +
+
+ Fattura registrata +
+
+
+ + + display:none + + + +
diff --git a/fiscal_epos_print/views/account_statement_view.xml b/fiscal_epos_print/views/account_statement_view.xml new file mode 100644 index 000000000000..38912cccd980 --- /dev/null +++ b/fiscal_epos_print/views/account_statement_view.xml @@ -0,0 +1,37 @@ + + + + + +