import { createElement, lazy } from 'react' const InvoiceInfo = lazy( () => import('../../components/Dashboard/Finance/Invoices/InvoiceInfo') ) const NewInvoice = lazy( () => import('../../components/Dashboard/Finance/Invoices/NewInvoice') ) const PostInvoice = lazy( () => import('../../components/Dashboard/Finance/Invoices/PostInvoice') ) const AcknowledgeInvoice = lazy( () => import('../../components/Dashboard/Finance/Invoices/AcknowledgeInvoice') ) import InvoiceIcon from '../../components/Icons/InvoiceIcon' import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import CheckIcon from '../../components/Icons/CheckIcon' import EditIcon from '../../components/Icons/EditIcon' import XMarkIcon from '../../components/Icons/XMarkIcon' import BinIcon from '../../components/Icons/BinIcon' import ListIcon from '../../components/Icons/ListIcon' export const Invoice = { name: 'invoice', label: 'Invoice', labelPlural: 'Invoices', url: '/dashboard/finance/invoices', prefix: 'INV', icon: InvoiceIcon, actions: [ { name: 'new', type: 'modal', pageName: 'list', modalWidth: 800, label: 'New Invoice', icon: PlusIcon, content: (objectData, { onOk } = {}) => { return createElement(NewInvoice, { defaultValues: objectData, onOk, reset: true }) } }, { name: 'list', type: 'page', pageName: 'list', label: 'List', icon: ListIcon }, { name: 'info', type: 'page', pageName: 'info', label: 'Info', default: true, row: true, icon: InfoCircleIcon }, { name: 'edit', type: 'page', pageName: 'info', label: 'Edit', row: true, icon: EditIcon, visible: (objectData) => { return !(objectData?._isEditing && objectData?._isEditing == true) }, disabled: (objectData) => { return objectData?.state?.type != 'draft' } }, { name: 'cancelEdit', label: 'Cancel Edits', type: 'page', pageName: 'info', icon: XMarkIcon, visible: (objectData) => { return objectData?._isEditing && objectData?._isEditing == true } }, { name: 'finishEdit', label: 'Save Edits', type: 'page', pageName: 'info', icon: CheckIcon, visible: (objectData) => { return objectData?._isEditing && objectData?._isEditing == true } }, { name: 'delete', type: 'page', pageName: 'info', label: 'Delete', icon: BinIcon, danger: true, visible: (objectData) => { return !(objectData?._isEditing && objectData?._isEditing == true) }, disabled: (objectData) => { return objectData?.state?.type != 'draft' } }, { type: 'divider' }, { name: 'newPayment', type: 'alias', objectType: 'payment', aliasAction: 'new', disabled: (objectData) => { const allowedStates = ['acknowledged', 'partiallyPaid', 'overdue'] return !allowedStates.includes(objectData?.state?.type) }, objectData: (objectData) => ({ invoice: objectData, amount: objectData?.grandTotalAmount }) }, { type: 'divider' }, { name: 'post', type: 'modal', modalWidth: 520, label: 'Post', icon: CheckIcon, visible: (objectData) => { return objectData?.state?.type == 'draft' }, content: (objectData, { onOk } = {}) => { return createElement(PostInvoice, { objectData, onOk }) } }, { name: 'acknowledge', type: 'modal', modalWidth: 520, label: 'Acknowledge', icon: CheckIcon, visible: (objectData) => { return objectData?.state?.type == 'sent' }, content: (objectData, { onOk } = {}) => { return createElement(AcknowledgeInvoice, { objectData, onOk }) } }, { name: 'cancel', type: 'page', pageName: 'info', label: 'Cancel', icon: XMarkIcon, danger: true, disabled: (objectData) => { const allowedStates = [ 'acknowledged', 'partiallyPaid', 'overdue', 'sent' ] return !allowedStates.includes(objectData?.state?.type) }, visible: (objectData) => { return objectData?.state?.type != 'draft' } } ], pages: [ { name: 'info', content: () => createElement(InvoiceInfo) } ], group: ['orderType'], filters: [ 'vendor', 'client', 'state', 'order', 'orderType', 'createdAt', 'updatedAt', '_reference' ], sorters: ['createdAt', 'state', 'updatedAt', 'invoiceDate', 'dueDate'], columns: [ '_reference', 'state', 'orderType', 'order', 'to', 'from', 'invoiceDate', 'dueDate', 'totalAmount', 'totalAmountWithTax', 'totalTaxAmount', 'shippingAmount', 'shippingAmountWithTax', 'grandTotalAmount', 'createdAt', 'updatedAt' ], properties: [ { name: '_id', label: 'ID', type: 'id', columnFixed: 'left', objectType: 'invoice', columnWidth: 140, showCopy: true }, { name: 'createdAt', label: 'Created At', type: 'dateTime', readOnly: true, columnWidth: 175 }, { name: '_reference', label: 'Reference', type: 'reference', columnFixed: 'left', required: true, objectType: 'invoice', showCopy: true, readOnly: true, columnWidth: 180 }, { name: 'updatedAt', label: 'Updated At', type: 'dateTime', readOnly: true, columnWidth: 175 }, { name: 'state', label: 'State', type: 'state', readOnly: true, columnWidth: 260 }, { name: 'issuedAt', label: 'Issued At', type: 'dateTime', readOnly: false, required: true, columnWidth: 175 }, { name: 'orderType', label: 'Order Type', type: 'objectType', masterFilter: ['purchaseOrder', 'salesOrder'], required: true, columnWidth: 150 }, { name: 'dueAt', label: 'Due At', type: 'dateTime', required: true, columnWidth: 175 }, { name: 'order', label: 'Order', type: 'object', objectType: (objectData) => { return objectData?.orderType }, required: true, showHyperlink: true, columnWidth: 200 }, { name: 'postedAt', label: 'Posted At', type: 'dateTime', readOnly: true, columnWidth: 175 }, { name: 'fromType', label: 'From Type', type: 'objectType', value: (objectData) => { return objectData?.orderType == 'purchaseOrder' ? 'vendor' : objectData?.fromType }, masterFilter: ['client', 'vendor'], readOnly: (objectData) => { return objectData?.orderType == 'purchaseOrder' ? true : false }, columnWidth: 150, required: true }, { name: 'acknowledgedAt', label: 'Acknowledged At', type: 'dateTime', readOnly: true, columnWidth: 175 }, { name: 'from', label: 'From', required: true, type: 'object', objectType: (objectData) => { return objectData?.fromType }, showHyperlink: true, readOnly: (objectData) => { return objectData?.orderType == 'purchaseOrder' ? true : false }, columnWidth: 200, value: (objectData) => { if (objectData?.orderType == 'purchaseOrder') { return objectData?.order?.vendor } else { return objectData?.from } } }, { name: 'cancelledAt', label: 'Cancelled At', type: 'dateTime', readOnly: true, columnWidth: 175 }, { name: 'toType', label: 'To Type', type: 'objectType', masterFilter: ['vendor', 'client'], value: (objectData) => { return objectData?.orderType == 'salesOrder' ? 'client' : objectData?.toType }, readOnly: (objectData) => { return objectData?.orderType == 'salesOrder' ? true : false }, columnWidth: 150, required: true }, { name: 'paidAt', label: 'Paid At', type: 'dateTime', readOnly: true, columnWidth: 175 }, { name: 'to', label: 'To', required: true, type: 'object', objectType: (objectData) => { return objectData?.toType }, showHyperlink: true, readOnly: (objectData) => { return objectData?.orderType == 'salesOrder' ? true : false }, columnWidth: 200, value: (objectData) => { if (objectData?.orderType == 'salesOrder') { return objectData?.order?.client } else { return objectData?.to } } }, { name: 'totalTaxAmount', label: 'Total Tax Amount', type: 'number', prefix: '£', roundNumber: 2, readOnly: true, columnWidth: 175 }, { name: 'totalAmountWithTax', label: 'Total Amount w/ Tax', type: 'number', prefix: '£', readOnly: true, columnWidth: 175, roundNumber: 2 }, { name: 'shippingAmount', label: 'Shipping Amount', type: 'number', prefix: '£', roundNumber: 2, readOnly: true, columnWidth: 150 }, { name: 'shippingAmountWithTax', label: 'Shipping Amount w/ Tax', type: 'number', prefix: '£', readOnly: true, roundNumber: 2, columnWidth: 200 }, { name: 'totalAmount', label: 'Total Amount', type: 'number', prefix: '£', roundNumber: 2, readOnly: true, columnWidth: 150 }, { name: 'grandTotalAmount', label: 'Grand Total Amount', type: 'number', prefix: '£', 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, columnWidth: 300, showHyperlink: true, masterFilter: (objectData, parentData) => { return { orderType: parentData?.orderType, order: parentData?.order?._id } } }, { name: 'invoiceQuantity', label: 'Quantity', type: 'number', required: true, columnWidth: 175 }, { name: 'invoiceAmount', label: 'Invoice Amount', type: 'number', prefix: '£', roundNumber: 2, required: true, columnWidth: 150 }, { name: 'taxRate', label: 'Tax Rate', type: 'object', objectType: 'taxRate', required: false, showHyperlink: true, columnWidth: 200 }, { name: 'invoiceAmountWithTax', label: 'Invoice Amount w/ Tax', type: 'number', prefix: '£', roundNumber: 2, required: true, readOnly: true, columnWidth: 200, value: (objectData) => { const invoiceAmount = objectData?.invoiceAmount || 0 if (!invoiceAmount) return 0 if (objectData?.taxRate?.rateType == 'percentage') { return ( (invoiceAmount * (1 + objectData?.taxRate?.rate / 100)).toFixed( 2 ) || 0 ) } else if (objectData?.taxRate?.rateType == 'amount') { return (invoiceAmount + objectData?.taxRate?.rate).toFixed(2) || 0 } return invoiceAmount } } ], 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, columnWidth: 300, showHyperlink: true, masterFilter: (objectData, parentData) => { return { orderType: parentData?.orderType, order: parentData?.order?._id } } }, { name: 'invoiceAmount', label: 'Invoice Amount', type: 'number', prefix: '£', roundNumber: 2, columnWidth: 175, required: true }, { name: 'taxRate', label: 'Tax Rate', type: 'object', objectType: 'taxRate', required: false, showHyperlink: true, columnWidth: 200 }, { name: 'invoiceAmountWithTax', label: 'Invoice Amount w/ Tax', type: 'number', prefix: '£', roundNumber: 2, required: true, readOnly: true, columnWidth: 200, value: (objectData) => { const invoiceAmount = objectData?.invoiceAmount || 0 if (!invoiceAmount) return 0 if (objectData?.taxRate?.rateType == 'percentage') { return ( (invoiceAmount * (1 + objectData?.taxRate?.rate / 100)).toFixed( 2 ) || 0 ) } else if (objectData?.taxRate?.rateType == 'amount') { return (invoiceAmount + objectData?.taxRate?.rate).toFixed(2) || 0 } return invoiceAmount } } ], 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.draftCount.count', label: 'Draft', type: 'number', color: 'default' }, { 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: 'acknowledged.acknowledgedCount.count', label: 'Acknowledged', type: 'number', color: 'purple' }, { name: 'due.dueCount.count', label: 'Due', type: 'number', color: 'warning', sum: [ 'sent.sentCount.count', 'partiallyPaid.partiallyPaidCount.count', 'overdue.overdueCount.count', 'acknowledged.acknowledgedCount.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', 'acknowledged.acknowledgedGrandTotalAmount.sum' ] }, { name: 'partiallyPaid.partiallyPaidCount.count', label: 'Partially Paid', type: 'number', color: 'processing' }, { name: 'overdue.overdueCount.count', label: 'Overdue', type: 'number', color: 'error' }, { name: 'overdue.overdueGrandTotalAmount.sum', label: 'Overdue Grand Total Amount', type: 'number', prefix: '£', roundNumber: 2, color: 'error' } ] }