approval of transactions works, is also now requires a matching transaction if the bin selected also uses hybrid control, rejection still needs to be implemented

This commit is contained in:
csawatzky 2025-06-04 15:20:46 -06:00
parent a083f7d8ed
commit 691b3be1f9
3 changed files with 85 additions and 26 deletions

View file

@ -712,6 +712,8 @@ export default function BinSettings(props: Props) {
return "Auto (lidar)" return "Auto (lidar)"
case pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC: case pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC:
return "Auto (cable)" return "Auto (cable)"
case pond.BinInventoryControl.BIN_INVENTORY_CONTROL_HYBRID_LIDAR:
return "Hybrid (lidar)"
default: default:
return "Manual" return "Manual"
} }

View file

@ -34,7 +34,11 @@ export default function BinTransactions(props: Props){
const [tablePageSize, setTablePageSize] = useState(10) const [tablePageSize, setTablePageSize] = useState(10)
const { openSnack } = useSnackbar(); const { openSnack } = useSnackbar();
const [total, setTotal] = useState(0) const [total, setTotal] = useState(0)
const [grainMessage, setGrainMessage] = useState("")
//only need to do this for the bins since if a bin is picked for the secondary we need to check its inventory control type,
//if it is also hybrid a matching transaction must be selected
const [bins, setBins] = useState<Map<string, Bin>>(new Map())
const [binOptions, setBinOptions] = useState<Option[]>([]) const [binOptions, setBinOptions] = useState<Option[]>([])
const [grainBagOptions, setGrainBagOptions] = useState<Option[]>([]) const [grainBagOptions, setGrainBagOptions] = useState<Option[]>([])
@ -53,18 +57,21 @@ export default function BinTransactions(props: Props){
const [toType, setToType] = useState<pond.ObjectType>(pond.ObjectType.OBJECT_TYPE_UNKNOWN) const [toType, setToType] = useState<pond.ObjectType>(pond.ObjectType.OBJECT_TYPE_UNKNOWN)
const [toOption, setToOption] = useState<Option | undefined>() const [toOption, setToOption] = useState<Option | undefined>()
const [requiresMatch, setRequiresMatch] = useState(false)
//the following object loads are loading the objects for options when editing transactions to accept as well as build the name map for display
//these only happen when going to the transactions tab on the bin page
//load bins //load bins
const loadBins = ()=>{ const loadBins = ()=>{
//limit of 0 will load them all but this load will only happen once regardless of how many times the dropdown for type is changed //limit of 0 will load them all but this load will only happen once regardless of how many times the dropdown for type is changed
binAPI.listBins(0, 0, "asc", "name").then(resp => { binAPI.listBins(0, 0, "asc", "name").then(resp => {
setBinOptions(resp.data.bins.map(b => { let options: Option[] = []
let binMap: Map<string, Bin> = new Map()
resp.data.bins.map(b => {
let bin = Bin.create(b) let bin = Bin.create(b)
return {label: bin.name(), value: bin.key()} options.push({label: bin.name(), value: bin.key()})
})) binMap.set(bin.key(), bin)
})
setBinOptions(options)
setBins(binMap)
}) })
} }
@ -107,31 +114,41 @@ export default function BinTransactions(props: Props){
loadTransactionsFor() loadTransactionsFor()
},[state, bin, loadTransactionsFor]) },[state, bin, loadTransactionsFor])
const closeDialog = () => {
setPossibleTransactionMatches([])
setselectedMatch(undefined)
setRequiresMatch(false)
setOpenDialog(false)
}
const submit = () => { const submit = () => {
console.log("submitting transaction")
let transaction = cloneDeep(selectedTransaction) let transaction = cloneDeep(selectedTransaction)
if(transaction){ if(transaction){
if(selectedMatch){ //use the dropdowns to update the from/to for the transaction, meaning if the user added another object it will adjust its inventory as well
if (transaction.transaction.fromKey === ""){ transaction.transaction.fromObject = fromType
transaction.transaction.fromObject = selectedMatch.transaction.fromObject transaction.transaction.fromKey = fromOption?.value ?? ""
transaction.transaction.fromKey = selectedMatch.transaction.fromKey transaction.transaction.toObject = toType
} transaction.transaction.toKey = toOption?.value ?? ""
if (transaction.transaction.toKey === ""){ //need to do a check to make sure the transaction data contains a grain transaction
transaction.transaction.toObject = selectedMatch.transaction.toObject if(transaction.transaction.transaction && transaction.transaction.transaction.grainTransaction) {
transaction.transaction.toKey = selectedMatch.transaction.toKey transaction.transaction.transaction.grainTransaction.message = grainMessage
}
} }
//submit approval //submit approval
transactionAPI.submitApproval(transaction.transaction.key, true, transaction.transaction).then(resp => { transactionAPI.submitApproval(transaction.transaction.key, true, transaction.transaction).then(resp => {
//in the response if it succeeded remove (not reject but fully delete it) the matched transaction //in the response if it succeeded remove (not reject but fully delete it) the matched transaction if one was selected
if(selectedMatch){ if(selectedMatch){
transactionAPI.removeTransaction(selectedMatch.transaction.key).then(resp => { transactionAPI.removeTransaction(selectedMatch.transaction.key).then(resp => {
openSnack("Updated and matched transactions") openSnack("Updated and matched transactions")
}).catch(err => { }).catch(err => {
openSnack("Updated primary transaction but failed to remove matched transaction")
}) })
}else{ }else{
openSnack("Updated Transaction") openSnack("Updated Transaction")
} }
}).catch(err => {
openSnack("There was a problem submitting transaction approval")
}) })
} }
} }
@ -166,7 +183,7 @@ export default function BinTransactions(props: Props){
let fromDisabled = selectedTransaction?.transaction.fromKey !== "" let fromDisabled = selectedTransaction?.transaction.fromKey !== ""
let toDisabled = selectedTransaction?.transaction.toKey !== "" let toDisabled = selectedTransaction?.transaction.toKey !== ""
return ( return (
<ResponsiveDialog open={openDialog} onClose={()=>{setOpenDialog(false)}}> <ResponsiveDialog open={openDialog} onClose={closeDialog}>
<DialogTitle>Accept Transaction</DialogTitle> <DialogTitle>Accept Transaction</DialogTitle>
<DialogContent> <DialogContent>
<Box> <Box>
@ -222,6 +239,15 @@ export default function BinTransactions(props: Props){
}) })
setPossibleTransactionMatches(matches) setPossibleTransactionMatches(matches)
}) })
//if the options are bin types check if the selected bin uses hybrid controls
if(fromType === pond.ObjectType.OBJECT_TYPE_BIN && val){
let bin = bins.get(val.value)
if(bin){
setRequiresMatch(
bin.inventoryControl() === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_HYBRID_LIDAR ||
bin.inventoryControl() === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_HYBRID_CABLE)
}
}
}} }}
renderInput={params => <TextField {...params} label="From" />} renderInput={params => <TextField {...params} label="From" />}
/> />
@ -262,6 +288,7 @@ export default function BinTransactions(props: Props){
getOptionLabel={(option: Option) => option.label} getOptionLabel={(option: Option) => option.label}
fullWidth fullWidth
onChange={(_, val) => { onChange={(_, val) => {
setToOption(val ?? undefined)
//once one is selected get the transactions for it that are requiring approval //once one is selected get the transactions for it that are requiring approval
transactionAPI.listTransactionsFor(0, 0, "asc", val?.value, pond.TransactionState.TRANSACTION_STATE_APPROVAL_REQUIRED).then(resp => { transactionAPI.listTransactionsFor(0, 0, "asc", val?.value, pond.TransactionState.TRANSACTION_STATE_APPROVAL_REQUIRED).then(resp => {
let matches: Transaction[] = [] let matches: Transaction[] = []
@ -273,14 +300,33 @@ export default function BinTransactions(props: Props){
}) })
setPossibleTransactionMatches(matches) setPossibleTransactionMatches(matches)
}) })
//if the options are bin types check if the selected bin uses hybrid controls
if(fromType === pond.ObjectType.OBJECT_TYPE_BIN && val){
let bin = bins.get(val.value)
if(bin){
setRequiresMatch(
bin.inventoryControl() === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_HYBRID_LIDAR ||
bin.inventoryControl() === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_HYBRID_CABLE)
}
}
}} }}
renderInput={params => <TextField {...params} label="From" />} renderInput={params => <TextField {...params} label="From" />}
/> />
</Box> </Box>
<Box>
<TextField
label="Message(optional)"
margin="dense"
fullWidth
variant="outlined"
value={grainMessage}
onChange={e => setGrainMessage(e.target.value)}
/>
</Box>
{toOption &&
<Box paddingTop={1}> <Box paddingTop={1}>
<Typography variant="h6">Possible matching Transactions</Typography> <Typography variant="h6">Possible matching Transactions</Typography>
<Typography>Pick a transaction below if you want to match them together</Typography> <Typography>{possibleTransactionMatches.length > 0 ? "Pick a transaction below if you want to match them together" : "No matching transactions found"}</Typography>
{possibleTransactionMatches.map(t => { {possibleTransactionMatches.map(t => {
// get the display information for the transaction // get the display information for the transaction
const grainTransaction = t.transaction.transaction?.grainTransaction ?? pond.GrainTransaction.create() const grainTransaction = t.transaction.transaction?.grainTransaction ?? pond.GrainTransaction.create()
@ -291,7 +337,7 @@ export default function BinTransactions(props: Props){
? grainTransaction.customGrain ? grainTransaction.customGrain
: GrainDescriber(grainTransaction.grainType).name : GrainDescriber(grainTransaction.grainType).name
return ( return (
<Card sx={{marginTop: 1, padding: 1}}> <Card sx={{marginTop: 1, padding: 1}} key={t.transaction.key}>
<Box display="flex" justifyContent="space-between" height={40}> <Box display="flex" justifyContent="space-between" height={40}>
<Typography sx={{fontWeight: 650, margin: "auto", marginLeft: 0}}> <Typography sx={{fontWeight: 650, margin: "auto", marginLeft: 0}}>
{moment(t.transaction.timestamp).fromNow()} {moment(t.transaction.timestamp).fromNow()}
@ -316,11 +362,22 @@ export default function BinTransactions(props: Props){
) )
})} })}
</Box> </Box>
}
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button variant="contained">Close</Button> <Button variant="contained" onClick={closeDialog}>Close</Button>
<Button variant="contained" color="error">Reject</Button> <Button variant="contained" color="error" onClick={closeDialog}>Reject</Button>
<Button variant="contained" color="primary">Approve</Button> <Button
variant="contained"
disabled={requiresMatch && selectedMatch === undefined}
color="primary"
onClick={() => {
submit()
closeDialog()
//re-load bin
}}>
Approve
</Button>
</DialogActions> </DialogActions>
</ResponsiveDialog> </ResponsiveDialog>
) )

View file

@ -166,7 +166,7 @@ export default function TransactionProvider(props: PropsWithChildren<Props>) {
if(view){ if(view){
url = url + "&as=" + view url = url + "&as=" + view
} }
return post<pond.SubmitTransactionApprovalResponse>(url, updatedData) return post<pond.SubmitTransactionApprovalResponse>(pondURL(url), updatedData)
} }
const removeTransaction = (key: string, otherTeam?: string) => { const removeTransaction = (key: string, otherTeam?: string) => {
@ -175,7 +175,7 @@ export default function TransactionProvider(props: PropsWithChildren<Props>) {
if(view){ if(view){
url = url + "&as=" + view url = url + "&as=" + view
} }
return del<pond.RemoveTransactionResponse>(url) return del<pond.RemoveTransactionResponse>(pondURL(url))
} }
return ( return (