fix: multiple fixes related with recyclers/stackers

feat: add bill destination unit for cash-in txs
feat: l-m communication regarding cash unit state
This commit is contained in:
Sérgio Salgado 2023-04-20 17:58:50 +01:00
parent 2638bd1717
commit 2967ad3a75
17 changed files with 573 additions and 102 deletions

View file

@ -27,7 +27,7 @@ const loyalty = require('./loyalty')
const transactionBatching = require('./tx-batching')
const state = require('./middlewares/state')
const { CASSETTE_MAX_CAPACITY, CASH_OUT_DISPENSE_READY, CONFIRMATION_CODE } = require('./constants')
const { CASH_UNIT_CAPACITY, CASH_OUT_DISPENSE_READY, CONFIRMATION_CODE } = require('./constants')
const notifier = require('./notifier')
@ -147,27 +147,63 @@ function plugins (settings, deviceId) {
return computedCassettes
}
function computeAvailableStackers (stackers, redeemableTxs) {
if (_.isEmpty(redeemableTxs)) return stackers
const sumTxs = (sum, tx) => {
// cash-out-helper sends 0 as fallback value, need to filter it out as there are no '0' denominations
const bills = _.filter(it => it.denomination > 0, tx.bills)
const sameDenominations = a => a[0]?.denomination === a[1]?.denomination
const doDenominationsMatch = _.every(sameDenominations, _.zip(stackers, bills))
if (!doDenominationsMatch) {
throw new Error('Denominations don\'t add up, stackers were changed.')
}
return _.map(r => r[0] + r[1].provisioned, _.zip(sum, tx.bills))
}
const provisioned = _.reduce(sumTxs, _.times(_.constant(0), _.size(stackers)), redeemableTxs)
const zipped = _.zip(_.map('count', stackers), provisioned)
const counts = _.map(r => r[0] - r[1], zipped)
if (_.some(_.lt(_, 0), counts)) {
throw new Error('Negative note count: %j', counts)
}
const computedStackers = []
_.forEach(it => {
computedStackers.push({
denomination: stackers[it].denomination,
count: counts[it]
})
}, _.times(_.identity(), _.size(stackers)))
return computedStackers
}
function buildAvailableCassettes (excludeTxId) {
const cashOutConfig = configManager.getCashOut(deviceId, settings.config)
if (!cashOutConfig.active) return Promise.resolve()
return Promise.all([dbm.cassetteCounts(deviceId), cashOutHelper.redeemableTxs(deviceId, excludeTxId)])
.then(([rec, _redeemableTxs]) => {
.then(([_cassettes, _redeemableTxs]) => {
const redeemableTxs = _.reject(_.matchesProperty('id', excludeTxId), _redeemableTxs)
const denominations = []
_.forEach(it => {
denominations.push(cashOutConfig[`cassette${it + 1}`])
}, _.times(_.identity(), rec.numberOfCassettes))
}, _.times(_.identity(), _cassettes.numberOfCassettes))
const virtualCassettes = [Math.max(...denominations) * 2]
const counts = argv.cassettes
? argv.cassettes.split(',')
: rec.counts
: _cassettes.counts
if (rec.counts.length !== denominations.length) {
if (_cassettes.counts.length !== denominations.length) {
throw new Error('Denominations and respective counts do not match!')
}
@ -177,7 +213,7 @@ function plugins (settings, deviceId) {
denomination: parseInt(denominations[it], 10),
count: parseInt(counts[it], 10)
})
}, _.times(_.identity(), rec.numberOfCassettes))
}, _.times(_.identity(), _cassettes.numberOfCassettes))
try {
return {
@ -194,6 +230,51 @@ function plugins (settings, deviceId) {
})
}
function buildAvailableStackers (excludeTxId) {
const cashOutConfig = configManager.getCashOut(deviceId, settings.config)
if (!cashOutConfig.active) return Promise.resolve()
return Promise.all([dbm.stackerCounts(deviceId), cashOutHelper.redeemableTxs(deviceId, excludeTxId)])
.then(([_stackers, _redeemableTxs]) => {
const redeemableTxs = _.reject(_.matchesProperty('id', excludeTxId), _redeemableTxs)
const denominations = []
_.forEach(it => {
denominations.push(cashOutConfig[`stacker${it + 1}f`], cashOutConfig[`stacker${it + 1}r`])
}, _.times(_.identity(), _stackers.numberOfStackers))
const virtualStackers = [Math.max(...denominations) * 2]
const counts = _stackers.counts
if (counts.length !== denominations.length) {
throw new Error('Denominations and respective counts do not match!')
}
const stackers = []
_.forEach(it => {
stackers.push({
denomination: parseInt(denominations[it], 10),
count: parseInt(counts[it], 10)
})
}, _.times(_.identity(), _stackers.numberOfStackers * 2))
try {
return {
stackers: computeAvailableStackers(stackers, redeemableTxs),
virtualStackers
}
} catch (err) {
logger.error(err)
return {
stackers,
virtualStackers
}
}
})
}
function fetchCurrentConfigVersion () {
const sql = `select id from user_config
where type=$1
@ -240,6 +321,7 @@ function plugins (settings, deviceId) {
return Promise.all([
buildAvailableCassettes(),
buildAvailableStackers(),
fetchCurrentConfigVersion(),
millisecondsToMinutes(getTimezoneOffset(localeConfig.timezone)),
loyalty.getNumberOfAvailablePromoCodes(),
@ -250,6 +332,7 @@ function plugins (settings, deviceId) {
])
.then(([
cassettes,
stackers,
configVersion,
timezone,
numberOfAvailablePromoCodes,
@ -273,6 +356,7 @@ function plugins (settings, deviceId) {
return {
cassettes,
stackers,
rates: buildRates(tickers),
balances: buildBalances(balances),
coins,
@ -652,7 +736,10 @@ function plugins (settings, deviceId) {
const denomination3f = cashOutConfig.stacker3f
const denomination3r = cashOutConfig.stacker3r
const cashOutEnabled = cashOutConfig.active
const isCassetteLow = (have, max, limit) => cashOutEnabled && ((have / max) * 100) < limit
const isUnitLow = (have, max, limit) => cashOutEnabled && ((have / max) * 100) < limit
// const isUnitHigh = (have, max, limit) => cashOutEnabled && ((have / max) * 100) > limit
// const isUnitOutOfBounds = (have, max, lowerBound, upperBound) => isUnitLow(have, max, lowerBound) || isUnitHigh(have, max, upperBound)
const notifications = configManager.getNotifications(null, device.deviceId, settings.config)
@ -667,7 +754,7 @@ function plugins (settings, deviceId) {
}
: null
const cassette1Alert = device.numberOfCassettes >= 1 && isCassetteLow(device.cashUnits.cassette1, CASSETTE_MAX_CAPACITY, notifications.fillingPercentageCassette1)
const cassette1Alert = device.numberOfCassettes >= 1 && isUnitLow(device.cashUnits.cassette1, CASH_UNIT_CAPACITY[device.model]['cassette'], notifications.fillingPercentageCassette1)
? {
code: 'LOW_CASH_OUT',
cassette: 1,
@ -679,7 +766,7 @@ function plugins (settings, deviceId) {
}
: null
const cassette2Alert = device.numberOfCassettes >= 2 && isCassetteLow(device.cashUnits.cassette2, CASSETTE_MAX_CAPACITY, notifications.fillingPercentageCassette2)
const cassette2Alert = device.numberOfCassettes >= 2 && isUnitLow(device.cashUnits.cassette2, CASH_UNIT_CAPACITY[device.model]['cassette'], notifications.fillingPercentageCassette2)
? {
code: 'LOW_CASH_OUT',
cassette: 2,
@ -691,7 +778,7 @@ function plugins (settings, deviceId) {
}
: null
const cassette3Alert = device.numberOfCassettes >= 3 && isCassetteLow(device.cashUnits.cassette3, CASSETTE_MAX_CAPACITY, notifications.fillingPercentageCassette3)
const cassette3Alert = device.numberOfCassettes >= 3 && isUnitLow(device.cashUnits.cassette3, CASH_UNIT_CAPACITY[device.model]['cassette'], notifications.fillingPercentageCassette3)
? {
code: 'LOW_CASH_OUT',
cassette: 3,
@ -703,7 +790,7 @@ function plugins (settings, deviceId) {
}
: null
const cassette4Alert = device.numberOfCassettes >= 4 && isCassetteLow(device.cashUnits.cassette4, CASSETTE_MAX_CAPACITY, notifications.fillingPercentageCassette4)
const cassette4Alert = device.numberOfCassettes >= 4 && isUnitLow(device.cashUnits.cassette4, CASH_UNIT_CAPACITY[device.model]['cassette'], notifications.fillingPercentageCassette4)
? {
code: 'LOW_CASH_OUT',
cassette: 4,
@ -715,9 +802,9 @@ function plugins (settings, deviceId) {
}
: null
const stacker1fAlert = device.numberOfStackers >= 1 && isCassetteLow(device.cashUnits.stacker1f, CASSETTE_MAX_CAPACITY, notifications.fillingPercentageStacker1f)
const stacker1fAlert = device.numberOfStackers >= 1 && isUnitLow(device.cashUnits.stacker1f, CASH_UNIT_CAPACITY[device.model]['stacker'], notifications.fillingPercentageStacker1f)
? {
code: 'LOW_CASH_OUT',
code: 'LOW_RECYCLER_STACKER',
cassette: 4,
machineName,
deviceId: device.deviceId,
@ -727,9 +814,9 @@ function plugins (settings, deviceId) {
}
: null
const stacker1rAlert = device.numberOfStackers >= 1 && isCassetteLow(device.cashUnits.stacker1r, CASSETTE_MAX_CAPACITY, notifications.fillingPercentageStacker1r)
const stacker1rAlert = device.numberOfStackers >= 1 && isUnitLow(device.cashUnits.stacker1r, CASH_UNIT_CAPACITY[device.model]['stacker'], notifications.fillingPercentageStacker1r)
? {
code: 'LOW_CASH_OUT',
code: 'LOW_RECYCLER_STACKER',
cassette: 4,
machineName,
deviceId: device.deviceId,
@ -739,21 +826,21 @@ function plugins (settings, deviceId) {
}
: null
const stacker2fAlert = device.numberOfStackers >= 2 && isCassetteLow(device.cashUnits.stacker2f, CASSETTE_MAX_CAPACITY, notifications.fillingPercentageStacker2f)
const stacker2fAlert = device.numberOfStackers >= 2 && isUnitLow(device.cashUnits.stacker2f, CASH_UNIT_CAPACITY[device.model]['stacker'], notifications.fillingPercentageStacker2f)
? {
code: 'LOW_CASH_OUT',
code: 'LOW_RECYCLER_STACKER',
cassette: 4,
machineName,
deviceId: device.deviceId,
notes: device.cashUnits.stacker1f,
denomination: denomination1f,
notes: device.cashUnits.stacker2f,
denomination: denomination2f,
fiatCode
}
: null
const stacker2rAlert = device.numberOfStackers >= 2 && isCassetteLow(device.cashUnits.stacker2r, CASSETTE_MAX_CAPACITY, notifications.fillingPercentageStacker2r)
const stacker2rAlert = device.numberOfStackers >= 2 && isUnitLow(device.cashUnits.stacker2r, CASH_UNIT_CAPACITY[device.model]['stacker'], notifications.fillingPercentageStacker2r)
? {
code: 'LOW_CASH_OUT',
code: 'LOW_RECYCLER_STACKER',
cassette: 4,
machineName,
deviceId: device.deviceId,
@ -763,9 +850,9 @@ function plugins (settings, deviceId) {
}
: null
const stacker3fAlert = device.numberOfStackers >= 3 && isCassetteLow(device.cashUnits.stacker3f, CASSETTE_MAX_CAPACITY, notifications.fillingPercentageStacker3f)
const stacker3fAlert = device.numberOfStackers >= 3 && isUnitLow(device.cashUnits.stacker3f, CASH_UNIT_CAPACITY[device.model]['stacker'], notifications.fillingPercentageStacker3f)
? {
code: 'LOW_CASH_OUT',
code: 'LOW_RECYCLER_STACKER',
cassette: 4,
machineName,
deviceId: device.deviceId,
@ -775,9 +862,9 @@ function plugins (settings, deviceId) {
}
: null
const stacker3rAlert = device.numberOfStackers >= 3 && isCassetteLow(device.cashUnits.stacker3r, CASSETTE_MAX_CAPACITY, notifications.fillingPercentageStacker3r)
const stacker3rAlert = device.numberOfStackers >= 3 && isUnitLow(device.cashUnits.stacker3r, CASH_UNIT_CAPACITY[device.model]['stacker'], notifications.fillingPercentageStacker3r)
? {
code: 'LOW_CASH_OUT',
code: 'LOW_RECYCLER_STACKER',
cassette: 4,
machineName,
deviceId: device.deviceId,