diff --git a/lib/bill-math.js b/lib/bill-math.js index 8b143873..c94cc2fb 100644 --- a/lib/bill-math.js +++ b/lib/bill-math.js @@ -1,15 +1,112 @@ const _ = require('lodash/fp') const sumService = require('@haensl/subset-sum') -const getSolution = (units, amount) => { - const billList = _.reduce( - (acc, value) => { - acc.push(..._.times(_.constant(value.denomination), value.count)) - return acc - }, - [], - units - ) +const BILL_LIST_MODES = { + LAST_UNIT_FIRST: 0, + FIRST_UNIT_FIRST: 1, + LOWEST_VALUE_FIRST: 2, + HIGHEST_VALUE_FIRST: 3, + UNIT_ROUND_ROBIN: 4, + VALUE_ROUND_ROBIN: 5 +} + +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 solution = _.countBy(Math.floor, solver.next().value) @@ -39,7 +136,7 @@ const solutionToOriginalUnits = (solution, units) => { } function makeChange(outCassettes, amount) { - const solution = getSolution(outCassettes, amount) + const solution = getSolution(outCassettes, amount, BILL_LIST_MODES.VALUE_ROUND_ROBIN) return solutionToOriginalUnits(solution, outCassettes) } diff --git a/lib/graphql/resolvers.js b/lib/graphql/resolvers.js index 984e090b..ea7100bc 100644 --- a/lib/graphql/resolvers.js +++ b/lib/graphql/resolvers.js @@ -5,6 +5,7 @@ const plugins = require('../plugins') const configManager = require('../new-config-manager') const { batchGetCustomInfoRequest, getCustomInfoRequests } = require('../new-admin/services/customInfoRequests') const state = require('../middlewares/state') +const { getMachine } = require('../machine-loader') const VERSION = require('../../package.json').version @@ -114,6 +115,7 @@ const staticConfig = ({ currentConfigVersion, deviceId, deviceName, pq, settings configManager.getOperatorInfo(settings.config), configManager.getReceipt(settings.config), !!configManager.getCashOut(deviceId, settings.config).active, + getMachine(deviceId, currentConfigVersion), ]) .then(([ enablePaperWalletOnly, @@ -124,6 +126,7 @@ const staticConfig = ({ currentConfigVersion, deviceId, deviceName, pq, settings operatorInfo, receiptInfo, twoWayMode, + { numberOfCassettes, numberOfStackers }, ]) => (currentConfigVersion && currentConfigVersion >= staticConf.configVersion) ? null : @@ -138,7 +141,7 @@ const staticConfig = ({ currentConfigVersion, deviceId, deviceName, pq, settings languages: localeInfo.languages, fiatCode: localeInfo.fiatCurrency }, - machineInfo: { deviceId, deviceName }, + machineInfo: { deviceId, deviceName, numberOfCassettes, numberOfStackers }, twoWayMode, speedtestFiles, urlsToPing, diff --git a/lib/graphql/types.js b/lib/graphql/types.js index 5c96df82..2de2b338 100644 --- a/lib/graphql/types.js +++ b/lib/graphql/types.js @@ -30,6 +30,8 @@ type OperatorInfo { type MachineInfo { deviceId: String! deviceName: String + numberOfCassettes: Int + numberOfStackers: Int } type ReceiptInfo { diff --git a/new-lamassu-admin/src/pages/Machines/MachineComponents/Cassettes/Cassettes.js b/new-lamassu-admin/src/pages/Machines/MachineComponents/Cassettes/Cassettes.js index 23ad89ab..d14db761 100644 --- a/new-lamassu-admin/src/pages/Machines/MachineComponents/Cassettes/Cassettes.js +++ b/new-lamassu-admin/src/pages/Machines/MachineComponents/Cassettes/Cassettes.js @@ -56,7 +56,7 @@ const ValidationSchema = Yup.object().shape({ .required('Required') .integer() .min(0) - .max(60), + .max(40), stacker1r: Yup.number() .label('Stacker 1R') .required('Required') diff --git a/new-lamassu-admin/src/pages/Maintenance/Wizard/Wizard.js b/new-lamassu-admin/src/pages/Maintenance/Wizard/Wizard.js index 2c26fda4..a57151c8 100644 --- a/new-lamassu-admin/src/pages/Maintenance/Wizard/Wizard.js +++ b/new-lamassu-admin/src/pages/Maintenance/Wizard/Wizard.js @@ -129,10 +129,21 @@ const Wizard = ({ machine, cashoutSettings, locale, onClose, save, error }) => { .required() .min(0) .max( - cashUnitCapacity[machine.model].stacker, - `${modelPrettifier[machine.model]} maximum stacker capacity is ${ - cashUnitCapacity[machine.model].stacker - } bills` + i === 1 + ? cashUnitCapacity[machine.model].stacker - + cashUnitCapacity[machine.model].escrow + : 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` ) }) }, diff --git a/new-lamassu-admin/src/pages/Maintenance/helper.js b/new-lamassu-admin/src/pages/Maintenance/helper.js index 3074ca39..1a3f11c2 100644 --- a/new-lamassu-admin/src/pages/Maintenance/helper.js +++ b/new-lamassu-admin/src/pages/Maintenance/helper.js @@ -166,7 +166,12 @@ const getElements = ( denomination={getCashoutSettings(id)?.[`stacker${it}f`]} currency={{ code: fiatCurrency }} notes={cashUnits[`stacker${it}f`]} - capacity={cashUnitCapacity[model].stacker} + capacity={ + it === 1 + ? cashUnitCapacity[model].stacker - + cashUnitCapacity[model].escrow + : cashUnitCapacity[model].stacker + } width={ widthsByCashUnits[getMaxNumberOfCashUnits(machines)]?.unitGraph } diff --git a/new-lamassu-admin/src/utils/machine.js b/new-lamassu-admin/src/utils/machine.js index 42a19490..4684e5ac 100644 --- a/new-lamassu-admin/src/utils/machine.js +++ b/new-lamassu-admin/src/utils/machine.js @@ -15,6 +15,7 @@ const cashUnitCapacity = { aveiro: { cashbox: 1500, stacker: 60, + escrow: 20, cassette: 500 }, tejo: {