feat: cashbox history tab

feat: add information fields to cashbox_batches table
This commit is contained in:
Sérgio Salgado 2021-04-12 01:53:11 +01:00 committed by Josh Harvey
parent 642016efeb
commit 21708aa75c
8 changed files with 267 additions and 13 deletions

View file

@ -1,4 +1,5 @@
const db = require('./db')
const _ = require('lodash/fp')
function createCashboxBatch (rec) {
const sql = 'INSERT INTO cashbox_batches (device_id, created) VALUES ($1, now()) RETURNING *'
@ -15,4 +16,23 @@ function createCashboxBatch (rec) {
})
}
module.exports = { createCashboxBatch }
function getBatches () {
const sql = `SELECT cb.id, cb.device_id, cb.created, cb.operation_type, cb.bill_count_override, cb.performed_by,
json_agg(b.*) AS bills FROM cashbox_batches cb LEFT JOIN bills b ON cb.id=b.cashbox_batch_id GROUP BY cb.id`
return db.any(sql).then(res => _.map(it => ({
id: it.id,
deviceId: it.device_id,
created: it.created,
operationType: it.operation_type,
billCountOverride: it.bill_count_override,
performedBy: it.performed_by,
bills: it.bills
}), res))
}
function getBillsByBatchId (id) {
const sql = `SELECT * FROM bills WHERE cashbox_batch_id=$1`
return db.any(sql, [id])
}
module.exports = { createCashboxBatch, getBatches, getBillsByBatchId }

View file

@ -0,0 +1,9 @@
const cashbox = require('../../../cashbox-batches')
const resolvers = {
Query: {
cashboxBatches: () => cashbox.getBatches()
}
}
module.exports = resolvers

View file

