diff --git a/lib/new-admin/graphql/schema.js b/lib/new-admin/graphql/schema.js index a81fbc84..718bac9c 100644 --- a/lib/new-admin/graphql/schema.js +++ b/lib/new-admin/graphql/schema.js @@ -5,6 +5,7 @@ const { GraphQLJSON, GraphQLJSONObject } = require('graphql-type-json') const got = require('got') const DataLoader = require('dataloader') +const cashbox = require('../../cashbox-batches') const machineLoader = require('../../machine-loader') const customers = require('../../customers') const { machineAction } = require('../machines') @@ -280,7 +281,18 @@ const typeDefs = gql` cashbox: Int } + type CashboxBatch { + id: ID + deviceId: ID + created: Date + operationType: String + customBillCount: Int + performedBy: String + bills: [Bills] + } + type Query { + cashboxBatches: [CashboxBatch] countries: [Country] currencies: [Currency] languages: [Language] @@ -352,6 +364,7 @@ const typeDefs = gql` toggleClearNotification(id: ID!, read: Boolean!): Notification clearAllNotifications: Notification cancelCashOutTransaction(id: ID): Transaction + editBatch(id: ID, performedBy: String): CashboxBatch } ` @@ -378,6 +391,7 @@ const resolvers = { latestEvent: parent => machineEventsLoader.load(parent.deviceId) }, Query: { + cashboxBatches: () => cashbox.getBatches(), countries: () => countries, currencies: () => currencies, languages: () => languages, @@ -449,7 +463,8 @@ const resolvers = { deletePromoCode: (...[, { codeId }]) => promoCodeManager.deletePromoCode(codeId), toggleClearNotification: (...[, { id, read }]) => notifierQueries.setRead(id, read), clearAllNotifications: () => notifierQueries.markAllAsRead(), - cancelCashOutTransaction: (...[, { id }]) => cashOutTx.cancel(id) + cancelCashOutTransaction: (...[, { id }]) => cashOutTx.cancel(id), + editBatch: (...[, { id, performedBy }]) => cashbox.editBatchById(id, performedBy) } } diff --git a/migrations/1617967601902-add-batches-type.js b/migrations/1617967601902-add-batches-type.js new file mode 100644 index 00000000..30989271 --- /dev/null +++ b/migrations/1617967601902-add-batches-type.js @@ -0,0 +1,21 @@ +var db = require('./db') + +exports.up = function (next) { + var sqls = [ + `CREATE TYPE cashbox_batch_type AS ENUM( + 'cash-in-empty', + 'cash-out-1-refill', + 'cash-out-1-empty', + 'cash-out-2-refill', + 'cash-out-2-empty' + )`, + `ALTER TABLE cashbox_batches ADD COLUMN operation_type cashbox_batch_type NOT NULL`, + `ALTER TABLE cashbox_batches ADD COLUMN bill_count_override SMALLINT`, + `ALTER TABLE cashbox_batches ADD COLUMN performed_by VARCHAR(64)` + ] + db.multi(sqls, next) +} + +exports.down = function (next) { + next() +} diff --git a/new-lamassu-admin/src/pages/Maintenance/CashCassettes.js b/new-lamassu-admin/src/pages/Maintenance/CashCassettes.js index dfcea4dc..5e68435b 100644 --- a/new-lamassu-admin/src/pages/Maintenance/CashCassettes.js +++ b/new-lamassu-admin/src/pages/Maintenance/CashCassettes.js @@ -2,7 +2,7 @@ import { useQuery, useMutation } from '@apollo/react-hooks' import { makeStyles } from '@material-ui/core' import gql from 'graphql-tag' import * as R from 'ramda' -import React from 'react' +import React, { useState } from 'react' import * as Yup from 'yup' import { Table as EditableTable } from 'src/components/editableTable' @@ -10,10 +10,13 @@ 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 { EmptyTable } from 'src/components/table' +import { ReactComponent as ReverseHistoryIcon } from 'src/styling/icons/circle buttons/history/white.svg' +import { ReactComponent as HistoryIcon } from 'src/styling/icons/circle buttons/history/zodiac.svg' import { fromNamespace } from 'src/utils/config' import styles from './CashCassettes.styles.js' import CashCassettesFooter from './CashCassettesFooter' +import CashboxHistory from './CashboxHistory' const useStyles = makeStyles(styles) @@ -108,6 +111,7 @@ const SET_CASSETTE_BILLS = gql` const CashCassettes = () => { const classes = useStyles() + const [showHistory, setShowHistory] = useState(false) const { data } = useQuery(GET_MACHINES_AND_CONFIG) @@ -200,23 +204,39 @@ const CashCassettes = () => { return ( <> - +
- + {!showHistory && ( + <> + - {data && R.isEmpty(machines) && ( - + {data && R.isEmpty(machines) && ( + + )} + + )} + {showHistory && ( + )}
{ + const classes = useStyles() + const [editing, setEditing] = useState(false) + const [error, setError] = useState(false) + const [fields, setFields] = useState({}) + + const { data, loading } = useQuery(GET_BATCHES) + + const [editBatch] = useMutation(EDIT_BATCH, { + refetchQueries: () => ['cashboxBatches'] + }) + + const batches = R.path(['cashboxBatches'])(data) + + const getOperationRender = { + 'cash-in-empty': ( + <> + + Cash-in emptied + + ), + 'cash-out-1-refill': ( + <> + + Cash-out 1 refill + + ), + 'cash-out-1-empty': ( + <> + + Cash-out 1 emptied + + ), + 'cash-out-2-refill': ( + <> + + Cash-out 2 refill + + ), + 'cash-out-2-empty': ( + <> + + Cash-out 2 emptied + + ) + } + + const save = row => { + schema + .isValid(fields) + .then(() => { + setError(false) + editBatch({ + variables: { id: row.id, performedBy: fields?.performedBy } + }) + }) + .catch(setError(true)) + return close() + } + + const close = () => { + setFields({}) + return setEditing(false) + } + + const elements = [ + { + name: 'operation', + header: 'Operation', + width: 200, + textAlign: 'left', + view: it => ( +
+ {getOperationRender[it.operationType]} +
+ ) + }, + { + name: 'machine', + header: 'Machine', + width: 200, + textAlign: 'left', + view: it => { + return R.find(R.propEq('id', it.deviceId))(machines).name + } + }, + { + name: 'billCount', + header: 'Bill Count', + width: 115, + textAlign: 'left', + input: NumberInput, + inputProps: { + decimalPlaces: 0 + }, + view: it => + R.isNil(it.customBillCount) ? it.bills.length : it.customBillCount + }, + { + name: 'total', + header: 'Total', + width: 100, + textAlign: 'right', + view: it => ( + + {R.sum(R.map(b => R.prop('fiat', b), it.bills))} {currency} + + ) + }, + { + name: 'date', + header: 'Date', + width: 135, + textAlign: 'right', + view: it => moment.utc(it.created).format('YYYY-MM-DD') + }, + { + name: 'time', + header: 'Time (h:m)', + width: 125, + textAlign: 'right', + view: it => moment.utc(it.created).format('HH:mm') + }, + { + name: 'performedBy', + header: 'Performed by', + width: 180, + textAlign: 'left', + view: it => { + if (!editing) + return R.isNil(it.performedBy) ? 'Unknown entity' : it.performedBy + return ( + + setFields({ ...fields, performedBy: e.target.value }) + } + error={error} + width={190 * 0.85} + value={fields.performedBy ?? ''} + /> + ) + } + }, + { + name: '', + header: 'Edit', + width: 150, + textAlign: 'right', + view: it => { + if (!editing) + return ( + { + setFields({}) + setEditing(true) + }}> + + + ) + return ( +
+ save(it)}> + Save + + + Cancel + +
+ ) + } + } + ] + + return ( + <> + {!loading && ( + + )} + + ) +} + +export default CashboxHistory