From 9d08173221f143992fb410dab2a099ac5127a28e Mon Sep 17 00:00:00 2001 From: Jared Kipe Date: Sun, 15 Nov 2020 15:07:02 -0800 Subject: [PATCH] [MIG] pos_pax: Rewrite for Odoo 14.0 + Owl --- pos_pax/__init__.py | 3 + pos_pax/__manifest__.py | 45 ++ pos_pax/models/__init__.py | 4 + pos_pax/models/pos_pax.py | 38 ++ pos_pax/models/update.py | 20 + pos_pax/static/src/css/pos_pax.css | 17 + pos_pax/static/src/js/PAXPaymentScreen.js | 183 +++++++ .../src/js/PAXPaymentScreenPaymentLines.js | 32 ++ .../src/js/PAXPaymentTransactionPopup.js | 39 ++ pos_pax/static/src/js/jquery_base64.js | 133 +++++ pos_pax/static/src/js/pax_device.js | 479 ++++++++++++++++++ pos_pax/static/src/js/pos_pax.js | 71 +++ pos_pax/static/src/xml/OrderReceipt.xml | 17 + .../src/xml/PAXPaymentScreenPaymentLines.xml | 21 + .../src/xml/PAXPaymentTransactionPopup.xml | 22 + pos_pax/views/pos_config_setting_views.xml | 23 + pos_pax/views/pos_pax_templates.xml | 16 + pos_pax/views/pos_pax_views.xml | 16 + 18 files changed, 1179 insertions(+) create mode 100644 pos_pax/__init__.py create mode 100644 pos_pax/__manifest__.py create mode 100644 pos_pax/models/__init__.py create mode 100644 pos_pax/models/pos_pax.py create mode 100644 pos_pax/models/update.py create mode 100644 pos_pax/static/src/css/pos_pax.css create mode 100644 pos_pax/static/src/js/PAXPaymentScreen.js create mode 100644 pos_pax/static/src/js/PAXPaymentScreenPaymentLines.js create mode 100644 pos_pax/static/src/js/PAXPaymentTransactionPopup.js create mode 100644 pos_pax/static/src/js/jquery_base64.js create mode 100644 pos_pax/static/src/js/pax_device.js create mode 100644 pos_pax/static/src/js/pos_pax.js create mode 100644 pos_pax/static/src/xml/OrderReceipt.xml create mode 100644 pos_pax/static/src/xml/PAXPaymentScreenPaymentLines.xml create mode 100644 pos_pax/static/src/xml/PAXPaymentTransactionPopup.xml create mode 100644 pos_pax/views/pos_config_setting_views.xml create mode 100644 pos_pax/views/pos_pax_templates.xml create mode 100644 pos_pax/views/pos_pax_views.xml diff --git a/pos_pax/__init__.py b/pos_pax/__init__.py new file mode 100644 index 00000000..09434554 --- /dev/null +++ b/pos_pax/__init__.py @@ -0,0 +1,3 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +from . import models diff --git a/pos_pax/__manifest__.py b/pos_pax/__manifest__.py new file mode 100644 index 00000000..8f2117c0 --- /dev/null +++ b/pos_pax/__manifest__.py @@ -0,0 +1,45 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +{ + 'name': 'POS PAX Terminal Credit Card', + 'author': 'Hibou Corp. ', + 'version': '14.0.1.0.0', + 'category': 'Point of Sale', + 'sequence': 6, + 'summary': 'PAX Terminal Credit card support for Point Of Sale', + 'description': """ +Allow credit card POS payments +============================== + +This module allows customers to pay for their orders with credit cards. +The transactions are processed on the PAX Terminal (no credit credit card +information through Odoo itself). + +Depending on Device and processor support, this integration can handle: + +* Magnetic swiped cards +* EMV chip cards +* Contactless (including Apple Pay, Samsung Pay, Google Pay) + + """, + 'depends': [ + 'web', + 'pos_sale', + 'hibou_professional', + ], + 'website': 'https://hibou.io', + 'data': [ + 'views/pos_config_setting_views.xml', + 'views/pos_pax_templates.xml', + 'views/pos_pax_views.xml', + ], + 'demo': [ + ], + 'qweb': [ + 'static/src/xml/OrderReceipt.xml', + 'static/src/xml/PAXPaymentTransactionPopup.xml', + 'static/src/xml/PAXPaymentScreenPaymentLines.xml', + ], + 'installable': True, + 'auto_install': False, +} diff --git a/pos_pax/models/__init__.py b/pos_pax/models/__init__.py new file mode 100644 index 00000000..c25936ee --- /dev/null +++ b/pos_pax/models/__init__.py @@ -0,0 +1,4 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +from . import pos_pax +from . import update diff --git a/pos_pax/models/pos_pax.py b/pos_pax/models/pos_pax.py new file mode 100644 index 00000000..2f84cc4e --- /dev/null +++ b/pos_pax/models/pos_pax.py @@ -0,0 +1,38 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +from odoo import models, fields, api, _ + + +class PoSPayment(models.Model): + _inherit = 'pos.payment' + + pax_card_number = fields.Char(string='Card Number', help='Masked credit card.') + pax_txn_id = fields.Char(string='PAX Transaction ID') + + +class PoSPaymentMethod(models.Model): + _inherit = 'pos.payment.method' + + def _get_payment_terminal_selection(self): + return super(PoSPaymentMethod, self)._get_payment_terminal_selection() + [('pax', 'PAX')] + + +class PosConfig(models.Model): + _inherit = 'pos.config' + + pax_endpoint = fields.Char(string='PAX Endpoint', + help='Endpoint for PAX device (include protocol (http or https) and port). ' + 'e.g. http://192.168.1.101:10009') + + +class PosOrder(models.Model): + _inherit = "pos.order" + + @api.model + def _payment_fields(self, order, ui_paymentline): + fields = super(PosOrder, self)._payment_fields(order, ui_paymentline) + fields.update({ + 'pax_card_number': ui_paymentline.get('pax_card_number'), + 'pax_txn_id': ui_paymentline.get('pax_txn_id'), + }) + return fields diff --git a/pos_pax/models/update.py b/pos_pax/models/update.py new file mode 100644 index 00000000..6a08ab50 --- /dev/null +++ b/pos_pax/models/update.py @@ -0,0 +1,20 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +from odoo import models + + +class PublisherWarrantyContract(models.AbstractModel): + _inherit = 'publisher_warranty.contract' + + def _get_hibou_modules(self): + modules = super(PublisherWarrantyContract, self)._get_hibou_modules() + try: + self.env.cr.execute( + 'SELECT COUNT(*) FROM pos_config WHERE pax_endpoint != \'\' AND pax_endpoint IS NOT NULL') + pax_count = self.env.cr.fetchone()[0] or 0 + modules.update({ + 'pos_pax': pax_count, + }) + except: + pass + return modules diff --git a/pos_pax/static/src/css/pos_pax.css b/pos_pax/static/src/css/pos_pax.css new file mode 100644 index 00000000..f6a20ec0 --- /dev/null +++ b/pos_pax/static/src/css/pos_pax.css @@ -0,0 +1,17 @@ +.pos .paymentline.selected.o_pos_pax_txn_pending, .pos .paymentline.o_pos_pax_txn_pending { + background: rgb(239, 153, 65); +} +.pos .col-tendered.edit.o_pos_pax_txn_pending { + color: rgb(239, 153, 65); + box-shadow: 0px 0px 0px 3px rgb(239, 153, 65); +} + +.pos .paymentline .pax-send-transaction-button { + cursor: pointer !important; + border: 1px solid rgba(255, 255, 255, 0.5); + background-color: rgba(255,255,255, 0.2); + display: block; + text-align: center; + margin: 6px 0 0 0; + padding: 2px 5px; +} diff --git a/pos_pax/static/src/js/PAXPaymentScreen.js b/pos_pax/static/src/js/PAXPaymentScreen.js new file mode 100644 index 00000000..d8ab41df --- /dev/null +++ b/pos_pax/static/src/js/PAXPaymentScreen.js @@ -0,0 +1,183 @@ +odoo.define('pos_pax.PAXPaymentScreen', function (require) { + 'use strict'; + + // Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + + const { _t } = require('web.core'); + const PaymentScreen = require('point_of_sale.PaymentScreen'); + const Registries = require('point_of_sale.Registries'); + const { useListener } = require('web.custom_hooks'); + const PAX = require('pos_pax.pax_device'); + + PAX.mDestinationIP = ''; + + const PAXPaymentScreen = (PaymentScreen) => + class extends PaymentScreen { + constructor() { + super(...arguments); + // tempting to use 'send-payment-request', + // but method implements things that don't seem to exist yet (payment_method.payment_terminal) + useListener('pax-send-payment-request', this._sendPAXPaymentRequest); + } + + async _sendPAXPaymentRequest({ detail: line }) { + this.pax_credit_transaction(line); + } + + pax_credit_transaction(line) { + var order = this.env.pos.get_order(); + + if(this.env.pos.getPAXOnlinePaymentJournals().length === 0) { + return; + } + + var self = this; + var transaction = {}; + + var purchase_amount = line.get_amount(); + + var transactionType = '01'; // SALE + if (purchase_amount < 0.0) { + purchase_amount = -purchase_amount; + transactionType = '02'; // RETURN + } + + transaction = { + command: 'T00', + version: '1.28', + transactionType: transactionType, + amountInformation: { + TransactionAmount: (purchase_amount * 100) | 0, // cast to integer + }, + cashierInformation: { + ClerkID: this.env.pos.user.name, + }, + traceInformation: { + ReferenceNumber: self.env.pos.get_order().uid, + // InvoiceNumber: self.env.pos.get_order().uid, + }, + } + + var def = new $.Deferred(); + + // show the transaction popup. + // the transaction deferred is used to update transaction status + self.showPopup('PAXPaymentTransactionPopup', { + transaction: def + }); + def.notify({ + message: _t('Handling transaction...'), + }); + + PAX.mDestinationIP = self.env.pos.config.pax_endpoint; + PAX.DoCredit(transaction, function (response) { + console.log(response); + var parsed_response = self.env.pos.decodePAXResponse(response); + if (parsed_response.fail) { + def.resolve({message: parsed_response.fail}) + return; + } + if (parsed_response.success) { + line.paid = true; + line.pax_card_number = parsed_response.card_num; + line.pax_txn_id = parsed_response.txn_id; + line.pax_approval = parsed_response.approval; + line.pax_txn_pending = false; + line.set_credit_card_name(); + order.trigger('change', order); + self.render(); + def.resolve({message: 'Approval ' + parsed_response.approval, auto_close: true}) + } + def.resolve({message: _t('Unknown response.')}) + }, function (fail){ + def.resolve({message: _t('Communication Failure: ') + fail}); + }); + } + + pax_do_reversal(line) { + var def = new $.Deferred(); + var self = this; + var transaction = {}; + + // show the transaction popup. + // the transaction deferred is used to update transaction status + this.showPopup('PAXPaymentTransactionPopup', { + transaction: def + }); + def.notify({ + message: 'Sending reversal...', + }); + + transaction = { + command: 'T00', + version: '1.28', + transactionType: (line.get_amount() > 0) ? '17' : '18', // V/SALE, V/RETURN + cashierInformation: { + ClerkID: this.env.pos.user.name, + }, + traceInformation: { + ReferenceNumber: this.env.pos.get_order().uid, + InvoiceNumber: '', + AuthCode: line.pax_approval, + TransactionNumber: line.pax_txn_id, + }, + } + + PAX.mDestinationIP = self.env.pos.config.pax_endpoint; + PAX.DoCredit(transaction, function (response) { + var parsed_response = self.env.pos.decodePAXResponse(response); + if (parsed_response.fail) { + def.resolve({message: parsed_response.fail}) + return; + } + if (parsed_response.success) { + def.resolve({ + message: _t('Reversal succeeded.'), + auto_close: true, + }); + self.remove_paymentline_by_ref(line); + return; + } + def.resolve({message: _t('Unknown response.')}) + }, function (fail){ + def.resolve({message: _t('Communication Failure: ') + fail}); + }); + } + + remove_paymentline_by_ref(line) { + this.env.pos.get_order().remove_paymentline(line); + this.render(); + } + + /** + * @override + */ + deletePaymentLine(event) { + const { cid } = event.detail; + const line = this.paymentLines.find((line) => line.cid === cid); + if (line.pax_txn_id) { + this.pax_do_reversal(line); + } else { + super.deletePaymentLine(event); + } + } + + /** + * @override + */ + addNewPaymentLine({ detail: paymentMethod }) { + const order = this.env.pos.get_order(); + const res = super.addNewPaymentLine(...arguments); + if (res && paymentMethod.use_payment_terminal == 'pax') { + order.selected_paymentline.pax_txn_pending = true; + order.trigger('change', order); + this.render(); + } + } + + }; + + Registries.Component.extend(PaymentScreen, PAXPaymentScreen); + + return PAXPaymentScreen; +}); diff --git a/pos_pax/static/src/js/PAXPaymentScreenPaymentLines.js b/pos_pax/static/src/js/PAXPaymentScreenPaymentLines.js new file mode 100644 index 00000000..0cd8c7f5 --- /dev/null +++ b/pos_pax/static/src/js/PAXPaymentScreenPaymentLines.js @@ -0,0 +1,32 @@ +odoo.define('pos_pax.PAXPaymentScreenPaymentLines', function (require) { + 'use strict'; + + // Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + + const PaymentScreenPaymentLines = require('point_of_sale.PaymentScreenPaymentLines'); + const Registries = require('point_of_sale.Registries'); + + const PAXPaymentLines = (PaymentScreenPaymentLines) => + class extends PaymentScreenPaymentLines { + /** + * @override + */ + selectedLineClass(line) { + return Object.assign({}, super.selectedLineClass(line), { + o_pos_pax_txn_pending: line.pax_txn_pending, + }); + } + /** + * @override + */ + unselectedLineClass(line) { + return Object.assign({}, super.unselectedLineClass(line), { + o_pos_pax_txn_pending: line.pax_txn_pending, + }); + } + }; + + Registries.Component.extend(PaymentScreenPaymentLines, PAXPaymentLines); + + return PAXPaymentLines; +}); diff --git a/pos_pax/static/src/js/PAXPaymentTransactionPopup.js b/pos_pax/static/src/js/PAXPaymentTransactionPopup.js new file mode 100644 index 00000000..8f417995 --- /dev/null +++ b/pos_pax/static/src/js/PAXPaymentTransactionPopup.js @@ -0,0 +1,39 @@ +odoo.define('pos_pax.PAXPaymentTransactionPopup', function(require) { + 'use strict'; + + // Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + + const { useState } = owl.hooks; + const AbstractAwaitablePopup = require('point_of_sale.AbstractAwaitablePopup'); + const Registries = require('point_of_sale.Registries'); + + class PAXPaymentTransactionPopup extends AbstractAwaitablePopup { + constructor() { + super(...arguments); + this.state = useState({ message: '', confirmButtonIsShown: false }); + this.props.transaction.then(data => { + if (data.auto_close) { + setTimeout(() => { + this.confirm(); + }, 2000) + } else { + this.state.confirmButtonIsShown = true; + } + this.state.message = data.message; + }).progress(data => { + this.state.message = data.message; + }) + } + } + PAXPaymentTransactionPopup.template = 'PAXPaymentTransactionPopup'; + PAXPaymentTransactionPopup.defaultProps = { + confirmText: 'Ok', + cancelText: 'Cancel', + title: 'PAX Online Payment', + body: '', + }; + + Registries.Component.add(PAXPaymentTransactionPopup); + + return PAXPaymentTransactionPopup; +}); diff --git a/pos_pax/static/src/js/jquery_base64.js b/pos_pax/static/src/js/jquery_base64.js new file mode 100644 index 00000000..56ab330b --- /dev/null +++ b/pos_pax/static/src/js/jquery_base64.js @@ -0,0 +1,133 @@ +odoo.define('pos_pax.jquery_base64', function (require) { + "use strict"; + + // Hibou Corp. © 2020 - Wrapped library for PAX Device + + var jQuery = window.jQuery; + + /*! + * jquery.base64.js 0.1 - https://github.com/yckart/jquery.base64.js + * Makes Base64 en & -decoding simpler as it is. + * + * Based upon: https://gist.github.com/Yaffle/1284012 + * + * Copyright (c) 2012 Yannick Albert (http://yckart.com) + * Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php). + * 2013/02/10 + **/ + ;(function ($) { + + var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", + a256 = '', + r64 = [256], + r256 = [256], + i = 0; + + var UTF8 = { + + /** + * Encode multi-byte Unicode string into utf-8 multiple single-byte characters + * (BMP / basic multilingual plane only) + * + * Chars in range U+0080 - U+07FF are encoded in 2 chars, U+0800 - U+FFFF in 3 chars + * + * @param {String} strUni Unicode string to be encoded as UTF-8 + * @returns {String} encoded string + */ + encode: function (strUni) { + // use regular expressions & String.replace callback function for better efficiency + // than procedural approaches + var strUtf = strUni.replace(/[\u0080-\u07ff]/g, // U+0080 - U+07FF => 2 bytes 110yyyyy, 10zzzzzz + function (c) { + var cc = c.charCodeAt(0); + return String.fromCharCode(0xc0 | cc >> 6, 0x80 | cc & 0x3f); + }) + .replace(/[\u0800-\uffff]/g, // U+0800 - U+FFFF => 3 bytes 1110xxxx, 10yyyyyy, 10zzzzzz + function (c) { + var cc = c.charCodeAt(0); + return String.fromCharCode(0xe0 | cc >> 12, 0x80 | cc >> 6 & 0x3F, 0x80 | cc & 0x3f); + }); + return strUtf; + }, + + /** + * Decode utf-8 encoded string back into multi-byte Unicode characters + * + * @param {String} strUtf UTF-8 string to be decoded back to Unicode + * @returns {String} decoded string + */ + decode: function (strUtf) { + // note: decode 3-byte chars first as decoded 2-byte strings could appear to be 3-byte char! + var strUni = strUtf.replace(/[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, // 3-byte chars + function (c) { // (note parentheses for precence) + var cc = ((c.charCodeAt(0) & 0x0f) << 12) | ((c.charCodeAt(1) & 0x3f) << 6) | (c.charCodeAt(2) & 0x3f); + return String.fromCharCode(cc); + }) + .replace(/[\u00c0-\u00df][\u0080-\u00bf]/g, // 2-byte chars + function (c) { // (note parentheses for precence) + var cc = (c.charCodeAt(0) & 0x1f) << 6 | c.charCodeAt(1) & 0x3f; + return String.fromCharCode(cc); + }); + return strUni; + } + }; + + while (i < 256) { + var c = String.fromCharCode(i); + a256 += c; + r256[i] = i; + r64[i] = b64.indexOf(c); + ++i; + } + + function code(s, discard, alpha, beta, w1, w2) { + s = String(s); + var buffer = 0, + i = 0, + length = s.length, + result = '', + bitsInBuffer = 0; + + while (i < length) { + var c = s.charCodeAt(i); + c = c < 256 ? alpha[c] : -1; + + buffer = (buffer << w1) + c; + bitsInBuffer += w1; + + while (bitsInBuffer >= w2) { + bitsInBuffer -= w2; + var tmp = buffer >> bitsInBuffer; + result += beta.charAt(tmp); + buffer ^= tmp << bitsInBuffer; + } + ++i; + } + if (!discard && bitsInBuffer > 0) result += beta.charAt(buffer << (w2 - bitsInBuffer)); + return result; + } + + var Plugin = $.base64 = function (dir, input, encode) { + return input ? Plugin[dir](input, encode) : dir ? null : this; + }; + + Plugin.btoa = Plugin.encode = function (plain, utf8encode) { + plain = Plugin.raw === false || Plugin.utf8encode || utf8encode ? UTF8.encode(plain) : plain; + plain = code(plain, false, r256, b64, 8, 6); + return plain + '===='.slice((plain.length % 4) || 4); + }; + + Plugin.atob = Plugin.decode = function (coded, utf8decode) { + coded = String(coded).split('='); + var i = coded.length; + do { + --i; + coded[i] = code(coded[i], true, r64, a256, 6, 8); + } while (i > 0); + coded = coded.join(''); + return Plugin.raw === false || Plugin.utf8decode || utf8decode ? UTF8.decode(coded) : coded; + }; + }(jQuery)); + + return jQuery; +}); diff --git a/pos_pax/static/src/js/pax_device.js b/pos_pax/static/src/js/pax_device.js new file mode 100644 index 00000000..14879313 --- /dev/null +++ b/pos_pax/static/src/js/pax_device.js @@ -0,0 +1,479 @@ +odoo.define('pos_pax.pax_device', function (require) { + "use strict"; + + var $ = require('pos_pax.jquery_base64'); + + // Hibou Corp. © 2020 - Wrapped library for PAX Device + // Additions and Fixes: + // 2020-11-12 Fixed variable i and ii in getLRC + // 2020-11-12 Fixed/added 'fail' mechanisms for XHR + + //HEX TO BASE64 + function hexToBase64(str) { + return $.base64.btoa(String.fromCharCode.apply(null, + str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" ")) + ); + } + + //BASE64 TO HEX + function base64ToHex(str) { + for (var i = 0, bin = $.base64.atob(str), hex = []; i < bin.length; ++i) { + var tmp = bin.charCodeAt(i).toString(16); + if (tmp.length === 1) tmp = "0" + tmp; + hex[hex.length] = tmp; + } + return hex.join(" "); + } + + function StringToHex(response){ + var responseHex = ""; + for(var i=0; i0)?String.fromCharCode(lrc):0; + }, + + + + //Connect to the server + HttpCommunication : function(commandType,url,callback,timeout,fail){ + var xhr = null; + if(window.XMLHttpRequest) { + xhr = new XMLHttpRequest(); + } else { + try{ + xhr = new ActiveXObject('Microsoft.XMLHttp'); + }catch(e){ + xhr = new ActiveXObject('msxml2.xmlhttp'); + } + } + //get + + xhr.open("GET", url,true); + xhr.onreadystatechange=function(){ + if(xhr.readyState==4) { + //alert(xhr.status); + if(xhr.status==200) { + var response = xhr.responseText; + console.log("Raw response: "+response); + + var checkParams = StringToHex(response).split(" ").pop(); + var RedundancyCheck = StringToHex(response).split(" ").pop().substring(1); + + var check = PAX.getLRC(checkParams); + + if(check == RedundancyCheck){ + //get package detail info + var packetInfo = []; + var len = StringToHex(response).indexOf("03"); + var hex = StringToHex(response).slice(0,len).split(/02|1c/); + + console.log(hex); + if(commandType == "DoCredit"){ + var subHex=[], subPacketInfo=[]; + for(var i=0; i0){ + subHex = hex[i].split("1f"); + console.log(subHex); + subPacketInfo = []; + for(var j=0; j"+customData+""); + // } + + + var url = this.mDestinationIP + '?' + final_b64; + console.log("URL: " + url); + + this.HttpCommunication('DoCredit',url,function(response){ + callback(response); + },PAX.timeout.DoCredit,fail); + } + }; + + return PAX; + +}); \ No newline at end of file diff --git a/pos_pax/static/src/js/pos_pax.js b/pos_pax/static/src/js/pos_pax.js new file mode 100644 index 00000000..cebe484b --- /dev/null +++ b/pos_pax/static/src/js/pos_pax.js @@ -0,0 +1,71 @@ +odoo.define('pos_pax.pos_pax', function (require) { +"use strict"; + +// Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +const pos_model = require('point_of_sale.models'); + +pos_model.PosModel = pos_model.PosModel.extend({ + getPAXOnlinePaymentJournals: function () { + var online_payment_methods = []; + + $.each(this.payment_methods, function (i, payment_method) { + if (payment_method.use_payment_terminal == 'pax') { + online_payment_methods.push({label: payment_method.name, item: payment_method.id}); + } + }); + return online_payment_methods; + }, + decodePAXResponse: function (data) { + if (data[5] == 'ABORTED') { + return {fail: 'Transaction Aborted'}; + } else if (data[5] == 'OK') { + return { + success: true, + approval: data[6][2], + txn_id: data[10][0], + card_num: '***' + data[9][0], + } + } + return {fail: 'Unknown Response. ' + data}; + }, +}); + +var _paylineproto = pos_model.Paymentline.prototype; +pos_model.Paymentline = pos_model.Paymentline.extend({ + init_from_JSON: function (json) { + _paylineproto.init_from_JSON.apply(this, arguments); + + this.paid = json.paid; + this.pax_txn_pending = json.pax_txn_pending; + this.pax_card_number = json.pax_card_number; + this.pax_approval = json.pax_approval; + this.pax_txn_id = json.pax_txn_id; + + this.set_credit_card_name(); + }, + export_as_JSON: function () { + return _.extend(_paylineproto.export_as_JSON.apply(this, arguments), { + paid: this.paid, + pax_txn_pending: this.pax_txn_pending, + pax_card_number: this.pax_card_number, + pax_approval: this.pax_approval, + pax_txn_id: this.pax_txn_id, + }); + }, + set_credit_card_name: function () { + if (this.pax_card_number) { + this.name = this.pax_card_number; + } + }, +}); + +var _order_super = pos_model.Order.prototype; +pos_model.Order = pos_model.Order.extend({ + electronic_payment_in_progress: function() { + var res = _order_super.electronic_payment_in_progress.apply(this, arguments); + return res || this.get_paymentlines().some(line => line.pax_txn_pending); + }, +}); + +}); diff --git a/pos_pax/static/src/xml/OrderReceipt.xml b/pos_pax/static/src/xml/OrderReceipt.xml new file mode 100644 index 00000000..b91928d2 --- /dev/null +++ b/pos_pax/static/src/xml/OrderReceipt.xml @@ -0,0 +1,17 @@ + + + + + + +
+ APPROVAL CODE: + + + +
+
+
+
+ +
diff --git a/pos_pax/static/src/xml/PAXPaymentScreenPaymentLines.xml b/pos_pax/static/src/xml/PAXPaymentScreenPaymentLines.xml new file mode 100644 index 00000000..37d291aa --- /dev/null +++ b/pos_pax/static/src/xml/PAXPaymentScreenPaymentLines.xml @@ -0,0 +1,21 @@ + + + + + + + WAITING FOR TXN +
+ Send +
+
+ + + + +
+
+ +
diff --git a/pos_pax/static/src/xml/PAXPaymentTransactionPopup.xml b/pos_pax/static/src/xml/PAXPaymentTransactionPopup.xml new file mode 100644 index 00000000..7c120b30 --- /dev/null +++ b/pos_pax/static/src/xml/PAXPaymentTransactionPopup.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/pos_pax/views/pos_config_setting_views.xml b/pos_pax/views/pos_config_setting_views.xml new file mode 100644 index 00000000..3041cb57 --- /dev/null +++ b/pos_pax/views/pos_config_setting_views.xml @@ -0,0 +1,23 @@ + + + + + pos.config.form.inherit.pax + pos.config + + + +
+
+ PAX +
+

Your PAX Device Endpoint

+ +
+
+
+
+
+
+ +
diff --git a/pos_pax/views/pos_pax_templates.xml b/pos_pax/views/pos_pax_templates.xml new file mode 100644 index 00000000..557a7f3f --- /dev/null +++ b/pos_pax/views/pos_pax_templates.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/pos_pax/views/pos_pax_views.xml b/pos_pax/views/pos_pax_views.xml new file mode 100644 index 00000000..a017ba1e --- /dev/null +++ b/pos_pax/views/pos_pax_views.xml @@ -0,0 +1,16 @@ + + + + + POS orders + pos.order + + + + + + + + + +