feat: add support for more than two cassettes

This commit is contained in:
Sérgio Salgado 2021-09-01 00:54:51 +01:00
parent 478af0fad0
commit 5788b6216d
13 changed files with 279 additions and 50 deletions

View file

@ -23,6 +23,8 @@ function getMachines () {
cashbox: r.cashbox,
cassette1: r.cassette1,
cassette2: r.cassette2,
cassette3: r.cassette3,
cassette4: r.cassette4,
version: r.version,
model: r.model,
pairedAt: new Date(r.created),
@ -100,6 +102,8 @@ function getMachine (machineId, config) {
cashbox: r.cashbox,
cassette1: r.cassette1,
cassette2: r.cassette2,
cassette3: r.cassette3,
cassette4: r.cassette4,
version: r.version,
model: r.model,
pairedAt: new Date(r.created),
@ -133,8 +137,8 @@ function emptyCashInBills (rec) {
}
function setCassetteBills (rec) {
const sql = 'update devices set cashbox=$1, cassette1=$2, cassette2=$3 where device_id=$4'
return db.none(sql, [rec.cashbox, rec.cassettes[0], rec.cassettes[1], rec.deviceId])
const sql = 'update devices set cashbox=$1, cassette1=$2, cassette2=$3, cassette3=$4, cassette4=$5 where device_id=$6'
return db.none(sql, [rec.cashbox, rec.cassettes[0], rec.cassettes[1], rec.cassettes[2], rec.cassettes[3], rec.deviceId])
}
function unpair (rec) {

View file

@ -77,6 +77,8 @@ const typeDefs = gql`
cashbox: Int
cassette1: Int
cassette2: Int
cassette3: Int
cassette4: Int
statuses: [MachineStatus]
latestEvent: MachineEvent
downloadSpeed: String
@ -333,7 +335,7 @@ const typeDefs = gql`
}
type Mutation {
machineAction(deviceId:ID!, action: MachineAction!, cashbox: Int, cassette1: Int, cassette2: Int, newName: String): Machine
machineAction(deviceId:ID!, action: MachineAction!, cashbox: Int, cassette1: Int, cassette2: Int, cassette3: Int, cassette4: Int, newName: String): Machine
setCustomer(customerId: ID!, customerInput: CustomerInput): Customer
saveConfig(config: JSONObject): JSONObject
# resetConfig(schemaVersion: Int): JSONObject
@ -421,7 +423,7 @@ const resolvers = {
bills: () => bills.getBills()
},
Mutation: {
machineAction: (...[, { deviceId, action, cashbox, cassette1, cassette2, newName }]) => machineAction({ deviceId, action, cashbox, cassette1, cassette2, newName }),
machineAction: (...[, { deviceId, action, cashbox, cassette1, cassette2, cassette3, cassette4, newName }]) => machineAction({ deviceId, action, cashbox, cassette1, cassette2, cassette3, cassette4, newName }),
createPairingTotem: (...[, { name }]) => pairing.totem(name),
saveAccounts: (...[, { accounts }]) => settingsLoader.saveAccounts(accounts),
// resetAccounts: (...[, { schemaVersion }]) => settingsLoader.resetAccounts(schemaVersion),

View file

@ -110,7 +110,14 @@ function plugins (settings, deviceId) {
const sumTxs = (sum, tx) => {
const bills = tx.bills
const sameDenominations = a => a[0].denomination === a[1].denomination
const doDenominationsMatch = _.every(sameDenominations, _.zip(cassettes, bills))
console.log('tx', tx)
console.log('cassettes', cassettes)
console.log('bills', bills)
const doDenominationsMatch = _.every(it => {
console.log('it', it)
return sameDenominations(it)
}, _.zip(cassettes, bills))
if (!doDenominationsMatch) {
throw new Error('Denominations don\'t add up, cassettes were changed.')
@ -119,7 +126,7 @@ function plugins (settings, deviceId) {
return _.map(r => r[0] + r[1].provisioned, _.zip(sum, tx.bills))
}
const provisioned = _.reduce(sumTxs, [0, 0], redeemableTxs)
const provisioned = _.reduce(sumTxs, Array(_.size(cassettes)).fill(0), redeemableTxs)
const zipped = _.zip(_.map('count', cassettes), provisioned)
const counts = _.map(r => r[0] - r[1], zipped)
@ -144,9 +151,9 @@ function plugins (settings, deviceId) {
if (!cashOutConfig.active) return Promise.resolve()
const denominations = [cashOutConfig.top, cashOutConfig.bottom]
const denominations = [cashOutConfig.cassette1, cashOutConfig.cassette2, cashOutConfig.cassette3, cashOutConfig.cassette4]
const virtualCassettes = [Math.max(cashOutConfig.top, cashOutConfig.bottom) * 2]
const virtualCassettes = [Math.max(...denominations) * 2]
return Promise.all([dbm.cassetteCounts(deviceId), cashOutHelper.redeemableTxs(deviceId, excludeTxId)])
.then(([rec, _redeemableTxs]) => {
@ -164,6 +171,14 @@ function plugins (settings, deviceId) {
{
denomination: parseInt(denominations[1], 10),
count: parseInt(counts[1], 10)
},
{
denomination: parseInt(denominations[2], 10),
count: parseInt(counts[2], 10)
},
{
denomination: parseInt(denominations[3], 10),
count: parseInt(counts[3], 10)
}
]
@ -245,6 +260,8 @@ function plugins (settings, deviceId) {
const coinsWithoutRate = _.map(mapCoinSettings, coinParams)
const areThereAvailablePromoCodes = arr[arr.length - 1] > 0
console.log('cassettes', cassettes)
return {
cassettes,
rates: buildRates(tickers),
@ -305,7 +322,7 @@ function plugins (settings, deviceId) {
function dispenseAck (tx) {
const cashOutConfig = configManager.getCashOut(deviceId, settings.config)
const cassettes = [cashOutConfig.top, cashOutConfig.bottom]
const cassettes = [cashOutConfig.cassette1, cashOutConfig.cassette2, cashOutConfig.cassette3, cashOutConfig.cassette4]
return dbm.addDispense(deviceId, tx, cassettes)
}
@ -560,8 +577,10 @@ function plugins (settings, deviceId) {
function checkDeviceCashBalances (fiatCode, device) {
const cashOutConfig = configManager.getCashOut(device.deviceId, settings.config)
const denomination1 = cashOutConfig.top
const denomination2 = cashOutConfig.bottom
const denomination1 = cashOutConfig.cassette1
const denomination2 = cashOutConfig.cassette2
const denomination3 = cashOutConfig.cassette3
const denomination4 = cashOutConfig.cassette4
const cashOutEnabled = cashOutConfig.active
const notifications = configManager.getNotifications(null, device.deviceId, settings.config)
@ -601,7 +620,31 @@ function plugins (settings, deviceId) {
}
: null
return _.compact([cashInAlert, cassette1Alert, cassette2Alert])
const cassette3Alert = cashOutEnabled && device.cassette3 < notifications.fiatBalanceCassette3
? {
code: 'LOW_CASH_OUT',
cassette: 3,
machineName,
deviceId: device.deviceId,
notes: device.cassette3,
denomination: denomination3,
fiatCode
}
: null
const cassette4Alert = cashOutEnabled && device.cassette4 < notifications.fiatBalanceCassette4
? {
code: 'LOW_CASH_OUT',
cassette: 4,
machineName,
deviceId: device.deviceId,
notes: device.cassette4,
denomination: denomination4,
fiatCode
}
: null
return _.compact([cashInAlert, cassette1Alert, cassette2Alert, cassette3Alert, cassette4Alert])
}
function checkCryptoBalances (fiatCode, devices) {

View file

@ -7,7 +7,7 @@ const NAME = 'FakeWallet'
const SECONDS = 1000
const PUBLISH_TIME = 3 * SECONDS
const AUTHORIZE_TIME = PUBLISH_TIME + 5 * SECONDS
const CONFIRM_TIME = AUTHORIZE_TIME + 120 * SECONDS
const CONFIRM_TIME = AUTHORIZE_TIME + 10 * SECONDS
let t0

View file

@ -26,12 +26,12 @@ exports.recordDeviceEvent = function recordDeviceEvent (deviceId, event) {
}
exports.cassetteCounts = function cassetteCounts (deviceId) {
const sql = 'SELECT cassette1, cassette2 FROM devices ' +
const sql = 'SELECT cassette1, cassette2, cassette3, cassette4 FROM devices ' +
'WHERE device_id=$1'
return db.one(sql, [deviceId])
.then(row => {
const counts = [row.cassette1, row.cassette2]
const counts = [row.cassette1, row.cassette2, row.cassette3, row.cassette4]
return {counts}
})
}

View file

@ -0,0 +1,39 @@
var db = require('./db')
const _ = require('lodash/fp')
const { saveConfig, loadLatest } = require('../lib/new-settings-loader')
const { getMachines } = require('../lib/machine-loader')
exports.up = function (next) {
var sql = [
'ALTER TABLE devices ADD COLUMN cassette3 INTEGER NOT NULL DEFAULT 0',
'ALTER TABLE devices ADD COLUMN cassette4 INTEGER NOT NULL DEFAULT 0',
'ALTER TABLE cash_out_txs ADD COLUMN provisioned_3 INTEGER',
'ALTER TABLE cash_out_txs ADD COLUMN provisioned_4 INTEGER',
'ALTER TABLE cash_out_txs ADD COLUMN denomination_3 INTEGER',
'ALTER TABLE cash_out_txs ADD COLUMN denomination_4 INTEGER'
]
return Promise.all([loadLatest(), getMachines()])
.then(([config, machines]) => {
const formattedMachines = _.map(it => _.pick(['deviceId'], it), machines)
const newConfig = _.reduce((acc, value) => {
if(_.includes(`cashOut_${value.deviceId}_top`, _.keys(config.config))) {
acc[`cashOut_${value.deviceId}_cassette1`] = config.config[`cashOut_${value.deviceId}_top`]
}
if(_.includes(`cashOut_${value.deviceId}_bottom`, _.keys(config.config))) {
acc[`cashOut_${value.deviceId}_cassette2`] = config.config[`cashOut_${value.deviceId}_bottom`]
}
return acc
}, {}, formattedMachines)
return saveConfig(newConfig)
.then(() => db.multi(sql, next))
.catch(err => next(err))
})
}
exports.down = function (next) {
next()
}

View file

@ -41,6 +41,8 @@ const GET_INFO = gql`
cashbox
cassette1
cassette2
cassette3
cassette4
}
config
}
@ -62,6 +64,9 @@ const CashOut = ({ name: SCREEN_KEY }) => {
}
const config = data?.config && fromNamespace(SCREEN_KEY)(data.config)
console.log('config', config)
const fudgeFactorActive = config?.fudgeFactorActive ?? false
const locale = data?.config && fromNamespace('locale')(data.config)
const machines = data?.machines ?? []

View file

@ -11,7 +11,7 @@ import WizardSplash from './WizardSplash'
import WizardStep from './WizardStep'
import { DenominationsSchema } from './helper'
const LAST_STEP = 4
const LAST_STEP = 6
const MODAL_WIDTH = 554
const MODAL_HEIGHT = 520
@ -52,8 +52,8 @@ const Wizard = ({ machine, locale, onClose, save, error }) => {
const steps = [
{
type: 'top',
display: 'Cassette 1 (Top)',
type: 'cassette1',
display: 'Cassette 1',
component: Autocomplete,
inputProps: {
options: R.map(it => ({ code: it, display: it }))(options),
@ -62,7 +62,7 @@ const Wizard = ({ machine, locale, onClose, save, error }) => {
}
},
{
type: 'bottom',
type: 'cassette2',
display: 'Cassette 2',
component: Autocomplete,
inputProps: {
@ -71,6 +71,26 @@ const Wizard = ({ machine, locale, onClose, save, error }) => {
valueProp: 'code'
}
},
{
type: 'cassette3',
display: 'Cassette 3',
component: Autocomplete,
inputProps: {
options: R.map(it => ({ code: it, display: it }))(options),
labelProp: 'display',
valueProp: 'code'
}
},
{
type: 'cassette4',
display: 'Cassette 4',
component: Autocomplete,
inputProps: {
options: R.map(it => ({ code: it, display: it }))(options),
labelProp: 'display',
valueProp: 'code'
}
},
{
type: 'zeroConfLimit',
display: '0-conf Limit',
@ -82,8 +102,10 @@ const Wizard = ({ machine, locale, onClose, save, error }) => {
const schema = () =>
Yup.object().shape({
top: Yup.number().required(),
bottom: step >= 2 ? Yup.number().required() : Yup.number()
cassette1: Yup.number().required(),
cassette2: step >= 2 ? Yup.number().required() : Yup.number(),
cassette3: step >= 3 ? Yup.number().required() : Yup.number(),
cassette4: step >= 4 ? Yup.number().required() : Yup.number()
})
return (

View file

@ -31,22 +31,29 @@ const WizardStep = ({
const cassetesArtworks = {
1: cassetteOne,
2: cassetteTwo
2: cassetteTwo,
3: cassetteOne,
4: cassetteTwo
}
return (
<div className={classes.content}>
<div className={classes.titleDiv}>
<Info2 className={classes.title}>{name}</Info2>
<Stepper steps={4} currentStep={step} />
<Stepper steps={6} currentStep={step} />
</div>
{step <= 2 && (
{step <= 4 && (
<Formik
validateOnBlur={false}
validateOnChange={false}
onSubmit={onContinue}
initialValues={{ top: '', bottom: '' }}
initialValues={{
cassette1: '',
cassette2: '',
cassette3: '',
cassette4: ''
}}
enableReinitialize
validationSchema={schema}>
<Form>
@ -95,7 +102,7 @@ const WizardStep = ({
</Formik>
)}
{step === 3 && (
{step === 5 && (
<Formik
validateOnBlur={false}
validateOnChange={false}

View file

@ -4,13 +4,23 @@ import { NumberInput } from 'src/components/inputs/formik'
const currencyMax = 999999999
const DenominationsSchema = Yup.object().shape({
top: Yup.number()
.label('Cassette 1 (Top)')
cassette1: Yup.number()
.label('Cassette 1')
.required()
.min(1)
.max(currencyMax),
bottom: Yup.number()
.label('Cassette 2 (Bottom)')
cassette2: Yup.number()
.label('Cassette 2')
.required()
.min(1)
.max(currencyMax),
cassette3: Yup.number()
.label('Cassette 3')
.required()
.min(1)
.max(currencyMax),
cassette4: Yup.number()
.label('Cassette 4')
.required()
.min(1)
.max(currencyMax),
@ -32,8 +42,8 @@ const getElements = (machines, { fiatCurrency } = {}) => {
editable: false
},
{
name: 'top',
header: 'Cassette 1 (Top)',
name: 'cassette1',
header: 'Cassette 1',
size: 'sm',
stripe: true,
width: 200,
@ -45,8 +55,34 @@ const getElements = (machines, { fiatCurrency } = {}) => {
suffix: fiatCurrency
},
{
name: 'bottom',
header: 'Cassette 2 (Bottom)',
name: 'cassette2',
header: 'Cassette 2',
size: 'sm',
stripe: true,
textAlign: 'right',
width: 200,
input: NumberInput,
inputProps: {
decimalPlaces: 0
},
suffix: fiatCurrency
},
{
name: 'cassette3',
header: 'Cassette 3',
size: 'sm',
stripe: true,
textAlign: 'right',
width: 200,
input: NumberInput,
inputProps: {
decimalPlaces: 0
},
suffix: fiatCurrency
},
{
name: 'cassette4',
header: 'Cassette 4',
size: 'sm',
stripe: true,
textAlign: 'right',

View file

@ -90,7 +90,7 @@ const CashCassettes = ({ machine, config, refetchData }) => {
view: (value, { deviceId }) => (
<CashOut
className={classes.cashbox}
denomination={getCashoutSettings(deviceId)?.top}
denomination={getCashoutSettings(deviceId)?.cassette1}
currency={{ code: fiatCurrency }}
notes={value}
/>
@ -109,7 +109,7 @@ const CashCassettes = ({ machine, config, refetchData }) => {
return (
<CashOut
className={classes.cashbox}
denomination={getCashoutSettings(deviceId)?.bottom}
denomination={getCashoutSettings(deviceId)?.cassette2}
currency={{ code: fiatCurrency }}
notes={value}
/>

View file

@ -26,13 +26,25 @@ const ValidationSchema = Yup.object().shape({
.min(0)
.max(1000),
cassette1: Yup.number()
.label('Cassette 1 (top)')
.label('Cassette 1')
.required()
.integer()
.min(0)
.max(500),
cassette2: Yup.number()
.label('Cassette 2 (bottom)')
.label('Cassette 2')
.required()
.integer()
.min(0)
.max(500),
cassette3: Yup.number()
.label('Cassette 3')
.required()
.integer()
.min(0)
.max(500),
cassette4: Yup.number()
.label('Cassette 4')
.required()
.integer()
.min(0)
@ -47,6 +59,8 @@ const GET_MACHINES_AND_CONFIG = gql`
cashbox
cassette1
cassette2
cassette3
cassette4
}
config
}
@ -69,6 +83,8 @@ const SET_CASSETTE_BILLS = gql`
$cashbox: Int!
$cassette1: Int!
$cassette2: Int!
$cassette3: Int!
$cassette4: Int!
) {
machineAction(
deviceId: $deviceId
@ -76,11 +92,15 @@ const SET_CASSETTE_BILLS = gql`
cashbox: $cashbox
cassette1: $cassette1
cassette2: $cassette2
cassette3: $cassette3
cassette4: $cassette4
) {
deviceId
cashbox
cassette1
cassette2
cassette3
cassette4
}
}
`
@ -103,14 +123,18 @@ const CashCassettes = () => {
const locale = data?.config && fromNamespace('locale')(data.config)
const fiatCurrency = locale?.fiatCurrency
const onSave = (...[, { id, cashbox, cassette1, cassette2 }]) => {
const onSave = (
...[, { id, cashbox, cassette1, cassette2, cassette3, cassette4 }]
) => {
return setCassetteBills({
variables: {
action: 'setCassetteBills',
deviceId: id,
cashbox,
cassette1,
cassette2
cassette2,
cassette3,
cassette4
}
})
}
@ -139,13 +163,13 @@ const CashCassettes = () => {
},
{
name: 'cassette1',
header: 'Cassette 1 (Top)',
header: 'Cassette 1',
width: 265,
stripe: true,
view: (value, { id }) => (
<CashOut
className={classes.cashbox}
denomination={getCashoutSettings(id)?.top}
denomination={getCashoutSettings(id)?.cassette1}
currency={{ code: fiatCurrency }}
notes={value}
/>
@ -157,14 +181,54 @@ const CashCassettes = () => {
},
{
name: 'cassette2',
header: 'Cassette 2 (Bottom)',
header: 'Cassette 2',
width: 265,
stripe: true,
view: (value, { id }) => {
return (
<CashOut
className={classes.cashbox}
denomination={getCashoutSettings(id)?.bottom}
denomination={getCashoutSettings(id)?.cassette2}
currency={{ code: fiatCurrency }}
notes={value}
/>
)
},
input: CashCassetteInput,
inputProps: {
decimalPlaces: 0
}
},
{
name: 'cassette3',
header: 'Cassette 3',
width: 265,
stripe: true,
view: (value, { id }) => {
return (
<CashOut
className={classes.cashbox}
denomination={getCashoutSettings(id)?.cassette3}
currency={{ code: fiatCurrency }}
notes={value}
/>
)
},
input: CashCassetteInput,
inputProps: {
decimalPlaces: 0
}
},
{
name: 'cassette4',
header: 'Cassette 4',
width: 265,
stripe: true,
view: (value, { id }) => {
return (
<CashOut
className={classes.cashbox}
denomination={getCashoutSettings(id)?.cassette4}
currency={{ code: fiatCurrency }}
notes={value}
/>

View file

@ -25,16 +25,23 @@ const CashCassettesFooter = ({
const classes = useStyles()
const cashout = config && fromNamespace('cashOut')(config)
const getCashoutSettings = id => fromNamespace(id)(cashout)
const reducerFn = (acc, { cassette1, cassette2, id }) => {
const topDenomination = getCashoutSettings(id).top ?? 0
const bottomDenomination = getCashoutSettings(id).bottom ?? 0
const reducerFn = (
acc,
{ cassette1, cassette2, cassette3, cassette4, id }
) => {
const cassette1Denomination = getCashoutSettings(id).cassette1 ?? 0
const cassette2Denomination = getCashoutSettings(id).cassette2 ?? 0
const cassette3Denomination = getCashoutSettings(id).cassette3 ?? 0
const cassette4Denomination = getCashoutSettings(id).cassette4 ?? 0
return [
(acc[0] += cassette1 * topDenomination),
(acc[1] += cassette2 * bottomDenomination)
(acc[0] += cassette1 * cassette1Denomination),
(acc[1] += cassette2 * cassette2Denomination),
(acc[2] += cassette3 * cassette3Denomination),
(acc[3] += cassette4 * cassette4Denomination)
]
}
const totalInCassettes = R.sum(R.reduce(reducerFn, [0, 0], machines))
const totalInCassettes = R.sum(R.reduce(reducerFn, [0, 0, 0, 0], machines))
/* const totalInCashBox = R.sum(
R.flatten(