diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/pos_mercury/static/src | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/pos_mercury/static/src')
10 files changed, 893 insertions, 0 deletions
diff --git a/addons/pos_mercury/static/src/css/pos_mercury.css b/addons/pos_mercury/static/src/css/pos_mercury.css new file mode 100644 index 00000000..967241f7 --- /dev/null +++ b/addons/pos_mercury/static/src/css/pos_mercury.css @@ -0,0 +1,3 @@ +.pos .paymentline.selected.o_pos_mercury_swipe_pending, .pos .paymentline.o_pos_mercury_swipe_pending { + background: rgb(239, 153, 65); +} diff --git a/addons/pos_mercury/static/src/js/OrderReceipt.js b/addons/pos_mercury/static/src/js/OrderReceipt.js new file mode 100644 index 00000000..35e77169 --- /dev/null +++ b/addons/pos_mercury/static/src/js/OrderReceipt.js @@ -0,0 +1,24 @@ +odoo.define('pos_mercury.OrderReceipt', function(require) { + 'use strict'; + + const OrderReceipt = require('point_of_sale.OrderReceipt'); + const Registries = require('point_of_sale.Registries'); + + const PosMercuryOrderReceipt = OrderReceipt => + class extends OrderReceipt { + /** + * The receipt has signature if one of the paymentlines + * is paid with mercury. + */ + get hasPosMercurySignature() { + for (let line of this.paymentlines) { + if (line.mercury_data) return true; + } + return false; + } + }; + + Registries.Component.extend(OrderReceipt, PosMercuryOrderReceipt); + + return OrderReceipt; +}); diff --git a/addons/pos_mercury/static/src/js/PaymentScreen.js b/addons/pos_mercury/static/src/js/PaymentScreen.js new file mode 100644 index 00000000..d803fff1 --- /dev/null +++ b/addons/pos_mercury/static/src/js/PaymentScreen.js @@ -0,0 +1,569 @@ +odoo.define('pos_mercury.PaymentScreen', function (require) { + 'use strict'; + + const { _t } = require('web.core'); + const PaymentScreen = require('point_of_sale.PaymentScreen'); + const Registries = require('point_of_sale.Registries'); + const NumberBuffer = require('point_of_sale.NumberBuffer'); + const { useBarcodeReader } = require('point_of_sale.custom_hooks'); + + // Lookup table to store status and error messages + const lookUpCodeTransaction = { + Approved: { + '000000': _t('Transaction approved'), + }, + TimeoutError: { + '001006': 'Global API Not Initialized', + '001007': 'Timeout on Response', + '003003': 'Socket Error sending request', + '003004': 'Socket already open or in use', + '003005': 'Socket Creation Failed', + '003006': 'Socket Connection Failed', + '003007': 'Connection Lost', + '003008': 'TCP/IP Failed to Initialize', + '003010': 'Time Out waiting for server response', + '003011': 'Connect Canceled', + '003053': 'Initialize Failed', + '009999': 'Unknown Error', + }, + FatalError: { + '-1': 'Timeout error', + '001001': 'General Failure', + '001003': 'Invalid Command Format', + '001004': 'Insufficient Fields', + '001011': 'Empty Command String', + '002000': 'Password Verified', + '002001': 'Queue Full', + '002002': 'Password Failed – Disconnecting', + '002003': 'System Going Offline', + '002004': 'Disconnecting Socket', + '002006': 'Refused ‘Max Connections’', + '002008': 'Duplicate Serial Number Detected', + '002009': 'Password Failed (Client / Server)', + '002010': 'Password failed (Challenge / Response)', + '002011': 'Internal Server Error – Call Provider', + '003002': 'In Process with server', + '003009': 'Control failed to find branded serial (password lookup failed)', + '003012': '128 bit CryptoAPI failed', + '003014': 'Threaded Auth Started Expect Response', + '003017': 'Failed to start Event Thread.', + '003050': 'XML Parse Error', + '003051': 'All Connections Failed', + '003052': 'Server Login Failed', + '004001': 'Global Response Length Error (Too Short)', + '004002': 'Unable to Parse Response from Global (Indistinguishable)', + '004003': 'Global String Error', + '004004': 'Weak Encryption Request Not Supported', + '004005': 'Clear Text Request Not Supported', + '004010': 'Unrecognized Request Format', + '004011': 'Error Occurred While Decrypting Request', + '004017': 'Invalid Check Digit', + '004018': 'Merchant ID Missing', + '004019': 'TStream Type Missing', + '004020': 'Could Not Encrypt Response- Call Provider', + '100201': 'Invalid Transaction Type', + '100202': 'Invalid Operator ID', + '100203': 'Invalid Memo', + '100204': 'Invalid Account Number', + '100205': 'Invalid Expiration Date', + '100206': 'Invalid Authorization Code', + '100207': 'Invalid Authorization Code', + '100208': 'Invalid Authorization Amount', + '100209': 'Invalid Cash Back Amount', + '100210': 'Invalid Gratuity Amount', + '100211': 'Invalid Purchase Amount', + '100212': 'Invalid Magnetic Stripe Data', + '100213': 'Invalid PIN Block Data', + '100214': 'Invalid Derived Key Data', + '100215': 'Invalid State Code', + '100216': 'Invalid Date of Birth', + '100217': 'Invalid Check Type', + '100218': 'Invalid Routing Number', + '100219': 'Invalid TranCode', + '100220': 'Invalid Merchant ID', + '100221': 'Invalid TStream Type', + '100222': 'Invalid Batch Number', + '100223': 'Invalid Batch Item Count', + '100224': 'Invalid MICR Input Type', + '100225': 'Invalid Driver’s License', + '100226': 'Invalid Sequence Number', + '100227': 'Invalid Pass Data', + '100228': 'Invalid Card Type', + }, + }; + + const PosMercuryPaymentScreen = (PaymentScreen) => + class extends PaymentScreen { + constructor() { + super(...arguments); + if (this.env.pos.getOnlinePaymentMethods().length !== 0) { + useBarcodeReader({ + credit: this.credit_code_action, + }); + } + // How long we wait for the odoo server to deliver the response of + // a Vantiv transaction + this.server_timeout_in_ms = 95000; + + // How many Vantiv transactions we send without receiving a + // response + this.server_retries = 3; + } + + _get_swipe_pending_line() { + var i = 0; + var lines = this.env.pos.get_order().get_paymentlines(); + + for (i = 0; i < lines.length; i++) { + if (lines[i].mercury_swipe_pending) { + return lines[i]; + } + } + + return 0; + } + + _does_credit_payment_line_exist(amount, card_number, card_brand, card_owner_name) { + var i = 0; + var lines = this.env.pos.get_order().get_paymentlines(); + + for (i = 0; i < lines.length; i++) { + if ( + lines[i].mercury_amount === amount && + lines[i].mercury_card_number === card_number && + lines[i].mercury_card_brand === card_brand && + lines[i].mercury_card_owner_name === card_owner_name + ) { + return true; + } + } + + return false; + } + + retry_mercury_transaction( + def, + response, + retry_nr, + can_connect_to_server, + callback, + args + ) { + var self = this; + var message = ''; + + if (retry_nr < self.server_retries) { + if (response) { + message = 'Retry #' + (retry_nr + 1) + '...<br/><br/>' + response.message; + } else { + message = 'Retry #' + (retry_nr + 1) + '...'; + } + def.notify({ + message: message, + }); + + setTimeout(function () { + callback.apply(self, args); + }, 1000); + } else { + if (response) { + message = + 'Error ' + + response.error + + ': ' + + lookUpCodeTransaction['TimeoutError'][response.error] + + '<br/>' + + response.message; + } else { + if (can_connect_to_server) { + message = self.env._t('No response from Vantiv (Vantiv down?)'); + } else { + message = self.env._t( + 'No response from server (connected to network?)' + ); + } + } + def.resolve({ + message: message, + auto_close: false, + }); + } + } + + // Handler to manage the card reader string + credit_code_transaction(parsed_result, old_deferred, retry_nr) { + var order = this.env.pos.get_order(); + if (order.get_due(order.selected_paymentline) < 0) { + this.showPopup('ErrorPopup', { + title: this.env._t('Refunds not supported'), + body: this.env._t( + "Credit card refunds are not supported. Instead select your credit card payment method, click 'Validate' and refund the original charge manually through the Vantiv backend." + ), + }); + return; + } + + if (this.env.pos.getOnlinePaymentMethods().length === 0) { + return; + } + + var self = this; + var decodedMagtek = self.env.pos.decodeMagtek(parsed_result.code); + + if (!decodedMagtek) { + this.showPopup('ErrorPopup', { + title: this.env._t('Could not read card'), + body: this.env._t( + 'This can be caused by a badly executed swipe or by not having your keyboard layout set to US QWERTY (not US International).' + ), + }); + return; + } + + var swipe_pending_line = self._get_swipe_pending_line(); + var purchase_amount = 0; + + if (swipe_pending_line) { + purchase_amount = swipe_pending_line.get_amount(); + } else { + purchase_amount = self.env.pos.get_order().get_due(); + } + + var transaction = { + encrypted_key: decodedMagtek['encrypted_key'], + encrypted_block: decodedMagtek['encrypted_block'], + transaction_type: 'Credit', + transaction_code: 'Sale', + invoice_no: self.env.pos.get_order().uid.replace(/-/g, ''), + purchase: purchase_amount, + payment_method_id: parsed_result.payment_method_id, + }; + + var def = old_deferred || new $.Deferred(); + retry_nr = retry_nr || 0; + + // show the transaction popup. + // the transaction deferred is used to update transaction status + // if we have a previous deferred it indicates that this is a retry + if (!old_deferred) { + self.showPopup('PaymentTransactionPopup', { + transaction: def, + }); + def.notify({ + message: this.env._t('Handling transaction...'), + }); + } + + this.rpc( + { + model: 'pos_mercury.mercury_transaction', + method: 'do_payment', + args: [transaction], + }, + { + timeout: self.server_timeout_in_ms, + } + ) + .then(function (data) { + // if not receiving a response from Vantiv, we should retry + if (data === 'timeout') { + self.retry_mercury_transaction( + def, + null, + retry_nr, + true, + self.credit_code_transaction, + [parsed_result, def, retry_nr + 1] + ); + return; + } + + if (data === 'not setup') { + def.resolve({ + message: self.env._t('Please setup your Vantiv merchant account.'), + }); + return; + } + + if (data === 'internal error') { + def.resolve({ + message: self.env._t('Odoo error while processing transaction.'), + }); + return; + } + + var response = self.env.pos.decodeMercuryResponse(data); + response.payment_method_id = parsed_result.payment_method_id; + + if (response.status === 'Approved') { + // AP* indicates a duplicate request, so don't add anything for those + if ( + response.message === 'AP*' && + self._does_credit_payment_line_exist( + response.authorize, + decodedMagtek['number'], + response.card_type, + decodedMagtek['name'] + ) + ) { + def.resolve({ + message: lookUpCodeTransaction['Approved'][response.error], + auto_close: true, + }); + } else { + // If the payment is approved, add a payment line + var order = self.env.pos.get_order(); + + if (swipe_pending_line) { + order.select_paymentline(swipe_pending_line); + } else { + order.add_paymentline( + self.payment_methods_by_id[parsed_result.payment_method_id] + ); + } + + order.selected_paymentline.paid = true; + order.selected_paymentline.mercury_swipe_pending = false; + order.selected_paymentline.mercury_amount = response.authorize; + order.selected_paymentline.set_amount(response.authorize); + order.selected_paymentline.mercury_card_number = + decodedMagtek['number']; + order.selected_paymentline.mercury_card_brand = response.card_type; + order.selected_paymentline.mercury_card_owner_name = + decodedMagtek['name']; + order.selected_paymentline.mercury_ref_no = response.ref_no; + order.selected_paymentline.mercury_record_no = response.record_no; + order.selected_paymentline.mercury_invoice_no = response.invoice_no; + order.selected_paymentline.mercury_auth_code = response.auth_code; + order.selected_paymentline.mercury_data = response; // used to reverse transactions + order.selected_paymentline.set_credit_card_name(); + + NumberBuffer.reset(); + order.trigger('change', order); // needed so that export_to_JSON gets triggered + self.render(); + + if (response.message === 'PARTIAL AP') { + def.resolve({ + message: self.env._t('Partially approved'), + auto_close: false, + }); + } else { + def.resolve({ + message: lookUpCodeTransaction['Approved'][response.error], + auto_close: true, + }); + } + } + } + + // if an error related to timeout or connectivity issues arised, then retry the same transaction + else { + if (lookUpCodeTransaction['TimeoutError'][response.error]) { + // recoverable error + self.retry_mercury_transaction( + def, + response, + retry_nr, + true, + self.credit_code_transaction, + [parsed_result, def, retry_nr + 1] + ); + } else { + // not recoverable + def.resolve({ + message: + 'Error ' + response.error + ':<br/>' + response.message, + auto_close: false, + }); + } + } + }) + .catch(function () { + self.retry_mercury_transaction( + def, + null, + retry_nr, + false, + self.credit_code_transaction, + [parsed_result, def, retry_nr + 1] + ); + }); + } + + credit_code_cancel() { + return; + } + + credit_code_action(parsed_result) { + var online_payment_methods = this.env.pos.getOnlinePaymentMethods(); + + if (online_payment_methods.length === 1) { + parsed_result.payment_method_id = online_payment_methods[0].item; + this.credit_code_transaction(parsed_result); + } else { + // this is for supporting another payment system like mercury + const selectionList = online_payment_methods.map((paymentMethod) => ({ + id: paymentMethod.item, + label: paymentMethod.label, + isSelected: false, + item: paymentMethod.item, + })); + this.showPopup('SelectionPopup', { + title: this.env._t('Pay with: '), + list: selectionList, + }).then(({ confirmed, payload: selectedPaymentMethod }) => { + if (confirmed) { + parsed_result.payment_method_id = selectedPaymentMethod; + this.credit_code_transaction(parsed_result); + } else { + this.credit_code_cancel(); + } + }); + } + } + + remove_paymentline_by_ref(line) { + this.env.pos.get_order().remove_paymentline(line); + NumberBuffer.reset(); + this.render(); + } + + do_reversal(line, is_voidsale, old_deferred, retry_nr) { + var def = old_deferred || new $.Deferred(); + var self = this; + retry_nr = retry_nr || 0; + + // show the transaction popup. + // the transaction deferred is used to update transaction status + this.showPopup('PaymentTransactionPopup', { + transaction: def, + }); + + var request_data = _.extend( + { + transaction_type: 'Credit', + transaction_code: 'VoidSaleByRecordNo', + }, + line.mercury_data + ); + + var message = ''; + var rpc_method = ''; + + if (is_voidsale) { + message = this.env._t('Reversal failed, sending VoidSale...'); + rpc_method = 'do_voidsale'; + } else { + message = this.env._t('Sending reversal...'); + rpc_method = 'do_reversal'; + } + + if (!old_deferred) { + def.notify({ + message: message, + }); + } + + this.rpc( + { + model: 'pos_mercury.mercury_transaction', + method: rpc_method, + args: [request_data], + }, + { + timeout: self.server_timeout_in_ms, + } + ) + .then(function (data) { + if (data === 'timeout') { + self.retry_mercury_transaction( + def, + null, + retry_nr, + true, + self.do_reversal, + [line, is_voidsale, def, retry_nr + 1] + ); + return; + } + + if (data === 'internal error') { + def.resolve({ + message: self.env._t('Odoo error while processing transaction.'), + }); + return; + } + + var response = self.env.pos.decodeMercuryResponse(data); + + if (!is_voidsale) { + if (response.status != 'Approved' || response.message != 'REVERSED') { + // reversal was not successful, send voidsale + self.do_reversal(line, true); + } else { + // reversal was successful + def.resolve({ + message: self.env._t('Reversal succeeded'), + }); + + self.remove_paymentline_by_ref(line); + } + } else { + // voidsale ended, nothing more we can do + if (response.status === 'Approved') { + def.resolve({ + message: self.env._t('VoidSale succeeded'), + }); + + self.remove_paymentline_by_ref(line); + } else { + def.resolve({ + message: + 'Error ' + response.error + ':<br/>' + response.message, + }); + } + } + }) + .catch(function () { + self.retry_mercury_transaction( + def, + null, + retry_nr, + false, + self.do_reversal, + [line, is_voidsale, def, retry_nr + 1] + ); + }); + } + + /** + * @override + */ + deletePaymentLine(event) { + const { cid } = event.detail; + const line = this.paymentLines.find((line) => line.cid === cid); + if (line.mercury_data) { + this.do_reversal(line, false); + } else { + super.deletePaymentLine(event); + } + } + + /** + * @override + */ + addNewPaymentLine({ detail: paymentMethod }) { + const order = this.env.pos.get_order(); + const res = super.addNewPaymentLine(...arguments); + if (res && paymentMethod.pos_mercury_config_id) { + order.selected_paymentline.mercury_swipe_pending = true; + order.trigger('change', order); + this.render(); + } + } + }; + + Registries.Component.extend(PaymentScreen, PosMercuryPaymentScreen); + + return PaymentScreen; +}); diff --git a/addons/pos_mercury/static/src/js/PaymentScreenPaymentLines.js b/addons/pos_mercury/static/src/js/PaymentScreenPaymentLines.js new file mode 100644 index 00000000..64228d79 --- /dev/null +++ b/addons/pos_mercury/static/src/js/PaymentScreenPaymentLines.js @@ -0,0 +1,30 @@ +odoo.define('pos_mercury.PaymentScreenPaymentLines', function (require) { + 'use strict'; + + const PaymentScreenPaymentLines = require('point_of_sale.PaymentScreenPaymentLines'); + const Registries = require('point_of_sale.Registries'); + + const PosMercuryPaymentLines = (PaymentScreenPaymentLines) => + class extends PaymentScreenPaymentLines { + /** + * @override + */ + selectedLineClass(line) { + return Object.assign({}, super.selectedLineClass(line), { + o_pos_mercury_swipe_pending: line.mercury_swipe_pending, + }); + } + /** + * @override + */ + unselectedLineClass(line) { + return Object.assign({}, super.unselectedLineClass(line), { + o_pos_mercury_swipe_pending: line.mercury_swipe_pending, + }); + } + }; + + Registries.Component.extend(PaymentScreenPaymentLines, PosMercuryPaymentLines); + + return PaymentScreenPaymentLines; +}); diff --git a/addons/pos_mercury/static/src/js/PaymentTransactionPopup.js b/addons/pos_mercury/static/src/js/PaymentTransactionPopup.js new file mode 100644 index 00000000..9b47da70 --- /dev/null +++ b/addons/pos_mercury/static/src/js/PaymentTransactionPopup.js @@ -0,0 +1,37 @@ +odoo.define('pos_mercury.PaymentTransactionPopup', function(require) { + 'use strict'; + + const { useState } = owl.hooks; + const AbstractAwaitablePopup = require('point_of_sale.AbstractAwaitablePopup'); + const Registries = require('point_of_sale.Registries'); + + class PaymentTransactionPopup 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; + }) + } + } + PaymentTransactionPopup.template = 'PaymentTransactionPopup'; + PaymentTransactionPopup.defaultProps = { + confirmText: 'Ok', + cancelText: 'Cancel', + title: 'Online Payment', + body: '', + }; + + Registries.Component.add(PaymentTransactionPopup); + + return PaymentTransactionPopup; +}); diff --git a/addons/pos_mercury/static/src/js/ProductScreen.js b/addons/pos_mercury/static/src/js/ProductScreen.js new file mode 100644 index 00000000..64d53048 --- /dev/null +++ b/addons/pos_mercury/static/src/js/ProductScreen.js @@ -0,0 +1,26 @@ +odoo.define('pos_mercury.ProductScreen', function (require) { + 'use strict'; + + const ProductScreen = require('point_of_sale.ProductScreen'); + const Registries = require('point_of_sale.Registries'); + const { useBarcodeReader } = require('point_of_sale.custom_hooks'); + + const PosMercuryProductScreen = (ProductScreen) => + class extends ProductScreen { + constructor() { + super(...arguments); + useBarcodeReader({ + credit: this.credit_error_action, + }); + } + credit_error_action() { + this.showPopup('ErrorPopup', { + body: this.env._t('Go to payment screen to use cards'), + }); + } + }; + + Registries.Component.extend(ProductScreen, PosMercuryProductScreen); + + return ProductScreen; +}); diff --git a/addons/pos_mercury/static/src/js/pos_mercury.js b/addons/pos_mercury/static/src/js/pos_mercury.js new file mode 100644 index 00000000..48342f24 --- /dev/null +++ b/addons/pos_mercury/static/src/js/pos_mercury.js @@ -0,0 +1,139 @@ +odoo.define('pos_mercury.pos_mercury', function (require) { +"use strict"; + +var pos_model = require('point_of_sale.models'); + +pos_model.load_fields('pos.payment.method', 'pos_mercury_config_id'); + +pos_model.PosModel = pos_model.PosModel.extend({ + getOnlinePaymentMethods: function () { + var online_payment_methods = []; + + $.each(this.payment_methods, function (i, payment_method) { + if (payment_method.pos_mercury_config_id) { + online_payment_methods.push({label: payment_method.name, item: payment_method.id}); + } + }); + + return online_payment_methods; + }, + decodeMagtek: function (magtekInput) { + // Regular expression to identify and extract data from the track 1 & 2 of the magnetic code + var _track1_regex = /%B?([0-9]*)\^([A-Z\/ -_]*)\^([0-9]{4})(.{3})([^?]+)\?/; + + var track1 = magtekInput.match(_track1_regex); + var magtek_generated = magtekInput.split('|'); + + var to_return = {}; + try { + track1.shift(); // get rid of complete match + to_return['number'] = track1.shift().substr(-4); + to_return['name'] = track1.shift(); + track1.shift(); // expiration date + track1.shift(); // service code + track1.shift(); // discretionary data + track1.shift(); // zero pad + + magtek_generated.shift(); // track1 and track2 + magtek_generated.shift(); // clear text crc + magtek_generated.shift(); // encryption counter + to_return['encrypted_block'] = magtek_generated.shift(); + magtek_generated.shift(); // enc session id + magtek_generated.shift(); // device serial + magtek_generated.shift(); // magneprint data + magtek_generated.shift(); // magneprint status + magtek_generated.shift(); // enc track3 + to_return['encrypted_key'] = magtek_generated.shift(); + magtek_generated.shift(); // enc track1 + magtek_generated.shift(); // reader enc status + + return to_return; + } catch (e) { + return 0; + } + }, + decodeMercuryResponse: function (data) { + // get rid of xml version declaration and just keep the RStream + // from the response because the xml contains two version + // declarations. One for the SOAP, and one for the content. Maybe + // we should unpack the SOAP layer in python? + data = data.replace(/.*<\?xml version="1.0"\?>/, ""); + data = data.replace(/<\/CreditTransactionResult>.*/, ""); + + var xml = $($.parseXML(data)); + var cmd_response = xml.find("CmdResponse"); + var tran_response = xml.find("TranResponse"); + + return { + status: cmd_response.find("CmdStatus").text(), + message: cmd_response.find("TextResponse").text(), + error: cmd_response.find("DSIXReturnCode").text(), + card_type: tran_response.find("CardType").text(), + auth_code: tran_response.find("AuthCode").text(), + acq_ref_data: tran_response.find("AcqRefData").text(), + process_data: tran_response.find("ProcessData").text(), + invoice_no: tran_response.find("InvoiceNo").text(), + ref_no: tran_response.find("RefNo").text(), + record_no: tran_response.find("RecordNo").text(), + purchase: parseFloat(tran_response.find("Purchase").text()), + authorize: parseFloat(tran_response.find("Authorize").text()), + }; + } +}); + +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.mercury_card_number = json.mercury_card_number; + this.mercury_card_brand = json.mercury_card_brand; + this.mercury_card_owner_name = json.mercury_card_owner_name; + this.mercury_ref_no = json.mercury_ref_no; + this.mercury_record_no = json.mercury_record_no; + this.mercury_invoice_no = json.mercury_invoice_no; + this.mercury_auth_code = json.mercury_auth_code; + this.mercury_data = json.mercury_data; + this.mercury_swipe_pending = json.mercury_swipe_pending; + + this.set_credit_card_name(); + }, + export_as_JSON: function () { + return _.extend(_paylineproto.export_as_JSON.apply(this, arguments), {paid: this.paid, + mercury_card_number: this.mercury_card_number, + mercury_card_brand: this.mercury_card_brand, + mercury_card_owner_name: this.mercury_card_owner_name, + mercury_ref_no: this.mercury_ref_no, + mercury_record_no: this.mercury_record_no, + mercury_invoice_no: this.mercury_invoice_no, + mercury_auth_code: this.mercury_auth_code, + mercury_data: this.mercury_data, + mercury_swipe_pending: this.mercury_swipe_pending}); + }, + set_credit_card_name: function () { + if (this.mercury_card_number) { + this.name = this.mercury_card_brand + " (****" + this.mercury_card_number + ")"; + } + }, + is_done: function () { + var res = _paylineproto.is_done.apply(this); + return res && !this.mercury_swipe_pending; + }, + export_for_printing: function () { + const result = _paylineproto.export_for_printing.apply(this, arguments); + result.mercury_data = this.mercury_data; + result.mercury_auth_code = this.mercury_auth_code; + return result; + } +}); + +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.mercury_swipe_pending); + }, +}); + +}); diff --git a/addons/pos_mercury/static/src/xml/OrderReceipt.xml b/addons/pos_mercury/static/src/xml/OrderReceipt.xml new file mode 100644 index 00000000..014884c0 --- /dev/null +++ b/addons/pos_mercury/static/src/xml/OrderReceipt.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates id="template" xml:space="preserve"> + + <t t-name="pos_mercury.OrderReceipt" t-inherit="point_of_sale.OrderReceipt" t-inherit-mode="extension" owl="1"> + <xpath expr="//t[@t-foreach='receipt.paymentlines']" position="inside"> + <t t-if="line.mercury_data"> + <div class="pos-receipt-left-padding"> + <span>APPROVAL CODE: </span> + <span> + <t t-esc="line.mercury_auth_code" /> + </span> + </div> + </t> + </xpath> + <xpath expr="//div[hasclass('pos-receipt-order-data')]" position="after"> + <div t-if="hasPosMercurySignature"> + <br /> + <div>CARDHOLDER WILL PAY CARD ISSUER</div> + <div>ABOVE AMOUNT PURSUANT</div> + <div>TO CARDHOLDER AGREEMENT</div> + <br /> + <br /> + <div>X______________________________</div> + </div> + </xpath> + </t> + +</templates> diff --git a/addons/pos_mercury/static/src/xml/PaymentScreenPaymentLines.xml b/addons/pos_mercury/static/src/xml/PaymentScreenPaymentLines.xml new file mode 100644 index 00000000..0a01d9f8 --- /dev/null +++ b/addons/pos_mercury/static/src/xml/PaymentScreenPaymentLines.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates id="template" xml:space="preserve"> + + <t t-name="pos_mercury.PaymentScreenPaymentLines" t-inherit="point_of_sale.PaymentScreenPaymentLines" t-inherit-mode="extension" owl="1"> + <xpath expr="//div[hasclass('paymentline')]//t[@t-esc='line.payment_method.name']" position="replace"> + <t t-if="!line.payment_method.is_cash_count and line.mercury_swipe_pending"> + <span>WAITING FOR SWIPE</span> + </t> + <t t-else=""> + <t t-esc="line.payment_method.name" /> + </t> + </xpath> + </t> + +</templates> diff --git a/addons/pos_mercury/static/src/xml/PaymentTransactionPopup.xml b/addons/pos_mercury/static/src/xml/PaymentTransactionPopup.xml new file mode 100644 index 00000000..39c05a8f --- /dev/null +++ b/addons/pos_mercury/static/src/xml/PaymentTransactionPopup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<templates id="template" xml:space="preserve"> + + <t t-name="PaymentTransactionPopup" owl="1"> + <div role="dialog" class="modal-dialog"> + <div class="popup"> + <p class="title"> + <t t-esc="props.title"></t> + </p> + <p class="body"> + <t t-esc="state.message"></t> + </p> + <div t-if="state.confirmButtonIsShown" class="footer"> + <div class="button cancel" t-on-click="confirm"> + <t t-esc="props.confirmText"></t> + </div> + </div> + </div> + </div> + </t> + +</templates> |
