fix: show batching errors on admin

fix: funding page should take into account batching transactions
This commit is contained in:
Sérgio Salgado 2022-02-03 20:54:48 +00:00
parent c838839522
commit 63a86ae8db
8 changed files with 31 additions and 15 deletions

View file

@ -47,6 +47,7 @@ const typeDef = gql`
txCustomerPhotoAt: Date txCustomerPhotoAt: Date
batched: Boolean batched: Boolean
batchTime: Date batchTime: Date
batchError: String
walletScore: Int walletScore: Int
} }

View file

@ -4,6 +4,7 @@ const settingsLoader = require('../../new-settings-loader')
const configManager = require('../../new-config-manager') const configManager = require('../../new-config-manager')
const wallet = require('../../wallet') const wallet = require('../../wallet')
const ticker = require('../../ticker') const ticker = require('../../ticker')
const txBatching = require('../../tx-batching')
const { utils: coinUtils } = require('lamassu-coins') const { utils: coinUtils } = require('lamassu-coins')
function computeCrypto (cryptoCode, _balance) { function computeCrypto (cryptoCode, _balance) {
@ -23,16 +24,17 @@ function computeFiat (rate, cryptoCode, _balance) {
function getSingleCoinFunding (settings, fiatCode, cryptoCode) { function getSingleCoinFunding (settings, fiatCode, cryptoCode) {
const promises = [ const promises = [
wallet.newFunding(settings, cryptoCode), wallet.newFunding(settings, cryptoCode),
ticker.getRates(settings, fiatCode, cryptoCode) ticker.getRates(settings, fiatCode, cryptoCode),
txBatching.getOpenBatchCryptoValue(cryptoCode)
] ]
return Promise.all(promises) return Promise.all(promises)
.then(([fundingRec, ratesRec]) => { .then(([fundingRec, ratesRec, batchRec]) => {
const rates = ratesRec.rates const rates = ratesRec.rates
const rate = (rates.ask.plus(rates.bid)).div(2) const rate = (rates.ask.plus(rates.bid)).div(2)
const fundingConfirmedBalance = fundingRec.fundingConfirmedBalance const fundingConfirmedBalance = fundingRec.fundingConfirmedBalance
const fiatConfirmedBalance = computeFiat(rate, cryptoCode, fundingConfirmedBalance) const fiatConfirmedBalance = computeFiat(rate, cryptoCode, fundingConfirmedBalance)
const pending = fundingRec.fundingPendingBalance const pending = fundingRec.fundingPendingBalance.minus(batchRec)
const fiatPending = computeFiat(rate, cryptoCode, pending) const fiatPending = computeFiat(rate, cryptoCode, pending)
const fundingAddress = fundingRec.fundingAddress const fundingAddress = fundingRec.fundingAddress
const fundingAddressUrl = coinUtils.buildUrl(cryptoCode, fundingAddress) const fundingAddressUrl = coinUtils.buildUrl(cryptoCode, fundingAddress)

View file

@ -54,10 +54,12 @@ function batch (
c.id_card_photo_path AS customer_id_card_photo_path, c.id_card_photo_path AS customer_id_card_photo_path,
txs.tx_customer_photo_at AS tx_customer_photo_at, txs.tx_customer_photo_at AS tx_customer_photo_at,
txs.tx_customer_photo_path AS tx_customer_photo_path, txs.tx_customer_photo_path AS tx_customer_photo_path,
((NOT txs.send_confirmed) AND (txs.created <= now() - interval $1)) AS expired ((NOT txs.send_confirmed) AND (txs.created <= now() - interval $1)) AS expired,
tb.error_message AS batch_error
FROM (SELECT *, ${cashInTx.TRANSACTION_STATES} AS txStatus FROM cash_in_txs) AS txs FROM (SELECT *, ${cashInTx.TRANSACTION_STATES} AS txStatus FROM cash_in_txs) AS txs
LEFT OUTER JOIN customers c ON txs.customer_id = c.id LEFT OUTER JOIN customers c ON txs.customer_id = c.id
LEFT JOIN devices d ON txs.device_id = d.device_id LEFT JOIN devices d ON txs.device_id = d.device_id
LEFT OUTER JOIN transaction_batches tb ON txs.batch_id = tb.id
WHERE txs.created >= $2 AND txs.created <= $3 ${ WHERE txs.created >= $2 AND txs.created <= $3 ${
id !== null ? `AND txs.device_id = $6` : `` id !== null ? `AND txs.device_id = $6` : ``
} }
@ -69,7 +71,7 @@ function batch (
AND ($12 is null or txs.to_address = $12) AND ($12 is null or txs.to_address = $12)
AND ($13 is null or txs.txStatus = $13) AND ($13 is null or txs.txStatus = $13)
${excludeTestingCustomers ? `AND c.is_test_customer is false` : ``} ${excludeTestingCustomers ? `AND c.is_test_customer is false` : ``}
AND (error IS NOT null OR fiat > 0) AND (error IS NOT null OR tb.error_message IS NOT null OR fiat > 0)
ORDER BY created DESC limit $4 offset $5` ORDER BY created DESC limit $4 offset $5`
const cashOutSql = `SELECT 'cashOut' AS tx_class, const cashOutSql = `SELECT 'cashOut' AS tx_class,
@ -151,6 +153,7 @@ const getCashOutStatus = it => {
const getCashInStatus = it => { const getCashInStatus = it => {
if (it.operatorCompleted) return 'Cancelled' if (it.operatorCompleted) return 'Cancelled'
if (it.hasError) return 'Error' if (it.hasError) return 'Error'
if (it.batchError) return 'Error'
if (it.sendConfirmed) return 'Sent' if (it.sendConfirmed) return 'Sent'
if (it.expired) return 'Expired' if (it.expired) return 'Expired'
return 'Pending' return 'Pending'
@ -176,9 +179,11 @@ function getCustomerTransactionsBatch (ids) {
c.name AS customer_name, c.name AS customer_name,
c.front_camera_path AS customer_front_camera_path, c.front_camera_path AS customer_front_camera_path,
c.id_card_photo_path AS customer_id_card_photo_path, c.id_card_photo_path AS customer_id_card_photo_path,
((NOT txs.send_confirmed) AND (txs.created <= now() - interval $2)) AS expired ((NOT txs.send_confirmed) AND (txs.created <= now() - interval $2)) AS expired,
tb.error_message AS batch_error
FROM cash_in_txs AS txs FROM cash_in_txs AS txs
LEFT OUTER JOIN customers c ON txs.customer_id = c.id LEFT OUTER JOIN customers c ON txs.customer_id = c.id
LEFT OUTER JOIN transaction_batches tb ON txs.batch_id = tb.id
WHERE c.id IN ($1^) WHERE c.id IN ($1^)
ORDER BY created DESC limit $3` ORDER BY created DESC limit $3`
@ -220,9 +225,11 @@ function single (txId) {
c.name AS customer_name, c.name AS customer_name,
c.front_camera_path AS customer_front_camera_path, c.front_camera_path AS customer_front_camera_path,
c.id_card_photo_path AS customer_id_card_photo_path, c.id_card_photo_path AS customer_id_card_photo_path,
((NOT txs.send_confirmed) AND (txs.created <= now() - interval $1)) AS expired ((NOT txs.send_confirmed) AND (txs.created <= now() - interval $1)) AS expired,
tb.error_message AS batch_error
FROM cash_in_txs AS txs FROM cash_in_txs AS txs
LEFT OUTER JOIN customers c ON txs.customer_id = c.id LEFT OUTER JOIN customers c ON txs.customer_id = c.id
LEFT OUTER JOIN transaction_batches tb ON txs.batch_id = tb.id
WHERE id=$2` WHERE id=$2`
const cashOutSql = `SELECT 'cashOut' AS tx_class, const cashOutSql = `SELECT 'cashOut' AS tx_class,

View file

@ -48,8 +48,7 @@ const lastBalance = {}
function _balance (settings, cryptoCode) { function _balance (settings, cryptoCode) {
return fetchWallet(settings, cryptoCode) return fetchWallet(settings, cryptoCode)
.then(r => r.wallet.balance(r.account, cryptoCode, settings, r.operatorId)) .then(r => r.wallet.balance(r.account, cryptoCode, settings, r.operatorId))
.then(balance => Promise.all([balance, supportsBatching(settings, cryptoCode)])) .then(balance => Promise.all([balance, getOpenBatchCryptoValue(cryptoCode)]))
.then(([balance, supportsBatching]) => Promise.all([balance, supportsBatching ? getOpenBatchCryptoValue(cryptoCode) : Promise.resolve(BN(0))]))
.then(([balance, reservedBalance]) => ({ balance: balance.minus(reservedBalance), reservedBalance, timestamp: Date.now() })) .then(([balance, reservedBalance]) => ({ balance: balance.minus(reservedBalance), reservedBalance, timestamp: Date.now() }))
.then(r => { .then(r => {
lastBalance[cryptoCode] = r lastBalance[cryptoCode] = r
@ -82,7 +81,7 @@ function sendCoins (settings, tx) {
function sendCoinsBatch (settings, txs, cryptoCode) { function sendCoinsBatch (settings, txs, cryptoCode) {
return fetchWallet(settings, cryptoCode) return fetchWallet(settings, cryptoCode)
.then(r => { .then(r => {
const feeMultiplier = settings[`wallets_${tx.cryptoCode}_feeMultiplier`] const feeMultiplier = settings[`wallets_${cryptoCode}_feeMultiplier`]
return r.wallet.sendCoinsBatch(r.account, txs, cryptoCode, feeMultiplier) return r.wallet.sendCoinsBatch(r.account, txs, cryptoCode, feeMultiplier)
.then(res => { .then(res => {
mem.clear(module.exports.balance) mem.clear(module.exports.balance)

View file

@ -61,9 +61,9 @@ const Row = ({
expandable && expandRow(id, data) expandable && expandRow(id, data)
onClick && onClick(data) onClick && onClick(data)
}} }}
error={data.error || data.hasError} error={data.error || data.hasError || data.batchError}
shouldShowError={false} shouldShowError={false}
errorMessage={data.errorMessage || data.hasError}> errorMessage={data.errorMessage || data.hasError || data.batchError}>
{elements.map(({ view = it => it?.toString(), ...props }, idx) => ( {elements.map(({ view = it => it?.toString(), ...props }, idx) => (
<Td key={idx} {...props}> <Td key={idx} {...props}>
{view(data)} {view(data)}

View file

@ -419,5 +419,6 @@ export default memo(
(prev, next) => (prev, next) =>
prev.it.id === next.it.id && prev.it.id === next.it.id &&
prev.it.hasError === next.it.hasError && prev.it.hasError === next.it.hasError &&
prev.it.batchError === next.it.batchError &&
getStatus(prev.it) === getStatus(next.it) getStatus(prev.it) === getStatus(next.it)
) )

View file

@ -116,6 +116,7 @@ const GET_TRANSACTIONS = gql`
isAnonymous isAnonymous
batched batched
batchTime batchTime
batchError
walletScore walletScore
} }
} }
@ -190,7 +191,7 @@ const Transactions = () => {
<div className={classes.overflowTd}>{getCustomerDisplayName(it)}</div> <div className={classes.overflowTd}>{getCustomerDisplayName(it)}</div>
{!it.isAnonymous && ( {!it.isAnonymous && (
<div onClick={() => redirect(it.customerId)}> <div onClick={() => redirect(it.customerId)}>
{it.hasError ? ( {it.hasError || it.batchError ? (
<CustomerLinkWhiteIcon className={classes.customerLinkIcon} /> <CustomerLinkWhiteIcon className={classes.customerLinkIcon} />
) : ( ) : (
<CustomerLinkIcon className={classes.customerLinkIcon} /> <CustomerLinkIcon className={classes.customerLinkIcon} />

View file

@ -1,3 +1,5 @@
import * as R from 'ramda'
const getCashOutStatus = it => { const getCashOutStatus = it => {
if (it.hasError === 'Operator cancel') return 'Cancelled' if (it.hasError === 'Operator cancel') return 'Cancelled'
if (it.hasError) return 'Error' if (it.hasError) return 'Error'
@ -8,7 +10,7 @@ const getCashOutStatus = it => {
const getCashInStatus = it => { const getCashInStatus = it => {
if (it.operatorCompleted) return 'Cancelled' if (it.operatorCompleted) return 'Cancelled'
if (it.hasError) return 'Error' if (it.hasError || it.batchError) return 'Error'
if (it.sendConfirmed) return 'Sent' if (it.sendConfirmed) return 'Sent'
if (it.expired) return 'Expired' if (it.expired) return 'Expired'
if (it.batched) return 'Batched' if (it.batched) return 'Batched'
@ -23,11 +25,14 @@ const getStatus = it => {
} }
const getStatusDetails = it => { const getStatusDetails = it => {
return it.hasError ? it.hasError : null if (!R.isNil(it.hasError)) return it.hasError
if (!R.isNil(it.batchError)) return `Batch error: ${it.batchError}`
return null
} }
const getStatusProperties = status => ({ const getStatusProperties = status => ({
hasError: status === 'Error' || null, hasError: status === 'Error' || null,
batchError: status === 'Error' || null,
dispense: status === 'Success' || null, dispense: status === 'Success' || null,
expired: status === 'Expired' || null, expired: status === 'Expired' || null,
operatorCompleted: status === 'Cancelled' || null, operatorCompleted: status === 'Cancelled' || null,