refactor: use recyclers instead of stackers

This commit is contained in:
Rafael Taranto 2023-10-09 14:41:33 +01:00
parent dc52cf4414
commit 21b88f182e
37 changed files with 2868 additions and 9599 deletions

View file

@ -0,0 +1,36 @@
#!/usr/bin/env node
require('../lib/environment-helper')
const _ = require('lodash')
const db = require('../lib/db')
if (process.argv.length !== 4) {
console.log('Usage: lamassu-update-recyclers <device_id> <number_of_recyclers>')
process.exit(1)
}
if (!_.isFinite(parseInt(process.argv[3]))) {
console.log('Error: <number_of_recyclers> is not a valid number (%s)', err)
process.exit(3)
}
if (parseInt(process.argv[3]) > 6 || parseInt(process.argv[3]) < 1) {
console.log('Error: <number_of_recyclers> is out of range. Should be a number between 1 and 3')
process.exit(3)
}
const deviceId = process.argv[2]
const numberOfRecyclers = parseInt(process.argv[3])
const query = `UPDATE devices SET number_of_recyclers = $1 WHERE device_id = $2`
db.none(query, [numberOfRecyclers, deviceId])
.then(() => {
console.log('Success! Device %s updated to %s recyclers', deviceId, numberOfRecyclers)
process.exit(0)
})
.catch(err => {
console.log('Error: %s', err)
process.exit(3)
})

View file

@ -1,36 +0,0 @@
#!/usr/bin/env node
require('../lib/environment-helper')
const _ = require('lodash')
const db = require('../lib/db')
if (process.argv.length !== 4) {
console.log('Usage: lamassu-update-stackers <device_id> <number_of_stackers>')
process.exit(1)
}
if (!_.isFinite(parseInt(process.argv[3]))) {
console.log('Error: <number_of_stackers> is not a valid number (%s)', err)
process.exit(3)
}
if (parseInt(process.argv[3]) > 3 || parseInt(process.argv[3]) < 1) {
console.log('Error: <number_of_stackers> is out of range. Should be a number between 1 and 3')
process.exit(3)
}
const deviceId = process.argv[2]
const numberOfStackers = parseInt(process.argv[3])
const query = `UPDATE devices SET number_of_stackers = $1 WHERE device_id = $2`
db.none(query, [numberOfStackers, deviceId])
.then(() => {
console.log('Success! Device %s updated to %s stackers', deviceId, numberOfStackers)
process.exit(0)
})
.catch(err => {
console.log('Error: %s', err)
process.exit(3)
})

View file

