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:
parent
f3ab63766e
commit
4655d733b2
7 changed files with 136 additions and 17 deletions
117
lib/bill-math.js
117
lib/bill-math.js
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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')
|
||||||
|
|
|
||||||
|
|
@ -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`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ const cashUnitCapacity = {
|
||||||
aveiro: {
|
aveiro: {
|
||||||
cashbox: 1500,
|
cashbox: 1500,
|
||||||
stacker: 60,
|
stacker: 60,
|
||||||
|
escrow: 20,
|
||||||
cassette: 500
|
cassette: 500
|
||||||
},
|
},
|
||||||
tejo: {
|
tejo: {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue