Enhance Invoice management features with new PostInvoice functionality
- Added PostInvoice component for posting invoices with confirmation dialog. - Updated InvoiceInfo component to include new invoice order items and shipments sections. - Modified NewInvoice component to set default issued and due dates. - Refactored Invoice model to include new fields for issuedAt, dueAt, invoiceOrderItems, and invoiceShipments. - Updated action names from 'send' to 'post' for clarity in the invoice workflow.
This commit is contained in:
parent
0bf16d844e
commit
bace57b436
@ -1,6 +1,6 @@
|
||||
import { useRef, useState } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { Space, Flex, Card } from 'antd'
|
||||
import { Space, Flex, Card, Modal } from 'antd'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import loglevel from 'loglevel'
|
||||
import config from '../../../../config.js'
|
||||
@ -8,6 +8,7 @@ import useCollapseState from '../../hooks/useCollapseState.js'
|
||||
import NotesPanel from '../../common/NotesPanel.jsx'
|
||||
import InfoCollapse from '../../common/InfoCollapse.jsx'
|
||||
import ObjectInfo from '../../common/ObjectInfo.jsx'
|
||||
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||
import ViewButton from '../../common/ViewButton.jsx'
|
||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||
@ -21,7 +22,13 @@ import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import {
|
||||
getModelByName,
|
||||
getModelProperty
|
||||
} from '../../../../database/ObjectModels.js'
|
||||
import OrderItemIcon from '../../../Icons/OrderItemIcon.jsx'
|
||||
import ShipmentIcon from '../../../Icons/ShipmentIcon.jsx'
|
||||
import PostInvoice from './PostInvoice.jsx'
|
||||
|
||||
const log = loglevel.getLogger('InvoiceInfo')
|
||||
log.setLevel(config.logLevel)
|
||||
@ -31,14 +38,13 @@ const InvoiceInfo = () => {
|
||||
const objectFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const invoiceId = new URLSearchParams(location.search).get('invoiceId')
|
||||
const [collapseState, updateCollapseState] = useCollapseState(
|
||||
'InvoiceInfo',
|
||||
{
|
||||
info: true,
|
||||
notes: true,
|
||||
auditLogs: true
|
||||
}
|
||||
)
|
||||
const [collapseState, updateCollapseState] = useCollapseState('InvoiceInfo', {
|
||||
info: true,
|
||||
invoiceOrderItems: true,
|
||||
invoiceShipments: true,
|
||||
notes: true,
|
||||
auditLogs: true
|
||||
})
|
||||
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
@ -48,6 +54,7 @@ const InvoiceInfo = () => {
|
||||
loading: false,
|
||||
objectData: {}
|
||||
})
|
||||
const [postInvoiceOpen, setPostInvoiceOpen] = useState(false)
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
@ -69,12 +76,17 @@ const InvoiceInfo = () => {
|
||||
delete: () => {
|
||||
objectFormRef?.current?.handleDelete?.()
|
||||
return true
|
||||
},
|
||||
post: () => {
|
||||
setPostInvoiceOpen(true)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
const editDisabled = getModelByName('invoice')
|
||||
?.actions?.find((action) => action.name === 'edit')
|
||||
?.disabled(objectFormState.objectData) ?? false
|
||||
const editDisabled =
|
||||
getModelByName('invoice')
|
||||
?.actions?.find((action) => action.name === 'edit')
|
||||
?.disabled(objectFormState.objectData) ?? false
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -99,6 +111,8 @@ const InvoiceInfo = () => {
|
||||
disabled={objectFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Invoice Information' },
|
||||
{ key: 'invoiceOrderItems', label: 'Invoice Order Items' },
|
||||
{ key: 'invoiceShipments', label: 'Invoice Shipments' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||
]}
|
||||
@ -171,6 +185,42 @@ const InvoiceInfo = () => {
|
||||
type='invoice'
|
||||
labelWidth='225px'
|
||||
objectData={objectData}
|
||||
visibleProperties={{
|
||||
invoiceOrderItems: false,
|
||||
invoiceShipments: false
|
||||
}}
|
||||
/>
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Invoice Order Items'
|
||||
icon={<OrderItemIcon />}
|
||||
active={collapseState.invoiceOrderItems}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('invoiceOrderItems', expanded)
|
||||
}
|
||||
collapseKey='invoiceOrderItems'
|
||||
>
|
||||
<ObjectProperty
|
||||
{...getModelProperty('invoice', 'invoiceOrderItems')}
|
||||
isEditing={isEditing}
|
||||
objectData={objectData}
|
||||
loading={loading}
|
||||
/>
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Invoice Shipments'
|
||||
icon={<ShipmentIcon />}
|
||||
active={collapseState.invoiceShipments}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('invoiceShipments', expanded)
|
||||
}
|
||||
collapseKey='invoiceShipments'
|
||||
>
|
||||
<ObjectProperty
|
||||
{...getModelProperty('invoice', 'invoiceShipments')}
|
||||
isEditing={isEditing}
|
||||
objectData={objectData}
|
||||
loading={loading}
|
||||
/>
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
@ -210,9 +260,26 @@ const InvoiceInfo = () => {
|
||||
</Flex>
|
||||
</ScrollBox>
|
||||
</Flex>
|
||||
<Modal
|
||||
open={postInvoiceOpen}
|
||||
onCancel={() => {
|
||||
setPostInvoiceOpen(false)
|
||||
}}
|
||||
width={500}
|
||||
footer={null}
|
||||
destroyOnHidden={true}
|
||||
centered={true}
|
||||
>
|
||||
<PostInvoice
|
||||
onOk={() => {
|
||||
setPostInvoiceOpen(false)
|
||||
actions.reload()
|
||||
}}
|
||||
objectData={objectFormState.objectData}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default InvoiceInfo
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import dayjs from 'dayjs'
|
||||
import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
@ -10,7 +11,8 @@ const NewInvoice = ({ onOk, reset, defaultValues }) => {
|
||||
reset={reset}
|
||||
defaultValues={{
|
||||
state: { type: 'draft' },
|
||||
invoiceType: 'sales',
|
||||
issuedAt: new Date(),
|
||||
dueAt: dayjs().add(3, 'day').toDate(),
|
||||
...defaultValues
|
||||
}}
|
||||
>
|
||||
@ -30,27 +32,10 @@ const NewInvoice = ({ onOk, reset, defaultValues }) => {
|
||||
visibleProperties={{
|
||||
orderType: true,
|
||||
order: true,
|
||||
vendor: true,
|
||||
invoiceDate: true,
|
||||
dueDate: true
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Optional',
|
||||
key: 'optional',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='invoice'
|
||||
column={1}
|
||||
bordered={false}
|
||||
isEditing={true}
|
||||
required={false}
|
||||
objectData={objectData}
|
||||
visibleProperties={{
|
||||
relatedOrderType: true,
|
||||
relatedOrder: true
|
||||
to: true,
|
||||
from: true,
|
||||
issuedAt: true,
|
||||
dueAt: true
|
||||
}}
|
||||
/>
|
||||
)
|
||||
@ -66,6 +51,8 @@ const NewInvoice = ({ onOk, reset, defaultValues }) => {
|
||||
visibleProperties={{
|
||||
_id: false,
|
||||
createdAt: false,
|
||||
invoiceOrderItems: false,
|
||||
invoiceShipments: false,
|
||||
updatedAt: false,
|
||||
_reference: false,
|
||||
totalAmount: false,
|
||||
|
||||
42
src/components/Dashboard/Finance/Invoices/PostInvoice.jsx
Normal file
42
src/components/Dashboard/Finance/Invoices/PostInvoice.jsx
Normal file
@ -0,0 +1,42 @@
|
||||
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 PostInvoice = ({ onOk, objectData }) => {
|
||||
const [postLoading, setPostLoading] = useState(false)
|
||||
const { sendObjectFunction } = useContext(ApiServerContext)
|
||||
|
||||
const handlePost = async () => {
|
||||
setPostLoading(true)
|
||||
try {
|
||||
const result = await sendObjectFunction(objectData._id, 'Invoice', 'post')
|
||||
if (result) {
|
||||
message.success('Invoice posted successfully')
|
||||
onOk(result)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error posting invoice:', error)
|
||||
} finally {
|
||||
setPostLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<MessageDialogView
|
||||
title={'Are you sure you want to post this invoice?'}
|
||||
description={`Posting invoice ${objectData?.name || objectData?._reference || objectData?._id} will set it to sent status.`}
|
||||
onOk={handlePost}
|
||||
okText='Post'
|
||||
okLoading={postLoading}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
PostInvoice.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
objectData: PropTypes.object
|
||||
}
|
||||
|
||||
export default PostInvoice
|
||||
@ -72,30 +72,16 @@ export const Invoice = {
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
name: 'send',
|
||||
label: 'Send',
|
||||
name: 'post',
|
||||
label: 'Post',
|
||||
type: 'button',
|
||||
icon: CheckIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/finance/invoices/info?invoiceId=${_id}&action=send`,
|
||||
`/dashboard/finance/invoices/info?invoiceId=${_id}&action=post`,
|
||||
visible: (objectData) => {
|
||||
return objectData?.state?.type == 'draft'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'markPaid',
|
||||
label: 'Mark Paid',
|
||||
type: 'button',
|
||||
icon: CheckIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/finance/invoices/info?invoiceId=${_id}&action=markPaid`,
|
||||
visible: (objectData) => {
|
||||
return (
|
||||
objectData?.state?.type == 'sent' ||
|
||||
objectData?.state?.type == 'partiallyPaid'
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'cancel',
|
||||
label: 'Cancel',
|
||||
@ -111,10 +97,7 @@ export const Invoice = {
|
||||
)
|
||||
},
|
||||
visible: (objectData) => {
|
||||
return (
|
||||
objectData?.state?.type == 'draft' ||
|
||||
objectData?.state?.type == 'sent'
|
||||
)
|
||||
return objectData?.state?.type == 'sent'
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -172,16 +155,11 @@ export const Invoice = {
|
||||
},
|
||||
{ name: 'state', label: 'State', type: 'state', readOnly: true },
|
||||
{
|
||||
name: 'invoiceDate',
|
||||
label: 'Invoice Date',
|
||||
type: 'date',
|
||||
readOnly: false
|
||||
},
|
||||
{
|
||||
name: 'dueDate',
|
||||
label: 'Due Date',
|
||||
type: 'date',
|
||||
readOnly: false
|
||||
name: 'issuedAt',
|
||||
label: 'Issued At',
|
||||
type: 'dateTime',
|
||||
readOnly: false,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'orderType',
|
||||
@ -190,6 +168,12 @@ export const Invoice = {
|
||||
masterFilter: ['purchaseOrder', 'salesOrder'],
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'dueAt',
|
||||
label: 'Due At',
|
||||
type: 'dateTime',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'order',
|
||||
label: 'Order',
|
||||
@ -201,8 +185,14 @@ export const Invoice = {
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'vendor',
|
||||
label: 'Vendor',
|
||||
name: 'postedAt',
|
||||
label: 'Posted At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'from',
|
||||
label: 'From',
|
||||
required: true,
|
||||
type: 'object',
|
||||
objectType: 'vendor',
|
||||
@ -212,34 +202,39 @@ export const Invoice = {
|
||||
if (objectData?.orderType == 'purchaseOrder') {
|
||||
return objectData?.order?.vendor
|
||||
} else {
|
||||
return objectData?.vendor
|
||||
return null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'sentAt',
|
||||
label: 'Sent At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'paidAt',
|
||||
label: 'Paid At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'to',
|
||||
label: 'To',
|
||||
required: true,
|
||||
type: 'object',
|
||||
objectType: 'client',
|
||||
showHyperlink: true,
|
||||
readOnly: true,
|
||||
value: (objectData) => {
|
||||
if (objectData?.orderType == 'salesOrder') {
|
||||
return objectData?.order?.client
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'cancelledAt',
|
||||
label: 'Cancelled At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'overdueAt',
|
||||
label: 'Overdue At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
|
||||
{
|
||||
name: 'totalTaxAmount',
|
||||
label: 'Total Tax Amount',
|
||||
@ -293,44 +288,277 @@ export const Invoice = {
|
||||
roundNumber: 2,
|
||||
columnWidth: 175,
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'invoiceOrderItems',
|
||||
label: 'Invoice Order Items',
|
||||
type: 'objectChildren',
|
||||
objectType: 'orderItem',
|
||||
properties: [
|
||||
{
|
||||
name: 'orderItem',
|
||||
label: 'Order Item',
|
||||
type: 'object',
|
||||
objectType: 'orderItem',
|
||||
required: true,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'invoiceQuantity',
|
||||
label: 'Quantity',
|
||||
type: 'number',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'invoiceAmount',
|
||||
label: 'Invoice Amount',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'taxRate',
|
||||
label: 'Tax Rate',
|
||||
type: 'object',
|
||||
objectType: 'taxRate',
|
||||
required: false,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'invoiceAmountWithTax',
|
||||
label: 'Invoice Amount w/ Tax',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
required: true,
|
||||
readOnly: true,
|
||||
value: (objectData) => {
|
||||
const invoiceAmount = objectData?.invoiceAmount || 0
|
||||
if (objectData?.taxRate?.rateType == 'percentage') {
|
||||
return (
|
||||
(invoiceAmount * (1 + objectData?.taxRate?.rate / 100)).toFixed(
|
||||
2
|
||||
) || undefined
|
||||
)
|
||||
} else if (objectData?.taxRate?.rateType == 'amount') {
|
||||
return (
|
||||
(invoiceAmount + objectData?.taxRate?.rate).toFixed(2) ||
|
||||
undefined
|
||||
)
|
||||
} else {
|
||||
return invoiceAmount || 0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
rollups: [
|
||||
{
|
||||
name: 'totalQuantity',
|
||||
label: 'Total Quantity',
|
||||
type: 'number',
|
||||
property: 'invoiceQuantity',
|
||||
value: (objectData) => {
|
||||
return objectData?.invoiceOrderItems?.reduce(
|
||||
(acc, item) => acc + (item.invoiceQuantity || 0),
|
||||
0
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'totalAmount',
|
||||
label: 'Total Amount',
|
||||
type: 'number',
|
||||
property: 'invoiceAmount',
|
||||
prefix: '£',
|
||||
fixedNumber: 2,
|
||||
value: (objectData) => {
|
||||
return objectData?.invoiceOrderItems
|
||||
?.reduce(
|
||||
(acc, item) =>
|
||||
acc + (Number.parseFloat(item.invoiceAmount) || 0),
|
||||
0
|
||||
)
|
||||
.toFixed(2)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'totalAmountWithTax',
|
||||
label: 'Total Amount w/ Tax',
|
||||
type: 'number',
|
||||
property: 'invoiceAmountWithTax',
|
||||
prefix: '£',
|
||||
fixedNumber: 2,
|
||||
value: (objectData) => {
|
||||
return objectData?.invoiceOrderItems
|
||||
?.reduce(
|
||||
(acc, item) =>
|
||||
acc + (Number.parseFloat(item.invoiceAmountWithTax) || 0),
|
||||
0
|
||||
)
|
||||
.toFixed(2)
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'invoiceShipments',
|
||||
label: 'Invoice Shipments',
|
||||
type: 'objectChildren',
|
||||
objectType: 'shipment',
|
||||
properties: [
|
||||
{
|
||||
name: 'shipment',
|
||||
label: 'Shipment',
|
||||
type: 'object',
|
||||
objectType: 'shipment',
|
||||
required: true,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'invoiceAmount',
|
||||
label: 'Invoice Amount',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'taxRate',
|
||||
label: 'Tax Rate',
|
||||
type: 'object',
|
||||
objectType: 'taxRate',
|
||||
required: false,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'invoiceAmountWithTax',
|
||||
label: 'Invoice Amount w/ Tax',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
required: true,
|
||||
readOnly: true,
|
||||
value: (objectData) => {
|
||||
const invoiceAmount = objectData?.invoiceAmount || 0
|
||||
if (objectData?.taxRate?.rateType == 'percentage') {
|
||||
return (
|
||||
(invoiceAmount * (1 + objectData?.taxRate?.rate / 100)).toFixed(
|
||||
2
|
||||
) || undefined
|
||||
)
|
||||
} else if (objectData?.taxRate?.rateType == 'amount') {
|
||||
return (
|
||||
(invoiceAmount + objectData?.taxRate?.rate).toFixed(2) ||
|
||||
undefined
|
||||
)
|
||||
} else {
|
||||
return invoiceAmount || 0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
rollups: [
|
||||
{
|
||||
name: 'totalAmount',
|
||||
label: 'Total Amount',
|
||||
type: 'number',
|
||||
property: 'invoiceAmount',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
value: (objectData) => {
|
||||
return objectData?.invoiceShipments
|
||||
?.reduce(
|
||||
(acc, shipment) => acc + (shipment.invoiceAmount || 0),
|
||||
0
|
||||
)
|
||||
.toFixed(2)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'totalAmountWithTax',
|
||||
label: 'Total Amount w/ Tax',
|
||||
type: 'number',
|
||||
property: 'invoiceAmountWithTax',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
value: (objectData) => {
|
||||
return objectData?.invoiceShipments
|
||||
?.reduce(
|
||||
(acc, shipment) =>
|
||||
acc + (Number.parseFloat(shipment.invoiceAmountWithTax) || 0),
|
||||
0
|
||||
)
|
||||
.toFixed(2)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
stats: [
|
||||
{
|
||||
name: 'draft.count',
|
||||
name: 'draft.draftCount.count',
|
||||
label: 'Draft',
|
||||
type: 'number',
|
||||
color: 'default'
|
||||
},
|
||||
{
|
||||
name: 'sent.count',
|
||||
name: 'draft.draftGrandTotalAmount.sum',
|
||||
label: 'Draft Grand Total Amount',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
color: 'default'
|
||||
},
|
||||
{
|
||||
name: 'sent.sentCount.count',
|
||||
label: 'Sent',
|
||||
type: 'number',
|
||||
color: 'cyan'
|
||||
},
|
||||
{
|
||||
name: 'partiallyPaid.count',
|
||||
name: 'due.dueCount.count',
|
||||
label: 'Due',
|
||||
type: 'number',
|
||||
color: 'warning',
|
||||
sum: [
|
||||
'sent.sentCount.count',
|
||||
'partiallyPaid.partiallyPaidCount.count',
|
||||
'overdue.overdueCount.count'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'due.dueGrandTotalAmount.sum',
|
||||
label: 'Due Grand Total Amount',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
color: 'warning',
|
||||
sum: [
|
||||
'sent.sentGrandTotalAmount.sum',
|
||||
'partiallyPaid.partiallyPaidGrandTotalAmount.sum',
|
||||
'overdue.overdueGrandTotalAmount.sum'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'partiallyPaid.partiallyPaidCount.count',
|
||||
label: 'Partially Paid',
|
||||
type: 'number',
|
||||
color: 'processing'
|
||||
},
|
||||
{
|
||||
name: 'paid.count',
|
||||
label: 'Paid',
|
||||
type: 'number',
|
||||
color: 'success'
|
||||
},
|
||||
{
|
||||
name: 'overdue.count',
|
||||
name: 'overdue.overdueCount.count',
|
||||
label: 'Overdue',
|
||||
type: 'number',
|
||||
color: 'error'
|
||||
},
|
||||
{
|
||||
name: 'cancelled.count',
|
||||
label: 'Cancelled',
|
||||
name: 'overdue.overdueGrandTotalAmount.sum',
|
||||
label: 'Overdue Grand Total Amount',
|
||||
type: 'number',
|
||||
color: 'default'
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
color: 'error'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user