feat: improve bill math

feat: add escrow storage for aveiro and changes in related UI
feat: add cassettes and stackers count to trader
This commit is contained in:
Sérgio Salgado 2023-04-26 23:44:52 +01:00
parent f3ab63766e
commit 4655d733b2
7 changed files with 136 additions and 17 deletions

View file

@ -1,15 +1,112 @@
const _ = require('lodash/fp') const _ = require('lodash/fp')
const sumService = require('@haensl/subset-sum') const sumService = require('@haensl/subset-sum')
const getSolution = (units, amount) => { const BILL_LIST_MODES = {
const billList = _.reduce( LAST_UNIT_FIRST: 0,
(acc, value) => { FIRST_UNIT_FIRST: 1,
acc.push(..._.times(_.constant(value.denomination), value.count)) LOWEST_VALUE_FIRST: 2,
return acc HIGHEST_VALUE_FIRST: 3,
}, UNIT_ROUND_ROBIN: 4,
[], VALUE_ROUND_ROBIN: 5
units }
)
const buildBillList = (units, mode) => {
switch (mode) {
case BILL_LIST_MODES.LAST_UNIT_FIRST:
return _.reduce(
(acc, value) => {
acc.push(..._.times(_.constant(value.denomination), value.count))
return acc
},
[],
_.reverse(units)
)
case BILL_LIST_MODES.FIRST_UNIT_FIRST:
return _.reduce(
(acc, value) => {
acc.push(..._.times(_.constant(value.denomination), value.count))
return acc
},
[],
units
)
case BILL_LIST_MODES.LOWEST_VALUE_FIRST:
return _.reduce(
(acc, value) => {
acc.push(..._.times(_.constant(value.denomination), value.count))
return acc
},
[],
_.orderBy(['denomination'], ['asc'])(units)
)
case BILL_LIST_MODES.HIGHEST_VALUE_FIRST:
return _.reduce(
(acc, value) => {
acc.push(..._.times(_.constant(value.denomination), value.count))
return acc
},
[],
_.orderBy(['denomination'], ['desc'])(units)
)
case BILL_LIST_MODES.UNIT_ROUND_ROBIN:
{
const amountOfBills = _.reduce(
(acc, value) => acc + value.count,
0,
units
)
const _units = _.cloneDeep(units)
const bills = []
for(let i = 0; i < amountOfBills; i++) {
const idx = i % _.size(_units)
if (_units[idx].count > 0) {
bills.push(_units[idx].denomination)
}
_units[idx].count--
if (_units[idx].count === 0) {
_units.splice(idx, 1)
}
}
return bills
}
case BILL_LIST_MODES.VALUE_ROUND_ROBIN:
{
const amountOfBills = _.reduce(
(acc, value) => acc + value.count,
0,
units
)
const _units = _.orderBy(['denomination'], ['asc'])(_.cloneDeep(units))
const bills = []
for(let i = 0; i < amountOfBills; i++) {
const idx = i % _.size(_units)
if (_units[idx].count > 0) {
bills.push(_units[idx].denomination)
}
_units[idx].count--
if (_units[idx].count === 0) {
_units.splice(idx, 1)
}
}
return bills
}
default:
throw new Error(`Invalid mode: ${mode}`)
}
}
const getSolution = (units, amount, mode) => {
const billList = buildBillList(units, mode)
const solver = sumService.subsetSum(billList, amount.toNumber()) const solver = sumService.subsetSum(billList, amount.toNumber())
const solution = _.countBy(Math.floor, solver.next().value) const solution = _.countBy(Math.floor, solver.next().value)
@ -39,7 +136,7 @@ const solutionToOriginalUnits = (solution, units) => {
} }
function makeChange(outCassettes, amount) { function makeChange(outCassettes, amount) {
const solution = getSolution(outCassettes, amount) const solution = getSolution(outCassettes, amount, BILL_LIST_MODES.VALUE_ROUND_ROBIN)
return solutionToOriginalUnits(solution, outCassettes) return solutionToOriginalUnits(solution, outCassettes)
} }

View file

@ -5,6 +5,7 @@ const plugins = require('../plugins')
const configManager = require('../new-config-manager') const configManager = require('../new-config-manager')
const { batchGetCustomInfoRequest, getCustomInfoRequests } = require('../new-admin/services/customInfoRequests') const { batchGetCustomInfoRequest, getCustomInfoRequests } = require('../new-admin/services/customInfoRequests')
const state = require('../middlewares/state') const state = require('../middlewares/state')
const { getMachine } = require('../machine-loader')
const VERSION = require('../../package.json').version const VERSION = require('../../package.json').version
@ -114,6 +115,7 @@ const staticConfig = ({ currentConfigVersion, deviceId, deviceName, pq, settings
configManager.getOperatorInfo(settings.config), configManager.getOperatorInfo(settings.config),
configManager.getReceipt(settings.config), configManager.getReceipt(settings.config),
!!configManager.getCashOut(deviceId, settings.config).active, !!configManager.getCashOut(deviceId, settings.config).active,
getMachine(deviceId, currentConfigVersion),
]) ])
.then(([ .then(([
enablePaperWalletOnly, enablePaperWalletOnly,
@ -124,6 +126,7 @@ const staticConfig = ({ currentConfigVersion, deviceId, deviceName, pq, settings
operatorInfo, operatorInfo,
receiptInfo, receiptInfo,
twoWayMode, twoWayMode,
{ numberOfCassettes, numberOfStackers },
]) => ]) =>
(currentConfigVersion && currentConfigVersion >= staticConf.configVersion) ? (currentConfigVersion && currentConfigVersion >= staticConf.configVersion) ?
null : null :
@ -138,7 +141,7 @@ const staticConfig = ({ currentConfigVersion, deviceId, deviceName, pq, settings
languages: localeInfo.languages, languages: localeInfo.languages,
fiatCode: localeInfo.fiatCurrency fiatCode: localeInfo.fiatCurrency
}, },
machineInfo: { deviceId, deviceName }, machineInfo: { deviceId, deviceName, numberOfCassettes, numberOfStackers },
twoWayMode, twoWayMode,
speedtestFiles, speedtestFiles,
urlsToPing, urlsToPing,

View file

@ -30,6 +30,8 @@ type OperatorInfo {
type MachineInfo { type MachineInfo {
deviceId: String! deviceId: String!
deviceName: String deviceName: String
numberOfCassettes: Int
numberOfStackers: Int
} }
type ReceiptInfo { type ReceiptInfo {

View file

@ -56,7 +56,7 @@ const ValidationSchema = Yup.object().shape({
.required('Required') .required('Required')
.integer() .integer()
.min(0) .min(0)
.max(60), .max(40),
stacker1r: Yup.number() stacker1r: Yup.number()
.label('Stacker 1R') .label('Stacker 1R')
.required('Required') .required('Required')

View file

@ -129,10 +129,21 @@ const Wizard = ({ machine, cashoutSettings, locale, onClose, save, error }) => {
.required() .required()
.min(0) .min(0)
.max( .max(
cashUnitCapacity[machine.model].stacker, i === 1
`${modelPrettifier[machine.model]} maximum stacker capacity is ${ ? cashUnitCapacity[machine.model].stacker -
cashUnitCapacity[machine.model].stacker cashUnitCapacity[machine.model].escrow
} bills` : cashUnitCapacity[machine.model].stacker,
i === 1
? `${
modelPrettifier[machine.model]
} maximum stacker capacity for the escrow unit is ${cashUnitCapacity[
machine.model
].stacker - cashUnitCapacity[machine.model].escrow} bills`
: `${
modelPrettifier[machine.model]
} maximum stacker capacity is ${
cashUnitCapacity[machine.model].stacker
} bills`
) )
}) })
}, },

View file

@ -166,7 +166,12 @@ const getElements = (
denomination={getCashoutSettings(id)?.[`stacker${it}f`]} denomination={getCashoutSettings(id)?.[`stacker${it}f`]}
currency={{ code: fiatCurrency }} currency={{ code: fiatCurrency }}
notes={cashUnits[`stacker${it}f`]} notes={cashUnits[`stacker${it}f`]}
capacity={cashUnitCapacity[model].stacker} capacity={
it === 1
? cashUnitCapacity[model].stacker -
cashUnitCapacity[model].escrow
: cashUnitCapacity[model].stacker
}
width={ width={
widthsByCashUnits[getMaxNumberOfCashUnits(machines)]?.unitGraph widthsByCashUnits[getMaxNumberOfCashUnits(machines)]?.unitGraph
} }

View file

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