[REF] pos_mercury: migration to OWL framework
This commit is contained in:
committed by
Pierre Masereel
parent
42b85f1115
commit
ee593a9167
@@ -34,7 +34,9 @@ following:
|
||||
'data/pos_mercury_demo.xml',
|
||||
],
|
||||
'qweb': [
|
||||
'static/src/xml/pos_mercury.xml',
|
||||
'static/src/xml/OrderReceipt.xml',
|
||||
'static/src/xml/PaymentScreenPaymentLines.xml',
|
||||
'static/src/xml/PaymentTransactionPopup.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
@@ -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;
|
||||
});
|
||||
@@ -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;
|
||||
});
|
||||
@@ -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;
|
||||
});
|
||||
@@ -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;
|
||||
});
|
||||
@@ -1,18 +1,8 @@
|
||||
odoo.define('pos_mercury.pos_mercury', function (require) {
|
||||
"use strict";
|
||||
|
||||
var core = require('web.core');
|
||||
var rpc = require('web.rpc');
|
||||
var screens = require('point_of_sale.screens');
|
||||
var gui = require('point_of_sale.gui');
|
||||
var pos_model = require('point_of_sale.models');
|
||||
|
||||
var _t = core._t;
|
||||
|
||||
var PopupWidget = require('point_of_sale.popups');
|
||||
var ScreenWidget = screens.ScreenWidget;
|
||||
var PaymentScreenWidget = screens.PaymentScreenWidget;
|
||||
|
||||
pos_model.load_fields('pos.payment.method', 'pos_mercury_config_id');
|
||||
|
||||
pos_model.PosModel = pos_model.PosModel.extend({
|
||||
@@ -140,508 +130,4 @@ pos_model.Order = pos_model.Order.extend({
|
||||
},
|
||||
});
|
||||
|
||||
// Lookup table to store status and error messages
|
||||
var 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',
|
||||
},
|
||||
};
|
||||
// Popup to show all transaction state for the payment.
|
||||
|
||||
var PaymentTransactionPopupWidget = PopupWidget.extend({
|
||||
template: 'PaymentTransactionPopupWidget',
|
||||
show: function (options) {
|
||||
var self = this;
|
||||
this._super(options);
|
||||
options.transaction.then(function (data) {
|
||||
if (data.auto_close) {
|
||||
setTimeout(function () {
|
||||
self.gui.close_popup();
|
||||
}, 2000);
|
||||
} else {
|
||||
self.close();
|
||||
self.$el.find('.popup').append('<div class="footer"><div class="button cancel">Ok</div></div>');
|
||||
}
|
||||
|
||||
self.$el.find('p.body').html(data.message);
|
||||
}).progress(function (data) {
|
||||
self.$el.find('p.body').html(data.message);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
gui.define_popup({name:'payment-transaction', widget: PaymentTransactionPopupWidget});
|
||||
|
||||
// On all screens, if a card is swipped, return a popup error.
|
||||
ScreenWidget.include({
|
||||
credit_error_action: function () {
|
||||
this.gui.show_popup('error-barcode',_t('Go to payment screen to use cards'));
|
||||
},
|
||||
|
||||
show: function () {
|
||||
this._super();
|
||||
if(this.pos.getOnlinePaymentMethods().length !== 0) {
|
||||
this.pos.barcode_reader.set_action_callback('credit', _.bind(this.credit_error_action, this));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// On Payment screen, allow online payments
|
||||
PaymentScreenWidget.include({
|
||||
// How long we wait for the odoo server to deliver the response of
|
||||
// a Vantiv transaction
|
||||
server_timeout_in_ms: 95000,
|
||||
|
||||
// How many Vantiv transactions we send without receiving a
|
||||
// response
|
||||
server_retries: 3,
|
||||
|
||||
_get_swipe_pending_line: function () {
|
||||
var i = 0;
|
||||
var lines = this.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: function (amount, card_number, card_brand, card_owner_name) {
|
||||
var i = 0;
|
||||
var lines = this.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: function (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 = _t("No response from Vantiv (Vantiv down?)");
|
||||
} else {
|
||||
message = _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: function (parsed_result, old_deferred, retry_nr) {
|
||||
var order = this.pos.get_order();
|
||||
if (order.get_due(order.selected_paymentline) < 0) {
|
||||
this.gui.show_popup('error',{
|
||||
'title': _t('Refunds not supported'),
|
||||
'body': _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.pos.getOnlinePaymentMethods().length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var decodedMagtek = self.pos.decodeMagtek(parsed_result.code);
|
||||
|
||||
if (! decodedMagtek) {
|
||||
this.gui.show_popup('error',{
|
||||
'title': _t('Could not read card'),
|
||||
'body': _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.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.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.gui.show_popup('payment-transaction', {
|
||||
transaction: def
|
||||
});
|
||||
def.notify({
|
||||
message: _t('Handling transaction...'),
|
||||
});
|
||||
}
|
||||
|
||||
rpc.query({
|
||||
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: _t("Please setup your Vantiv merchant account.")
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (data === "internal error") {
|
||||
def.resolve({
|
||||
message: _t("Odoo error while processing transaction.")
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var response = self.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.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();
|
||||
|
||||
self.order_changes();
|
||||
self.reset_input();
|
||||
self.render_paymentlines();
|
||||
order.trigger('change', order); // needed so that export_to_JSON gets triggered
|
||||
|
||||
if (response.message === "PARTIAL AP") {
|
||||
def.resolve({
|
||||
message: _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: function () {
|
||||
return;
|
||||
},
|
||||
|
||||
credit_code_action: function (parsed_result) {
|
||||
var self = this;
|
||||
var online_payment_methods = this.pos.getOnlinePaymentMethods();
|
||||
|
||||
if (online_payment_methods.length === 1) {
|
||||
parsed_result.payment_method_id = online_payment_methods[0].item;
|
||||
self.credit_code_transaction(parsed_result);
|
||||
} else { // this is for supporting another payment system like mercury
|
||||
this.gui.show_popup('selection',{
|
||||
title: _t('Pay with: '),
|
||||
list: online_payment_methods,
|
||||
confirm: function (item) {
|
||||
parsed_result.payment_method_id = item;
|
||||
self.credit_code_transaction(parsed_result);
|
||||
},
|
||||
cancel: self.credit_code_cancel,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
remove_paymentline_by_ref: function (line) {
|
||||
this.pos.get_order().remove_paymentline(line);
|
||||
this.reset_input();
|
||||
this.render_paymentlines();
|
||||
},
|
||||
|
||||
do_reversal: function (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.gui.show_popup('payment-transaction', {
|
||||
transaction: def
|
||||
});
|
||||
|
||||
var request_data = _.extend({
|
||||
'transaction_type': 'Credit',
|
||||
'transaction_code': 'VoidSaleByRecordNo',
|
||||
}, line.mercury_data);
|
||||
|
||||
var message = "";
|
||||
var rpc_method = "";
|
||||
|
||||
if (is_voidsale) {
|
||||
message = _t("Reversal failed, sending VoidSale...");
|
||||
rpc_method = "do_voidsale";
|
||||
} else {
|
||||
message = _t("Sending reversal...");
|
||||
rpc_method = "do_reversal";
|
||||
}
|
||||
|
||||
if (! old_deferred) {
|
||||
def.notify({
|
||||
message: message,
|
||||
});
|
||||
}
|
||||
|
||||
rpc.query({
|
||||
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: _t("Odoo error while processing transaction.")
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var response = self.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: _t("Reversal succeeded"),
|
||||
});
|
||||
|
||||
self.remove_paymentline_by_ref(line);
|
||||
}
|
||||
} else { // voidsale ended, nothing more we can do
|
||||
if (response.status === 'Approved') {
|
||||
def.resolve({
|
||||
message: _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]);
|
||||
});
|
||||
},
|
||||
|
||||
click_delete_paymentline: function (cid) {
|
||||
var lines = this.pos.get_order().get_paymentlines();
|
||||
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (lines[i].cid === cid && lines[i].mercury_data) {
|
||||
this.do_reversal(lines[i], false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this._super(cid);
|
||||
},
|
||||
|
||||
// make sure there is only one paymentline waiting for a swipe
|
||||
click_paymentmethods: function (id) {
|
||||
var order = this.pos.get_order();
|
||||
var payment_method = this.pos.payment_methods_by_id[id];
|
||||
var res = this._super(id);
|
||||
if (res && payment_method.pos_mercury_config_id) {
|
||||
order.selected_paymentline.mercury_swipe_pending = true;
|
||||
this.render_paymentlines();
|
||||
order.trigger('change', order); // needed so that export_to_JSON gets triggered
|
||||
}
|
||||
},
|
||||
|
||||
show: function () {
|
||||
this._super();
|
||||
if (this.pos.getOnlinePaymentMethods().length !== 0) {
|
||||
this.pos.barcode_reader.set_action_callback('credit', _.bind(this.credit_code_action, this));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -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='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>
|
||||
@@ -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('col-name')]//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>
|
||||
@@ -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>
|
||||
@@ -1,72 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<templates id="template" inherit_id="point_of_sale.template">
|
||||
<t t-name="PaymentTransactionPopupWidget">
|
||||
<div role="dialog" class="modal-dialog">
|
||||
<div class="popup">
|
||||
<p class="title">Online Payment</p>
|
||||
<p class="body"></p>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-extend="PaymentScreen-Paymentlines">
|
||||
<t t-jquery=".col-name" t-operation="inner">
|
||||
<t t-if="! line.payment_method.is_cash_count">
|
||||
<t t-if="line.mercury_swipe_pending">
|
||||
<div>WAITING FOR SWIPE</div>
|
||||
</t>
|
||||
<t t-if="! line.mercury_swipe_pending">
|
||||
<t t-esc='line.payment_method.name' />
|
||||
</t>
|
||||
</t>
|
||||
<t t-if="line.payment_method.is_cash_count">
|
||||
<t t-esc='line.payment_method.name' />
|
||||
</t>
|
||||
</t>
|
||||
<!-- if there is payment status, then it is terminal paymentline, so payment-terminal class should be added. -->
|
||||
<t t-jquery=".body div[t-attf-class*='paymentline selected']">
|
||||
this.removeAttr('class');
|
||||
this.attr('t-attf-class', 'paymentline selected#{(line.get_payment_status() ? \' payment-terminal\' : \'\') + (line.mercury_swipe_pending ? \' o_pos_mercury_swipe_pending\' : \'\')}');
|
||||
</t>
|
||||
<t t-jquery=".body .paymentline[t-att-data-cid*='line.cid']">
|
||||
this.removeAttr('class');
|
||||
this.attr('t-attf-class', 'paymentline #{line.mercury_swipe_pending ? \'o_pos_mercury_swipe_pending\' : \'\'}');
|
||||
</t>
|
||||
<t t-jquery=".body .col-tendered.edit">
|
||||
this.removeAttr('class');
|
||||
this.attr('t-attf-class', 'col-tendered edit #{line.mercury_swipe_pending ? \'o_pos_mercury_swipe_pending\' : \'\'}');
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-name="PosMercurySignature">
|
||||
<t t-foreach="paymentlines" t-as="paymentline">
|
||||
<t t-if="paymentline.mercury_data && ! printed_signature">
|
||||
<br />
|
||||
<div>CARDHOLDER WILL PAY CARD ISSUER</div>
|
||||
<div>ABOVE AMOUNT PURSUANT</div>
|
||||
<div>TO CARDHOLDER AGREEMENT</div>
|
||||
<br />
|
||||
<br />
|
||||
<div>X______________________________</div>
|
||||
<t t-set="printed_signature" t-value="true"/>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-extend="OrderReceipt">
|
||||
<t t-jquery="t[t-foreach*='paymentlines'][t-as*='line']" t-operation="append">
|
||||
<t t-if="line.mercury_data">
|
||||
<div class="pos-receipt-left-padding">
|
||||
APPROVAL CODE: <t t-esc="line.mercury_auth_code"/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
<t t-jquery="receipt" t-operation="append">
|
||||
<div>
|
||||
<t t-call="PosMercurySignature"/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -5,6 +5,11 @@
|
||||
<template id="assets" inherit_id="point_of_sale.assets">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/pos_mercury/static/src/js/pos_mercury.js"></script>
|
||||
<script type="text/javascript" src="/pos_mercury/static/src/js/OrderReceipt.js"></script>
|
||||
<script type="text/javascript" src="/pos_mercury/static/src/js/PaymentScreen.js"></script>
|
||||
<script type="text/javascript" src="/pos_mercury/static/src/js/PaymentScreenPaymentLines.js"></script>
|
||||
<script type="text/javascript" src="/pos_mercury/static/src/js/PaymentTransactionPopup.js"></script>
|
||||
<script type="text/javascript" src="/pos_mercury/static/src/js/ProductScreen.js"></script>
|
||||
<link rel="stylesheet" href="/pos_mercury/static/src/css/pos_mercury.css"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user