feat: add stacker counter field to the machine

feat: use the stacker amount to properly render the cash cassettes table
feat: add stacker notifications to the plugins backend
This commit is contained in:
Sérgio Salgado 2023-04-17 18:46:24 +01:00
parent 211c4b1ea7
commit 2638bd1717
14 changed files with 519 additions and 138 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-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

@ -35,6 +35,7 @@ function toMachineObject (r) {
stacker3r: r.stacker3r stacker3r: r.stacker3r
}, },
numberOfCassettes: r.number_of_cassettes, numberOfCassettes: r.number_of_cassettes,
numberOfStackers: r.number_of_stackers,
version: r.version, version: r.version,
model: r.model, model: r.model,
pairedAt: new Date(r.created), pairedAt: new Date(r.created),

View file

@ -16,6 +16,7 @@ const typeDef = gql`
model: String model: String
cashUnits: CashUnits cashUnits: CashUnits
numberOfCassettes: Int numberOfCassettes: Int
numberOfStackers: Int
statuses: [MachineStatus] statuses: [MachineStatus]
latestEvent: MachineEvent latestEvent: MachineEvent
downloadSpeed: String downloadSpeed: String

View file

@ -645,6 +645,12 @@ function plugins (settings, deviceId) {
const denomination2 = cashOutConfig.cassette2 const denomination2 = cashOutConfig.cassette2
const denomination3 = cashOutConfig.cassette3 const denomination3 = cashOutConfig.cassette3
const denomination4 = cashOutConfig.cassette4 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 cashOutEnabled = cashOutConfig.active const cashOutEnabled = cashOutConfig.active
const isCassetteLow = (have, max, limit) => cashOutEnabled && ((have / max) * 100) < limit const isCassetteLow = (have, max, limit) => cashOutEnabled && ((have / max) * 100) < limit
@ -709,7 +715,91 @@ function plugins (settings, deviceId) {
} }
: null : null
return _.compact([cashInAlert, cassette1Alert, cassette2Alert, cassette3Alert, cassette4Alert]) const stacker1fAlert = device.numberOfStackers >= 1 && isCassetteLow(device.cashUnits.stacker1f, CASSETTE_MAX_CAPACITY, notifications.fillingPercentageStacker1f)
? {
code: 'LOW_CASH_OUT',
cassette: 4,
machineName,
deviceId: device.deviceId,
notes: device.cashUnits.stacker1f,
denomination: denomination1f,
fiatCode
}
: null
const stacker1rAlert = device.numberOfStackers >= 1 && isCassetteLow(device.cashUnits.stacker1r, CASSETTE_MAX_CAPACITY, notifications.fillingPercentageStacker1r)
? {
code: 'LOW_CASH_OUT',
cassette: 4,
machineName,
deviceId: device.deviceId,
notes: device.cashUnits.stacker1r,
denomination: denomination1r,
fiatCode
}
: null
const stacker2fAlert = device.numberOfStackers >= 2 && isCassetteLow(device.cashUnits.stacker2f, CASSETTE_MAX_CAPACITY, notifications.fillingPercentageStacker2f)
? {
code: 'LOW_CASH_OUT',
cassette: 4,
machineName,
deviceId: device.deviceId,
notes: device.cashUnits.stacker1f,
denomination: denomination1f,
fiatCode
}
: null
const stacker2rAlert = device.numberOfStackers >= 2 && isCassetteLow(device.cashUnits.stacker2r, CASSETTE_MAX_CAPACITY, notifications.fillingPercentageStacker2r)
? {
code: 'LOW_CASH_OUT',
cassette: 4,
machineName,
deviceId: device.deviceId,
notes: device.cashUnits.stacker2r,
denomination: denomination2r,
fiatCode
}
: null
const stacker3fAlert = device.numberOfStackers >= 3 && isCassetteLow(device.cashUnits.stacker3f, CASSETTE_MAX_CAPACITY, notifications.fillingPercentageStacker3f)
? {
code: 'LOW_CASH_OUT',
cassette: 4,
machineName,
deviceId: device.deviceId,
notes: device.cashUnits.stacker3f,
denomination: denomination3f,
fiatCode
}
: null
const stacker3rAlert = device.numberOfStackers >= 3 && isCassetteLow(device.cashUnits.stacker3r, CASSETTE_MAX_CAPACITY, notifications.fillingPercentageStacker3r)
? {
code: 'LOW_CASH_OUT',
cassette: 4,
machineName,
deviceId: device.deviceId,
notes: device.cashUnits.stacker3r,
denomination: denomination3r,
fiatCode
}
: null
return _.compact([
cashInAlert,
cassette1Alert,
cassette2Alert,
cassette3Alert,
cassette4Alert,
stacker1fAlert,
stacker1rAlert,
stacker2fAlert,
stacker2rAlert,
stacker3fAlert,
stacker3rAlert
])
} }
function checkCryptoBalances (fiatCode, devices) { function checkCryptoBalances (fiatCode, devices) {

View file

@ -33,7 +33,8 @@ exports.up = function (next) {
ADD COLUMN stacker2f INTEGER NOT NULL DEFAULT 0, ADD COLUMN stacker2f INTEGER NOT NULL DEFAULT 0,
ADD COLUMN stacker2r INTEGER NOT NULL DEFAULT 0, ADD COLUMN stacker2r INTEGER NOT NULL DEFAULT 0,
ADD COLUMN stacker3f INTEGER NOT NULL DEFAULT 0, ADD COLUMN stacker3f INTEGER NOT NULL DEFAULT 0,
ADD COLUMN stacker3r INTEGER NOT NULL DEFAULT 0`, ADD COLUMN stacker3r INTEGER NOT NULL DEFAULT 0
ADD COLUMN number_of_stackers INTEGER NOT NULL DEFAULT 0`,
`ALTER TABLE cash_out_txs `ALTER TABLE cash_out_txs
ADD COLUMN provisioned_1f INTEGER, ADD COLUMN provisioned_1f INTEGER,
ADD COLUMN provisioned_1r INTEGER, ADD COLUMN provisioned_1r INTEGER,

View file

@ -54,6 +54,7 @@ const GET_INFO = gql`
stacker3r stacker3r
} }
numberOfCassettes numberOfCassettes
numberOfStackers
} }
config config
} }

