diff --git a/src/components/Dashboard/Finance/Payments/AuthorisePayment.jsx b/src/components/Dashboard/Finance/Payments/AuthorisePayment.jsx new file mode 100644 index 0000000..c7822d0 --- /dev/null +++ b/src/components/Dashboard/Finance/Payments/AuthorisePayment.jsx @@ -0,0 +1,46 @@ +import { useState, useContext } from 'react' +import PropTypes from 'prop-types' +import { ApiServerContext } from '../../context/ApiServerContext' +import { message } from 'antd' +import MessageDialogView from '../../common/MessageDialogView.jsx' + +const AuthorisePayment = ({ onOk, objectData }) => { + const [authoriseLoading, setAuthoriseLoading] = useState(false) + const { sendObjectFunction } = useContext(ApiServerContext) + + const handleAuthorise = async () => { + setAuthoriseLoading(true) + try { + const result = await sendObjectFunction( + objectData._id, + 'Payment', + 'authorise' + ) + if (result) { + message.success('Payment authorised successfully') + onOk(result) + } + } catch (error) { + console.error('Error authorising payment:', error) + } finally { + setAuthoriseLoading(false) + } + } + + return ( + + ) +} + +AuthorisePayment.propTypes = { + onOk: PropTypes.func.isRequired, + objectData: PropTypes.object +} + +export default AuthorisePayment diff --git a/src/components/Dashboard/Finance/Payments/CancelPayment.jsx b/src/components/Dashboard/Finance/Payments/CancelPayment.jsx new file mode 100644 index 0000000..b98083a --- /dev/null +++ b/src/components/Dashboard/Finance/Payments/CancelPayment.jsx @@ -0,0 +1,46 @@ +import { useState, useContext } from 'react' +import PropTypes from 'prop-types' +import { ApiServerContext } from '../../context/ApiServerContext' +import { message } from 'antd' +import MessageDialogView from '../../common/MessageDialogView.jsx' + +const CancelPayment = ({ onOk, objectData }) => { + const [cancelLoading, setCancelLoading] = useState(false) + const { sendObjectFunction } = useContext(ApiServerContext) + + const handleCancel = async () => { + setCancelLoading(true) + try { + const result = await sendObjectFunction( + objectData._id, + 'Payment', + 'cancel' + ) + if (result) { + message.success('Payment cancelled successfully') + onOk(result) + } + } catch (error) { + console.error('Error cancelling payment:', error) + } finally { + setCancelLoading(false) + } + } + + return ( + + ) +} + +CancelPayment.propTypes = { + onOk: PropTypes.func.isRequired, + objectData: PropTypes.object +} + +export default CancelPayment diff --git a/src/components/Dashboard/Finance/Payments/DeclinePayment.jsx b/src/components/Dashboard/Finance/Payments/DeclinePayment.jsx new file mode 100644 index 0000000..ca5014e --- /dev/null +++ b/src/components/Dashboard/Finance/Payments/DeclinePayment.jsx @@ -0,0 +1,46 @@ +import { useState, useContext } from 'react' +import PropTypes from 'prop-types' +import { ApiServerContext } from '../../context/ApiServerContext' +import { message } from 'antd' +import MessageDialogView from '../../common/MessageDialogView.jsx' + +const DeclinePayment = ({ onOk, objectData }) => { + const [declineLoading, setDeclineLoading] = useState(false) + const { sendObjectFunction } = useContext(ApiServerContext) + + const handleDecline = async () => { + setDeclineLoading(true) + try { + const result = await sendObjectFunction( + objectData._id, + 'Payment', + 'decline' + ) + if (result) { + message.success('Payment declined successfully') + onOk(result) + } + } catch (error) { + console.error('Error declining payment:', error) + } finally { + setDeclineLoading(false) + } + } + + return ( + + ) +} + +DeclinePayment.propTypes = { + onOk: PropTypes.func.isRequired, + objectData: PropTypes.object +} + +export default DeclinePayment diff --git a/src/components/Dashboard/Finance/Payments/NewPayment.jsx b/src/components/Dashboard/Finance/Payments/NewPayment.jsx index 71c5663..840e638 100644 --- a/src/components/Dashboard/Finance/Payments/NewPayment.jsx +++ b/src/components/Dashboard/Finance/Payments/NewPayment.jsx @@ -53,6 +53,8 @@ const NewPayment = ({ onOk, reset, defaultValues }) => { updatedAt: false, _reference: false, postedAt: false, + authorisedAt: false, + declinedAt: false, cancelledAt: false }} isEditing={false} diff --git a/src/components/Dashboard/Finance/Payments/PaymentInfo.jsx b/src/components/Dashboard/Finance/Payments/PaymentInfo.jsx index b574b36..8bb6430 100644 --- a/src/components/Dashboard/Finance/Payments/PaymentInfo.jsx +++ b/src/components/Dashboard/Finance/Payments/PaymentInfo.jsx @@ -24,6 +24,9 @@ import UserNotifierToggle from '../../common/UserNotifierToggle.jsx' import ScrollBox from '../../common/ScrollBox.jsx' import { getModelByName } from '../../../../database/ObjectModels.js' import PostPayment from './PostPayment.jsx' +import AuthorisePayment from './AuthorisePayment.jsx' +import DeclinePayment from './DeclinePayment.jsx' +import CancelPayment from './CancelPayment.jsx' const log = loglevel.getLogger('PaymentInfo') log.setLevel(config.logLevel) @@ -48,6 +51,9 @@ const PaymentInfo = () => { objectData: {} }) const [postPaymentOpen, setPostPaymentOpen] = useState(false) + const [authorisePaymentOpen, setAuthorisePaymentOpen] = useState(false) + const [declinePaymentOpen, setDeclinePaymentOpen] = useState(false) + const [cancelPaymentOpen, setCancelPaymentOpen] = useState(false) const actions = { edit: () => { @@ -69,6 +75,18 @@ const PaymentInfo = () => { post: () => { setPostPaymentOpen(true) return true + }, + authorise: () => { + setAuthorisePaymentOpen(true) + return true + }, + decline: () => { + setDeclinePaymentOpen(true) + return true + }, + cancel: () => { + setCancelPaymentOpen(true) + return true } } @@ -234,6 +252,60 @@ const PaymentInfo = () => { objectData={objectFormState.objectData} /> + { + setAuthorisePaymentOpen(false) + }} + width={515} + footer={null} + destroyOnHidden={true} + centered={true} + > + { + setAuthorisePaymentOpen(false) + objectFormRef?.current.handleFetchObject() + }} + objectData={objectFormState.objectData} + /> + + { + setDeclinePaymentOpen(false) + }} + width={515} + footer={null} + destroyOnHidden={true} + centered={true} + > + { + setDeclinePaymentOpen(false) + objectFormRef?.current.handleFetchObject() + }} + objectData={objectFormState.objectData} + /> + + { + setCancelPaymentOpen(false) + }} + width={515} + footer={null} + destroyOnHidden={true} + centered={true} + > + { + setCancelPaymentOpen(false) + objectFormRef?.current.handleFetchObject() + }} + objectData={objectFormState.objectData} + /> + ) } diff --git a/src/components/Dashboard/common/StateTag.jsx b/src/components/Dashboard/common/StateTag.jsx index 89220a7..7c9c1b9 100644 --- a/src/components/Dashboard/common/StateTag.jsx +++ b/src/components/Dashboard/common/StateTag.jsx @@ -140,6 +140,14 @@ const StateTag = ({ state, showBadge = true, style = {} }) => { status = 'magenta' text = 'Posted' break + case 'authorised': + status = 'success' + text = 'Authorised' + break + case 'declined': + status = 'error' + text = 'Declined' + break case 'received': status = 'success' text = 'Received' diff --git a/src/database/models/Payment.js b/src/database/models/Payment.js index ce74451..0de2816 100644 --- a/src/database/models/Payment.js +++ b/src/database/models/Payment.js @@ -82,6 +82,35 @@ export const Payment = { return objectData?.state?.type == 'draft' } }, + { + name: 'authorise', + label: 'Authorise', + type: 'button', + icon: CheckIcon, + url: (_id) => + `/dashboard/finance/payments/info?paymentId=${_id}&action=authorise`, + disabled: (objectData) => { + return objectData?.state?.type != 'posted' + }, + visible: (objectData) => { + return objectData?.state?.type == 'posted' + } + }, + { + name: 'decline', + label: 'Decline', + type: 'button', + icon: XMarkIcon, + danger: true, + url: (_id) => + `/dashboard/finance/payments/info?paymentId=${_id}&action=decline`, + disabled: (objectData) => { + return objectData?.state?.type != 'posted' + }, + visible: (objectData) => { + return objectData?.state?.type == 'posted' + } + }, { name: 'cancel', label: 'Cancel', @@ -91,13 +120,10 @@ export const Payment = { url: (_id) => `/dashboard/finance/payments/info?paymentId=${_id}&action=cancel`, disabled: (objectData) => { - return objectData?.state?.type == 'cancelled' + return objectData?.state?.type != 'posted' }, visible: (objectData) => { - return ( - objectData?.state?.type == 'draft' || - objectData?.state?.type == 'posted' - ) + return objectData?.state?.type == 'posted' } } ], @@ -197,6 +223,20 @@ export const Payment = { readOnly: true, columnWidth: 175 }, + { + name: 'authorisedAt', + label: 'Authorised At', + type: 'dateTime', + readOnly: true, + columnWidth: 175 + }, + { + name: 'declinedAt', + label: 'Declined At', + type: 'dateTime', + readOnly: true, + columnWidth: 175 + }, { name: 'cancelledAt', label: 'Cancelled At', @@ -256,6 +296,34 @@ export const Payment = { prefix: '£', roundNumber: 2, color: 'cyan' + }, + { + name: 'authorised.authorisedCount.count', + label: 'Authorised', + type: 'number', + color: 'green' + }, + { + name: 'authorised.authorisedAmount.sum', + label: 'Authorised Amount', + type: 'number', + prefix: '£', + roundNumber: 2, + color: 'green' + }, + { + name: 'declined.declinedCount.count', + label: 'Declined', + type: 'number', + color: 'red' + }, + { + name: 'declined.declinedAmount.sum', + label: 'Declined Amount', + type: 'number', + prefix: '£', + roundNumber: 2, + color: 'red' } ] }