@ -2,6 +2,7 @@ const { mergeResolvers } = require('@graphql-tools/merge')
const bill = require('./bill.resolver')
const blacklist = require('./blacklist.resolver')
const cashbox = require('./cashbox.resolver')
const config = require('./config.resolver')
const currency = require('./currency.resolver')
const customer = require('./customer.resolver')
@ -22,6 +23,7 @@ const version = require('./version.resolver')
const resolvers = [
bill,
blacklist,
cashbox,
config,
currency,
customer,

View file

@ -0,0 +1,19 @@
const { gql } = require('apollo-server-express')
const typeDef = gql`
type CashboxBatch {
id: ID
deviceId: ID
created: Date
operationType: String
customBillCount: Int
performedBy: String
bills: [Bill]
}
type Query {
cashboxBatches: [CashboxBatch]
}
`
module.exports = typeDef

View file

@ -2,6 +2,7 @@ const { mergeTypeDefs } = require('@graphql-tools/merge')
const bill = require('./bill.type')
const blacklist = require('./blacklist.type')
const cashbox = require('./cashbox.type')
const config = require('./config.type')
const currency = require('./currency.type')
const customer = require('./customer.type')
@ -22,6 +23,7 @@ const version = require('./version.type')
const types = [
bill,
blacklist,
cashbox,
config,
currency,
customer,

View file

@ -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()
}

View file

@ -12,10 +12,13 @@ import { NumberInput, CashCassetteInput } from 'src/components/inputs/formik'
import TitleSection from 'src/components/layout/TitleSection'
import { EmptyTable } from 'src/components/table'
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg'
import { ReactComponent as ReverseListingViewIcon } from 'src/styling/icons/circle buttons/listing-view/white.svg'
import { ReactComponent as ListingViewIcon } from 'src/styling/icons/circle buttons/listing-view/zodiac.svg'
import { fromNamespace } from 'src/utils/config'
import styles from './CashCassettes.styles.js'
import CashCassettesFooter from './CashCassettesFooter'
import CashboxHistory from './CashboxHistory'
import Wizard from './Wizard/Wizard'
const useStyles = makeStyles(styles)
@ -90,6 +93,7 @@ const SET_CASSETTE_BILLS = gql`
const CashCassettes = () => {
const classes = useStyles()
const [showHistory, setShowHistory] = useState(false)
const { data } = useQuery(GET_MACHINES_AND_CONFIG)
const [wizard, setWizard] = useState(false)
@ -200,21 +204,37 @@ const CashCassettes = () => {
return (
<>
<TitleSection title="Cash Cassettes" />
<TitleSection
title="Cash Cassettes"
button={{
text: 'Cashbox history',
icon: ListingViewIcon,
inverseIcon: ReverseListingViewIcon,
toggle: setShowHistory
}}
iconClassName={classes.listViewButton}
/>
<div className={classes.tableContainer}>
<EditableTable
error={error?.message}
name="cashboxes"
stripeWhen={isCashOutDisabled}
elements={elements}
data={machines}
validationSchema={ValidationSchema}
tbodyWrapperClass={classes.tBody}
/>
{!showHistory && (
<>
<EditableTable
error={error?.message}
name="cashboxes"
enableEdit
stripeWhen={isCashOutDisabled}
elements={elements}
data={machines}
save={onSave}
validationSchema={ValidationSchema}
tbodyWrapperClass={classes.tBody}
/>
{data && R.isEmpty(machines) && (
<EmptyTable message="No machines so far" />
{data && R.isEmpty(machines) && (
<EmptyTable message="No machines so far" />
)}
</>
)}
{showHistory && <CashboxHistory machines={machines} />}
</div>
<CashCassettesFooter
currencyCode={fiatCurrency}

View file

@ -0,0 +1,161 @@
import { useQuery } from '@apollo/react-hooks'
import { makeStyles } from '@material-ui/core'
import gql from 'graphql-tag'
import moment from 'moment'
import * as R from 'ramda'
import React from 'react'
import { NumberInput } from 'src/components/inputs/formik'
import DataTable from 'src/components/tables/DataTable'
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'
const GET_BATCHES = gql`
query cashboxBatches {
cashboxBatches {
id
deviceId
created
operationType
customBillCount
performedBy
bills {
fiat
deviceId
created
cashbox
}
}
}
`
const styles = {
operationType: {
marginLeft: 8
},
operationTypeWrapper: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center'
}
}
const useStyles = makeStyles(styles)
const CashboxHistory = ({ machines }) => {
const classes = useStyles()
const { data } = useQuery(GET_BATCHES)
const batches = R.path(['cashboxBatches'])(data)
const getOperationRender = {
'cash-in-empty': (
<>
<TxInIcon />
<span className={classes.operationType}>Cash-in emptied</span>
</>
),
'cash-out-1-refill': (
<>
<TxOutIcon />
<span className={classes.operationType}>Cash-out 1 refill</span>
</>
),
'cash-out-1-empty': (
<>
<TxOutIcon />
<span className={classes.operationType}>Cash-out 1 emptied</span>
</>
),
'cash-out-2-refill': (
<>
<TxOutIcon />
<span className={classes.operationType}>Cash-out 2 refill</span>
</>
),
'cash-out-2-empty': (
<>
<TxOutIcon />
<span className={classes.operationType}>Cash-out 2 emptied</span>
</>
)
}
const elements = [
{
name: 'operation',
header: 'Operation',
width: 200,
textAlign: 'left',
view: it => (
<div className={classes.operationTypeWrapper}>
{getOperationRender[it.operationType]}
</div>
)
},
{
name: 'machine',
header: 'Machine',
width: 190,
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: 125,
textAlign: 'right',
view: it => R.sum(R.map(b => R.prop('fiat', b), it.bills))
},
{
name: 'date',
header: 'Date',
width: 125,
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: 200,
textAlign: 'left',
view: it => (R.isNil(it.performedBy) ? 'Unknown entity' : it.performedBy)
},
{
name: '',
header: 'Edit',
width: 120,
textAlign: 'right',
view: it => 'aaaaa'
}
]
return (
<>
<DataTable name="cashboxHistory" elements={elements} data={batches} />
</>
)
}
export default CashboxHistory