View file

@ -17,7 +17,7 @@ const MODAL_WIDTH = 554
const MODAL_HEIGHT = 520 const MODAL_HEIGHT = 520
const Wizard = ({ machine, locale, onClose, save, error }) => { const Wizard = ({ machine, locale, onClose, save, error }) => {
const LAST_STEP = machine.numberOfCassettes + 1 const LAST_STEP = machine.numberOfCassettes + machine.numberOfStackers + 1
const [{ step, config }, setState] = useState({ const [{ step, config }, setState] = useState({
step: 0, step: 0,
config: { active: true } config: { active: true }
@ -46,7 +46,8 @@ const Wizard = ({ machine, locale, onClose, save, error }) => {
}) })
} }
const steps = R.map( const steps = R.concat(
R.map(
it => ({ it => ({
type: `cassette${it}`, type: `cassette${it}`,
display: `Cassette ${it}`, display: `Cassette ${it}`,
@ -58,6 +59,35 @@ const Wizard = ({ machine, locale, onClose, save, error }) => {
} }
}), }),
R.range(1, machine.numberOfCassettes + 1) R.range(1, machine.numberOfCassettes + 1)
),
R.chain(
it => [
{
type: `stacker${it}f`,
display: `Stacker ${it}F`,
component: Autocomplete,
inputProps: {
options: options,
labelProp: 'display',
valueProp: 'code'
}
},
{
type: `stacker${it}r`,
display: `Stacker ${it}R`,
component: Autocomplete,
inputProps: {
options: options,
labelProp: 'display',
valueProp: 'code'
}
}
],
R.range(
machine.numberOfCassettes + 1,
machine.numberOfCassettes + machine.numberOfStackers + 1
)
)
) )
const schema = () => const schema = () =>

View file