@ -50,17 +50,17 @@ function insertNewBills (t, billRows, machineTx) {
const columns = ['id', 'fiat', 'fiat_code', 'crypto_code', 'cash_in_fee', 'cash_in_txs_id', 'device_time', 'destination_unit']
const sql = pgp.helpers.insert(dbBills, columns, 'bills')
const deviceID = machineTx.deviceId
const sql2 = `update devices set stacker1f = stacker1f + $2, stacker1r = stacker1r + $3, stacker2f = stacker2f + $4, stacker2r = stacker2r + $5, stacker3f = stacker3f + $6, stacker3r = stacker3r + $7
const sql2 = `update devices set recycler1 = recycler1 + $2, recycler2 = recycler2 + $3, recycler3 = recycler3 + $4, recycler4 = recycler4 + $5, recycler5 = recycler5 + $6, recycler6 = recycler6 + $7
where device_id = $1`
return t.none(sql2, [
deviceID,
getBillsByDestination('stacker1f').length,
getBillsByDestination('stacker1r').length,
getBillsByDestination('stacker2f').length,
getBillsByDestination('stacker2r').length,
getBillsByDestination('stacker3f').length,
getBillsByDestination('stacker3r').length
getBillsByDestination('recycler1').length,
getBillsByDestination('recycler2').length,
getBillsByDestination('recycler3').length,
getBillsByDestination('recycler4').length,
getBillsByDestination('recycler5').length,
getBillsByDestination('recycler6').length
])
.then(() => {
return t.none(sql)

View file

@ -40,7 +40,7 @@ function mapDispense (tx) {
const res = {}
_.forEach(it => {
const suffix = bills[it].name.replace(/cassette|stacker/gi, '')
const suffix = _.snakeCase(bills[it].name.replace(/cassette/gi, ''))
res[`provisioned_${suffix}`] = bills[it].provisioned
res[`denomination_${suffix}`] = bills[it].denomination
res[`dispensed_${suffix}`] = bills[it].dispensed

View file

@ -18,31 +18,33 @@ case
end`
const MAX_CASSETTES = 4
const MAX_STACKERS = 3
const MAX_RECYCLERS = 6
const BILL_FIELDS = [
'denomination1',
'denomination2',
'denomination3',
'denomination4',
'denomination1f',
'denomination1r',
'denomination2f',
'denomination2r',
'denomination3f',
'denomination3r',
'provisioned1',
'provisioned2',
'provisioned3',
'provisioned4',
'provisioned1f',
'provisioned1r',
'provisioned2f',
'provisioned2r',
'provisioned3f',
'provisioned3r'
const SNAKE_CASE_BILL_FIELDS = [
'denomination_1',
'denomination_2',
'denomination_3',
'denomination_4',
'denomination_recycler_1',
'denomination_recycler_2',
'denomination_recycler_3',
'denomination_recycler_4',
'denomination_recycler_5',
'denomination_recycler_6',
'provisioned_1',
'provisioned_2',
'provisioned_3',
'provisioned_4',
'provisioned_recycler_1',
'provisioned_recycler_2',
'provisioned_recycler_3',
'provisioned_recycler_4',
'provisioned_recycler_5',
'provisioned_recycler_6'
]
const BILL_FIELDS = _.map(_.camelCase, BILL_FIELDS)
module.exports = { redeemableTxs, toObj, toDb, REDEEMABLE_AGE, CASH_OUT_TRANSACTION_STATES }
const mapValuesWithKey = _.mapValues.convert({cap: false})
@ -69,19 +71,17 @@ function convertBigNumFields (obj) {
}
function convertField (key) {
return _.includes('denomination', key) || _.includes('provisioned', key) ? key : _.snakeCase(key)
return _.snakeCase(key)
}
function addDbBills (tx) {
const bills = tx.bills
if (_.isEmpty(bills)) return tx
const billFields = _.map(it => _.replace(/(denomination|provisioned)/g, '$1_')(it), BILL_FIELDS)
const billsObj = _.flow(
_.reduce(
(acc, value) => {
const suffix = value.name.replace(/cassette|stacker/gi, '')
const suffix = _.snakeCase(value.name.replace(/cassette/gi, ''))
return {
...acc,
[`provisioned_${suffix}`]: value.provisioned,
@ -96,7 +96,7 @@ function addDbBills (tx) {
return _.assign({ [value]: 0 })(acc)
},
{}
)(_.difference(billFields, _.keys(it)))
)(_.difference(SNAKE_CASE_BILL_FIELDS, _.keys(it)))
return _.assign(missingKeys, it)
}
)(bills)
@ -118,7 +118,7 @@ function toObj (row) {
let newObj = {}
keys.forEach(key => {
const objKey = key.match(/denomination|provisioned/g) ? key.replace(/_/g, '') : _.camelCase(key)
const objKey = _.camelCase(key)
if (key === 'received_crypto_atoms' && row[key]) {
newObj[objKey] = new BN(row[key])
return
@ -138,16 +138,7 @@ function toObj (row) {
const billFieldsArr = _.concat(
_.map(it => ({ name: `cassette${it + 1}`, denomination: newObj[`denomination${it + 1}`], provisioned: newObj[`provisioned${it + 1}`] }))(_.range(0, MAX_CASSETTES)),
_.reduce(
(acc, value) => {
acc.push(
{ name: `stacker${value + 1}f`, denomination: newObj[`denomination${value + 1}f`], provisioned: newObj[`provisioned${value + 1}f`] },
{ name: `stacker${value + 1}r`, denomination: newObj[`denomination${value + 1}r`], provisioned: newObj[`provisioned${value + 1}r`] }
)
return acc
},
[]
)(_.range(0, MAX_STACKERS))
_.map(it => ({ name: `recycler${it + 1}`, denomination: newObj[`denomination_recycler${it + 1}`], provisioned: newObj[`provisioned_recycler${it + 1}`] }))(_.range(0, MAX_RECYCLERS)),
)
// There can't be bills with denomination === 0.
@ -164,7 +155,9 @@ function redeemableTxs (deviceId) {
and dispense=$3
and (
provisioned_1 is not null or provisioned_2 is not null or provisioned_3 is not null or provisioned_4 is not null or
provisioned_1f is not null or provisioned_1r is not null or provisioned_2f is not null or provisioned_2r is not null or provisioned_3f is not null or provisioned_3r is not null
provisioned_recycler_1 is not null or provisioned_recycler_2 is not null or
provisioned_recycler_3 is not null or provisioned_recycler_4 is not null or
provisioned_recycler_5 is not null or provisioned_recycler_6 is not null
)
and extract(epoch from (now() - greatest(created, confirmed_at))) < $4`

View file

@ -58,7 +58,7 @@ function postProcess (txVector, justAuthorized, pi) {
if ((newTx.dispense && !oldTx.dispense) || (newTx.redeem && !oldTx.redeem)) {
return pi.buildAvailableUnits(newTx.id)
.then(_units => {
const units = _.concat(_units.cassettes, _units.stackers)
const units = _.concat(_units.cassettes, _units.recyclers)
logger.silly('Computing bills to dispense:', {
txId: newTx.id,
units: units,
@ -74,7 +74,7 @@ function postProcess (txVector, justAuthorized, pi) {
const rec = {}
_.forEach(it => {
const suffix = bills[it].name.replace(/cassette|stacker/gi, '')
const suffix = _.snakeCase(bills[it].name.replace(/cassette/gi, ''))
rec[`provisioned_${suffix}`] = bills[it].provisioned
rec[`denomination_${suffix}`] = bills[it].denomination
}, _.times(_.identity(), _.size(bills)))

View file

@ -30,7 +30,8 @@ function createCashboxBatch (deviceId, cashboxCount) {
function updateMachineWithBatch (machineContext, oldCashboxCount) {
const cashUnits = machineContext.cashUnits
const isValidContext = _.has(['deviceId', 'cashUnits'], machineContext) && _.has(['cashbox', 'cassette1', 'cassette2', 'cassette3', 'cassette4', 'stacker1f', 'stacker1r', 'stacker2f', 'stacker2r', 'stacker3f', 'stacker3r'], cashUnits)
const cashUnitNames = ['cashbox', 'cassette1', 'cassette2', 'cassette3', 'cassette4', 'recycler1', 'recycler2', 'recycler3', 'recycler4', 'recycler5', 'recycler6']
const isValidContext = _.has(['deviceId', 'cashUnits'], machineContext) && _.has(cashUnitNames, cashUnits)
const cassettes = _.filter(it => !_.isNil(it))([cashUnits.cassette1, cashUnits.cassette2, cashUnits.cassette3, cashUnits.cassette4])
const isCassetteAmountWithinRange = _.inRange(constants.CASH_OUT_MINIMUM_AMOUNT_OF_CASSETTES, constants.CASH_OUT_MAXIMUM_AMOUNT_OF_CASSETTES + 1, _.size(cassettes))
if (!isValidContext && !isCassetteAmountWithinRange)
@ -48,17 +49,17 @@ function updateMachineWithBatch (machineContext, oldCashboxCount) {
bills.cashbox_batch_id IS NULL`, [batchId, deviceId])
const q3 = t.none(`UPDATE empty_unit_bills SET cashbox_batch_id=$1
WHERE empty_unit_bills.device_id = $2 AND empty_unit_bills.cashbox_batch_id IS NULL`, [batchId, deviceId])
const q4 = t.none(`UPDATE devices SET cassette1=$1, cassette2=$2, cassette3=$3, cassette4=$4, stacker1f=$5, stacker1r=$6, stacker2f=$7, stacker2r=$8, stacker3f=$9, stacker3r=$10 WHERE device_id=$11`, [
const q4 = t.none(`UPDATE devices SET cassette1=$1, cassette2=$2, cassette3=$3, cassette4=$4, recycler1=$5, recycler2=$6, recycler3=$7, recycler3=$8, recycler4=$9, recycler5=$10 WHERE device_id=$11`, [
cashUnits.cassette1,
cashUnits.cassette2,
cashUnits.cassette3,
cashUnits.cassette4,
cashUnits.stacker1f,
cashUnits.stacker1r,
cashUnits.stacker2f,
cashUnits.stacker2r,
cashUnits.stacker3f,
cashUnits.stacker3r,
cashUnits.recycler1,
cashUnits.recycler2,
cashUnits.recycler3,
cashUnits.recycler4,
cashUnits.recycler5,
cashUnits.recycler6,
machineContext.deviceId
])

View file

@ -28,7 +28,7 @@ const CASH_UNIT_CAPACITY = {
},
aveiro: {
cashbox: 1500,
stacker: 60,
recycler: 60,
cassette: 500
},
tejo: {

View file

@ -127,7 +127,7 @@ const staticConfig = ({ currentConfigVersion, deviceId, deviceName, pq, settings
operatorInfo,
receiptInfo,
twoWayMode,
{ numberOfCassettes, numberOfStackers },
{ numberOfCassettes, numberOfRecyclers },
]) =>
(currentConfigVersion && currentConfigVersion >= staticConf.configVersion) ?
null :
@ -142,7 +142,7 @@ const staticConfig = ({ currentConfigVersion, deviceId, deviceName, pq, settings
languages: localeInfo.languages,
fiatCode: localeInfo.fiatCurrency
},
machineInfo: { deviceId, deviceName, numberOfCassettes, numberOfStackers },
machineInfo: { deviceId, deviceName, numberOfCassettes, numberOfRecyclers },
twoWayMode,
speedtestFiles,
urlsToPing,
@ -171,24 +171,24 @@ const dynamicConfig = ({ deviceId, operatorId, pid, pq, settings, }) => {
)(cassettes) :
null
const massageStackers = stackers =>
stackers ?
const massageRecyclers = recyclers =>
recyclers ?
_.flow(
stackers => _.set('physical', _.get('stackers', stackers), stackers),
stackers => _.set('virtual', _.get('virtualStackers', stackers), stackers),
_.unset('stackers'),
_.unset('virtualStackers')
)(stackers) :
recyclers => _.set('physical', _.get('recyclers', recyclers), recyclers),
recyclers => _.set('virtual', _.get('virtualRecyclers', recyclers), recyclers),
_.unset('recyclers'),
_.unset('virtualRecyclers')
)(recyclers) :
null
state.pids = _.update(operatorId, _.set(deviceId, { pid, ts: Date.now() }), state.pids)
const res = _.flow(
_.pick(['areThereAvailablePromoCodes', 'balances', 'cassettes', 'stackers', 'coins', 'rates']),
_.pick(['areThereAvailablePromoCodes', 'balances', 'cassettes', 'recyclers', 'coins', 'rates']),
_.update('cassettes', massageCassettes),
_.update('stackers', massageStackers),
_.update('recyclers', massageRecyclers),
/* [{ cryptoCode, rates }, ...] => [[cryptoCode, rates], ...] */
_.update('coins', _.map(({ cryptoCode, rates }) => [cryptoCode, rates])),
@ -201,10 +201,10 @@ const dynamicConfig = ({ deviceId, operatorId, pid, pq, settings, }) => {
/* Group the separate objects by cryptoCode */
/* { balances, coins, rates } => { cryptoCode: { balance, ask, bid, cashIn, cashOut }, ... } */
({ areThereAvailablePromoCodes, balances, cassettes, stackers, coins, rates }) => ({
({ areThereAvailablePromoCodes, balances, cassettes, recyclers, coins, rates }) => ({
areThereAvailablePromoCodes,
cassettes,
stackers,
recyclers,
coins: _.flow(
_.reduce(
(ret, [cryptoCode, obj]) => _.update(cryptoCode, _.assign(obj), ret),

View file

@ -32,7 +32,7 @@ type MachineInfo {
deviceId: String!
deviceName: String
numberOfCassettes: Int
numberOfStackers: Int
numberOfRecyclers: Int
}
type ReceiptInfo {
@ -173,7 +173,7 @@ type PhysicalCassette {
count: Int!
}
type PhysicalStacker {
type PhysicalRecycler {
name: String!
number: Int!
denomination: Int!
@ -185,15 +185,15 @@ type Cassettes {
virtual: [Int!]!
}
type Stackers {
physical: [PhysicalStacker!]!
type Recyclers {
physical: [PhysicalRecycler!]!
virtual: [Int!]!
}
type DynamicConfig {
areThereAvailablePromoCodes: Boolean!
cassettes: Cassettes
stackers: Stackers
recyclers: Recyclers
coins: [DynamicCoinValues!]!
reboot: Boolean!
shutdown: Boolean!

View file

@ -42,15 +42,15 @@ function toMachineObject (r) {
cassette2: r.cassette2,
cassette3: r.cassette3,
cassette4: r.cassette4,
stacker1f: r.stacker1f,
stacker1r: r.stacker1r,
stacker2f: r.stacker2f,
stacker2r: r.stacker2r,
stacker3f: r.stacker3f,
stacker3r: r.stacker3r
recycler1: r.recycler1,
recycler2: r.recycler2,
recycler3: r.recycler3,
recycler4: r.recycler4,
recycler5: r.recycler5,
recycler6: r.recycler6
},
numberOfCassettes: r.number_of_cassettes,
numberOfStackers: r.number_of_stackers,
numberOfRecyclers: r.number_of_recyclers,
version: r.version,
model: r.model,
pairedAt: new Date(r.created),
@ -172,19 +172,19 @@ function renameMachine (rec) {
function resetCashOutBills (rec) {
const detailB = notifierUtils.buildDetail({ deviceId: rec.deviceId })
const { cassette1, cassette2, cassette3, cassette4, stacker1f, stacker1r, stacker2f, stacker2r, stacker3f, stacker3r } = rec.cashUnits
const sql = `UPDATE devices SET cassette1=$1, cassette2=$2, cassette3=$3, cassette4=$4, stacker1f=$5, stacker1r=$6, stacker2f=$7, stacker2r=$8, stacker3f=$9, stacker3r=$10 WHERE device_id=$11;`
return db.none(sql, [cassette1, cassette2, cassette3, cassette4, stacker1f, stacker1r, stacker2f, stacker2r, stacker3f, stacker3r, rec.deviceId]).then(() => notifierQueries.invalidateNotification(detailB, 'fiatBalance'))
const { cassette1, cassette2, cassette3, cassette4, recycler1, recycler2, recycler3, recycler4, recycler5, recycler6 } = rec.cashUnits
const sql = `UPDATE devices SET cassette1=$1, cassette2=$2, cassette3=$3, cassette4=$4, recycler1=$5, recycler2=$6, recycler3=$7, recycler4=$8, recycler5=$9, recycler6=$10 WHERE device_id=$11;`
return db.none(sql, [cassette1, cassette2, cassette3, cassette4, recycler1, recycler2, recycler3, recycler4, recycler5, recycler6, rec.deviceId]).then(() => notifierQueries.invalidateNotification(detailB, 'fiatBalance'))
}
function setCassetteBills (rec) {
const { cashbox, cassette1, cassette2, cassette3, cassette4, stacker1f, stacker1r, stacker2f, stacker2r, stacker3f, stacker3r } = rec.cashUnits
const { cashbox, cassette1, cassette2, cassette3, cassette4, recycler1, recycler2, recycler3, recycler4, recycler5, recycler6 } = rec.cashUnits
return getMachine(rec.deviceId)
.then(machine => {
const oldCashboxCount = machine?.cashUnits?.cashbox
if (_.isNil(oldCashboxCount) || cashbox === oldCashboxCount) {
const sql = 'UPDATE devices SET cassette1=$1, cassette2=$2, cassette3=$3, cassette4=$4, stacker1f=$5, stacker1r=$6, stacker2f=$7, stacker2r=$8, stacker3f=$9, stacker3r=$10 WHERE device_id=$11'
return db.none(sql, [cassette1, cassette2, cassette3, cassette4, stacker1f, stacker1r, stacker2f, stacker2r, stacker3f, stacker3r, rec.deviceId])
const sql = 'UPDATE devices SET cassette1=$1, cassette2=$2, cassette3=$3, cassette4=$4, recycler1=$5, recycler2=$6, recycler3=$7, recycler4=$8, recycler5=$9, recycler6=$10 WHERE device_id=$11'
return db.none(sql, [cassette1, cassette2, cassette3, cassette4, recycler1, recycler2, recycler3, recycler4, recycler5, recycler6, rec.deviceId])
}
return batching.updateMachineWithBatch({ ...rec, oldCashboxValue: oldCashboxCount })
@ -199,7 +199,7 @@ function emptyMachineUnits ({ deviceId, newUnits, fiatCode }) {
(acc, value) => ({
...acc,
[value]: {
operationName: `cash-${_.replace(/(cassette|stacker)/g, '$1-')(value)}-empty`,
operationName: `cash-${_.replace(/(cassette|recycler)/g, '$1-')(value)}-empty`,
delta: newUnits[value] - machine.cashUnits[value],
denomination: value !== 'cashbox' ? cashoutSettings[value] : null
}
@ -224,7 +224,7 @@ function emptyMachineUnits ({ deviceId, newUnits, fiatCode }) {
fiat_code: fiatCode,
device_id: deviceId
// TODO: Uncomment this if we decide to keep track of bills across multiple operations. For now, we'll just create the emptying operations for each unit affected, but not relate these events with individual bills and just use the field for the cashbox batch event
// cash_unit_operation_id: _.find(it => it.operation_type === `cash-${_.replace(/(cassette|stacker)/g, '$1-')(value)}-empty`, operationsToCreate).id
// cash_unit_operation_id: _.find(it => it.operation_type === `cash-${_.replace(/(cassette|recycler)/g, '$1-')(value)}-empty`, operationsToCreate).id
}), Math.abs(unit.delta)))
},
[],
@ -241,17 +241,17 @@ function emptyMachineUnits ({ deviceId, newUnits, fiatCode }) {
const q1= t.none(pgp.helpers.insert(operationsToCreate, q1Cols, 'cash_unit_operation'))
const q2Cols = ['id', 'fiat', 'fiat_code', 'device_id']
const q2 = t.none(pgp.helpers.insert(billArr, q2Cols, 'empty_unit_bills'))
const q3 = t.none(`UPDATE devices SET cassette1=$1, cassette2=$2, cassette3=$3, cassette4=$4, stacker1f=$5, stacker1r=$6, stacker2f=$7, stacker2r=$8, stacker3f=$9, stacker3r=$10 WHERE device_id=$11`, [
const q3 = t.none(`UPDATE devices SET cassette1=$1, cassette2=$2, cassette3=$3, cassette4=$4, recycler1=$5, recycler2=$6, recycler3=$7, recycler4=$8, recycler5=$9, recycler6=$10 WHERE device_id=$11`, [
_.defaultTo(machine.cashUnits.cassette1, newUnits.cassette1),
_.defaultTo(machine.cashUnits.cassette2, newUnits.cassette2),
_.defaultTo(machine.cashUnits.cassette3, newUnits.cassette3),
_.defaultTo(machine.cashUnits.cassette4, newUnits.cassette4),
_.defaultTo(machine.cashUnits.stacker1f, newUnits.stacker1f),
_.defaultTo(machine.cashUnits.stacker1r, newUnits.stacker1r),
_.defaultTo(machine.cashUnits.stacker2f, newUnits.stacker2f),
_.defaultTo(machine.cashUnits.stacker2r, newUnits.stacker2r),
_.defaultTo(machine.cashUnits.stacker3f, newUnits.stacker3f),
_.defaultTo(machine.cashUnits.stacker3r, newUnits.stacker3r),
_.defaultTo(machine.cashUnits.recycler1, newUnits.recycler1),
_.defaultTo(machine.cashUnits.recycler2, newUnits.recycler2),
_.defaultTo(machine.cashUnits.recycler3, newUnits.recycler3),
_.defaultTo(machine.cashUnits.recycler4, newUnits.recycler4),
_.defaultTo(machine.cashUnits.recycler5, newUnits.recycler5),
_.defaultTo(machine.cashUnits.recycler6, newUnits.recycler6),
deviceId
])
@ -267,7 +267,7 @@ function refillMachineUnits ({ deviceId, newUnits }) {
(acc, value) => ({
...acc,
[value]: {
operationName: `cash-${_.replace(/(stacker)/g, '$1-')(value)}-refill`,
operationName: `cash-${_.replace(/(recycler)/g, '$1-')(value)}-refill`,
delta: newUnits[value] - machine.cashUnits[value]
}
}),
@ -290,17 +290,17 @@ function refillMachineUnits ({ deviceId, newUnits }) {
return db.tx(t => {
const q1Cols = ['id', 'device_id', 'operation_type']
const q1= t.none(pgp.helpers.insert(operationsToCreate, q1Cols, 'cash_unit_operation'))
const q2 = t.none(`UPDATE devices SET cassette1=$1, cassette2=$2, cassette3=$3, cassette4=$4, stacker1f=$5, stacker1r=$6, stacker2f=$7, stacker2r=$8, stacker3f=$9, stacker3r=$10 WHERE device_id=$11`, [
const q2 = t.none(`UPDATE devices SET cassette1=$1, cassette2=$2, cassette3=$3, cassette4=$4, recycler1=$5, recycler2=$6, recycler3=$7, recycler4=$8, recycler5=$9, recycler6=$10 WHERE device_id=$11`, [
_.defaultTo(machine.cashUnits.cassette1, newUnits.cassette1),
_.defaultTo(machine.cashUnits.cassette2, newUnits.cassette2),
_.defaultTo(machine.cashUnits.cassette3, newUnits.cassette3),
_.defaultTo(machine.cashUnits.cassette4, newUnits.cassette4),
_.defaultTo(machine.cashUnits.stacker1f, newUnits.stacker1f),
_.defaultTo(machine.cashUnits.stacker1r, newUnits.stacker1r),
_.defaultTo(machine.cashUnits.stacker2f, newUnits.stacker2f),
_.defaultTo(machine.cashUnits.stacker2r, newUnits.stacker2r),
_.defaultTo(machine.cashUnits.stacker3f, newUnits.stacker3f),
_.defaultTo(machine.cashUnits.stacker3r, newUnits.stacker3r),
_.defaultTo(machine.cashUnits.recycler1, newUnits.recycler1),
_.defaultTo(machine.cashUnits.recycler2, newUnits.recycler2),
_.defaultTo(machine.cashUnits.recycler3, newUnits.recycler3),
_.defaultTo(machine.cashUnits.recycler4, newUnits.recycler4),
_.defaultTo(machine.cashUnits.recycler5, newUnits.recycler5),
_.defaultTo(machine.cashUnits.recycler6, newUnits.recycler6),
deviceId
])

View file

@ -42,7 +42,7 @@ function machineAction (type, value) {
state.emptyUnit[operatorId] = { [deviceId]: pid }
break
case 'refillUnit':
logger.debug(`Refilling stackers from machine '${deviceId}' from operator ${operatorId}`)
logger.debug(`Refilling recyclers from machine '${deviceId}' from operator ${operatorId}`)
state.refillUnit[operatorId] = { [deviceId]: pid }
break
default:

View file

@ -16,7 +16,7 @@ const typeDef = gql`
model: String
cashUnits: CashUnits
numberOfCassettes: Int
numberOfStackers: Int
numberOfRecyclers: Int
statuses: [MachineStatus]
latestEvent: MachineEvent
downloadSpeed: String
@ -30,12 +30,12 @@ const typeDef = gql`
cassette2: Int
cassette3: Int
cassette4: Int
stacker1f: Int
stacker1r: Int
stacker2f: Int
stacker2r: Int
stacker3f: Int
stacker3r: Int
recycler1: Int
recycler2: Int
recycler3: Int
recycler4: Int
recycler5: Int
recycler6: Int
}
input CashUnitsInput {
@ -44,12 +44,12 @@ const typeDef = gql`
cassette2: Int
cassette3: Int
cassette4: Int
stacker1f: Int
stacker1r: Int
stacker2f: Int
stacker2r: Int
stacker3f: Int
stacker3r: Int
recycler1: Int
recycler2: Int
recycler3: Int
recycler4: Int
recycler5: Int
recycler6: Int
}
type UnpairedMachine {

View file

@ -154,9 +154,9 @@ function advancedBatch (data) {
'dispense', 'notified', 'redeem', 'phone', 'error',
'created', 'confirmedAt', 'hdIndex', 'swept', 'timedout',
'dispenseConfirmed', 'provisioned1', 'provisioned2', 'provisioned3', 'provisioned4',
'provisioned1f', 'provisioned1r', 'provisioned2f', 'provisioned2r', 'provisioned3f', 'provisioned3r',
'provisionedRecycler1', 'provisionedRecycler2', 'provisionedRecycler3', 'provisionedRecycler4', 'provisionedRecycler5', 'provisionedRecycler6',
'denomination1', 'denomination2', 'denomination3', 'denomination4',
'denomination1f', 'denomination1r', 'denomination2f', 'denomination2r', 'denomination3f', 'denomination3r',
'denominationRecycler1', 'denominationRecycler2', 'denominationRecycler3', 'denominationRecycler4', 'denominationRecycler5', 'denominationRecycler6',
'errorCode', 'customerId', 'txVersion', 'publishedAt', 'termsAccepted', 'layer2Address',
'commissionPercentage', 'rawTickerPrice', 'receivedCryptoAtoms',
'discount', 'txHash', 'customerPhone', 'customerIdCardDataNumber',

View file

@ -10,7 +10,7 @@ const CA_PATH = process.env.CA_PATH
// A machine on an older version (no multicassette code) could be paired with a server with multicassette code.
// This makes sure that the server stores a default value
const DEFAULT_NUMBER_OF_CASSETTES = 2
const DEFAULT_NUMBER_OF_STACKERS = 0
const DEFAULT_NUMBER_OF_RECYCLERS = 0
function pullToken (token) {
const sql = `delete from pairing_tokens
@ -37,16 +37,16 @@ function unpair (deviceId) {
)
}
function pair (token, deviceId, machineModel, numOfCassettes = DEFAULT_NUMBER_OF_CASSETTES, numOfStackers = DEFAULT_NUMBER_OF_STACKERS) {
function pair (token, deviceId, machineModel, numOfCassettes = DEFAULT_NUMBER_OF_CASSETTES, numOfRecyclers = DEFAULT_NUMBER_OF_RECYCLERS) {
return pullToken(token)
.then(r => {
if (r.expired) return false
const insertSql = `insert into devices (device_id, name, number_of_cassettes, number_of_stackers) values ($1, $2, $3, $4)
const insertSql = `insert into devices (device_id, name, number_of_cassettes, number_of_recyclers) values ($1, $2, $3, $4)
on conflict (device_id)
do update set paired=TRUE, display=TRUE`
return db.none(insertSql, [deviceId, r.name, numOfCassettes, numOfStackers])
return db.none(insertSql, [deviceId, r.name, numOfCassettes, numOfRecyclers])
.then(() => true)
})
.catch(err => {

View file

@ -147,42 +147,42 @@ function plugins (settings, deviceId) {
return computedCassettes
}
function computeAvailableStackers (stackers, redeemableTxs) {
if (_.isEmpty(redeemableTxs)) return stackers
function computeAvailableRecyclers (recyclers, redeemableTxs) {
if (_.isEmpty(redeemableTxs)) return recyclers
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 => _.includes('stacker', it.name) && it.denomination > 0, tx.bills)
const bills = _.filter(it => _.includes('recycler', it.name) && it.denomination > 0, tx.bills)
const sameDenominations = a => a[0]?.denomination === a[1]?.denomination
const doDenominationsMatch = _.every(sameDenominations, _.zip(stackers, bills))
const doDenominationsMatch = _.every(sameDenominations, _.zip(recyclers, bills))
if (!doDenominationsMatch) {
throw new Error('Denominations don\'t add up, stackers were changed.')
throw new Error('Denominations don\'t add up, recyclers 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 provisioned = _.reduce(sumTxs, _.times(_.constant(0), _.size(recyclers)), redeemableTxs)
const zipped = _.zip(_.map('count', recyclers), 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 = []
const computedRecyclers = []
_.forEach(it => {
computedStackers.push({
number: stackers[it].number,
name: stackers[it].name,
denomination: stackers[it].denomination,
computedRecyclers.push({
number: recyclers[it].number,
name: recyclers[it].name,
denomination: recyclers[it].denomination,
count: counts[it]
})
}, _.times(_.identity(), _.size(stackers)))
}, _.times(_.identity(), _.size(recyclers)))
return computedStackers
return computedRecyclers
}
function buildAvailableCassettes (excludeTxId) {
@ -231,62 +231,57 @@ function plugins (settings, deviceId) {
})
}
function buildAvailableStackers (excludeTxId) {
function buildAvailableRecyclers (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]) => {
return Promise.all([dbm.recyclerCounts(deviceId), cashOutHelper.redeemableTxs(deviceId, excludeTxId)])
.then(([_recyclers, _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))
denominations.push(cashOutConfig[`recycler${it + 1}`])
}, _.times(_.identity(), _recyclers.numberOfRecyclers))
const virtualStackers = denominations.length ? [Math.max(..._.flatten(denominations)) * 2] : []
const virtualRecyclers = denominations.length ? [Math.max(..._.flatten(denominations)) * 2] : []
const counts = _stackers.counts
const counts = _recyclers.counts
if (counts.length !== denominations.length) {
throw new Error('Denominations and respective counts do not match!')
}
const stackers = []
const recyclers = []
_.forEach(it => {
stackers.push({
number: 1 + it * 2,
name: `stacker${it + 1}f`,
denomination: parseInt(denominations[it][0], 10),
count: parseInt(counts[it][0], 10)
recyclers.push({
number: it + 1,
name: `recycler${it + 1}`,
denomination: parseInt(denominations[it], 10),
count: parseInt(counts[it], 10)
})
stackers.push({
number: 2 + it * 2,
name: `stacker${it + 1}r`,
denomination: parseInt(denominations[it][1], 10),
count: parseInt(counts[it][1], 10)
})
}, _.times(_.identity(), _stackers.numberOfStackers))
}, _.times(_.identity(), _recyclers.numberOfRecyclers))
try {
return {
stackers: computeAvailableStackers(stackers, redeemableTxs),
virtualStackers
recyclers: computeAvailableRecyclers(recyclers, redeemableTxs),
virtualRecyclers
}
} catch (err) {
logger.error(err)
return {
stackers,
virtualStackers
recyclers,
virtualRecyclers
}
}
})
}
function buildAvailableUnits (excludeTxId) {
return Promise.all([buildAvailableCassettes(excludeTxId), buildAvailableStackers(excludeTxId)])
.then(([cassettes, stackers]) => ({ cassettes: cassettes.cassettes, stackers: stackers.stackers }))
return Promise.all([buildAvailableCassettes(excludeTxId), buildAvailableRecyclers(excludeTxId)])
.then(([cassettes, recyclers]) => ({ cassettes: cassettes.cassettes, recyclers: recyclers.recyclers }))
}
function fetchCurrentConfigVersion () {
@ -336,7 +331,7 @@ function plugins (settings, deviceId) {
return Promise.all([
buildAvailableCassettes(),
buildAvailableStackers(),
buildAvailableRecyclers(),
fetchCurrentConfigVersion(),
millisecondsToMinutes(getTimezoneOffset(localeConfig.timezone)),
loyalty.getNumberOfAvailablePromoCodes(),
@ -347,7 +342,7 @@ function plugins (settings, deviceId) {
])
.then(([
cassettes,
stackers,
recyclers,
configVersion,
timezone,
numberOfAvailablePromoCodes,
@ -371,7 +366,7 @@ function plugins (settings, deviceId) {
return {
cassettes,
stackers,
recyclers: recyclers,
rates: buildRates(tickers),
balances: buildBalances(balances),
coins,
@ -744,12 +739,12 @@ function plugins (settings, deviceId) {
const denomination2 = cashOutConfig.cassette2
const denomination3 = cashOutConfig.cassette3
const denomination4 = cashOutConfig.cassette4
const denomination1f = cashOutConfig.stacker1f
const denomination1r = cashOutConfig.stacker1r
const denomination2f = cashOutConfig.stacker2f
const denomination2r = cashOutConfig.stacker2r
const denomination3f = cashOutConfig.stacker3f
const denomination3r = cashOutConfig.stacker3r
const denominationRecycler1 = cashOutConfig.recycler1
const denominationRecycler2 = cashOutConfig.recycler2
const denominationRecycler3 = cashOutConfig.recycler3
const denominationRecycler4 = cashOutConfig.recycler4
const denominationRecycler5 = cashOutConfig.recycler5
const denominationRecycler6 = cashOutConfig.recycler6
const cashOutEnabled = cashOutConfig.active
const isUnitLow = (have, max, limit) => cashOutEnabled && ((have / max) * 100) < limit
// const isUnitHigh = (have, max, limit) => cashOutEnabled && ((have / max) * 100) > limit
@ -817,74 +812,74 @@ function plugins (settings, deviceId) {
}
: null
const stacker1fAlert = device.numberOfStackers >= 1 && isUnitLow(device.cashUnits.stacker1f, getCashUnitCapacity(device.model, 'stacker'), notifications.fillingPercentageStacker1f)
const recycler1Alert = device.numberOfRecyclers >= 1 && isUnitLow(device.cashUnits.recycler1, getCashUnitCapacity(device.model, 'recycler'), notifications.fillingPercentageRecycler1)
? {
code: 'LOW_RECYCLER_STACKER',
cassette: 4,
machineName,
deviceId: device.deviceId,
notes: device.cashUnits.stacker1f,
denomination: denomination1f,
notes: device.cashUnits.recycler1,
denomination: denominationRecycler1,
fiatCode
}
: null
const stacker1rAlert = device.numberOfStackers >= 1 && isUnitLow(device.cashUnits.stacker1r, getCashUnitCapacity(device.model, 'stacker'), notifications.fillingPercentageStacker1r)
const recycler2Alert = device.numberOfRecyclers >= 2 && isUnitLow(device.cashUnits.recycler2, getCashUnitCapacity(device.model, 'recycler'), notifications.fillingPercentageRecycler2)
? {
code: 'LOW_RECYCLER_STACKER',
cassette: 4,
machineName,
deviceId: device.deviceId,
notes: device.cashUnits.stacker1r,
denomination: denomination1r,
notes: device.cashUnits.recycler2,
denomination: denominationRecycler2,
fiatCode
}
: null
const stacker2fAlert = device.numberOfStackers >= 2 && isUnitLow(device.cashUnits.stacker2f, getCashUnitCapacity(device.model, 'stacker'), notifications.fillingPercentageStacker2f)
const recycler3Alert = device.numberOfRecyclers >= 3 && isUnitLow(device.cashUnits.recycler3, getCashUnitCapacity(device.model, 'recycler'), notifications.fillingPercentageRecycler3)
? {
code: 'LOW_RECYCLER_STACKER',
cassette: 4,
machineName,
deviceId: device.deviceId,
notes: device.cashUnits.stacker2f,
denomination: denomination2f,
notes: device.cashUnits.recycler3,
denomination: denominatinoRecycler3,
fiatCode
}
: null
const stacker2rAlert = device.numberOfStackers >= 2 && isUnitLow(device.cashUnits.stacker2r, getCashUnitCapacity(device.model, 'stacker'), notifications.fillingPercentageStacker2r)
const recycler4Alert = device.numberOfRecyclers >= 4 && isUnitLow(device.cashUnits.recycler4, getCashUnitCapacity(device.model, 'recycler'), notifications.fillingPercentageRecycler4)
? {
code: 'LOW_RECYCLER_STACKER',
cassette: 4,
machineName,
deviceId: device.deviceId,
notes: device.cashUnits.stacker2r,
denomination: denomination2r,
notes: device.cashUnits.recycler4,
denomination: denominationRecycler4,
fiatCode
}
: null
const stacker3fAlert = device.numberOfStackers >= 3 && isUnitLow(device.cashUnits.stacker3f, getCashUnitCapacity(device.model, 'stacker'), notifications.fillingPercentageStacker3f)
const recycler5Alert = device.numberOfRecyclers >= 5 && isUnitLow(device.cashUnits.recycler5, getCashUnitCapacity(device.model, 'recycler'), notifications.fillingPercentageRecycler5)
? {
code: 'LOW_RECYCLER_STACKER',
cassette: 4,
machineName,
deviceId: device.deviceId,
notes: device.cashUnits.stacker3f,
denomination: denomination3f,
notes: device.cashUnits.recycler5,
denomination: denominationRecycler5,
fiatCode
}
: null
const stacker3rAlert = device.numberOfStackers >= 3 && isUnitLow(device.cashUnits.stacker3r, getCashUnitCapacity(device.model, 'stacker'), notifications.fillingPercentageStacker3r)
const recycler6Alert = device.numberOfRecyclers >= 6 && isUnitLow(device.cashUnits.recycler6, getCashUnitCapacity(device.model, 'recycler'), notifications.fillingPercentageRecycler6)
? {
code: 'LOW_RECYCLER_STACKER',
cassette: 4,
machineName,
deviceId: device.deviceId,
notes: device.cashUnits.stacker3r,
denomination: denomination3r,
notes: device.cashUnits.recycler6,
denomination: denominationRecycler6,
fiatCode
}
: null
@ -895,12 +890,12 @@ function plugins (settings, deviceId) {
cassette2Alert,
cassette3Alert,
cassette4Alert,
stacker1fAlert,
stacker1rAlert,
stacker2fAlert,
stacker2rAlert,
stacker3fAlert,
stacker3rAlert
recycler1Alert,
recycler2Alert,
recycler3Alert,
recycler4Alert,
recycler5Alert,
recycler6Alert
])
}

View file

@ -40,18 +40,18 @@ exports.cassetteCounts = function cassetteCounts (deviceId) {
})
}
exports.stackerCounts = function stackerCounts (deviceId) {
const sql = 'SELECT stacker1f, stacker1r, stacker2f, stacker2r, stacker3f, stacker3r, number_of_stackers FROM devices ' +
exports.recyclerCounts = function recyclerCounts (deviceId) {
const sql = 'SELECT recycler1, recycler2, recycler3, recycler4, recycler5, recycler6, number_of_recyclers FROM devices ' +
'WHERE device_id=$1'
return db.one(sql, [deviceId])
.then(row => {
const counts = []
_.forEach(it => {
counts.push([row[`stacker${it + 1}f`], row[`stacker${it + 1}r`]])
}, _.times(_.identity(), row.number_of_stackers))
counts.push(row[`recycler${it + 1}`])
}, _.times(_.identity(), row.number_of_recyclers))
return { numberOfStackers: row.number_of_stackers, counts }
return { numberOfRecyclers: row.number_of_recyclers, counts }
})
}

View file

@ -11,9 +11,9 @@ function pair (req, res, next) {
const deviceId = req.deviceId
const model = req.query.model
const numOfCassettes = req.query.numOfCassettes
const numOfStackers = req.query.numOfStackers
const numOfRecyclers = req.query.numOfRecyclers
return pairing.pair(token, deviceId, model, numOfCassettes, numOfStackers)
return pairing.pair(token, deviceId, model, numOfCassettes, numOfRecyclers)
.then(isValid => {
if (isValid) return res.json({ status: 'paired' })
throw httpError('Pairing failed')

View file

@ -0,0 +1,86 @@
const db = require('./db')
exports.up = function (next) {
var sql = [
`ALTER TABLE cash_out_actions
RENAME COLUMN provisioned_1f TO provisioned_recycler_1,
RENAME COLUMN provisioned_1r TO provisioned_recycler_2,
RENAME COLUMN provisioned_2f TO provisioned_recycler_3,
RENAME COLUMN provisioned_2r TO provisioned_recycler_4,
RENAME COLUMN provisioned_3f TO provisioned_recycler_5,
RENAME COLUMN provisioned_3r TO provisioned_recycler_6,
RENAME COLUMN dispensed_1f TO dispensed_recycler_1,
RENAME COLUMN dispensed_1r TO dispensed_recycler_2,
RENAME COLUMN dispensed_2f TO dispensed_recycler_3,
RENAME COLUMN dispensed_2r TO dispensed_recycler_4,
RENAME COLUMN dispensed_3f TO dispensed_recycler_5,
RENAME COLUMN dispensed_3r TO dispensed_recycler_6,
RENAME COLUMN rejected_1f TO rejected_recycler_1,
RENAME COLUMN rejected_1r TO rejected_recycler_2,
RENAME COLUMN rejected_2f TO rejected_recycler_3,
RENAME COLUMN rejected_2r TO rejected_recycler_4,
RENAME COLUMN rejected_3f TO rejected_recycler_5,
RENAME COLUMN rejected_3r TO rejected_recycler_6,
RENAME COLUMN denomination_1f TO denomination_recycler_1,
RENAME COLUMN denomination_1r TO denomination_recycler_2,
RENAME COLUMN denomination_2f TO denomination_recycler_3,
RENAME COLUMN denomination_2r TO denomination_recycler_4,
RENAME COLUMN denomination_3f TO denomination_recycler_5,
RENAME COLUMN denomination_3r TO denomination_recycler_6`,
`ALTER TABLE devices
RENAME COLUMN stacker1f TO recycler1,
RENAME COLUMN stacker1r TO recycler2,
RENAME COLUMN stacker2f TO recycler3,
RENAME COLUMN stacker2r TO recycler4,
RENAME COLUMN stacker3f TO recycler5,
RENAME COLUMN stacker3r TO recycler6,
RENAME COLUMN number_of_stackers TO number_of_recyclers`,
`ALTER TABLE cash_out_txs
RENAME COLUMN provisioned_1f TO provisioned_recycler_1,
RENAME COLUMN provisioned_1r TO provisioned_recycler_2,
RENAME COLUMN provisioned_2f TO provisioned_recycler_3,
RENAME COLUMN provisioned_2r TO provisioned_recycler_4,
RENAME COLUMN provisioned_3f TO provisioned_recycler_5,
RENAME COLUMN provisioned_3r TO provisioned_recycler_6,
RENAME COLUMN denomination_1f TO denomination_recycler_1,
RENAME COLUMN denomination_1r TO denomination_recycler_2,
RENAME COLUMN denomination_2f TO denomination_recycler_3,
RENAME COLUMN denomination_2r TO denomination_recycler_4,
RENAME COLUMN denomination_3f TO denomination_recycler_5,
RENAME COLUMN denomination_3r TO denomination_recycler_6`,
`ALTER TYPE cash_unit
RENAME VALUE 'stacker1f' TO 'recycler1',
RENAME VALUE 'stacker1r' TO 'recycler2',
RENAME VALUE 'stacker2f' TO 'recycler3',
RENAME VALUE 'stacker2r' TO 'recycler4',
RENAME VALUE 'stacker3f' TO 'recycler5',
RENAME VALUE 'stacker3r' TO 'recycler6',
`,
`ALTER TYPE cash_unit_operation_type
RENAME VALUE 'cash-stacker-1f-refill' TO 'cash-recycler-1-refill',
RENAME VALUE 'cash-stacker-1f-empty' TO 'cash-recycler-1-empty',
RENAME VALUE 'cash-stacker-1f-count-change' TO 'cash-recycler-1-count-change',
RENAME VALUE 'cash-stacker-1r-refill' TO 'cash-recycler-2-refill',
RENAME VALUE 'cash-stacker-1r-empty' TO 'cash-recycler-2-empty',
RENAME VALUE 'cash-stacker-1r-count-change' TO 'cash-recycler-2-count-change',
RENAME VALUE 'cash-stacker-2f-refill' TO 'cash-recycler-3-refill',
RENAME VALUE 'cash-stacker-2f-empty' TO 'cash-recycler-3-empty',
RENAME VALUE 'cash-stacker-2f-count-change' TO 'cash-recycler-3-count-change',
RENAME VALUE 'cash-stacker-2r-refill' TO 'cash-recycler-4-refill',
RENAME VALUE 'cash-stacker-2r-empty' TO 'cash-recycler-4-empty',
RENAME VALUE 'cash-stacker-2r-count-change' TO 'cash-recycler-4-count-change',
RENAME VALUE 'cash-stacker-3f-refill' TO 'cash-recycler-5-refill',
RENAME VALUE 'cash-stacker-3f-empty' TO 'cash-recycler-5-empty',
RENAME VALUE 'cash-stacker-3f-count-change' TO 'cash-recycler-5-count-change',
RENAME VALUE 'cash-stacker-3r-refill' TO 'cash-recycler-6-refill',
RENAME VALUE 'cash-stacker-3r-empty' TO 'cash-recycler-6-empty',
RENAME VALUE 'cash-stacker-3r-count-change' TO 'cash-recycler-6-count-change',
`
]
db.multi(sql, next)
}
exports.down = function (next) {
next()
}

View file

@ -215,7 +215,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => {
command: 'refillUnit',
display: 'Refill',
message:
'Triggering this action will refill the stackers in this machine, by using bills present in its cassettes. This action may require manual operation of the cassettes and close attention to make sure that the denominations in the cassettes match the denominations in the stackers.'
'Triggering this action will refill the recyclers in this machine, by using bills present in its cassettes. This action may require manual operation of the cassettes and close attention to make sure that the denominations in the cassettes match the denominations in the recyclers.'
})
}}>
Refill Unit

View file

@ -46,15 +46,15 @@ const GET_INFO = gql`
cassette2
cassette3
cassette4
stacker1f
stacker1r
stacker2f
stacker2r
stacker3f
stacker3r
recycler1
recycler2
recycler3
recycler4
recycler5
recycler6
}
numberOfCassettes
numberOfStackers
numberOfRecyclers
}
config
}

View file

@ -18,7 +18,8 @@ const MODAL_HEIGHT = 520
const Wizard = ({ machine, locale, onClose, save, error }) => {
// Each stacker counts as two steps, one for front and another for rear
const LAST_STEP = machine.numberOfCassettes + machine.numberOfStackers * 2 + 1
const LAST_STEP =
machine.numberOfCassettes + machine.numberOfRecyclers * 2 + 1
const [{ step, config }, setState] = useState({
step: 0,
config: { active: true }
@ -65,9 +66,9 @@ const Wizard = ({ machine, locale, onClose, save, error }) => {
R.chain(
it => [
{
type: `stacker${it}f`,
model: 'stacker',
display: `Stacker ${it}F`,
type: `recycler${it * 2 - 1}`,
model: 'recycler',
display: `Recycler ${it}`,
component: Autocomplete,
inputProps: {
options: options,
@ -76,9 +77,9 @@ const Wizard = ({ machine, locale, onClose, save, error }) => {
}
},
{
type: `stacker${it}r`,
model: 'stacker',
display: `Stacker ${it}R`,
type: `recycler${it * 2}`,
model: 'recycler',
display: `Recycler ${it}`,
component: Autocomplete,
inputProps: {
options: options,
@ -87,7 +88,7 @@ const Wizard = ({ machine, locale, onClose, save, error }) => {
}
}
],
R.range(1, machine.numberOfStackers + 1)
R.range(1, machine.numberOfRecyclers + 1)
)
)
@ -117,38 +118,38 @@ const Wizard = ({ machine, locale, onClose, save, error }) => {
: Yup.number()
.transform(transformNumber)
.nullable(),
stacker1f:
machine.numberOfStackers >= 1 && step >= machine.numberOfCassettes + 1
recycler1:
machine.numberOfRecyclers >= 1 && step >= machine.numberOfCassettes + 1
? Yup.number().required()
: Yup.number()
.transform(transformNumber)
.nullable(),
stacker1r:
machine.numberOfStackers >= 1 && step >= machine.numberOfCassettes + 2
recycler2:
machine.numberOfRecyclers >= 2 && step >= machine.numberOfCassettes + 2
? Yup.number().required()
: Yup.number()
.transform(transformNumber)
.nullable(),
stacker2f:
machine.numberOfStackers >= 2 && step >= machine.numberOfCassettes + 3
recycler3:
machine.numberOfRecyclers >= 3 && step >= machine.numberOfCassettes + 3
? Yup.number().required()
: Yup.number()
.transform(transformNumber)
.nullable(),
stacker2r:
machine.numberOfStackers >= 2 && step >= machine.numberOfCassettes + 4
recycler4:
machine.numberOfRecyclers >= 4 && step >= machine.numberOfCassettes + 4
? Yup.number().required()
: Yup.number()
.transform(transformNumber)
.nullable(),
stacker3f:
machine.numberOfStackers >= 3 && step >= machine.numberOfCassettes + 5
recycler5:
machine.numberOfRecyclers >= 5 && step >= machine.numberOfCassettes + 5
? Yup.number().required()
: Yup.number()
.transform(transformNumber)
.nullable(),
stacker3r:
machine.numberOfStackers >= 3 && step >= machine.numberOfCassettes + 6
recycler6:
machine.numberOfRecyclers >= 6 && step >= machine.numberOfCassettes + 6
? Yup.number().required()
: Yup.number()
.transform(transformNumber)

View file

@ -41,38 +41,38 @@ const DenominationsSchema = Yup.object().shape({
.max(CURRENCY_MAX)
.nullable()
.transform(transformNumber),
stacker1f: Yup.number()
.label('Stacker 1')
recycler1: Yup.number()
.label('Recycler 1')
.min(1)
.max(CURRENCY_MAX)
.nullable()
.transform(transformNumber),
stacker1r: Yup.number()
.label('Stacker 1')
recycler2: Yup.number()
.label('Recycler 2')
.min(1)
.max(CURRENCY_MAX)
.nullable()
.transform(transformNumber),
stacker2f: Yup.number()
.label('Stacker 2')
recycler3: Yup.number()
.label('Recycler 3')
.min(1)
.max(CURRENCY_MAX)
.nullable()
.transform(transformNumber),
stacker2r: Yup.number()
.label('Stacker 2')
recycler4: Yup.number()
.label('Recycler 4')
.min(1)
.max(CURRENCY_MAX)
.nullable()
.transform(transformNumber),
stacker3f: Yup.number()
.label('Stacker 3')
recycler5: Yup.number()
.label('Recycler 5')
.min(1)
.max(CURRENCY_MAX)
.nullable()
.transform(transformNumber),
stacker3r: Yup.number()
.label('Stacker 3')
recycler6: Yup.number()
.label('Recycler 6')
.min(1)
.max(CURRENCY_MAX)
.nullable()
@ -85,11 +85,12 @@ const getElements = (machines, locale = {}, classes) => {
...R.map(it => it.numberOfCassettes, machines),
0
)
const maxNumberOfStackers = Math.max(
...R.map(it => it.numberOfStackers, machines),
const maxNumberOfRecyclers = Math.max(
...R.map(it => it.numberOfRecyclers, machines),
0
)
const numberOfCashUnits = maxNumberOfCassettes + maxNumberOfStackers
const numberOfCashUnits =
maxNumberOfCassettes + Math.ceil(maxNumberOfRecyclers / 2)
const options = getBillOptions(locale, denominations)
const cassetteProps =
@ -140,29 +141,31 @@ const getElements = (machines, locale = {}, classes) => {
)
R.until(
R.gt(R.__, maxNumberOfStackers),
it => {
elements.push({
names: [`stacker${it}f`, `stacker${it}r`],
header: `Stacker ${it}`,
size: 'sm',
stripe: true,
textAlign: 'right',
width: widthsByNumberOfUnits[numberOfCashUnits]?.cassette,
prefix: it => (R.last(it) === 'f' ? 'F' : 'R'),
suffix: fiatCurrency,
bold: bold,
input: options?.length > 0 ? Autocomplete : NumberInput,
inputProps: cassetteProps,
doubleHeader: 'Denominations of Cassettes & Recyclers',
isHidden: machine =>
it >
machines.find(({ deviceId }) => deviceId === machine.id)
.numberOfStackers
})
return R.add(1, it)
},
1
R.gt(
R.__,
Math.ceil(maxNumberOfRecyclers),
it => {
elements.push({
names: [`recycler${it * 2 - 1}`, `recycler${it * 2}`],
header: `Recyclers ${it * 2 - 1} ${it * 2}`,
size: 'sm',
stripe: true,
textAlign: 'right',
width: widthsByNumberOfUnits[numberOfCashUnits]?.cassette,
suffix: fiatCurrency,
bold: bold,
input: options?.length > 0 ? Autocomplete : NumberInput,
inputProps: cassetteProps,
doubleHeader: 'Denominations of Cassettes & Recyclers',
isHidden: machine =>
it >
machines.find(({ deviceId }) => deviceId === machine.id)
.numberOfRecyclers
})
return R.add(1, it)
},
1
)
)
return elements

View file

@ -30,15 +30,15 @@ const GET_DATA = gql`
cassette2
cassette3
cassette4
stacker1f
stacker1r
stacker2f
stacker2r
stacker3f
stacker3r
recycler1
recycler2
recycler3
recycler4
recycler5
recycler6
}
numberOfCassettes
numberOfStackers
numberOfRecyclers
statuses {
label
type

View file

@ -28,12 +28,12 @@ const SET_CASSETTE_BILLS = gql`
cassette2
cassette3
cassette4
stacker1f
stacker1r
stacker2f
stacker2r
stacker3f
stacker3r
recycler1
recycler2
recycler3
recycler4
recycler5
recycler6
}
}
}

View file

@ -35,15 +35,15 @@ const GET_INFO = gql`
cassette2
cassette3
cassette4
stacker1f
stacker1r
stacker2f
stacker2r
stacker3f
stacker3r
recycler1
recycler2
recycler3
recycler4
recycler5
recycler6
}
numberOfCassettes
numberOfStackers
numberOfRecyclers
statuses {
label
type

View file

@ -114,7 +114,7 @@ const CashUnitDetails = ({
))(R.keys(billCount))}
</div>
<div className={classes.unitList}>
{machine.numberOfStackers === 0 &&
{machine.numberOfRecyclers === 0 &&
R.map(it => (
<>
<div className={classes.col}>
@ -142,7 +142,7 @@ const CashUnitDetails = ({
)}
</>
))(R.range(1, machine.numberOfCassettes + 1))}
{machine.numberOfStackers > 0 && (
{machine.numberOfRecyclers > 0 && (
<>
<div className={classes.col}>
<Label1
@ -174,51 +174,51 @@ const CashUnitDetails = ({
{R.map(it => (
<>
<div className={classes.col}>
<Label1
noMargin
className={classes.label}>{`Stacker ${it}`}</Label1>
<Label1 noMargin className={classes.label}>{`Recycler ${it *
2 -
1}`}</Label1>
<div className={classes.loadingBoxes}>
<CashOut
width={60}
height={40}
currency={{ code: currency }}
notes={machine.cashUnits[`stacker${it}f`]}
notes={machine.cashUnits[`recycler${it * 2 - 1}`]}
denomination={
getCashoutSettings(machine.id ?? machine.deviceId)[
`stacker${it}f`
`recycler${it}`
]
}
threshold={
fillingPercentageSettings[
`fillingPercentageStacker${it}f`
`fillingPercentageRecycler${it * 2 - 1}`
]
}
capacity={getCashUnitCapacity(machine.model, 'stacker')}
capacity={getCashUnitCapacity(machine.model, 'recycler')}
/>
<CashOut
width={60}
height={40}
currency={{ code: currency }}
notes={machine.cashUnits[`stacker${it}r`]}
notes={machine.cashUnits[`recycler${it * 2}`]}
denomination={
getCashoutSettings(machine.id ?? machine.deviceId)[
`stacker${it}r`
`recycler${it * 2}`
]
}
threshold={
fillingPercentageSettings[
`fillingPercentageStacker${it}r`
`fillingPercentageRecycler${it * 2}`
]
}
capacity={getCashUnitCapacity(machine.model, 'stacker')}
capacity={getCashUnitCapacity(machine.model, 'recycler')}
/>
</div>
</div>
{it !== machine.numberOfStackers && (
{it !== machine.numberOfRecyclers && (
<span className={classes.verticalLine} />
)}
</>
))(R.range(1, machine.numberOfStackers + 1))}
))(R.range(1, machine.numberOfRecyclers / 2 + 1))}
</>
)}
</div>

View file

@ -40,15 +40,15 @@ const GET_MACHINES_AND_CONFIG = gql`
cassette2
cassette3
cassette4
stacker1f
stacker1r
stacker2f
stacker2r
stacker3f
stacker3r
recycler1
recycler2
recycler3
recycler4
recycler5
recycler6
}
numberOfCassettes
numberOfStackers
numberOfRecyclers
}
unpairedMachines {
id: deviceId
@ -84,12 +84,12 @@ const SET_CASSETTE_BILLS = gql`
cassette2
cassette3
cassette4
stacker1f
stacker1r
stacker2f
stacker2r
stacker3f
stacker3r
recycler1
recycler2
recycler3
recycler4
recycler5
recycler6
}
}
}

View file

@ -43,29 +43,29 @@ const CashCassettesFooter = ({
acc,
{
cashUnits: {
stacker1f,
stacker1r,
stacker2f,
stacker2r,
stacker3f,
stacker3r
recycler1,
recycler2,
recycler3,
recycler4,
recycler5,
recycler6
},
id
}
) => {
const stacker1fDenomination = getCashoutSettings(id).stacker1f ?? 0
const stacker1rDenomination = getCashoutSettings(id).stacker1r ?? 0
const stacker2fDenomination = getCashoutSettings(id).stacker2f ?? 0
const stacker2rDenomination = getCashoutSettings(id).stacker2r ?? 0
const stacker3fDenomination = getCashoutSettings(id).stacker3f ?? 0
const stacker3rDenomination = getCashoutSettings(id).stacker3r ?? 0
const recycler1Denomination = getCashoutSettings(id).recycler1 ?? 0
const recycler2Denomination = getCashoutSettings(id).recycler2 ?? 0
const recycler3Denomination = getCashoutSettings(id).recycler3 ?? 0
const recycler4Denomination = getCashoutSettings(id).recycler4 ?? 0
const recycler5Denomination = getCashoutSettings(id).recycler5 ?? 0
const recycler6Denomination = getCashoutSettings(id).recycler6 ?? 0
return [
(acc[0] += stacker1f * stacker1fDenomination),
(acc[1] += stacker1r * stacker1rDenomination),
(acc[0] += stacker2f * stacker2fDenomination),
(acc[1] += stacker2r * stacker2rDenomination),
(acc[0] += stacker3f * stacker3fDenomination),
(acc[1] += stacker3r * stacker3rDenomination)
(acc[0] += recycler1 * recycler1Denomination),
(acc[1] += recycler2 * recycler2Denomination),
(acc[0] += recycler3 * recycler3Denomination),
(acc[1] += recycler4 * recycler4Denomination),
(acc[0] += recycler5 * recycler5Denomination),
(acc[1] += recycler6 * recycler6Denomination)
]
}

View file

@ -31,12 +31,12 @@ const GET_MACHINES = gql`
cassette2
cassette3
cassette4
stacker1f
stacker1r
stacker2f
stacker2r
stacker3f
stacker3r
recycler1
recycler2
recycler3
recycler4
recycler5
recycler6
}
version
model

View file

@ -18,13 +18,13 @@ const CASSETTE_FIELDS = R.map(
R.range(1, MAX_NUMBER_OF_CASSETTES + 1)
)
const STACKER_FIELDS = [
'stacker1f',
'stacker1r',
'stacker2f',
'stacker2r',
'stacker3f',
'stacker3r'
const RECYCLER_FIELDS = [
'recycler1',
'recycler2',
'recycler3',
'recycler4',
'recycler5',
'recycler6'
]
const Wizard = ({ machine, cashoutSettings, locale, onClose, save, error }) => {
@ -37,9 +37,9 @@ const Wizard = ({ machine, cashoutSettings, locale, onClose, save, error }) => {
R.isEmpty(cashoutSettings) || !cashoutSettings?.active
const numberOfCassettes = isCashOutDisabled ? 0 : machine.numberOfCassettes
const numberOfStackers = machine.numberOfStackers
const numberOfRecyclers = machine.numberOfRecyclers
// const LAST_STEP = numberOfCassettes + numberOfStackers * 2 + 1
// const LAST_STEP = numberOfCassettes + numberOfRecyclers * 2 + 1
const LAST_STEP = numberOfCassettes + 1
const title = `Update counts`
@ -56,7 +56,7 @@ const Wizard = ({ machine, cashoutSettings, locale, onClose, save, error }) => {
)
}
const buildStackerObj = cassetteInput => {
const buildRecyclerObj = cassetteInput => {
return R.reduce(
(acc, value) => {
acc[value] = machine.cashUnits[value]
@ -64,7 +64,7 @@ const Wizard = ({ machine, cashoutSettings, locale, onClose, save, error }) => {
return acc
},
{},
STACKER_FIELDS
RECYCLER_FIELDS
)
}
@ -77,12 +77,12 @@ const Wizard = ({ machine, cashoutSettings, locale, onClose, save, error }) => {
].includes('YES')
const cassettes = buildCassetteObj(it)
const stackers = buildStackerObj(it)
const recyclers = buildRecyclerObj(it)
const cashUnits = {
cashbox: wasCashboxEmptied ? 0 : machine?.cashUnits.cashbox,
...cassettes,
...stackers
...recyclers
}
save(machine.id, cashUnits)
@ -184,21 +184,21 @@ const Wizard = ({ machine, cashoutSettings, locale, onClose, save, error }) => {
)
: {}
const makeStackersInitialValues = () =>
const makeRecyclersInitialValues = () =>
!R.isEmpty(cashoutSettings)
? R.reduce(
(acc, value) => {
acc[`stacker${value}f`] = ''
acc[`stacker${value}r`] = ''
acc[`recycler${value * 2 - 1}`] = ''
acc[`recycler${value * 2}`] = ''
return acc
},
{},
R.range(1, numberOfStackers + 1)
R.range(1, numberOfRecyclers + 1)
)
: {}
const makeInitialValues = () =>
R.merge(makeCassettesInitialValues(), makeStackersInitialValues())
R.merge(makeCassettesInitialValues(), makeRecyclersInitialValues())
const steps = R.pipe(
// R.concat(makeStackerSteps(numberOfStackers)),

View file

@ -108,7 +108,7 @@ const useStyles = makeStyles(styles)
const CASHBOX_STEP = 1
const cassetesArtworks = (step, numberOfCassettes, numberOfStackers) => {
const cassetesArtworks = (step, numberOfCassettes, numberOfRecyclers) => {
const cassetteStepsStart = CASHBOX_STEP + 1
return [
[cassetteOne],
@ -118,7 +118,7 @@ const cassetesArtworks = (step, numberOfCassettes, numberOfStackers) => {
][numberOfCassettes - cassetteStepsStart + 1][step - cassetteStepsStart]
}
const getCashUnitFieldName = (step, numberOfCassettes, numberOfStackers) => {
const getCashUnitFieldName = (step, numberOfCassettes, numberOfRecyclers) => {
if (step === CASHBOX_STEP) return { name: 'cashbox', category: 'cashbox' }
const cassetteStepsStart = CASHBOX_STEP + 1
if (step < cassetteStepsStart + numberOfCassettes)
@ -126,13 +126,11 @@ const getCashUnitFieldName = (step, numberOfCassettes, numberOfStackers) => {
name: `cassette${step - cassetteStepsStart + 1}`,
category: 'cassette'
}
const stackerStepsStart = CASHBOX_STEP + numberOfCassettes + 1
if (step < stackerStepsStart + numberOfStackers * 2)
const recyclerStepsStart = CASHBOX_STEP + numberOfCassettes + 1
if (step < recyclerStepsStart + numberOfRecyclers * 2)
return {
name: `stacker${Math.ceil((step - stackerStepsStart + 1) / 2)}${
(step - stackerStepsStart) % 2 === 0 ? 'f' : 'r'
}`,
category: 'stacker'
name: `recycler${Math.ceil(step - recyclerStepsStart + 1)}`,
category: 'recycler'
}
}
@ -158,11 +156,11 @@ const WizardStep = ({
]
const numberOfCassettes = machine.numberOfCassettes
const numberOfStackers = machine.numberOfStackers
const numberOfRecyclers = machine.numberOfRecyclers
const {
name: cashUnitField,
category: cashUnitCategory
} = getCashUnitFieldName(step, numberOfCassettes, numberOfStackers)
} = getCashUnitFieldName(step, numberOfCassettes, numberOfRecyclers)
const originalCashUnitCount = machine?.cashUnits?.[cashUnitField]
const cashUnitDenomination = cashoutSettings?.[cashUnitField]
@ -268,7 +266,7 @@ const WizardStep = ({
src={cassetesArtworks(
step,
numberOfCassettes,
numberOfStackers
numberOfRecyclers
)}></img>
<div className={classes.formWrapper}>
<div
@ -293,7 +291,7 @@ const WizardStep = ({
{startCase(cashUnitField)} (
{R.includes('cassette', cashUnitField)
? `dispenser`
: R.includes('stacker', cashUnitField)
: R.includes('recycler', cashUnitField)
? `recycler`
: ``}
)

View file

@ -78,36 +78,40 @@ const getElements = (
<CashOutLite
width={'100%'}
currency={{ code: fiatCurrency }}
notes={m.cashUnits[`stacker${it}f`]}
notes={m.cashUnits[`recycler${it * 2 - 1}`]}
denomination={
getCashoutSettings(m.id ?? m.deviceId)[`stacker${it}f`]
getCashoutSettings(m.id ?? m.deviceId)[
`recycler${it * 2 - 1}`
]
}
threshold={
fillingPercentageSettings[
`fillingPercentageStacker${it}f`
`fillingPercentageRecycler${it * 2 - 1}`
]
}
capacity={getCashUnitCapacity(m.model, 'stacker')}
capacity={getCashUnitCapacity(m.model, 'recycler')}
/>
<CashOutLite
width={'100%'}
currency={{ code: fiatCurrency }}
notes={m.cashUnits[`stacker${it}r`]}
notes={m.cashUnits[`recycler${it * 2}`]}
denomination={
getCashoutSettings(m.id ?? m.deviceId)[`stacker${it}r`]
getCashoutSettings(m.id ?? m.deviceId)[
`recycler${it * 2}`
]
}
threshold={
fillingPercentageSettings[
`fillingPercentageStacker${it}r`
`fillingPercentageRecycler${it * 2}`
]
}
capacity={getCashUnitCapacity(m.model, 'stacker')}
capacity={getCashUnitCapacity(m.model, 'recycler')}
/>
{it !== m.numberOfStackers && (
{it !== m.numberOfRecyclers && (
<span className={classes.verticalLine} />
)}
</>
))(R.range(1, m.numberOfStackers + 1))}
))(R.range(1, m.numberOfRecyclers / 2 + 1))}
</div>
</div>
)

View file

@ -36,7 +36,7 @@ const GET_INFO = gql`
name
deviceId
numberOfCassettes
numberOfStackers
numberOfRecyclers
}
cryptoCurrencies {
code

View file

@ -21,7 +21,7 @@ const CASH_IN_KEY = 'fiatBalanceAlertsCashIn'
const CASH_OUT_KEY = 'fiatBalanceAlertsCashOut'
const RECYCLER_STACKER_KEY = 'fiatBalanceAlertsRecyclerStacker'
const DEFAULT_NUMBER_OF_CASSETTES = 2
const DEFAULT_NUMBER_OF_STACKERS = 0
const DEFAULT_NUMBER_OF_RECYCLERS = 0
const notesMin = 0
const notesMax = 9999999
@ -41,9 +41,9 @@ const FiatBalance = ({ section, min = 0, max = 100, fieldWidth = 80 }) => {
DEFAULT_NUMBER_OF_CASSETTES
)
const maxNumberOfStackers = Math.max(
...R.map(it => it.numberOfStackers, machines),
DEFAULT_NUMBER_OF_STACKERS
const maxNumberOfRecyclers = Math.max(
...R.map(it => it.numberOfRecyclers, machines),
DEFAULT_NUMBER_OF_RECYCLERS
)
const percentValidation = Yup.number()
@ -64,12 +64,12 @@ const FiatBalance = ({ section, min = 0, max = 100, fieldWidth = 80 }) => {
fillingPercentageCassette2: percentValidation,
fillingPercentageCassette3: percentValidation,
fillingPercentageCassette4: percentValidation,
fillingPercentageStacker1f: percentValidation,
fillingPercentageStacker1r: percentValidation,
fillingPercentageStacker2f: percentValidation,
fillingPercentageStacker2r: percentValidation,
fillingPercentageStacker3f: percentValidation,
fillingPercentageStacker3r: percentValidation
fillingPercentageRecycler1: percentValidation,
fillingPercentageRecycler2: percentValidation,
fillingPercentageRecycler3: percentValidation,
fillingPercentageRecycler4: percentValidation,
fillingPercentageRecycler5: percentValidation,
fillingPercentageRecycler6: percentValidation
})
return (
@ -83,12 +83,12 @@ const FiatBalance = ({ section, min = 0, max = 100, fieldWidth = 80 }) => {
fillingPercentageCassette2: data?.fillingPercentageCassette2 ?? '',
fillingPercentageCassette3: data?.fillingPercentageCassette3 ?? '',
fillingPercentageCassette4: data?.fillingPercentageCassette4 ?? '',
fillingPercentageStacker1f: data?.fillingPercentageStacker1f ?? '',
fillingPercentageStacker1r: data?.fillingPercentageStacker1r ?? '',
fillingPercentageStacker2f: data?.fillingPercentageStacker2f ?? '',
fillingPercentageStacker2r: data?.fillingPercentageStacker2r ?? '',
fillingPercentageStacker3f: data?.fillingPercentageStacker3f ?? '',
fillingPercentageStacker3r: data?.fillingPercentageStacker3r ?? ''
fillingPercentageRecycler1: data?.fillingPercentageRecycler1 ?? '',
fillingPercentageRecycler2: data?.fillingPercentageRecycler2 ?? '',
fillingPercentageRecycler3: data?.fillingPercentageRecycler3 ?? '',
fillingPercentageRecycler4: data?.fillingPercentageRecycler4 ?? '',
fillingPercentageRecycler5: data?.fillingPercentageRecycler5 ?? '',
fillingPercentageRecycler6: data?.fillingPercentageRecycler6 ?? ''
}}
validationSchema={schema}
onSubmit={it => save(section, schema.cast(it))}
@ -169,7 +169,7 @@ const FiatBalance = ({ section, min = 0, max = 100, fieldWidth = 80 }) => {
<Form className={classes.form}>
<PromptWhenDirty />
<Header
title="Cash recycling (stackers)"
title="Cash recycling"
editing={isEditing(RECYCLER_STACKER_KEY)}
disabled={isDisabled(RECYCLER_STACKER_KEY)}
setEditing={it => setEditing(RECYCLER_STACKER_KEY, it)}
@ -183,8 +183,9 @@ const FiatBalance = ({ section, min = 0, max = 100, fieldWidth = 80 }) => {
labelClassName={classes.cashboxLabel}
emptyPartClassName={classes.cashboxEmptyPart}
percent={
values[`fillingPercentageStacker${it + 1}f`] ??
data[`stacker${it + 1}f`]
values[
`fillingPercentageRecycler${(it + 1) * 2 - 1}`
] ?? data[`recycler${(it + 1) * 2 - 1}f`]
}
applyColorVariant
applyFiatBalanceAlertsStyling
@ -192,10 +193,12 @@ const FiatBalance = ({ section, min = 0, max = 100, fieldWidth = 80 }) => {
cashOut
/>
<div className={classes.col2}>
<TL2 className={classes.title}>Stacker {it + 1}F</TL2>
<TL2 className={classes.title}>
Recycler {(it + 1) * 2 - 1}
</TL2>
<EditableNumber
label="Alert me under"
name={`fillingPercentageStacker${it + 1}f`}
name={`fillingPercentageRecycler${(it + 1) * 2 - 1}`}
editing={isEditing(RECYCLER_STACKER_KEY)}
displayValue={x => (x === '' ? '-' : x)}
decoration="%"
@ -210,8 +213,8 @@ const FiatBalance = ({ section, min = 0, max = 100, fieldWidth = 80 }) => {
labelClassName={classes.cashboxLabel}
emptyPartClassName={classes.cashboxEmptyPart}
percent={
values[`fillingPercentageStacker${it + 1}r`] ??
data[`stacker${it + 1}r`]
values[`fillingPercentageRecycler${(it + 1) * 2}`] ??
data[`recycler${(it + 1) * 2}`]
}
applyColorVariant
applyFiatBalanceAlertsStyling
@ -219,10 +222,12 @@ const FiatBalance = ({ section, min = 0, max = 100, fieldWidth = 80 }) => {
cashOut
/>
<div className={classes.col2}>
<TL2 className={classes.title}>Stacker {it + 1}R</TL2>
<TL2 className={classes.title}>
Recycler {(it + 1) * 2}
</TL2>
<EditableNumber
label="Alert me under"
name={`fillingPercentageStacker${it + 1}r`}
name={`fillingPercentageRecycler${(it + 1) * 2}`}
editing={isEditing(RECYCLER_STACKER_KEY)}
displayValue={x => (x === '' ? '-' : x)}
decoration="%"
@ -232,7 +237,7 @@ const FiatBalance = ({ section, min = 0, max = 100, fieldWidth = 80 }) => {
</div>
</>
],
R.times(R.identity, maxNumberOfStackers)
R.times(R.identity, maxNumberOfRecyclers)
)}
</div>
</Form>

View file

@ -22,7 +22,7 @@ const cashUnitCapacity = {
},
aveiro: {
cashbox: 1500,
stacker: 60,
recycler: 60,
escrow: 20,
cassette: 500
},

11481
package-lock.json generated

File diff suppressed because it is too large Load diff