@ -38,6 +38,7 @@ const GET_DATA = gql`
stacker3r stacker3r
} }
numberOfCassettes numberOfCassettes
numberOfStackers
statuses { statuses {
label label
type type

View file

@ -50,7 +50,43 @@ const ValidationSchema = Yup.object().shape({
.required('Required') .required('Required')
.integer() .integer()
.min(0) .min(0)
.max(500) .max(500),
stacker1f: Yup.number()
.label('Stacker 1F')
.required('Required')
.integer()
.min(0)
.max(60),
stacker1r: Yup.number()
.label('Stacker 1R')
.required('Required')
.integer()
.min(0)
.max(60),
stacker2f: Yup.number()
.label('Stacker 2F')
.required('Required')
.integer()
.min(0)
.max(60),
stacker2r: Yup.number()
.label('Stacker 2R')
.required('Required')
.integer()
.min(0)
.max(60),
stacker3f: Yup.number()
.label('Stacker 3F')
.required('Required')
.integer()
.min(0)
.max(60),
stacker3r: Yup.number()
.label('Stacker 3R')
.required('Required')
.integer()
.min(0)
.max(60)
}) })
const SET_CASSETTE_BILLS = gql` const SET_CASSETTE_BILLS = gql`

View file

@ -43,6 +43,7 @@ const GET_INFO = gql`
stacker3r stacker3r
} }
numberOfCassettes numberOfCassettes
numberOfStackers
statuses { statuses {
label label
type type

View file

@ -10,8 +10,6 @@ import Modal from 'src/components/Modal'
import { IconButton, Button } from 'src/components/buttons' import { IconButton, Button } from 'src/components/buttons'
import { Table as EditableTable } from 'src/components/editableTable' import { Table as EditableTable } from 'src/components/editableTable'
import { RadioGroup } from 'src/components/inputs' import { RadioGroup } from 'src/components/inputs'
import { CashOut, CashIn } from 'src/components/inputs/cashbox/Cashbox'
import { NumberInput, CashCassetteInput } from 'src/components/inputs/formik'
import TitleSection from 'src/components/layout/TitleSection' import TitleSection from 'src/components/layout/TitleSection'
import { EmptyTable } from 'src/components/table' import { EmptyTable } from 'src/components/table'
import { P, Label1 } from 'src/components/typography' import { P, Label1 } from 'src/components/typography'
@ -20,40 +18,16 @@ import { ReactComponent as ReverseHistoryIcon } from 'src/styling/icons/circle b
import { ReactComponent as HistoryIcon } from 'src/styling/icons/circle buttons/history/zodiac.svg' import { ReactComponent as HistoryIcon } from 'src/styling/icons/circle buttons/history/zodiac.svg'
import { fromNamespace, toNamespace } from 'src/utils/config' import { fromNamespace, toNamespace } from 'src/utils/config'
import { MANUAL, AUTOMATIC } from 'src/utils/constants' import { MANUAL, AUTOMATIC } from 'src/utils/constants'
import { hasRecycler } from 'src/utils/machine'
import { onlyFirstToUpper } from 'src/utils/string' import { onlyFirstToUpper } from 'src/utils/string'
import styles from './CashCassettes.styles' import styles from './CashCassettes.styles'
import CashCassettesFooter from './CashCassettesFooter' import CashCassettesFooter from './CashCassettesFooter'
import CashboxHistory from './CashboxHistory' import CashboxHistory from './CashboxHistory'
import Wizard from './Wizard/Wizard' import Wizard from './Wizard/Wizard'
import helper from './helper'
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
const widthsByNumberOfCassettes = {
2: {
machine: 250,
cashbox: 260,
cassette: 300,
cassetteGraph: 80,
editWidth: 90
},
3: {
machine: 220,
cashbox: 215,
cassette: 225,
cassetteGraph: 60,
editWidth: 90
},
4: {
machine: 190,
cashbox: 180,
cassette: 185,
cassetteGraph: 50,
editWidth: 90
}
}
const ValidationSchema = Yup.object().shape({ const ValidationSchema = Yup.object().shape({
name: Yup.string().required(), name: Yup.string().required(),
cashbox: Yup.number() cashbox: Yup.number()
@ -85,7 +59,43 @@ const ValidationSchema = Yup.object().shape({
.required() .required()
.integer() .integer()
.min(0) .min(0)
.max(500) .max(500),
stacker1f: Yup.number()
.label('Stacker 1F')
.required('Required')
.integer()
.min(0)
.max(60),
stacker1r: Yup.number()
.label('Stacker 1R')
.required('Required')
.integer()
.min(0)
.max(60),
stacker2f: Yup.number()
.label('Stacker 2F')
.required('Required')
.integer()
.min(0)
.max(60),
stacker2r: Yup.number()
.label('Stacker 2R')
.required('Required')
.integer()
.min(0)
.max(60),
stacker3f: Yup.number()
.label('Stacker 3F')
.required('Required')
.integer()
.min(0)
.max(60),
stacker3r: Yup.number()
.label('Stacker 3R')
.required('Required')
.integer()
.min(0)
.max(60)
}) })
const GET_MACHINES_AND_CONFIG = gql` const GET_MACHINES_AND_CONFIG = gql`
@ -108,6 +118,7 @@ const GET_MACHINES_AND_CONFIG = gql`
stacker3r stacker3r
} }
numberOfCassettes numberOfCassettes
numberOfStackers
} }
unpairedMachines { unpairedMachines {
id: deviceId id: deviceId
@ -177,12 +188,11 @@ const CashCassettes = () => {
const [machineId, setMachineId] = useState('') const [machineId, setMachineId] = useState('')
const machines = R.path(['machines'])(data) ?? [] const machines = R.path(['machines'])(data) ?? []
const [nonRecyclerMachines, recyclerMachines] = R.partition(hasRecycler)( const [stackerMachines, nonStackerMachines] = R.partition(
machines it => it.numberOfStackers > 0
) )(machines)
const unpairedMachines = R.path(['unpairedMachines'])(data) ?? [] const unpairedMachines = R.path(['unpairedMachines'])(data) ?? []
const config = R.path(['config'])(data) ?? {} const config = R.path(['config'])(data) ?? {}
const fillingPercentageSettings = fromNamespace('notifications', config)
const [setCassetteBills, { error }] = useMutation(SET_CASSETTE_BILLS, { const [setCassetteBills, { error }] = useMutation(SET_CASSETTE_BILLS, {
refetchQueries: () => ['getData'] refetchQueries: () => ['getData']
}) })
@ -200,10 +210,6 @@ const CashCassettes = () => {
const cashout = data?.config && fromNamespace('cashOut')(data.config) const cashout = data?.config && fromNamespace('cashOut')(data.config)
const locale = data?.config && fromNamespace('locale')(data.config) const locale = data?.config && fromNamespace('locale')(data.config)
const fiatCurrency = locale?.fiatCurrency const fiatCurrency = locale?.fiatCurrency
const maxNumberOfCassettes = Math.max(
...R.map(it => it.numberOfCassettes, nonRecyclerMachines),
0
)
const getCashoutSettings = id => fromNamespace(id)(cashout) const getCashoutSettings = id => fromNamespace(id)(cashout)
const isCashOutDisabled = ({ id }) => !getCashoutSettings(id).active const isCashOutDisabled = ({ id }) => !getCashoutSettings(id).active
@ -243,85 +249,23 @@ const CashCassettes = () => {
setSelectedRadio(selectedRadio) setSelectedRadio(selectedRadio)
} }
const elements = [ const nonStackerElements = helper.getElements(
{ nonStackerMachines,
name: 'name', classes,
header: 'Machine', config,
width: widthsByNumberOfCassettes[maxNumberOfCassettes]?.machine, bills,
view: name => <>{name}</>, setMachineId,
input: ({ field: { value: name } }) => <>{name}</> setWizard
},
{
name: 'cashbox',
header: 'Cash box',
width: widthsByNumberOfCassettes[maxNumberOfCassettes]?.cashbox,
view: (_, { id, cashUnits }) => (
<CashIn
currency={{ code: fiatCurrency }}
notes={cashUnits.cashbox}
total={R.sum(R.map(it => it.fiat, bills[id] ?? []))}
/>
),
input: NumberInput,
inputProps: {
decimalPlaces: 0
}
}
]
R.until(
R.gt(R.__, maxNumberOfCassettes),
it => {
elements.push({
name: `cassette${it}`,
header: `Cassette ${it}`,
width: widthsByNumberOfCassettes[maxNumberOfCassettes]?.cassette,
stripe: true,
doubleHeader: 'Cash-out',
view: (_, { id, cashUnits }) => (
<CashOut
className={classes.cashbox}
denomination={getCashoutSettings(id)?.[`cassette${it}`]}
currency={{ code: fiatCurrency }}
notes={cashUnits[`cassette${it}`]}
width={
widthsByNumberOfCassettes[maxNumberOfCassettes]?.cassetteGraph
}
threshold={
fillingPercentageSettings[`fillingPercentageCassette${it}`]
}
/>
),
isHidden: ({ numberOfCassettes }) => it > numberOfCassettes,
input: CashCassetteInput,
inputProps: {
decimalPlaces: 0,
width: widthsByNumberOfCassettes[maxNumberOfCassettes]?.cassetteGraph,
inputClassName: classes.cashbox
}
})
return R.add(1, it)
},
1
) )
elements.push({ const stackerElements = helper.getElements(
name: 'edit', stackerMachines,
header: 'Edit', classes,
width: widthsByNumberOfCassettes[maxNumberOfCassettes]?.editWidth, config,
textAlign: 'center', bills,
view: (_, { id }) => { setMachineId,
return ( setWizard
<IconButton
onClick={() => {
setMachineId(id)
setWizard(true)
}}>
<EditIcon />
</IconButton>
) )
}
})
return ( return (
!dataLoading && ( !dataLoading && (
@ -381,18 +325,18 @@ const CashCassettes = () => {
error={error?.message} error={error?.message}
name="cashboxes" name="cashboxes"
stripeWhen={isCashOutDisabled} stripeWhen={isCashOutDisabled}
elements={elements} elements={nonStackerElements}
data={nonRecyclerMachines} data={nonStackerMachines}
validationSchema={ValidationSchema} validationSchema={ValidationSchema}
tbodyWrapperClass={classes.tBody} tbodyWrapperClass={classes.tBody}
/> />
<EditableTable <EditableTable
error={error?.message} error={error?.message}
name="cashboxes" name="recyclerCashboxes"
stripeWhen={isCashOutDisabled} stripeWhen={isCashOutDisabled}
elements={elements} elements={stackerElements}
data={recyclerMachines} data={stackerMachines}
validationSchema={ValidationSchema} validationSchema={ValidationSchema}
tbodyWrapperClass={classes.tBody} tbodyWrapperClass={classes.tBody}
/> />

View file

@ -0,0 +1,241 @@
import * as R from 'ramda'
import { IconButton } from 'src/components/buttons'
import { CashOut, CashIn } from 'src/components/inputs/cashbox/Cashbox'
import { NumberInput, CashCassetteInput } from 'src/components/inputs/formik'
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg'
import { fromNamespace } from 'src/utils/config'
const widthsByCashUnits = {
2: {
machine: 250,
cashbox: 260,
cassette: 300,
unitGraph: 80,
editWidth: 90
},
3: {
machine: 220,
cashbox: 215,
cassette: 225,
unitGraph: 60,
editWidth: 90
},
4: {
machine: 190,
cashbox: 180,
cassette: 185,
unitGraph: 50,
editWidth: 90
},
5: {
machine: 170,
cashbox: 140,
cassette: 160,
unitGraph: 45,
editWidth: 90
},
6: {
machine: 150,
cashbox: 130,
cassette: 142,
unitGraph: 45,
editWidth: 70
},
7: {
machine: 140,
cashbox: 115,
cassette: 125,
unitGraph: 40,
editWidth: 70
},
8: {
machine: 100,
cashbox: 115,
cassette: 122,
unitGraph: 35,
editWidth: 70
}
}
const getMaxNumberOfCassettesMap = machines =>
Math.max(...R.map(it => it.numberOfCassettes, machines), 0)
const getMaxNumberOfStackersMap = machines =>
Math.max(...R.map(it => it.numberOfStackers, machines), 0)
// Each stacker counts as two cash units (front and rear)
const getMaxNumberOfCashUnits = machines =>
Math.max(
...R.map(it => it.numberOfCassettes + it.numberOfStackers * 2, machines),
0
)
const getElements = (
machines,
classes,
config,
bills,
setMachineId,
setWizard
) => {
const fillingPercentageSettings = fromNamespace('notifications', config)
const locale = fromNamespace('locale')(config)
const cashout = fromNamespace('cashOut')(config)
const fiatCurrency = locale?.fiatCurrency
const getCashoutSettings = id => fromNamespace(id)(cashout)
const elements = [
{
name: 'name',
header: 'Machine',
width: widthsByCashUnits[getMaxNumberOfCashUnits(machines)]?.machine,
view: name => <>{name}</>,
input: ({ field: { value: name } }) => <>{name}</>
},
{
name: 'cashbox',
header: 'Cash box',
width: widthsByCashUnits[getMaxNumberOfCashUnits(machines)]?.cashbox,
view: (_, { id, cashUnits }) => (
<CashIn
currency={{ code: fiatCurrency }}
notes={cashUnits.cashbox}
total={R.sum(R.map(it => it.fiat, bills[id] ?? []))}
/>
),
input: NumberInput,
inputProps: {
decimalPlaces: 0
}
}
]
R.until(
R.gt(R.__, getMaxNumberOfCassettesMap(machines)),
it => {
elements.push({
name: `cassette${it}`,
header: `Cassette ${it}`,
width: widthsByCashUnits[getMaxNumberOfCashUnits(machines)]?.cassette,
stripe: true,
doubleHeader: 'Cash-out',
view: (_, { id, cashUnits }) => (
<CashOut
className={classes.cashbox}
denomination={getCashoutSettings(id)?.[`cassette${it}`]}
currency={{ code: fiatCurrency }}
notes={cashUnits[`cassette${it}`]}
width={
widthsByCashUnits[getMaxNumberOfCashUnits(machines)]?.unitGraph
}
threshold={
fillingPercentageSettings[`fillingPercentageCassette${it}`]
}
/>
),
isHidden: ({ numberOfCassettes }) => it > numberOfCassettes,
input: CashCassetteInput,
inputProps: {
decimalPlaces: 0,
width:
widthsByCashUnits[getMaxNumberOfCashUnits(machines)]?.unitGraph,
inputClassName: classes.cashbox
}
})
return R.add(1, it)
},
1
)
R.until(
R.gt(R.__, getMaxNumberOfStackersMap(machines)),
it => {
elements.push(
{
name: `stacker${it}f`,
header: `Stacker ${it}F`,
width: widthsByCashUnits[getMaxNumberOfCashUnits(machines)]?.cassette,
stripe: true,
doubleHeader: 'Cash recycling',
view: (_, { id, cashUnits }) => (
<CashOut
className={classes.cashbox}
denomination={getCashoutSettings(id)?.[`stacker${it}f`]}
currency={{ code: fiatCurrency }}
notes={cashUnits[`stacker${it}f`]}
width={
widthsByCashUnits[getMaxNumberOfCashUnits(machines)]?.unitGraph
}
threshold={
fillingPercentageSettings[`fillingPercentageStacker${it}f`]
}
/>
),
isHidden: ({ numberOfStackers }) => it > numberOfStackers,
input: CashCassetteInput,
inputProps: {
decimalPlaces: 0,
width:
widthsByCashUnits[getMaxNumberOfCashUnits(machines)]?.unitGraph,
inputClassName: classes.cashbox
}
},
{
name: `stacker${it}r`,
header: `Stacker ${it}R`,
width: widthsByCashUnits[getMaxNumberOfCashUnits(machines)]?.cassette,
stripe: true,
doubleHeader: 'Cash recycling',
view: (_, { id, cashUnits }) => (
<CashOut
className={classes.cashbox}
denomination={getCashoutSettings(id)?.[`stacker${it}r`]}
currency={{ code: fiatCurrency }}
notes={cashUnits[`stacker${it}r`]}
width={
widthsByCashUnits[getMaxNumberOfCashUnits(machines)]?.unitGraph
}
threshold={
fillingPercentageSettings[`fillingPercentageStacker${it}r`]
}
/>
),
isHidden: ({ numberOfStackers }) => it > numberOfStackers,
input: CashCassetteInput,
inputProps: {
decimalPlaces: 0,
width:
widthsByCashUnits[getMaxNumberOfCashUnits(machines)]?.unitGraph,
inputClassName: classes.cashbox
}
}
)
return R.add(1, it)
},
1
)
elements.push({
name: 'edit',
header: 'Edit',
width: widthsByCashUnits[getMaxNumberOfCashUnits(machines)]?.editWidth,
textAlign: 'center',
view: (_, { id }) => {
return (
<IconButton
onClick={() => {
setMachineId(id)
setWizard(true)
}}>
<EditIcon />
</IconButton>
)
}
})
return elements
}
export default { getElements }

View file

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

View file

@ -7,9 +7,6 @@ const modelPrettifier = {
grandola: 'Grândola' grandola: 'Grândola'
} }
const hasRecycler = machine =>
machine.model === 'aveiro' || machine.model === 'grandola'
const cashUnitCapacity = { const cashUnitCapacity = {
tejo: { tejo: {
cashbox: 1000, cashbox: 1000,
@ -22,4 +19,4 @@ const cashUnitCapacity = {
} }
} }
export { modelPrettifier, cashUnitCapacity, hasRecycler } export { modelPrettifier, cashUnitCapacity }