Compare commits
18 Commits
13bbcbe50e
...
e19200c059
| Author | SHA1 | Date | |
|---|---|---|---|
| e19200c059 | |||
| 8508442d2e | |||
| 8945bb9a17 | |||
| cd902d8f13 | |||
| aad5326c7e | |||
| f7532338b6 | |||
| 2879b8648d | |||
| c10daf008e | |||
| b955f42b88 | |||
| 6da485bc8f | |||
| 8b481a9617 | |||
| f24bc27e2c | |||
| 2f323181f5 | |||
| dbb6be8654 | |||
| 3673a7a1ec | |||
| 4995d37aa2 | |||
| 9f05bfd929 | |||
| 863f0ad90c |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
8
assets/icons/shipmenticon.svg
Normal file
8
assets/icons/shipmenticon.svg
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(0.607884,0,0,0.607884,2,6.470704)">
|
||||
<path d="M48.669,64.047L37.295,64.047C35.825,69.222 31.078,73.031 25.453,73.031C19.815,73.031 15.034,69.222 13.552,64.047L10.547,64.047C3.781,64.047 0,60.266 0,53.516L0,11.688C0,4.938 3.781,1.141 10.547,1.141L57.859,1.141C64.609,1.141 68.391,4.938 68.391,11.688L68.391,18.422L76.766,18.422C80.406,18.422 82.859,19.344 85.031,21.781L96.078,34.266C98.109,36.547 98.703,38.266 98.703,41.906L98.703,47.796L90.672,43.432L90.672,42.578C90.672,41.062 90.219,39.797 89.312,38.766L79.844,28.141C78.703,26.859 77.484,26.453 75.859,26.453L68.391,26.453L68.391,41.102L60.359,45.47L60.359,12.328C60.359,10.141 59.281,9.141 57.172,9.141L11.203,9.141C9.078,9.141 8.031,10.141 8.031,12.328L8.031,52.859C8.031,55.063 9.078,56.031 11.203,56.031L13.984,56.031C15.827,51.524 20.28,48.344 25.453,48.344C30.614,48.344 35.037,51.524 36.866,56.031L48.805,56.031C48.713,56.664 48.669,57.335 48.669,58.048L48.669,64.047ZM71.404,39.488C71.395,39.38 71.391,39.269 71.391,39.156L71.391,29.766L75.031,29.766C76.172,29.766 76.938,30.188 77.719,31.062L86.219,40.641C86.421,40.864 86.586,41.062 86.706,41.277L84.098,39.859C80.017,37.638 75.532,37.513 71.404,39.488ZM25.453,66.422C28.609,66.422 31.156,63.828 31.156,60.656C31.156,57.5 28.609,54.906 25.453,54.906C22.281,54.906 19.688,57.5 19.688,60.656C19.688,63.828 22.281,66.422 25.453,66.422Z"/>
|
||||
</g>
|
||||
<path d="M36.262,57.455L47.835,63.705C48.595,64.12 49.484,64.12 50.258,63.705L61.815,57.455C63.32,56.645 64,55.748 64,53.768L64,41.758C64,40.158 63.393,39.124 61.975,38.357L51.928,32.897C50.069,31.885 48.016,31.885 46.157,32.897L36.117,38.357C34.693,39.124 34.085,40.157 34.085,41.757L34.085,53.767C34.085,55.747 34.772,56.644 36.262,57.455M38.295,54.7C37.585,54.313 37.325,53.89 37.325,53.2L37.325,43.393L47.35,48.882L47.35,59.694L38.295,54.7ZM59.79,54.7L50.728,59.694L50.728,48.882L60.76,43.393L60.76,53.201C60.76,53.889 60.5,54.313 59.79,54.701M49.043,45.941L39.177,40.559L42.447,38.751L52.341,44.125L49.043,45.941ZM55.798,42.274L45.918,36.893L47.632,35.939C48.572,35.429 49.498,35.408 50.452,35.939L58.915,40.559L55.798,42.274Z" style="fill-rule:nonzero;"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@ -3,6 +3,7 @@
|
||||
.ant-descriptions-item-content,
|
||||
.ant-menu-title-content,
|
||||
.ant-tag,
|
||||
.ant-card,
|
||||
.ant-checkbox-label,
|
||||
.ant-btn,
|
||||
.ant-breadcrumb-link,
|
||||
@ -24,7 +25,11 @@
|
||||
.ant-segmented-item-label,
|
||||
.ant-badge-status-text,
|
||||
.ant-tree-title,
|
||||
.ant-select {
|
||||
.ant-select,
|
||||
.ant-progress,
|
||||
.ant-collapse,
|
||||
.ant-radio-group,
|
||||
[class*=' ant-radio'] {
|
||||
font-family: 'DM Sans';
|
||||
}
|
||||
|
||||
@ -47,6 +52,15 @@
|
||||
vertical-align: top !important;
|
||||
}
|
||||
|
||||
.object-display-tag .ant-typography-ellipsis-single-line > code {
|
||||
background-color: transparent !important;
|
||||
border: 1px solid transparent !important;
|
||||
max-width: calc(100%);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 95%;
|
||||
}
|
||||
|
||||
.flag {
|
||||
line-height: 0.7;
|
||||
}
|
||||
@ -346,12 +360,19 @@ body {
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
|
||||
.ant-select-selection-item .ant-tag,
|
||||
.ant-select-selection-item .object-display-tag,
|
||||
.ant-select-tree-title .object-display-tag {
|
||||
background: transparent !important;
|
||||
padding: 0;
|
||||
margin-right: 1px !important;
|
||||
padding-top: 3px;
|
||||
}
|
||||
|
||||
.ant-select-selection-item .object-display-tag {
|
||||
top: 1.5px;
|
||||
}
|
||||
|
||||
.ant-select-tree-title .object-display-tag {
|
||||
top: 0.5px;
|
||||
}
|
||||
|
||||
.ant-select-outlined.ant-select-multiple
|
||||
|
||||
@ -3,12 +3,12 @@ import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewFilamentStock = ({ onOk, reset }) => {
|
||||
const NewFilamentStock = ({ onOk, reset, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm
|
||||
type={'filamentStock'}
|
||||
reset={reset}
|
||||
defaultValues={{ state: { type: 'unconsumed' } }}
|
||||
defaultValues={{ state: { type: 'unconsumed' }, ...defaultValues }}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
@ -64,7 +64,8 @@ const NewFilamentStock = ({ onOk, reset }) => {
|
||||
|
||||
NewFilamentStock.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewFilamentStock
|
||||
|
||||
@ -7,6 +7,8 @@ import ProductStockIcon from '../../Icons/ProductStockIcon'
|
||||
import StockEventIcon from '../../Icons/StockEventIcon'
|
||||
import StockAuditIcon from '../../Icons/StockAuditIcon'
|
||||
import PurchaseOrderIcon from '../../Icons/PurchaseOrderIcon'
|
||||
import ShipmentIcon from '../../Icons/ShipmentIcon'
|
||||
import OrderItemIcon from '../../Icons/OrderItemIcon'
|
||||
|
||||
const items = [
|
||||
{
|
||||
@ -41,6 +43,18 @@ const items = [
|
||||
icon: <PurchaseOrderIcon />,
|
||||
path: '/dashboard/inventory/purchaseorders'
|
||||
},
|
||||
{
|
||||
key: 'orderitems',
|
||||
label: 'Order Items',
|
||||
icon: <OrderItemIcon />,
|
||||
path: '/dashboard/inventory/orderitems'
|
||||
},
|
||||
{
|
||||
key: 'shipments',
|
||||
label: 'Shipments',
|
||||
icon: <ShipmentIcon />,
|
||||
path: '/dashboard/inventory/shipments'
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
key: 'stockevents',
|
||||
@ -63,7 +77,9 @@ const routeKeyMap = {
|
||||
'/dashboard/inventory/productstocks': 'productstocks',
|
||||
'/dashboard/inventory/stockevents': 'stockevents',
|
||||
'/dashboard/inventory/stockaudits': 'stockaudits',
|
||||
'/dashboard/inventory/purchaseorders': 'purchaseorders'
|
||||
'/dashboard/inventory/purchaseorders': 'purchaseorders',
|
||||
'/dashboard/inventory/orderitems': 'orderitems',
|
||||
'/dashboard/inventory/shipments': 'shipments'
|
||||
}
|
||||
|
||||
const InventorySidebar = (props) => {
|
||||
|
||||
101
src/components/Dashboard/Inventory/OrderItems.jsx
Normal file
101
src/components/Dashboard/Inventory/OrderItems.jsx
Normal file
@ -0,0 +1,101 @@
|
||||
import { useState, useRef } from 'react'
|
||||
import { Button, Flex, Space, Modal, Dropdown, message } from 'antd'
|
||||
import NewOrderItem from './OrderItems/NewOrderItem'
|
||||
import ObjectTable from '../common/ObjectTable'
|
||||
import PlusIcon from '../../Icons/PlusIcon'
|
||||
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||
import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
|
||||
const OrderItems = () => {
|
||||
const [messageApi, contextHolder] = message.useMessage()
|
||||
const [newOrderItemOpen, setNewOrderItemOpen] = useState(false)
|
||||
const tableRef = useRef()
|
||||
|
||||
const [viewMode, setViewMode] = useViewMode('orderItems')
|
||||
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
useColumnVisibility('orderItems')
|
||||
|
||||
const actionItems = {
|
||||
items: [
|
||||
{
|
||||
label: 'New Order Item',
|
||||
key: 'newOrderItem',
|
||||
icon: <PlusIcon />
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
label: 'Reload List',
|
||||
key: 'reloadList',
|
||||
icon: <ReloadIcon />
|
||||
}
|
||||
],
|
||||
onClick: ({ key }) => {
|
||||
if (key === 'reloadList') {
|
||||
tableRef.current?.reload()
|
||||
} else if (key === 'newOrderItem') {
|
||||
setNewOrderItemOpen(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex vertical={'true'} gap='large'>
|
||||
{contextHolder}
|
||||
<Flex justify={'space-between'}>
|
||||
<Space size='small'>
|
||||
<Dropdown menu={actionItems}>
|
||||
<Button>Actions</Button>
|
||||
</Dropdown>
|
||||
<ColumnViewButton
|
||||
type='orderItem'
|
||||
loading={false}
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
|
||||
onClick={() =>
|
||||
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
|
||||
}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
<ObjectTable
|
||||
ref={tableRef}
|
||||
visibleColumns={columnVisibility}
|
||||
type='orderItem'
|
||||
cards={viewMode === 'cards'}
|
||||
/>
|
||||
</Flex>
|
||||
<Modal
|
||||
open={newOrderItemOpen}
|
||||
styles={{ content: { paddingBottom: '24px' } }}
|
||||
footer={null}
|
||||
width={800}
|
||||
onCancel={() => {
|
||||
setNewOrderItemOpen(false)
|
||||
}}
|
||||
destroyOnHidden={true}
|
||||
>
|
||||
<NewOrderItem
|
||||
onOk={() => {
|
||||
setNewOrderItemOpen(false)
|
||||
messageApi.success('New order item created successfully.')
|
||||
tableRef.current?.reload()
|
||||
}}
|
||||
reset={newOrderItemOpen}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default OrderItems
|
||||
@ -0,0 +1,99 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewOrderItem = ({ onOk, reset, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm
|
||||
type={'orderItem'}
|
||||
reset={reset}
|
||||
defaultValues={{ syncAmount: null, quantity: 1, ...defaultValues }}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
{
|
||||
title: 'Required',
|
||||
key: 'required',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='orderItem'
|
||||
column={1}
|
||||
bordered={false}
|
||||
isEditing={true}
|
||||
required={true}
|
||||
objectData={objectData}
|
||||
visibleProperties={{
|
||||
syncAmount: false,
|
||||
itemAmount: false,
|
||||
totalAmount: false,
|
||||
totalAmountWithTax: false,
|
||||
quantity: false
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Pricing',
|
||||
key: 'pricing',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='orderItem'
|
||||
column={1}
|
||||
bordered={false}
|
||||
isEditing={true}
|
||||
objectData={objectData}
|
||||
visibleProperties={{
|
||||
syncAmount: true,
|
||||
itemAmount: true,
|
||||
quantity: true,
|
||||
taxRate: true,
|
||||
totalAmountWithTax: true,
|
||||
totalAmount: true
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Summary',
|
||||
key: 'summary',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='orderItem'
|
||||
column={1}
|
||||
bordered={false}
|
||||
visibleProperties={{
|
||||
_id: false,
|
||||
createdAt: false,
|
||||
updatedAt: false
|
||||
}}
|
||||
isEditing={false}
|
||||
objectData={objectData}
|
||||
/>
|
||||
)
|
||||
}
|
||||
]
|
||||
return (
|
||||
<WizardView
|
||||
steps={steps}
|
||||
loading={submitLoading}
|
||||
formValid={formValid}
|
||||
title='New Order Item'
|
||||
onSubmit={() => {
|
||||
handleSubmit()
|
||||
onOk()
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
</NewObjectForm>
|
||||
)
|
||||
}
|
||||
|
||||
NewOrderItem.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewOrderItem
|
||||
202
src/components/Dashboard/Inventory/OrderItems/OrderItemInfo.jsx
Normal file
202
src/components/Dashboard/Inventory/OrderItems/OrderItemInfo.jsx
Normal file
@ -0,0 +1,202 @@
|
||||
import { useRef, useState } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { Space, Flex, Card } from 'antd'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import loglevel from 'loglevel'
|
||||
import config from '../../../../config.js'
|
||||
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 ViewButton from '../../common/ViewButton.jsx'
|
||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||
import ObjectForm from '../../common/ObjectForm.jsx'
|
||||
import EditButtons from '../../common/EditButtons.jsx'
|
||||
import LockIndicator from '../../common/LockIndicator.jsx'
|
||||
import ActionHandler from '../../common/ActionHandler.jsx'
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
|
||||
const log = loglevel.getLogger('OrderItemInfo')
|
||||
log.setLevel(config.logLevel)
|
||||
|
||||
const OrderItemInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const orderItemId = new URLSearchParams(location.search).get('orderItemId')
|
||||
const [collapseState, updateCollapseState] = useCollapseState(
|
||||
'OrderItemInfo',
|
||||
{
|
||||
info: true,
|
||||
notes: true,
|
||||
auditLogs: true
|
||||
}
|
||||
)
|
||||
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
lock: null,
|
||||
loading: false,
|
||||
objectData: {}
|
||||
})
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current?.handleFetchObject?.()
|
||||
return true
|
||||
},
|
||||
edit: () => {
|
||||
objectFormRef?.current?.startEditing?.()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef?.current?.cancelEditing?.()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef?.current?.handleUpdate?.()
|
||||
return true
|
||||
},
|
||||
delete: () => {
|
||||
objectFormRef?.current?.handleDelete?.()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
gap='large'
|
||||
vertical='true'
|
||||
style={{
|
||||
maxHeight: '100%',
|
||||
minHeight: 0
|
||||
}}
|
||||
>
|
||||
<Flex justify={'space-between'}>
|
||||
<Space size='middle'>
|
||||
<Space size='small'>
|
||||
<ObjectActions
|
||||
type='orderItem'
|
||||
id={orderItemId}
|
||||
disabled={objectFormState.loading}
|
||||
objectData={objectFormState.objectData}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Order Item Information' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||
]}
|
||||
visibleState={collapseState}
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
</Space>
|
||||
<Space>
|
||||
<EditButtons
|
||||
isEditing={objectFormState.isEditing}
|
||||
handleUpdate={() => {
|
||||
actionHandlerRef.current.callAction('finishEdit')
|
||||
}}
|
||||
cancelEditing={() => {
|
||||
actionHandlerRef.current.callAction('cancelEdit')
|
||||
}}
|
||||
startEditing={() => {
|
||||
actionHandlerRef.current.callAction('edit')
|
||||
}}
|
||||
editLoading={objectFormState.editLoading}
|
||||
formValid={objectFormState.formValid}
|
||||
disabled={objectFormState.lock?.locked || objectFormState.loading}
|
||||
loading={objectFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
|
||||
<ScrollBox>
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<ObjectForm
|
||||
id={orderItemId}
|
||||
type='orderItem'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
>
|
||||
{({ loading, isEditing, objectData }) => (
|
||||
<Flex vertical gap={'large'}>
|
||||
<InfoCollapse
|
||||
title='Order Item Information'
|
||||
icon={<InfoCircleIcon />}
|
||||
active={collapseState.info}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('info', expanded)
|
||||
}
|
||||
collapseKey='info'
|
||||
>
|
||||
<ObjectInfo
|
||||
loading={loading}
|
||||
indicator={<LoadingOutlined />}
|
||||
isEditing={isEditing}
|
||||
type='orderItem'
|
||||
objectData={objectData}
|
||||
labelWidth='200px'
|
||||
/>
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
)}
|
||||
</ObjectForm>
|
||||
</ActionHandler>
|
||||
<InfoCollapse
|
||||
title='Notes'
|
||||
icon={<NoteIcon />}
|
||||
active={collapseState.notes}
|
||||
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
||||
collapseKey='notes'
|
||||
>
|
||||
<Card>
|
||||
<NotesPanel _id={orderItemId} type='orderItem' />
|
||||
</Card>
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Audit Logs'
|
||||
icon={<AuditLogIcon />}
|
||||
active={collapseState.auditLogs}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('auditLogs', expanded)
|
||||
}
|
||||
collapseKey='auditLogs'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
type='auditLog'
|
||||
masterFilter={{ 'parent._id': orderItemId }}
|
||||
visibleColumns={{ _id: false, 'parent._id': false }}
|
||||
/>
|
||||
)}
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
</ScrollBox>
|
||||
</Flex>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default OrderItemInfo
|
||||
@ -3,12 +3,12 @@ import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewPartStock = ({ onOk, reset }) => {
|
||||
const NewPartStock = ({ onOk, reset, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm
|
||||
type={'partStock'}
|
||||
reset={reset}
|
||||
defaultValues={{ state: { type: 'new' } }}
|
||||
defaultValues={{ state: { type: 'new' }, ...defaultValues }}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
@ -64,7 +64,8 @@ const NewPartStock = ({ onOk, reset }) => {
|
||||
|
||||
NewPartStock.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewPartStock
|
||||
|
||||
@ -157,6 +157,7 @@ const PartStockInfo = () => {
|
||||
isEditing={isEditing}
|
||||
type='partStock'
|
||||
objectData={objectData}
|
||||
labelWidth='175px'
|
||||
visibleProperties={{
|
||||
content: false,
|
||||
testObject: false
|
||||
|
||||
@ -5,12 +5,12 @@ import WizardView from '../../common/WizardView'
|
||||
import { getModelProperty } from '../../../../database/ObjectModels.js'
|
||||
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||
|
||||
const NewPurchaseOrder = ({ onOk, reset }) => {
|
||||
const NewPurchaseOrder = ({ onOk, reset, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm
|
||||
type={'purchaseOrder'}
|
||||
reset={reset}
|
||||
defaultValues={{ state: { type: 'new' } }}
|
||||
defaultValues={{ state: { type: 'new' }, ...defaultValues }}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
@ -83,7 +83,8 @@ const NewPurchaseOrder = ({ onOk, reset }) => {
|
||||
|
||||
NewPurchaseOrder.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewPurchaseOrder
|
||||
|
||||
@ -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'
|
||||
@ -21,9 +21,8 @@ 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 { getModelProperty } from '../../../../database/ObjectModels.js'
|
||||
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||
import OrderItemsIcon from '../../../Icons/OrderItemsIcon.jsx'
|
||||
import OrderItemsIcon from '../../../Icons/OrderItemIcon.jsx'
|
||||
import NewOrderItem from '../OrderItems/NewOrderItem.jsx'
|
||||
|
||||
const log = loglevel.getLogger('PurchaseOrderInfo')
|
||||
log.setLevel(config.logLevel)
|
||||
@ -32,6 +31,7 @@ const PurchaseOrderInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const [newOrderItemOpen, setNewOrderItemOpen] = useState(false)
|
||||
const purchaseOrderId = new URLSearchParams(location.search).get(
|
||||
'purchaseOrderId'
|
||||
)
|
||||
@ -73,6 +73,10 @@ const PurchaseOrderInfo = () => {
|
||||
delete: () => {
|
||||
objectFormRef?.current?.handleDelete?.()
|
||||
return true
|
||||
},
|
||||
newOrderItem: () => {
|
||||
setNewOrderItemOpen(true)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,11 +184,10 @@ const PurchaseOrderInfo = () => {
|
||||
}
|
||||
collapseKey='info'
|
||||
>
|
||||
<ObjectProperty
|
||||
{...getModelProperty('purchaseOrder', 'items')}
|
||||
isEditing={isEditing}
|
||||
objectData={objectData}
|
||||
loading={loading}
|
||||
<ObjectTable
|
||||
type='orderItem'
|
||||
masterFilter={{ 'order._id': purchaseOrderId }}
|
||||
visibleColumns={{ order: false }}
|
||||
/>
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
@ -224,6 +227,27 @@ const PurchaseOrderInfo = () => {
|
||||
</Flex>
|
||||
</ScrollBox>
|
||||
</Flex>
|
||||
<Modal
|
||||
open={newOrderItemOpen}
|
||||
onCancel={() => {
|
||||
setNewOrderItemOpen(false)
|
||||
}}
|
||||
width={800}
|
||||
footer={null}
|
||||
destroyOnHidden={true}
|
||||
>
|
||||
<NewOrderItem
|
||||
onOk={() => {
|
||||
setNewOrderItemOpen(false)
|
||||
}}
|
||||
reset={newOrderItemOpen}
|
||||
defaultValues={{
|
||||
order: { _id: purchaseOrderId },
|
||||
orderType: 'purchaseOrder',
|
||||
syncAmount: 'itemCost'
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
101
src/components/Dashboard/Inventory/Shipments.jsx
Normal file
101
src/components/Dashboard/Inventory/Shipments.jsx
Normal file
@ -0,0 +1,101 @@
|
||||
import { useState, useRef } from 'react'
|
||||
import { Button, Flex, Space, Modal, Dropdown, message } from 'antd'
|
||||
import NewShipment from './Shipments/NewShipment'
|
||||
import ObjectTable from '../common/ObjectTable'
|
||||
import PlusIcon from '../../Icons/PlusIcon'
|
||||
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||
import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
|
||||
const Shipments = () => {
|
||||
const [messageApi, contextHolder] = message.useMessage()
|
||||
const [newShipmentOpen, setNewShipmentOpen] = useState(false)
|
||||
const tableRef = useRef()
|
||||
|
||||
const [viewMode, setViewMode] = useViewMode('shipments')
|
||||
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
useColumnVisibility('shipments')
|
||||
|
||||
const actionItems = {
|
||||
items: [
|
||||
{
|
||||
label: 'New Shipment',
|
||||
key: 'newShipment',
|
||||
icon: <PlusIcon />
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
label: 'Reload List',
|
||||
key: 'reloadList',
|
||||
icon: <ReloadIcon />
|
||||
}
|
||||
],
|
||||
onClick: ({ key }) => {
|
||||
if (key === 'reloadList') {
|
||||
tableRef.current?.reload()
|
||||
} else if (key === 'newShipment') {
|
||||
setNewShipmentOpen(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex vertical={'true'} gap='large'>
|
||||
{contextHolder}
|
||||
<Flex justify={'space-between'}>
|
||||
<Space size='small'>
|
||||
<Dropdown menu={actionItems}>
|
||||
<Button>Actions</Button>
|
||||
</Dropdown>
|
||||
<ColumnViewButton
|
||||
type='shipment'
|
||||
loading={false}
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
|
||||
onClick={() =>
|
||||
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
|
||||
}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
<ObjectTable
|
||||
ref={tableRef}
|
||||
visibleColumns={columnVisibility}
|
||||
type='shipment'
|
||||
cards={viewMode === 'cards'}
|
||||
/>
|
||||
</Flex>
|
||||
<Modal
|
||||
open={newShipmentOpen}
|
||||
styles={{ content: { paddingBottom: '24px' } }}
|
||||
footer={null}
|
||||
width={800}
|
||||
onCancel={() => {
|
||||
setNewShipmentOpen(false)
|
||||
}}
|
||||
destroyOnHidden={true}
|
||||
>
|
||||
<NewShipment
|
||||
onOk={() => {
|
||||
setNewShipmentOpen(false)
|
||||
messageApi.success('New shipment created successfully.')
|
||||
tableRef.current?.reload()
|
||||
}}
|
||||
reset={newShipmentOpen}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Shipments
|
||||
94
src/components/Dashboard/Inventory/Shipments/NewShipment.jsx
Normal file
94
src/components/Dashboard/Inventory/Shipments/NewShipment.jsx
Normal file
@ -0,0 +1,94 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
import { getModelProperty } from '../../../../database/ObjectModels.js'
|
||||
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||
|
||||
const NewShipment = ({ onOk, reset, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm
|
||||
type={'shipment'}
|
||||
reset={reset}
|
||||
defaultValues={{ state: { type: 'pending' }, ...defaultValues }}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
{
|
||||
title: 'Required',
|
||||
key: 'required',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='shipment'
|
||||
column={1}
|
||||
bordered={false}
|
||||
isEditing={true}
|
||||
required={true}
|
||||
objectData={objectData}
|
||||
visibleProperties={{
|
||||
items: false,
|
||||
cost: false,
|
||||
shippedDate: false,
|
||||
expectedDeliveryDate: false,
|
||||
actualDeliveryDate: false,
|
||||
notes: false
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Items',
|
||||
key: 'items',
|
||||
content: (
|
||||
<ObjectProperty
|
||||
{...getModelProperty('shipment', 'items')}
|
||||
isEditing={true}
|
||||
objectData={objectData}
|
||||
loading={submitLoading}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Summary',
|
||||
key: 'summary',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='shipment'
|
||||
column={1}
|
||||
bordered={false}
|
||||
visibleProperties={{
|
||||
_id: false,
|
||||
createdAt: false,
|
||||
updatedAt: false,
|
||||
items: false
|
||||
}}
|
||||
isEditing={false}
|
||||
objectData={objectData}
|
||||
/>
|
||||
)
|
||||
}
|
||||
]
|
||||
return (
|
||||
<WizardView
|
||||
steps={steps}
|
||||
loading={submitLoading}
|
||||
formValid={formValid}
|
||||
title='New Shipment'
|
||||
onSubmit={() => {
|
||||
handleSubmit()
|
||||
onOk()
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
</NewObjectForm>
|
||||
)
|
||||
}
|
||||
|
||||
NewShipment.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewShipment
|
||||
229
src/components/Dashboard/Inventory/Shipments/ShipmentInfo.jsx
Normal file
229
src/components/Dashboard/Inventory/Shipments/ShipmentInfo.jsx
Normal file
@ -0,0 +1,229 @@
|
||||
import { useRef, useState } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { Space, Flex, Card } from 'antd'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import loglevel from 'loglevel'
|
||||
import config from '../../../../config.js'
|
||||
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 ViewButton from '../../common/ViewButton.jsx'
|
||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||
import ObjectForm from '../../common/ObjectForm.jsx'
|
||||
import EditButtons from '../../common/EditButtons.jsx'
|
||||
import LockIndicator from '../../common/LockIndicator.jsx'
|
||||
import ActionHandler from '../../common/ActionHandler.jsx'
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
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 { getModelProperty } from '../../../../database/ObjectModels.js'
|
||||
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||
import OrderItemsIcon from '../../../Icons/OrderItemIcon.jsx'
|
||||
|
||||
const log = loglevel.getLogger('ShipmentInfo')
|
||||
log.setLevel(config.logLevel)
|
||||
|
||||
const ShipmentInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const shipmentId = new URLSearchParams(location.search).get('shipmentId')
|
||||
const [collapseState, updateCollapseState] = useCollapseState(
|
||||
'ShipmentInfo',
|
||||
{
|
||||
info: true,
|
||||
notes: true,
|
||||
auditLogs: true
|
||||
}
|
||||
)
|
||||
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
lock: null,
|
||||
loading: false,
|
||||
objectData: {}
|
||||
})
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current?.handleFetchObject?.()
|
||||
return true
|
||||
},
|
||||
edit: () => {
|
||||
objectFormRef?.current?.startEditing?.()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef?.current?.cancelEditing?.()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef?.current?.handleUpdate?.()
|
||||
return true
|
||||
},
|
||||
delete: () => {
|
||||
objectFormRef?.current?.handleDelete?.()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
gap='large'
|
||||
vertical='true'
|
||||
style={{
|
||||
maxHeight: '100%',
|
||||
minHeight: 0
|
||||
}}
|
||||
>
|
||||
<Flex justify={'space-between'}>
|
||||
<Space size='middle'>
|
||||
<Space size='small'>
|
||||
<ObjectActions
|
||||
type='shipment'
|
||||
id={shipmentId}
|
||||
disabled={objectFormState.loading}
|
||||
objectData={objectFormState.objectData}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Shipment Information' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||
]}
|
||||
visibleState={collapseState}
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
type='shipment'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
</Space>
|
||||
<Space>
|
||||
<EditButtons
|
||||
isEditing={objectFormState.isEditing}
|
||||
handleUpdate={() => {
|
||||
actionHandlerRef.current.callAction('finishEdit')
|
||||
}}
|
||||
cancelEditing={() => {
|
||||
actionHandlerRef.current.callAction('cancelEdit')
|
||||
}}
|
||||
startEditing={() => {
|
||||
actionHandlerRef.current.callAction('edit')
|
||||
}}
|
||||
editLoading={objectFormState.editLoading}
|
||||
formValid={objectFormState.formValid}
|
||||
disabled={objectFormState.lock?.locked || objectFormState.loading}
|
||||
loading={objectFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
|
||||
<ScrollBox>
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<ObjectForm
|
||||
id={shipmentId}
|
||||
type='shipment'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
>
|
||||
{({ loading, isEditing, objectData }) => (
|
||||
<Flex vertical gap={'large'}>
|
||||
<InfoCollapse
|
||||
title='Shipment Information'
|
||||
icon={<InfoCircleIcon />}
|
||||
active={collapseState.info}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('info', expanded)
|
||||
}
|
||||
collapseKey='info'
|
||||
>
|
||||
<ObjectInfo
|
||||
loading={loading}
|
||||
indicator={<LoadingOutlined />}
|
||||
isEditing={isEditing}
|
||||
type='shipment'
|
||||
objectData={objectData}
|
||||
visibleProperties={{
|
||||
items: false
|
||||
}}
|
||||
/>
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Shipment Items'
|
||||
icon={<OrderItemsIcon />}
|
||||
active={collapseState.info}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('info', expanded)
|
||||
}
|
||||
collapseKey='info'
|
||||
>
|
||||
<ObjectProperty
|
||||
{...getModelProperty('shipment', 'items')}
|
||||
isEditing={isEditing}
|
||||
objectData={objectData}
|
||||
loading={loading}
|
||||
/>
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
)}
|
||||
</ObjectForm>
|
||||
</ActionHandler>
|
||||
<InfoCollapse
|
||||
title='Notes'
|
||||
icon={<NoteIcon />}
|
||||
active={collapseState.notes}
|
||||
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
||||
collapseKey='notes'
|
||||
>
|
||||
<Card>
|
||||
<NotesPanel _id={shipmentId} type='shipment' />
|
||||
</Card>
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Audit Logs'
|
||||
icon={<AuditLogIcon />}
|
||||
active={collapseState.auditLogs}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('auditLogs', expanded)
|
||||
}
|
||||
collapseKey='auditLogs'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
type='auditLog'
|
||||
masterFilter={{ 'parent._id': shipmentId }}
|
||||
visibleColumns={{ _id: false, 'parent._id': false }}
|
||||
/>
|
||||
)}
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
</ScrollBox>
|
||||
</Flex>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ShipmentInfo
|
||||
@ -3,12 +3,12 @@ import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewStockAudit = ({ onOk, reset }) => {
|
||||
const NewStockAudit = ({ onOk, reset, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm
|
||||
type={'stockAudit'}
|
||||
reset={reset}
|
||||
defaultValues={{ state: { type: 'new' } }}
|
||||
defaultValues={{ state: { type: 'new' }, ...defaultValues }}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
@ -64,7 +64,8 @@ const NewStockAudit = ({ onOk, reset }) => {
|
||||
|
||||
NewStockAudit.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewStockAudit
|
||||
|
||||
@ -3,13 +3,14 @@ import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewCourierService = ({ onOk }) => {
|
||||
const NewCourierService = ({ onOk, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm
|
||||
type={'courierService'}
|
||||
defaultValues={{
|
||||
active: true,
|
||||
tracked: false
|
||||
tracked: false,
|
||||
...defaultValues
|
||||
}}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
@ -80,7 +81,8 @@ const NewCourierService = ({ onOk }) => {
|
||||
|
||||
NewCourierService.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewCourierService
|
||||
|
||||
@ -3,9 +3,9 @@ import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewCourier = ({ onOk }) => {
|
||||
const NewCourier = ({ onOk, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm type={'courier'}>
|
||||
<NewObjectForm type={'courier'} defaultValues={{ ...defaultValues }}>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
{
|
||||
@ -74,7 +74,8 @@ const NewCourier = ({ onOk }) => {
|
||||
|
||||
NewCourier.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewCourier
|
||||
|
||||
@ -3,11 +3,11 @@ import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewDocumentPrinter = ({ onOk }) => {
|
||||
const NewDocumentPrinter = ({ onOk, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm
|
||||
type={'documentPrinter'}
|
||||
defaultValues={{ active: true, global: false }}
|
||||
defaultValues={{ active: true, global: false, ...defaultValues }}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
@ -79,7 +79,8 @@ const NewDocumentPrinter = ({ onOk }) => {
|
||||
|
||||
NewDocumentPrinter.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewDocumentPrinter
|
||||
|
||||
@ -3,9 +3,9 @@ import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewDocumentSize = ({ onOk }) => {
|
||||
const NewDocumentSize = ({ onOk, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm type={'documentSize'}>
|
||||
<NewObjectForm type={'documentSize'} defaultValues={{ ...defaultValues }}>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
{
|
||||
@ -60,7 +60,8 @@ const NewDocumentSize = ({ onOk }) => {
|
||||
|
||||
NewDocumentSize.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewDocumentSize
|
||||
|
||||
@ -163,6 +163,7 @@ const DocumentTemplateInfo = () => {
|
||||
content: false,
|
||||
testObject: false
|
||||
}}
|
||||
labelWidth='175px'
|
||||
/>
|
||||
)
|
||||
}}
|
||||
|
||||
@ -3,11 +3,11 @@ import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewDocumentTemplate = ({ onOk }) => {
|
||||
const NewDocumentTemplate = ({ onOk, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm
|
||||
type={'documentTemplate'}
|
||||
defaultValues={{ active: true, global: false }}
|
||||
defaultValues={{ active: true, global: false, ...defaultValues }}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
@ -79,7 +79,8 @@ const NewDocumentTemplate = ({ onOk }) => {
|
||||
|
||||
NewDocumentTemplate.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewDocumentTemplate
|
||||
|
||||
@ -3,11 +3,15 @@ import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewPart = ({ onOk }) => {
|
||||
const NewPart = ({ onOk, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm
|
||||
type={'part'}
|
||||
defaultValues={{ priceMode: 'margin', globalPricing: true }}
|
||||
defaultValues={{
|
||||
priceMode: 'margin',
|
||||
globalPricing: true,
|
||||
...defaultValues
|
||||
}}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
@ -112,7 +116,8 @@ const NewPart = ({ onOk }) => {
|
||||
|
||||
NewPart.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewPart
|
||||
|
||||
@ -3,9 +3,12 @@ import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewProduct = ({ onOk }) => {
|
||||
const NewProduct = ({ onOk, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm type={'product'} defaultValues={{ priceMode: 'margin' }}>
|
||||
<NewObjectForm
|
||||
type={'product'}
|
||||
defaultValues={{ priceMode: 'margin', ...defaultValues }}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
{
|
||||
@ -98,7 +101,8 @@ const NewProduct = ({ onOk }) => {
|
||||
|
||||
NewProduct.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewProduct
|
||||
|
||||
@ -3,9 +3,9 @@ import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewTaxRate = ({ onOk }) => {
|
||||
const NewTaxRate = ({ onOk, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm type={'taxRate'}>
|
||||
<NewObjectForm type={'taxRate'} defaultValues={{ ...defaultValues }}>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
{
|
||||
@ -74,7 +74,8 @@ const NewTaxRate = ({ onOk }) => {
|
||||
|
||||
NewTaxRate.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewTaxRate
|
||||
|
||||
@ -3,9 +3,9 @@ import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewTaxRecord = ({ onOk }) => {
|
||||
const NewTaxRecord = ({ onOk, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm type={'taxRecord'}>
|
||||
<NewObjectForm type={'taxRecord'} defaultValues={{ ...defaultValues }}>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
{
|
||||
@ -74,7 +74,8 @@ const NewTaxRecord = ({ onOk }) => {
|
||||
|
||||
NewTaxRecord.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewTaxRecord
|
||||
|
||||
@ -3,9 +3,12 @@ import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewVendor = ({ onOk }) => {
|
||||
const NewVendor = ({ onOk, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm type={'vendor'}>
|
||||
<NewObjectForm
|
||||
type={'vendor'}
|
||||
defaultValues={{ active: true, ...defaultValues }}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
{
|
||||
@ -74,7 +77,8 @@ const NewVendor = ({ onOk }) => {
|
||||
|
||||
NewVendor.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewVendor
|
||||
|
||||
@ -3,12 +3,13 @@ import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewGCodeFile = ({ onOk }) => {
|
||||
const NewGCodeFile = ({ onOk, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm
|
||||
type={'gcodeFile'}
|
||||
defaultValues={{
|
||||
state: { type: 'draft' }
|
||||
state: { type: 'draft' },
|
||||
...defaultValues
|
||||
}}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
@ -83,7 +84,8 @@ const NewGCodeFile = ({ onOk }) => {
|
||||
|
||||
NewGCodeFile.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewGCodeFile
|
||||
|
||||
@ -3,12 +3,13 @@ import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewJob = ({ onOk }) => {
|
||||
const NewJob = ({ onOk, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm
|
||||
type={'job'}
|
||||
defaultValues={{
|
||||
state: { type: 'draft' }
|
||||
state: { type: 'draft' },
|
||||
...defaultValues
|
||||
}}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
@ -66,7 +67,8 @@ const NewJob = ({ onOk }) => {
|
||||
|
||||
NewJob.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewJob
|
||||
|
||||
@ -3,7 +3,7 @@ import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewPrinter = ({ onOk }) => {
|
||||
const NewPrinter = ({ onOk, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm
|
||||
type={'printer'}
|
||||
@ -12,7 +12,8 @@ const NewPrinter = ({ onOk }) => {
|
||||
port: 7125,
|
||||
protocol: 'ws'
|
||||
},
|
||||
active: true
|
||||
active: true,
|
||||
...defaultValues
|
||||
}}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
@ -103,7 +104,8 @@ const NewPrinter = ({ onOk }) => {
|
||||
|
||||
NewPrinter.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewPrinter
|
||||
|
||||
@ -54,7 +54,6 @@ const FileUpload = ({
|
||||
currentFiles.length === 0
|
||||
: !currentFiles
|
||||
setHasNoItems(noItems)
|
||||
console.log('No items', noItems)
|
||||
}, [currentFiles, multiple])
|
||||
|
||||
const handleFileUpload = async (file) => {
|
||||
|
||||
@ -11,6 +11,8 @@ const { Text, Link } = Typography
|
||||
|
||||
const IdDisplay = ({
|
||||
id,
|
||||
reference = null,
|
||||
plainCode = false,
|
||||
type,
|
||||
showCopy = true,
|
||||
longId = true,
|
||||
@ -44,23 +46,37 @@ const IdDisplay = ({
|
||||
displayId = prefix + ':' + id.toString().slice(-6)
|
||||
}
|
||||
|
||||
if (reference) {
|
||||
displayId = prefix + ':' + reference
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex
|
||||
align={'end'}
|
||||
align={'center'}
|
||||
className='iddisplay'
|
||||
style={{ minWidth: '0px', width: '100%' }}
|
||||
>
|
||||
{(() => {
|
||||
const textElement = (
|
||||
<Text
|
||||
code
|
||||
ellipsis
|
||||
var textElement = (
|
||||
<code
|
||||
style={showCopy ? { marginRight: 6, minWidth: '0px' } : undefined}
|
||||
>
|
||||
{displayId}
|
||||
</Text>
|
||||
</code>
|
||||
)
|
||||
|
||||
if (plainCode == false) {
|
||||
textElement = (
|
||||
<Text
|
||||
code
|
||||
ellipsis
|
||||
style={showCopy ? { marginRight: 6, minWidth: '0px' } : undefined}
|
||||
>
|
||||
{displayId}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
|
||||
// If hyperlink is enabled
|
||||
if (showHyperlink && hyperlink != null) {
|
||||
const linkElement = (
|
||||
@ -128,9 +144,11 @@ const IdDisplay = ({
|
||||
IdDisplay.propTypes = {
|
||||
id: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
reference: PropTypes.string,
|
||||
showCopy: PropTypes.bool,
|
||||
longId: PropTypes.bool,
|
||||
showHyperlink: PropTypes.bool,
|
||||
plainCode: PropTypes.bool,
|
||||
showSpotlight: PropTypes.bool
|
||||
}
|
||||
|
||||
|
||||
@ -259,7 +259,6 @@ const ObjectChildTable = ({
|
||||
if (rollup && typeof rollup.value === 'function') {
|
||||
try {
|
||||
const updatedObjectData = { ...objectData }
|
||||
console.log('UPDATED OBJECT DATA', value)
|
||||
updatedObjectData[property.name] = value
|
||||
summaryRow[property.name] = rollup.value(updatedObjectData)
|
||||
} catch (e) {
|
||||
|
||||
@ -1,24 +1,60 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { Typography, Flex, Badge, Tag } from 'antd'
|
||||
import { useState, useEffect, useContext, useCallback } from 'react'
|
||||
import { Typography, Flex, Badge, Tag, Popover } from 'antd'
|
||||
import { useState, useEffect, useContext, useCallback, useRef } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { getModelByName } from '../../../database/ObjectModels'
|
||||
import { ApiServerContext } from '../context/ApiServerContext'
|
||||
import { AuthContext } from '../context/AuthContext'
|
||||
import merge from 'lodash/merge'
|
||||
import IdDisplay from './IdDisplay'
|
||||
import SpotlightTooltip from './SpotlightTooltip'
|
||||
|
||||
const { Text } = Typography
|
||||
const { Text, Link } = Typography
|
||||
|
||||
const ObjectDisplay = ({ object, objectType }) => {
|
||||
const ObjectDisplay = ({
|
||||
object,
|
||||
objectType,
|
||||
showHyperlink = false,
|
||||
showSpotlight = true
|
||||
}) => {
|
||||
const [objectData, setObjectData] = useState(object)
|
||||
const { subscribeToObjectUpdates, connected } = useContext(ApiServerContext)
|
||||
const { subscribeToObjectUpdates, connected, fetchSpotlightData } =
|
||||
useContext(ApiServerContext)
|
||||
const { token } = useContext(AuthContext)
|
||||
const navigate = useNavigate()
|
||||
const idRef = useRef(null)
|
||||
|
||||
// Update event handler
|
||||
const updateObjectEventHandler = useCallback((value) => {
|
||||
setObjectData((prev) => merge({}, prev, value))
|
||||
}, [])
|
||||
|
||||
// Detect minimal objects that only contain an _id
|
||||
const isMinimalObject = useCallback((obj) => {
|
||||
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return false
|
||||
const keys = Object.keys(obj)
|
||||
return keys.length === 1 && keys[0] === '_id' && obj._id
|
||||
}, [])
|
||||
|
||||
// If only an _id is provided, fetch the full object via spotlight
|
||||
const fetchFullObjectIfNeeded = useCallback(
|
||||
async (obj) => {
|
||||
if (!isMinimalObject(obj) || !objectType) return obj
|
||||
try {
|
||||
const model = getModelByName(objectType)
|
||||
const spotlightQuery = `${model.prefix}:${obj._id}`
|
||||
const spotlightResult = await fetchSpotlightData(spotlightQuery)
|
||||
if (spotlightResult && typeof spotlightResult === 'object') {
|
||||
return spotlightResult
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch spotlight data:', err)
|
||||
}
|
||||
return obj
|
||||
},
|
||||
[fetchSpotlightData, isMinimalObject, objectType]
|
||||
)
|
||||
|
||||
// Subscribe to object updates when component mounts
|
||||
useEffect(() => {
|
||||
if (object?._id && objectType && connected && token) {
|
||||
@ -43,46 +79,132 @@ const ObjectDisplay = ({ object, objectType }) => {
|
||||
|
||||
// Update local state when object prop changes
|
||||
useEffect(() => {
|
||||
setObjectData(object)
|
||||
}, [object])
|
||||
|
||||
if (idRef.current == object?._id) return
|
||||
idRef.current = object?._id
|
||||
let cancelled = false
|
||||
const hydrateObject = async () => {
|
||||
const fullObject = await fetchFullObjectIfNeeded(object)
|
||||
if (!cancelled) setObjectData(fullObject)
|
||||
}
|
||||
hydrateObject()
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [object, fetchFullObjectIfNeeded])
|
||||
console.log(objectData, objectType)
|
||||
if (!objectData) {
|
||||
return <Text type='secondary'>n/a</Text>
|
||||
}
|
||||
|
||||
const model = getModelByName(objectType)
|
||||
const Icon = model.icon
|
||||
const prefix = model.prefix
|
||||
|
||||
// Get hyperlink URL from model's default actions
|
||||
var hyperlink = null
|
||||
const defaultModelActions =
|
||||
model.actions?.filter((action) => action.default == true) || []
|
||||
const objectId = objectData._id
|
||||
|
||||
if (defaultModelActions.length >= 1 && objectId) {
|
||||
hyperlink = defaultModelActions[0].url(objectId)
|
||||
}
|
||||
|
||||
// Render name with hyperlink/spotlight support
|
||||
const renderNameDisplay = () => {
|
||||
if (!objectData?.name) return null
|
||||
|
||||
const textElement = (
|
||||
<Text ellipsis style={{ lineHeight: '1', paddingBottom: '2px' }}>
|
||||
{objectData.name}
|
||||
</Text>
|
||||
)
|
||||
|
||||
// If hyperlink is enabled
|
||||
if (showHyperlink && hyperlink != null) {
|
||||
const linkElement = (
|
||||
<Link onClick={() => navigate(hyperlink)} ellipsis>
|
||||
{textElement}
|
||||
</Link>
|
||||
)
|
||||
|
||||
if (showSpotlight && objectId) {
|
||||
return (
|
||||
<Popover
|
||||
content={
|
||||
<SpotlightTooltip
|
||||
query={prefix + ':' + objectId}
|
||||
type={objectType}
|
||||
/>
|
||||
}
|
||||
trigger={['hover', 'click']}
|
||||
placement='topLeft'
|
||||
arrow={false}
|
||||
style={{ padding: 0 }}
|
||||
>
|
||||
{linkElement}
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
return linkElement
|
||||
}
|
||||
|
||||
// If hyperlink is disabled
|
||||
if (showSpotlight && objectId) {
|
||||
return (
|
||||
<Popover
|
||||
content={
|
||||
<SpotlightTooltip
|
||||
query={prefix + ':' + objectId}
|
||||
type={objectType}
|
||||
/>
|
||||
}
|
||||
trigger={['hover', 'click']}
|
||||
placement='topLeft'
|
||||
arrow={false}
|
||||
>
|
||||
{textElement}
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
return textElement
|
||||
}
|
||||
|
||||
return (
|
||||
<Tag
|
||||
style={{
|
||||
margin: 0,
|
||||
border: 'none',
|
||||
minWidth: 0,
|
||||
paddingBottom: '2.5px',
|
||||
maxWidth: '100%'
|
||||
}}
|
||||
className='object-display-tag'
|
||||
>
|
||||
<Flex
|
||||
gap={objectData?.color ? 'small' : '4px'}
|
||||
gap={objectData?.color ? 'small' : '5px'}
|
||||
align='center'
|
||||
style={{ minWidth: 0 }}
|
||||
style={{ minWidth: 0, height: '24px' }}
|
||||
>
|
||||
<Icon style={{ marginBottom: '-3px' }} />
|
||||
<Icon />
|
||||
<Flex gap={'small'} align='center' style={{ minWidth: 0 }}>
|
||||
{objectData?.color ? <Badge color={objectData?.color} /> : null}
|
||||
<div style={{ paddingTop: '1.5px', minWidth: 0 }}>
|
||||
{objectData?.name ? (
|
||||
<Text ellipsis style={{ lineHeight: '1' }}>
|
||||
{objectData.name}
|
||||
</Text>
|
||||
) : null}
|
||||
{objectData?.color ? (
|
||||
<Badge
|
||||
color={objectData?.color}
|
||||
style={{ marginBottom: '1.5px' }}
|
||||
/>
|
||||
) : null}
|
||||
<div style={{ minWidth: 0 }}>
|
||||
{renderNameDisplay()}
|
||||
|
||||
{objectData?._id && !objectData?.name ? (
|
||||
<IdDisplay
|
||||
id={objectData?._id}
|
||||
reference={objectData?._reference || undefined}
|
||||
type={objectType}
|
||||
longId={false}
|
||||
showCopy={false}
|
||||
showHyperlink={showHyperlink}
|
||||
showSpotlight={showSpotlight}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
@ -95,7 +217,9 @@ const ObjectDisplay = ({ object, objectType }) => {
|
||||
ObjectDisplay.propTypes = {
|
||||
object: PropTypes.object,
|
||||
objectType: PropTypes.string,
|
||||
style: PropTypes.object
|
||||
style: PropTypes.object,
|
||||
showHyperlink: PropTypes.bool,
|
||||
showSpotlight: PropTypes.bool
|
||||
}
|
||||
|
||||
export default ObjectDisplay
|
||||
|
||||
@ -134,6 +134,10 @@ const ObjectForm = forwardRef(
|
||||
return []
|
||||
}
|
||||
|
||||
// Clone currentData to allow sequential updates
|
||||
// We use this working copy to calculate subsequent dependent values
|
||||
const workingData = merge({}, currentData)
|
||||
|
||||
const normalizedPath = (name, parentPath = []) => {
|
||||
if (Array.isArray(name)) {
|
||||
return [...parentPath, ...name]
|
||||
@ -159,11 +163,17 @@ const ObjectForm = forwardRef(
|
||||
|
||||
const computedEntries = []
|
||||
|
||||
const processProperty = (property, scopeData, parentPath = []) => {
|
||||
const processProperty = (property, parentPath = []) => {
|
||||
if (!property?.name) return
|
||||
|
||||
const propertyPath = normalizedPath(property.name, parentPath)
|
||||
|
||||
// Determine the scope data for calculation (parent object)
|
||||
const scopeData =
|
||||
parentPath.length === 0
|
||||
? workingData
|
||||
: getValueAtPath(workingData, parentPath)
|
||||
|
||||
if (property.value && typeof property.value === 'function') {
|
||||
try {
|
||||
const computedValue = property.value(scopeData || {})
|
||||
@ -172,6 +182,8 @@ const ObjectForm = forwardRef(
|
||||
namePath: propertyPath,
|
||||
value: computedValue
|
||||
})
|
||||
// Update workingData so subsequent properties can use this value
|
||||
set(workingData, propertyPath, computedValue)
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
@ -186,29 +198,25 @@ const ObjectForm = forwardRef(
|
||||
property.properties.length > 0
|
||||
) {
|
||||
if (property.type === 'objectChildren') {
|
||||
const childValues = getValueAtPath(currentData, propertyPath)
|
||||
// Use workingData to get the latest state of children
|
||||
const childValues = getValueAtPath(workingData, propertyPath)
|
||||
if (Array.isArray(childValues)) {
|
||||
childValues.forEach((childData = {}, index) => {
|
||||
childValues.forEach((_, index) => {
|
||||
property.properties.forEach((childProperty) => {
|
||||
processProperty(childProperty, childData || {}, [
|
||||
...propertyPath,
|
||||
index
|
||||
])
|
||||
processProperty(childProperty, [...propertyPath, index])
|
||||
})
|
||||
})
|
||||
}
|
||||
} else {
|
||||
const nestedScope =
|
||||
getValueAtPath(currentData, propertyPath) || {}
|
||||
property.properties.forEach((childProperty) => {
|
||||
processProperty(childProperty, nestedScope || {}, propertyPath)
|
||||
processProperty(childProperty, propertyPath)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
modelDefinition.properties.forEach((property) => {
|
||||
processProperty(property, currentData)
|
||||
processProperty(property, [])
|
||||
})
|
||||
|
||||
return computedEntries
|
||||
@ -294,7 +302,17 @@ const ObjectForm = forwardRef(
|
||||
fetchObject
|
||||
)
|
||||
}
|
||||
}, [fetchObject, fetchObjectLock, id, type, form, messageApi, showError])
|
||||
}, [
|
||||
fetchObject,
|
||||
fetchObjectLock,
|
||||
id,
|
||||
type,
|
||||
form,
|
||||
messageApi,
|
||||
showError,
|
||||
calculateComputedValues,
|
||||
model
|
||||
])
|
||||
|
||||
// Update event handler
|
||||
const updateObjectEventHandler = useCallback((value) => {
|
||||
@ -354,7 +372,6 @@ const ObjectForm = forwardRef(
|
||||
const startEditing = () => {
|
||||
setIsEditing(true)
|
||||
isEditingRef.current = true
|
||||
console.log('IS EDITING TRUE')
|
||||
setObjectData((prev) => ({ ...prev, _isEditing: isEditingRef.current }))
|
||||
onStateChangeRef.current({
|
||||
isEditing: true,
|
||||
@ -379,7 +396,6 @@ const ObjectForm = forwardRef(
|
||||
setIsEditing(false)
|
||||
isEditingRef.current = false
|
||||
form.setFieldsValue(resetFormData)
|
||||
console.log('IS EDITING FALSE')
|
||||
setObjectData({ ...resetFormData, _isEditing: isEditingRef.current })
|
||||
}
|
||||
|
||||
@ -486,7 +502,7 @@ const ObjectForm = forwardRef(
|
||||
// Calculate computed values based on current form data
|
||||
const currentFormData = {
|
||||
...(serverObjectData.current || {}),
|
||||
...allFormValues
|
||||
...changedValues
|
||||
}
|
||||
const computedEntries = calculateComputedValues(
|
||||
currentFormData,
|
||||
@ -519,7 +535,7 @@ const ObjectForm = forwardRef(
|
||||
mergedFormValues._isEditing = isEditingRef.current
|
||||
|
||||
setObjectData((prev) => {
|
||||
return { ...prev, ...mergedFormValues }
|
||||
return merge({}, prev, mergedFormValues)
|
||||
})
|
||||
}}
|
||||
>
|
||||
|
||||
@ -131,6 +131,10 @@ const ObjectProperty = ({
|
||||
options = options(objectData)
|
||||
}
|
||||
|
||||
if (readOnly && typeof readOnly == 'function' && objectData) {
|
||||
readOnly = readOnly(objectData)
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
value = getPropertyValue(objectData, name)
|
||||
}
|
||||
@ -364,7 +368,13 @@ const ObjectProperty = ({
|
||||
}
|
||||
case 'object': {
|
||||
if (value && value._id) {
|
||||
return <ObjectDisplay object={value} objectType={objectType} />
|
||||
return (
|
||||
<ObjectDisplay
|
||||
object={value}
|
||||
objectType={objectType}
|
||||
showHyperlink={showHyperlink}
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<Text type='secondary' {...textParams}>
|
||||
@ -439,6 +449,24 @@ const ObjectProperty = ({
|
||||
)
|
||||
}
|
||||
}
|
||||
case 'reference': {
|
||||
if (value) {
|
||||
return (
|
||||
<IdDisplay
|
||||
id={value}
|
||||
reference={value}
|
||||
type={objectType}
|
||||
{...rest}
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<Text type='secondary' {...textParams}>
|
||||
n/a
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
}
|
||||
case 'miscId': {
|
||||
return <MiscId value={value} {...rest} />
|
||||
}
|
||||
|
||||
@ -47,11 +47,23 @@ const ObjectSelect = ({
|
||||
const [objectList, setObjectList] = useState([])
|
||||
const [treeSelectValue, setTreeSelectValue] = useState(null)
|
||||
const [initialLoading, setInitialLoading] = useState(true)
|
||||
const valueRef = useRef(null)
|
||||
|
||||
// Refs to track value changes
|
||||
const prevValueRef = useRef(value)
|
||||
const isInternalChangeRef = useRef(false)
|
||||
|
||||
// Normalize a value to an identity string so we can detect in-place _id updates
|
||||
const getValueIdentity = useCallback((val) => {
|
||||
if (val && typeof val === 'object') {
|
||||
if (val._id) return String(val._id)
|
||||
if (val.value && typeof val.value === 'object' && val.value._id)
|
||||
return String(val.value._id)
|
||||
}
|
||||
return JSON.stringify(val)
|
||||
}, [])
|
||||
const prevValueIdentityRef = useRef(getValueIdentity(value))
|
||||
|
||||
// Utility function to check if object only contains _id
|
||||
const isMinimalObject = useCallback((obj) => {
|
||||
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
|
||||
@ -162,7 +174,6 @@ const ObjectSelect = ({
|
||||
const buildTreeData = useCallback(
|
||||
(data, pIdx = 0, parentKeys = [], filterPath = []) => {
|
||||
if (!data || !Array.isArray(data)) return []
|
||||
console.log(data, pIdx, properties.length)
|
||||
// If we are past the grouping properties, these are leaf objects
|
||||
if (pIdx >= properties.length) {
|
||||
return data.map((object) => {
|
||||
@ -180,6 +191,7 @@ const ObjectSelect = ({
|
||||
objectType={type}
|
||||
objectData={object}
|
||||
isEditing={false}
|
||||
style={{ top: '-0.5px' }}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
@ -342,12 +354,14 @@ const ObjectSelect = ({
|
||||
value &&
|
||||
typeof value === 'object' &&
|
||||
value !== null &&
|
||||
!initialized &&
|
||||
valueRef.current !== value &&
|
||||
type != 'unknown'
|
||||
) {
|
||||
console.log('fetching full object', value)
|
||||
valueRef.current = value
|
||||
// Check if value is a minimal object and fetch full object if needed
|
||||
const fullValue = await fetchFullObjectIfNeeded(value)
|
||||
|
||||
console.log('fullValue', fullValue)
|
||||
// Build a new filter from value's properties that are in the properties list
|
||||
const valueFilter = { ...filter }
|
||||
properties.forEach((prop) => {
|
||||
@ -370,7 +384,8 @@ const ObjectSelect = ({
|
||||
})
|
||||
// Fetch with the new filter
|
||||
handleFetchObjectsProperties(valueFilter)
|
||||
setTreeSelectValue(fullValue._id)
|
||||
console.log('setting treeSelectValue', valueRef.current._id)
|
||||
setTreeSelectValue(valueRef.current._id)
|
||||
setInitialized(true)
|
||||
return
|
||||
}
|
||||
@ -410,6 +425,10 @@ const ObjectSelect = ({
|
||||
|
||||
const prevValuesRef = useRef({ type, masterFilter })
|
||||
|
||||
useEffect(() => {
|
||||
console.log('treeSelectValue', treeSelectValue)
|
||||
}, [treeSelectValue])
|
||||
|
||||
useEffect(() => {
|
||||
const prevValues = prevValuesRef.current
|
||||
|
||||
@ -433,8 +452,9 @@ const ObjectSelect = ({
|
||||
|
||||
useEffect(() => {
|
||||
// Check if value has actually changed
|
||||
const currentValueIdentity = getValueIdentity(value)
|
||||
const hasValueChanged =
|
||||
JSON.stringify(prevValueRef.current) !== JSON.stringify(value)
|
||||
prevValueIdentityRef.current !== currentValueIdentity
|
||||
|
||||
if (hasValueChanged) {
|
||||
const changeSource = isInternalChangeRef.current ? 'internal' : 'external'
|
||||
@ -451,8 +471,9 @@ const ObjectSelect = ({
|
||||
|
||||
// Update the previous value reference
|
||||
prevValueRef.current = value
|
||||
prevValueIdentityRef.current = currentValueIdentity
|
||||
}
|
||||
}, [value])
|
||||
}, [value, getValueIdentity])
|
||||
|
||||
const placeholder = useMemo(
|
||||
() =>
|
||||
|
||||
@ -88,8 +88,8 @@ const ObjectTable = forwardRef(
|
||||
const [, contextHolder] = message.useMessage()
|
||||
const tableRef = useRef(null)
|
||||
const model = getModelByName(type)
|
||||
const [tableFilter, setTableFilter] = useState({})
|
||||
const [tableSorter, setTableSorter] = useState({})
|
||||
const tableFilterRef = useRef({})
|
||||
const tableSorterRef = useRef({})
|
||||
const [initialized, setInitialized] = useState(false)
|
||||
|
||||
// Table state
|
||||
@ -98,6 +98,7 @@ const ObjectTable = forwardRef(
|
||||
const [hasMore, setHasMore] = useState(true)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [lazyLoading, setLazyLoading] = useState(false)
|
||||
const [tableData, setTableData] = useState([])
|
||||
|
||||
const subscribedIdsRef = useRef([])
|
||||
// const [typeSubscribed, setTypeSubscribed] = useState(false)
|
||||
@ -159,17 +160,17 @@ const ObjectTable = forwardRef(
|
||||
const fetchData = useCallback(
|
||||
async (pageNum = 1, filter = null, sorter = null) => {
|
||||
if (filter == null) {
|
||||
filter = tableFilter
|
||||
filter = tableFilterRef.current
|
||||
} else {
|
||||
setTableFilter(filter)
|
||||
tableFilterRef.current = filter
|
||||
}
|
||||
if (sorter == null) {
|
||||
sorter = tableSorter
|
||||
sorter = tableSorterRef.current
|
||||
} else {
|
||||
setTableSorter({
|
||||
tableSorterRef.current = {
|
||||
field: sorter.field,
|
||||
order: sorter.order
|
||||
})
|
||||
}
|
||||
}
|
||||
console.log('Fetching data...')
|
||||
try {
|
||||
@ -213,15 +214,7 @@ const ObjectTable = forwardRef(
|
||||
throw error
|
||||
}
|
||||
},
|
||||
[
|
||||
type,
|
||||
masterFilter,
|
||||
pageSize,
|
||||
tableFilter,
|
||||
tableSorter,
|
||||
onDataChange,
|
||||
fetchObjects
|
||||
]
|
||||
[type, masterFilter, pageSize, onDataChange, fetchObjects]
|
||||
)
|
||||
|
||||
const loadNextPage = useCallback(() => {
|
||||
@ -480,8 +473,8 @@ const ObjectTable = forwardRef(
|
||||
|
||||
if (hasChanged) {
|
||||
setPages([])
|
||||
setTableFilter({})
|
||||
setTableSorter({})
|
||||
tableFilterRef.current = {}
|
||||
tableSorterRef.current = {}
|
||||
setInitialized(false)
|
||||
setLoading(true)
|
||||
setLazyLoading(false)
|
||||
@ -560,6 +553,12 @@ const ObjectTable = forwardRef(
|
||||
useEffect(() => {
|
||||
pagesRef.current = pages
|
||||
}, [pages])
|
||||
|
||||
// Flatten pages array for table display
|
||||
useEffect(() => {
|
||||
setTableData(pages.flatMap((page) => page.items))
|
||||
}, [pages])
|
||||
|
||||
// Add columns in the order specified by model.columns
|
||||
model.columns.forEach((colName) => {
|
||||
const prop = modelProperties.find((p) => p.name === colName)
|
||||
@ -601,9 +600,8 @@ const ObjectTable = forwardRef(
|
||||
const isSortable = model.sorters && model.sorters.includes(prop.name)
|
||||
|
||||
const columnConfig = {
|
||||
sorter: isSortable,
|
||||
sorter: isSortable ? { multiple: 1 } : undefined,
|
||||
title: prop.label,
|
||||
dataIndex: prop.name,
|
||||
width: prop.columnWidth || width,
|
||||
fixed: isMobile ? undefined : fixed,
|
||||
key: prop.name,
|
||||
@ -663,9 +661,6 @@ const ObjectTable = forwardRef(
|
||||
})
|
||||
}
|
||||
|
||||
// Flatten pages array for table display
|
||||
const tableData = pages.flatMap((page) => page.items)
|
||||
|
||||
// Card view rendering
|
||||
const cardsContainerRef = useRef(null)
|
||||
|
||||
|
||||
@ -24,7 +24,9 @@ const ObjectTypeSelect = ({
|
||||
})
|
||||
.map((model) => ({
|
||||
value: model.name,
|
||||
label: <ObjectTypeDisplay objectType={model.name} />,
|
||||
label: (
|
||||
<ObjectTypeDisplay objectType={model.name} style={{ top: '-0.5px' }} />
|
||||
),
|
||||
searchText: model.label?.toLowerCase() || ''
|
||||
}))
|
||||
|
||||
|
||||
@ -37,13 +37,11 @@ const PrinterMiscPanel = ({ id, showControls = true }) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (id && connected == true) {
|
||||
console.log('subscribing to misc event')
|
||||
const miscEventUnsubscribe = subscribeToObjectEvent(
|
||||
id,
|
||||
'printer',
|
||||
'misc',
|
||||
(event) => {
|
||||
console.log('misc event', event)
|
||||
setMiscData((prev) => {
|
||||
const merged = merge({}, prev, event.data)
|
||||
return merged
|
||||
|
||||
@ -55,13 +55,11 @@ const PrinterPositionPanel = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (id && connected == true) {
|
||||
console.log('subscribing to motion event')
|
||||
const motionEventUnsubscribe = subscribeToObjectEvent(
|
||||
id,
|
||||
'printer',
|
||||
'motion',
|
||||
(event) => {
|
||||
console.log('motion event', event)
|
||||
setPositionData((prev) => {
|
||||
const merged = merge({}, prev, event.data)
|
||||
return merged
|
||||
|
||||
@ -101,9 +101,7 @@ const PrinterTemperaturePanel = ({
|
||||
type: 'setTemperature',
|
||||
data
|
||||
},
|
||||
(result) => {
|
||||
console.log('setTemperatureResult', result)
|
||||
}
|
||||
() => {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,10 +90,7 @@ const ActionsModalProvider = ({ children }) => {
|
||||
// Use a small timeout to ensure the modal is fully rendered and visible
|
||||
setTimeout(() => {
|
||||
if (visible) {
|
||||
console.log('visible', visible)
|
||||
console.log('inputRef.current', inputRef.current)
|
||||
if (visible && inputRef.current) {
|
||||
console.log('focusing input')
|
||||
const input = inputRef.current.input
|
||||
if (input) {
|
||||
input.focus()
|
||||
|
||||
@ -89,8 +89,7 @@ const ApiServerProvider = ({ children }) => {
|
||||
|
||||
newSocket.on('connect', () => {
|
||||
logger.debug('Api Server connected')
|
||||
newSocket.emit('authenticate', { token: token }, (result) => {
|
||||
console.log('Auth result', result)
|
||||
newSocket.emit('authenticate', { token: token }, () => {
|
||||
setConnecting(false)
|
||||
setConnected(true)
|
||||
setError(null)
|
||||
@ -328,11 +327,6 @@ const ApiServerProvider = ({ children }) => {
|
||||
const offObjectTypeUpdatesEvent = useCallback((objectType, callback) => {
|
||||
if (socketRef.current && socketRef.current.connected == true) {
|
||||
// Remove callback from the subscribed callbacks map
|
||||
console.log(
|
||||
'Unsubscribing from type',
|
||||
objectType,
|
||||
subscribedCallbacksRef.current.has(objectType)
|
||||
)
|
||||
if (subscribedCallbacksRef.current.has(objectType)) {
|
||||
const callbacks = subscribedCallbacksRef.current
|
||||
.get(objectType)
|
||||
@ -453,7 +447,6 @@ const ApiServerProvider = ({ children }) => {
|
||||
.filter((cb) => cb !== callback)
|
||||
if (callbacks.length === 0) {
|
||||
subscribedCallbacksRef.current.delete(callbacksRefKey)
|
||||
console.log('Unsubscribing from object event:', callbacksRefKey)
|
||||
socketRef.current.emit('unsubscribeObjectEvent', {
|
||||
_id: id,
|
||||
objectType,
|
||||
@ -481,7 +474,6 @@ const ApiServerProvider = ({ children }) => {
|
||||
subscribedCallbacksRef.current.get(callbacksRefKey).length
|
||||
|
||||
if (callbacksLength <= 0) {
|
||||
console.log('Subscribing to object event:', callbacksRefKey)
|
||||
socketRef.current.emit(
|
||||
'subscribeToObjectEvent',
|
||||
{
|
||||
|
||||
6
src/components/Icons/OrderItemIcon.jsx
Normal file
6
src/components/Icons/OrderItemIcon.jsx
Normal file
@ -0,0 +1,6 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import CustomIconSvg from '../../../assets/icons/orderitemicon.svg?react'
|
||||
|
||||
const OrderItemIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||
|
||||
export default OrderItemIcon
|
||||
@ -1,6 +0,0 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import CustomIconSvg from '../../../assets/icons/orderitemsicon.svg?react'
|
||||
|
||||
const OrderItemsIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||
|
||||
export default OrderItemsIcon
|
||||
6
src/components/Icons/ShipmentIcon.jsx
Normal file
6
src/components/Icons/ShipmentIcon.jsx
Normal file
@ -0,0 +1,6 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import CustomIconSvg from '../../../assets/icons/shipmenticon.svg?react'
|
||||
|
||||
const ShipmentIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||
|
||||
export default ShipmentIcon
|
||||
@ -18,6 +18,8 @@ import { StockAudit } from './models/StockAudit'
|
||||
import { PartStock } from './models/PartStock'
|
||||
import { ProductStock } from './models/ProductStock'
|
||||
import { PurchaseOrder } from './models/PurchaseOrder'
|
||||
import { OrderItem } from './models/OrderItem'
|
||||
import { Shipment } from './models/Shipment'
|
||||
import { AuditLog } from './models/AuditLog'
|
||||
import { User } from './models/User'
|
||||
import { NoteType } from './models/NoteType'
|
||||
@ -51,6 +53,8 @@ export const objectModels = [
|
||||
PartStock,
|
||||
ProductStock,
|
||||
PurchaseOrder,
|
||||
OrderItem,
|
||||
Shipment,
|
||||
AuditLog,
|
||||
User,
|
||||
NoteType,
|
||||
@ -85,6 +89,8 @@ export {
|
||||
PartStock,
|
||||
ProductStock,
|
||||
PurchaseOrder,
|
||||
OrderItem,
|
||||
Shipment,
|
||||
AuditLog,
|
||||
User,
|
||||
NoteType,
|
||||
|
||||
@ -9,13 +9,12 @@ export const AuditLog = {
|
||||
columns: [
|
||||
'_id',
|
||||
'owner',
|
||||
'owner._id',
|
||||
'parent._id',
|
||||
'parent',
|
||||
'operation',
|
||||
'changes',
|
||||
'createdAt'
|
||||
],
|
||||
filters: ['_id', 'owner._id', 'parent._id', 'operation'],
|
||||
filters: ['_id', 'owner', 'parent', 'operation'],
|
||||
sorters: ['createdAt'],
|
||||
properties: [
|
||||
{
|
||||
@ -50,18 +49,8 @@ export const AuditLog = {
|
||||
},
|
||||
columnFixed: 'left',
|
||||
value: null,
|
||||
showCopy: true
|
||||
},
|
||||
{
|
||||
name: 'owner._id',
|
||||
label: 'Owner ID',
|
||||
type: 'id',
|
||||
objectType: (objectData) => {
|
||||
return objectData.ownerType
|
||||
},
|
||||
columnFixed: 'left',
|
||||
showHyperlink: true,
|
||||
showCopy: true
|
||||
showCopy: true,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'parent',
|
||||
@ -71,17 +60,8 @@ export const AuditLog = {
|
||||
return objectData.parentType
|
||||
},
|
||||
value: null,
|
||||
showCopy: true
|
||||
},
|
||||
{
|
||||
name: 'parent._id',
|
||||
label: 'Parent ID',
|
||||
type: 'id',
|
||||
objectType: (objectData) => {
|
||||
return objectData.parentType
|
||||
},
|
||||
showHyperlink: true,
|
||||
showCopy: true
|
||||
showCopy: true,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'operation',
|
||||
|
||||
@ -74,15 +74,14 @@ export const CourierService = {
|
||||
'name',
|
||||
'_id',
|
||||
'courier',
|
||||
'courier._id',
|
||||
'tracked',
|
||||
'deliveryTime',
|
||||
'active'
|
||||
],
|
||||
filters: ['name', '_id', 'courier._id', 'active', 'deliveryTime', 'tracked'],
|
||||
filters: ['name', '_id', 'courier', 'active', 'deliveryTime', 'tracked'],
|
||||
sorters: [
|
||||
'name',
|
||||
'courier._id',
|
||||
'courier',
|
||||
'active',
|
||||
'tracked',
|
||||
'estimatedDeliveryTime',
|
||||
@ -126,13 +125,6 @@ export const CourierService = {
|
||||
showHyperlink: true,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'courier._id',
|
||||
label: 'Courier ID',
|
||||
type: 'id',
|
||||
objectType: 'courier',
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'deliveryTime',
|
||||
label: 'Delivery Time',
|
||||
|
||||
@ -121,16 +121,8 @@ export const DocumentJob = {
|
||||
type: 'object',
|
||||
objectType: (objectData) => {
|
||||
return objectData?.objectType
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'object._id',
|
||||
label: 'Object ID',
|
||||
type: 'id',
|
||||
showHyperlink: true,
|
||||
objectType: (objectData) => {
|
||||
return objectData?.objectType
|
||||
}
|
||||
},
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'documentTemplate',
|
||||
@ -139,6 +131,7 @@ export const DocumentJob = {
|
||||
columnWidth: 150,
|
||||
type: 'object',
|
||||
objectType: 'documentTemplate',
|
||||
showHyperlink: true,
|
||||
masterFilter: (objectData) => {
|
||||
return {
|
||||
active: true,
|
||||
@ -147,13 +140,6 @@ export const DocumentJob = {
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'documentTemplate._id',
|
||||
label: 'Template ID',
|
||||
type: 'id',
|
||||
showHyperlink: true,
|
||||
objectType: 'documentTemplate'
|
||||
},
|
||||
{
|
||||
name: 'documentPrinter',
|
||||
label: 'Printer',
|
||||
@ -161,19 +147,13 @@ export const DocumentJob = {
|
||||
columnWidth: 150,
|
||||
type: 'object',
|
||||
objectType: 'documentPrinter',
|
||||
showHyperlink: true,
|
||||
masterFilter: () => {
|
||||
return {
|
||||
active: true,
|
||||
online: true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'documentPrinter._id',
|
||||
label: 'Printer ID',
|
||||
type: 'id',
|
||||
showHyperlink: true,
|
||||
objectType: 'documentPrinter'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -66,7 +66,6 @@ export const DocumentPrinter = {
|
||||
'_id',
|
||||
'state',
|
||||
'host',
|
||||
'host._id',
|
||||
'tags',
|
||||
'connectedAt',
|
||||
'updatedAt'
|
||||
@ -132,15 +131,8 @@ export const DocumentPrinter = {
|
||||
label: 'Vendor',
|
||||
type: 'object',
|
||||
objectType: 'vendor',
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'vendor._id',
|
||||
label: 'Vendor ID',
|
||||
type: 'id',
|
||||
objectType: 'vendor',
|
||||
showHyperlink: true,
|
||||
readOnly: true
|
||||
required: false,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'host',
|
||||
@ -150,14 +142,6 @@ export const DocumentPrinter = {
|
||||
objectType: 'host',
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'host._id',
|
||||
label: 'Host ID',
|
||||
type: 'id',
|
||||
objectType: 'host',
|
||||
showCopy: true,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'connection.interface',
|
||||
label: 'Interface',
|
||||
@ -211,14 +195,7 @@ export const DocumentPrinter = {
|
||||
label: 'Current Document Size',
|
||||
required: false,
|
||||
type: 'object',
|
||||
objectType: 'documentSize'
|
||||
},
|
||||
{
|
||||
name: 'currentDocumentSize._id',
|
||||
label: 'Current Document Size ID',
|
||||
type: 'id',
|
||||
objectType: 'documentSize',
|
||||
showCopy: true,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
|
||||
@ -78,9 +78,7 @@ export const DocumentTemplate = {
|
||||
'objectType',
|
||||
'tags',
|
||||
'parent',
|
||||
'parent._id',
|
||||
'documentSize',
|
||||
'documentSize._id',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
],
|
||||
@ -158,14 +156,7 @@ export const DocumentTemplate = {
|
||||
label: 'Document Size',
|
||||
required: true,
|
||||
type: 'object',
|
||||
objectType: 'documentSize'
|
||||
},
|
||||
{
|
||||
name: 'documentSize._id',
|
||||
label: 'Document Size ID',
|
||||
type: 'id',
|
||||
objectType: 'documentSize',
|
||||
showCopy: true,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
@ -175,17 +166,7 @@ export const DocumentTemplate = {
|
||||
type: 'object',
|
||||
masterFilter: { global: true, active: true },
|
||||
objectType: 'documentTemplate',
|
||||
empty: (documentTemplate) => {
|
||||
return documentTemplate.global
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'parent._id',
|
||||
label: 'Parent ID',
|
||||
required: false,
|
||||
type: 'id',
|
||||
showHyperlink: true,
|
||||
objectType: 'documentTemplate',
|
||||
empty: (documentTemplate) => {
|
||||
return documentTemplate.global
|
||||
}
|
||||
|
||||
@ -65,14 +65,13 @@ export const Filament = {
|
||||
'type',
|
||||
'color',
|
||||
'vendor',
|
||||
'vendor._id',
|
||||
'cost',
|
||||
'density',
|
||||
'diameter',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
],
|
||||
filters: ['_id', 'name', 'type', 'color', 'cost', 'vendor', 'vendor._id'],
|
||||
filters: ['_id', 'name', 'type', 'color', 'cost', 'vendor'],
|
||||
sorters: [
|
||||
'name',
|
||||
'createdAt',
|
||||
@ -116,14 +115,7 @@ export const Filament = {
|
||||
label: 'Vendor',
|
||||
required: true,
|
||||
type: 'object',
|
||||
objectType: 'vendor'
|
||||
},
|
||||
{
|
||||
name: 'vendor._id',
|
||||
label: 'Vendor ID',
|
||||
type: 'id',
|
||||
objectType: 'vendor',
|
||||
showCopy: true,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
|
||||
@ -23,7 +23,6 @@ export const FilamentStock = {
|
||||
'currentWeight',
|
||||
'startingWeight',
|
||||
'filament',
|
||||
'filament._id',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
],
|
||||
@ -65,15 +64,8 @@ export const FilamentStock = {
|
||||
objectType: 'filament',
|
||||
readOnly: true,
|
||||
initial: true,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'filament._id',
|
||||
label: 'Filament ID',
|
||||
type: 'id',
|
||||
objectType: 'filament',
|
||||
showHyperlink: true,
|
||||
readOnly: true
|
||||
required: true,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'currentWeight',
|
||||
|
||||
@ -122,31 +122,16 @@ export const GCodeFile = {
|
||||
value: null,
|
||||
required: true,
|
||||
showPreview: false,
|
||||
showHyperlink: false,
|
||||
showHyperlink: true,
|
||||
filter: ['.gcode', '.g']
|
||||
},
|
||||
{
|
||||
name: 'file._id',
|
||||
label: 'File ID',
|
||||
type: 'id',
|
||||
value: null,
|
||||
objectType: 'file',
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'filament',
|
||||
label: 'Filament',
|
||||
type: 'object',
|
||||
value: null,
|
||||
objectType: 'filament',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'filament._id',
|
||||
label: 'Filament ID',
|
||||
type: 'id',
|
||||
value: null,
|
||||
objectType: 'filament',
|
||||
required: true,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
@ -236,17 +221,8 @@ export const GCodeFile = {
|
||||
label: 'Part',
|
||||
type: 'object',
|
||||
objectType: 'part',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'part._id',
|
||||
label: 'Part ID',
|
||||
type: 'id',
|
||||
objectType: 'part',
|
||||
showHyperlink: true,
|
||||
value: (objectData) => {
|
||||
return objectData?.part?._id
|
||||
}
|
||||
required: true,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'quantity',
|
||||
|
||||
@ -40,13 +40,12 @@ export const Job = {
|
||||
columns: [
|
||||
'_id',
|
||||
'gcodeFile',
|
||||
'gcodeFile._id',
|
||||
'quantity',
|
||||
'state',
|
||||
|
||||
'createdAt'
|
||||
],
|
||||
filters: ['state', '_id', 'gcodeFile._id', 'quantity'],
|
||||
filters: ['state', '_id', 'gcodeFile', 'quantity'],
|
||||
sorters: ['createdAt', 'state', 'quantity', 'gcodeFile'],
|
||||
properties: [
|
||||
{
|
||||
@ -113,13 +112,7 @@ export const Job = {
|
||||
type: 'object',
|
||||
columnFixed: 'left',
|
||||
objectType: 'gcodeFile',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'gcodeFile._id',
|
||||
label: 'GCode File ID',
|
||||
type: 'id',
|
||||
objectType: 'gcodeFile',
|
||||
required: true,
|
||||
showHyperlink: true
|
||||
}
|
||||
]
|
||||
|
||||
@ -31,9 +31,30 @@ export const Note = {
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'parent._id',
|
||||
label: 'Parent ID',
|
||||
type: 'id',
|
||||
name: '_reference',
|
||||
label: 'Reference',
|
||||
type: 'reference',
|
||||
objectType: 'note',
|
||||
showCopy: true,
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'updatedAt',
|
||||
label: 'Updated At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'user',
|
||||
label: 'User',
|
||||
type: 'object',
|
||||
objectType: 'user',
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'parent',
|
||||
label: 'Parent',
|
||||
type: 'object',
|
||||
objectType: (objectData) => {
|
||||
return objectData.parentType
|
||||
},
|
||||
@ -54,24 +75,6 @@ export const Note = {
|
||||
objectType: 'noteType',
|
||||
showHyperlink: true,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'noteType._id',
|
||||
label: 'Note Type ID',
|
||||
type: 'id',
|
||||
objectType: 'noteType'
|
||||
},
|
||||
{
|
||||
name: 'user._id',
|
||||
label: 'User ID',
|
||||
type: 'id',
|
||||
objectType: 'user'
|
||||
},
|
||||
{
|
||||
name: 'updatedAt',
|
||||
label: 'Updated At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
214
src/database/models/OrderItem.js
Normal file
214
src/database/models/OrderItem.js
Normal file
@ -0,0 +1,214 @@
|
||||
import OrderItemIcon from '../../components/Icons/OrderItemIcon'
|
||||
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||
|
||||
export const OrderItem = {
|
||||
name: 'orderItem',
|
||||
label: 'Order Item',
|
||||
prefix: 'ODI',
|
||||
icon: OrderItemIcon,
|
||||
actions: [
|
||||
{
|
||||
name: 'info',
|
||||
label: 'Info',
|
||||
default: true,
|
||||
row: true,
|
||||
icon: InfoCircleIcon,
|
||||
url: (_id) => `/dashboard/inventory/orderitems/info?orderItemId=${_id}`
|
||||
}
|
||||
],
|
||||
group: [],
|
||||
filters: ['itemType', 'item', 'order'],
|
||||
sorters: ['createdAt', 'updatedAt', 'itemAmount', 'quantity'],
|
||||
columns: [
|
||||
'_id',
|
||||
|
||||
'itemType',
|
||||
'item',
|
||||
'itemAmount',
|
||||
'quantity',
|
||||
'totalAmount',
|
||||
'taxRate',
|
||||
'totalAmountWithTax',
|
||||
'order',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
name: '_id',
|
||||
label: 'ID',
|
||||
type: 'id',
|
||||
columnFixed: 'left',
|
||||
objectType: 'orderItem',
|
||||
columnWidth: 140,
|
||||
showCopy: true
|
||||
},
|
||||
{
|
||||
name: 'createdAt',
|
||||
label: 'Created At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
|
||||
{
|
||||
name: 'orderType',
|
||||
label: 'Order Type',
|
||||
type: 'objectType',
|
||||
masterFilter: ['purchaseOrder', 'salesOrder'],
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'updatedAt',
|
||||
label: 'Updated At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'order',
|
||||
label: 'Order',
|
||||
type: 'object',
|
||||
showHyperlink: true,
|
||||
objectType: (objectData) => {
|
||||
return objectData?.orderType
|
||||
},
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'itemType',
|
||||
label: 'Item Type',
|
||||
type: 'objectType',
|
||||
masterFilter: ['part', 'packaging'],
|
||||
required: true,
|
||||
columnWidth: 125
|
||||
},
|
||||
{
|
||||
name: 'item',
|
||||
label: 'Item',
|
||||
type: 'object',
|
||||
objectType: (objectData) => {
|
||||
return objectData?.itemType
|
||||
},
|
||||
required: true,
|
||||
showHyperlink: true,
|
||||
columnWidth: 300
|
||||
},
|
||||
{
|
||||
name: 'syncAmount',
|
||||
label: 'Sync Amount',
|
||||
type: 'select',
|
||||
|
||||
options: [
|
||||
{ label: 'Item Cost', value: 'itemCost' },
|
||||
{ label: 'Item Price', value: 'itemPrice' },
|
||||
{ label: 'None', value: null }
|
||||
],
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'itemAmount',
|
||||
label: 'Item Amount',
|
||||
type: 'number',
|
||||
required: true,
|
||||
prefix: '£',
|
||||
min: 0,
|
||||
step: 0.01,
|
||||
readOnly: (objectData) => {
|
||||
return objectData?.syncAmount != null
|
||||
},
|
||||
columnWidth: 150,
|
||||
value: (objectData) => {
|
||||
if (objectData?.item?.cost && objectData?.syncAmount == 'itemCost') {
|
||||
return objectData?.item?.cost || undefined
|
||||
}
|
||||
if (objectData?.item?.price && objectData?.syncAmount == 'itemPrice') {
|
||||
return objectData?.item?.price || undefined
|
||||
}
|
||||
return objectData?.itemAmount || undefined
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'quantity',
|
||||
label: 'Quantity',
|
||||
type: 'number',
|
||||
required: true,
|
||||
columnWidth: 150
|
||||
},
|
||||
{
|
||||
name: 'totalAmount',
|
||||
label: 'Total Amount',
|
||||
type: 'number',
|
||||
required: true,
|
||||
prefix: '£',
|
||||
min: 0,
|
||||
step: 0.01,
|
||||
columnWidth: 150,
|
||||
readOnly: true,
|
||||
value: (objectData) => {
|
||||
if (objectData?.itemAmount && objectData?.quantity) {
|
||||
return (
|
||||
(objectData?.itemAmount || 0) * (objectData?.quantity || 0)
|
||||
).toFixed(2)
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'taxRate',
|
||||
label: 'Tax Rate',
|
||||
type: 'object',
|
||||
objectType: 'taxRate',
|
||||
showHyperlink: true,
|
||||
value: (objectData) => {
|
||||
if (
|
||||
objectData?.item?.costTaxRate?._id &&
|
||||
objectData?.syncAmount == 'itemCost'
|
||||
) {
|
||||
return objectData?.item?.costTaxRate || undefined
|
||||
} else if (
|
||||
objectData?.item?.priceTaxRate?._id &&
|
||||
objectData?.syncAmount == 'itemPrice'
|
||||
) {
|
||||
return objectData?.item?.priceTaxRate || undefined
|
||||
} else {
|
||||
return objectData?.taxRate || undefined
|
||||
}
|
||||
},
|
||||
readOnly: (objectData) => {
|
||||
return objectData?.syncAmount != null
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'totalAmountWithTax',
|
||||
label: 'Total Amount w/ Tax',
|
||||
type: 'number',
|
||||
required: true,
|
||||
readOnly: true,
|
||||
prefix: '£',
|
||||
min: 0,
|
||||
step: 0.01,
|
||||
columnWidth: 175,
|
||||
value: (objectData) => {
|
||||
const totalAmount = objectData?.itemAmount * objectData?.quantity || 0
|
||||
if (objectData?.taxRate?.rateType == 'percentage') {
|
||||
if (objectData?.quantity == undefined || objectData?.quantity == 0) {
|
||||
return undefined
|
||||
}
|
||||
return (
|
||||
(
|
||||
(totalAmount || 0) *
|
||||
(1 + objectData?.taxRate?.rate / 100)
|
||||
).toFixed(2) || undefined
|
||||
)
|
||||
} else if (objectData?.taxRate?.rateType == 'amount') {
|
||||
return (
|
||||
((totalAmount || 0) + objectData?.taxRate?.rate).toFixed(2) ||
|
||||
undefined
|
||||
)
|
||||
} else {
|
||||
return totalAmount || 0
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -58,15 +58,8 @@ export const Part = {
|
||||
}
|
||||
}
|
||||
],
|
||||
columns: [
|
||||
'name',
|
||||
'_id',
|
||||
'product',
|
||||
'product._id',
|
||||
'globalPricing',
|
||||
'createdAt'
|
||||
],
|
||||
filters: ['name', '_id', 'product', 'product._id', 'globalPricing'],
|
||||
columns: ['name', '_id', 'product', 'globalPricing', 'createdAt'],
|
||||
filters: ['name', '_id', 'product', 'globalPricing'],
|
||||
sorters: ['name', 'email', 'role', 'createdAt', '_id'],
|
||||
properties: [
|
||||
{
|
||||
@ -103,6 +96,7 @@ export const Part = {
|
||||
required: true,
|
||||
type: 'object',
|
||||
objectType: 'vendor',
|
||||
showHyperlink: true,
|
||||
value: (objectData) => {
|
||||
console.log(objectData?.vendor, objectData?.product?.vendor)
|
||||
if (!objectData?.vendor && objectData?.product?.vendor) {
|
||||
@ -112,14 +106,6 @@ export const Part = {
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'vendor._id',
|
||||
label: 'Vendor ID',
|
||||
readOnly: true,
|
||||
type: 'id',
|
||||
showHyperlink: true,
|
||||
objectType: 'vendor'
|
||||
},
|
||||
{
|
||||
name: 'cost',
|
||||
label: 'Cost',
|
||||
@ -132,7 +118,7 @@ export const Part = {
|
||||
},
|
||||
{
|
||||
name: 'costWithTax',
|
||||
label: 'Cost with Tax',
|
||||
label: 'Cost w/ Tax',
|
||||
columnWidth: 150,
|
||||
required: true,
|
||||
readOnly: true,
|
||||
@ -163,15 +149,8 @@ export const Part = {
|
||||
label: 'Cost Tax Rate',
|
||||
required: true,
|
||||
type: 'object',
|
||||
objectType: 'taxRate'
|
||||
},
|
||||
{
|
||||
name: 'costTaxRate._id',
|
||||
label: 'Cost Tax Rate ID',
|
||||
readOnly: true,
|
||||
type: 'id',
|
||||
showHyperlink: true,
|
||||
objectType: 'taxRate'
|
||||
objectType: 'taxRate',
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'price',
|
||||
@ -201,7 +180,7 @@ export const Part = {
|
||||
},
|
||||
{
|
||||
name: 'priceWithTax',
|
||||
label: 'Price with Tax',
|
||||
label: 'Price w/ Tax',
|
||||
columnWidth: 150,
|
||||
required: true,
|
||||
readOnly: true,
|
||||
@ -251,15 +230,8 @@ export const Part = {
|
||||
label: 'Price Tax Rate',
|
||||
required: true,
|
||||
type: 'object',
|
||||
objectType: 'taxRate'
|
||||
},
|
||||
{
|
||||
name: 'priceTaxRate._id',
|
||||
label: 'Price Tax Rate ID',
|
||||
readOnly: true,
|
||||
type: 'id',
|
||||
showHyperlink: true,
|
||||
objectType: 'taxRate'
|
||||
objectType: 'taxRate',
|
||||
showHyperlink: true
|
||||
},
|
||||
|
||||
{
|
||||
@ -267,14 +239,7 @@ export const Part = {
|
||||
label: 'File',
|
||||
type: 'file',
|
||||
value: null,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'file._id',
|
||||
label: 'File ID',
|
||||
type: 'id',
|
||||
value: null,
|
||||
objectType: 'file',
|
||||
required: false,
|
||||
showHyperlink: true
|
||||
}
|
||||
]
|
||||
|
||||
@ -25,7 +25,6 @@ export const PartStock = {
|
||||
'startingQuantity',
|
||||
'currentQuantity',
|
||||
'part',
|
||||
'part._id',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
],
|
||||
@ -66,26 +65,12 @@ export const PartStock = {
|
||||
required: true,
|
||||
masterFilter: ['subJob']
|
||||
},
|
||||
|
||||
{
|
||||
name: 'consumedAt',
|
||||
label: 'Consumed At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'part',
|
||||
label: 'Part',
|
||||
type: 'object',
|
||||
objectType: 'part',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'part._id',
|
||||
label: 'Part ID',
|
||||
type: 'id',
|
||||
objectType: 'part',
|
||||
readOnly: true,
|
||||
required: true,
|
||||
showHyperlink: true
|
||||
},
|
||||
|
||||
@ -98,17 +83,8 @@ export const PartStock = {
|
||||
columnWidth: 200,
|
||||
objectType: (objectData) => {
|
||||
return objectData?.sourceType
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'source._id',
|
||||
label: 'Source ID',
|
||||
type: 'id',
|
||||
readOnly: true,
|
||||
columnWidth: 200,
|
||||
objectType: (objectData) => {
|
||||
return objectData?.sourceType
|
||||
}
|
||||
},
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'currentQuantity',
|
||||
@ -124,13 +100,6 @@ export const PartStock = {
|
||||
return objectData.currentQuantity
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'startingQuantity',
|
||||
label: 'Starting Quantity',
|
||||
type: 'number',
|
||||
columnWidth: 200,
|
||||
required: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -213,16 +213,7 @@ export const Printer = {
|
||||
]
|
||||
}
|
||||
],
|
||||
columns: [
|
||||
'name',
|
||||
'_id',
|
||||
'state',
|
||||
'host',
|
||||
'host._id',
|
||||
'tags',
|
||||
'connectedAt',
|
||||
'updatedAt'
|
||||
],
|
||||
columns: ['name', '_id', 'state', 'host', 'tags', 'connectedAt', 'updatedAt'],
|
||||
filters: ['name', '_id', 'state', 'tags'],
|
||||
sorters: ['name', 'state', 'connectedAt'],
|
||||
group: ['tags'],
|
||||
@ -286,30 +277,16 @@ export const Printer = {
|
||||
label: 'Vendor',
|
||||
type: 'object',
|
||||
objectType: 'vendor',
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'vendor._id',
|
||||
label: 'Vendor ID',
|
||||
type: 'id',
|
||||
objectType: 'vendor',
|
||||
showHyperlink: true,
|
||||
readOnly: true
|
||||
required: false,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'host',
|
||||
label: 'Host',
|
||||
type: 'object',
|
||||
objectType: 'host',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'host._id',
|
||||
label: 'Host ID',
|
||||
type: 'id',
|
||||
objectType: 'host',
|
||||
showHyperlink: true,
|
||||
readOnly: true
|
||||
required: true,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'moonraker.host',
|
||||
@ -354,45 +331,24 @@ export const Printer = {
|
||||
label: 'Filament Stock',
|
||||
type: 'object',
|
||||
objectType: 'filamentStock',
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'currentFilamentStock._id',
|
||||
label: 'Filament Stock ID',
|
||||
type: 'id',
|
||||
objectType: 'filamentStock',
|
||||
showHyperlink: true,
|
||||
readOnly: true
|
||||
required: false,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'currentJob',
|
||||
label: 'Current Job',
|
||||
type: 'object',
|
||||
objectType: 'job',
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'currentJob._id',
|
||||
label: 'Current Job ID',
|
||||
type: 'id',
|
||||
objectType: 'job',
|
||||
showHyperlink: true,
|
||||
readOnly: true
|
||||
required: false,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'currentSubJob',
|
||||
label: 'Current Sub Job',
|
||||
type: 'object',
|
||||
objectType: 'subJob',
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'currentSubJob._id',
|
||||
label: 'Current Sub Job ID',
|
||||
type: 'id',
|
||||
objectType: 'subJob',
|
||||
showHyperlink: true,
|
||||
readOnly: true
|
||||
required: false,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'alerts',
|
||||
|
||||
@ -64,12 +64,11 @@ export const Product = {
|
||||
'name',
|
||||
'tags',
|
||||
'vendor',
|
||||
'vendor._id',
|
||||
'price',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
],
|
||||
filters: ['_id', 'name', 'type', 'color', 'cost', 'vendor', 'vendor._id'],
|
||||
filters: ['_id', 'name', 'type', 'color', 'cost', 'vendor'],
|
||||
sorters: ['name', 'createdAt', 'type', 'vendor', 'cost', 'updatedAt'],
|
||||
properties: [
|
||||
{
|
||||
@ -103,15 +102,8 @@ export const Product = {
|
||||
label: 'Vendor',
|
||||
required: true,
|
||||
type: 'object',
|
||||
objectType: 'vendor'
|
||||
},
|
||||
{
|
||||
name: 'vendor._id',
|
||||
label: 'Vendor ID',
|
||||
readOnly: true,
|
||||
type: 'id',
|
||||
showHyperlink: true,
|
||||
objectType: 'vendor'
|
||||
objectType: 'vendor',
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'version',
|
||||
@ -167,17 +159,8 @@ export const Product = {
|
||||
label: 'Part',
|
||||
type: 'object',
|
||||
objectType: 'part',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'part._id',
|
||||
label: 'Part ID',
|
||||
type: 'id',
|
||||
objectType: 'part',
|
||||
showHyperlink: true,
|
||||
value: (objectData) => {
|
||||
return objectData?.part?._id
|
||||
}
|
||||
required: true,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'quantity',
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import PurchaseOrderIcon from '../../components/Icons/PurchaseOrderIcon'
|
||||
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||
import PlusIcon from '../../components/Icons/PlusIcon'
|
||||
|
||||
export const PurchaseOrder = {
|
||||
name: 'purchaseOrder',
|
||||
@ -15,12 +16,20 @@ export const PurchaseOrder = {
|
||||
icon: InfoCircleIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/inventory/purchaseorders/info?purchaseOrderId=${_id}`
|
||||
},
|
||||
{
|
||||
name: 'New Order Item',
|
||||
label: 'New Order Item',
|
||||
type: 'button',
|
||||
icon: PlusIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/inventory/purchaseorders/info?purchaseOrderId=${_id}&action=newOrderItem`
|
||||
}
|
||||
],
|
||||
group: ['vendor'],
|
||||
filters: ['vendor', 'vendor._id'],
|
||||
filters: ['vendor'],
|
||||
sorters: ['createdAt', 'state', 'updatedAt'],
|
||||
columns: ['_id', 'createdAt', 'state', 'updatedAt', 'vendor', 'vendor._id'],
|
||||
columns: ['_id', 'createdAt', 'state', 'updatedAt', 'vendor'],
|
||||
properties: [
|
||||
{
|
||||
name: '_id',
|
||||
@ -49,183 +58,8 @@ export const PurchaseOrder = {
|
||||
label: 'Vendor',
|
||||
required: true,
|
||||
type: 'object',
|
||||
objectType: 'vendor'
|
||||
},
|
||||
{
|
||||
name: 'vendor._id',
|
||||
label: 'Vendor ID',
|
||||
readOnly: true,
|
||||
type: 'id',
|
||||
showHyperlink: true,
|
||||
objectType: 'vendor'
|
||||
},
|
||||
{
|
||||
name: 'items',
|
||||
label: 'Order Items',
|
||||
type: 'objectChildren',
|
||||
required: true,
|
||||
properties: [
|
||||
{
|
||||
name: 'itemType',
|
||||
label: 'Item Type',
|
||||
type: 'objectType',
|
||||
masterFilter: ['part', 'packaging'],
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'item',
|
||||
label: 'Item',
|
||||
type: 'object',
|
||||
objectType: (objectData) => {
|
||||
return objectData?.itemType
|
||||
},
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'item._id',
|
||||
label: 'Item ID',
|
||||
type: 'id',
|
||||
objectType: (objectData) => {
|
||||
return objectData?.itemType
|
||||
},
|
||||
showHyperlink: true,
|
||||
value: (objectData) => {
|
||||
return objectData?.item?._id
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
name: 'itemCost',
|
||||
label: 'Item Cost',
|
||||
type: 'number',
|
||||
required: true,
|
||||
prefix: '£',
|
||||
min: 0,
|
||||
step: 0.01,
|
||||
columnWidth: 150,
|
||||
value: (objectData) => {
|
||||
if (objectData?.item) {
|
||||
return objectData?.item?.cost || undefined
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'quantity',
|
||||
label: 'Quantity',
|
||||
type: 'number',
|
||||
required: true,
|
||||
columnWidth: 150
|
||||
},
|
||||
{
|
||||
name: 'totalCost',
|
||||
label: 'Total Cost',
|
||||
type: 'number',
|
||||
required: true,
|
||||
prefix: '£',
|
||||
min: 0,
|
||||
step: 0.01,
|
||||
columnWidth: 150,
|
||||
value: (objectData) => {
|
||||
return (
|
||||
(objectData?.itemCost || 0) * (objectData?.quantity || 0) ||
|
||||
undefined
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'taxRate',
|
||||
label: 'Tax Rate',
|
||||
type: 'object',
|
||||
objectType: 'taxRate',
|
||||
value: (objectData) => {
|
||||
if (objectData?.item) {
|
||||
console.log(objectData?.item)
|
||||
return objectData?.item?.costTaxRate || undefined
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'taxRate._id',
|
||||
label: 'Tax Rate ID',
|
||||
type: 'id',
|
||||
showHyperlink: true,
|
||||
objectType: 'taxRate',
|
||||
value: (objectData) => {
|
||||
return objectData?.taxRate?._id || undefined
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'totalCostWithTax',
|
||||
label: 'Total Cost With Tax',
|
||||
type: 'number',
|
||||
required: true,
|
||||
readOnly: true,
|
||||
prefix: '£',
|
||||
min: 0,
|
||||
step: 0.01,
|
||||
columnWidth: 175,
|
||||
value: (objectData) => {
|
||||
if (objectData?.taxRate?.rateType == 'percentage') {
|
||||
return (
|
||||
(
|
||||
(objectData?.totalCost || 0) *
|
||||
(1 + objectData?.taxRate?.rate / 100)
|
||||
).toFixed(2) || undefined
|
||||
)
|
||||
} else if (objectData?.taxRate?.rateType == 'amount') {
|
||||
return (
|
||||
(
|
||||
(objectData?.totalCost || 0) + objectData?.taxRate?.rate
|
||||
).toFixed(2) || undefined
|
||||
)
|
||||
} else {
|
||||
return objectData?.totalCost || undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
rollups: [
|
||||
{
|
||||
name: 'totalQuantity',
|
||||
label: 'Total',
|
||||
type: 'number',
|
||||
property: 'quantity',
|
||||
value: (objectData) => {
|
||||
return objectData?.items?.reduce(
|
||||
(acc, item) => acc + item.quantity,
|
||||
0
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'totalCost',
|
||||
label: 'Total',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
property: 'totalCost',
|
||||
value: (objectData) => {
|
||||
return objectData?.items
|
||||
?.reduce((acc, item) => acc + (item.totalCost || 0), 0)
|
||||
.toFixed(2)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'totalCostWithTax',
|
||||
label: 'Total',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
property: 'totalCostWithTax',
|
||||
value: (objectData) => {
|
||||
return objectData?.items
|
||||
?.reduce((acc, item) => acc + (item.totalCostWithTax || 0), 0)
|
||||
.toFixed(2)
|
||||
}
|
||||
}
|
||||
]
|
||||
objectType: 'vendor',
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'cost',
|
||||
|
||||
280
src/database/models/Shipment.js
Normal file
280
src/database/models/Shipment.js
Normal file
@ -0,0 +1,280 @@
|
||||
import ShipmentIcon from '../../components/Icons/ShipmentIcon'
|
||||
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||
|
||||
export const Shipment = {
|
||||
name: 'shipment',
|
||||
label: 'Shipment',
|
||||
prefix: 'SHM',
|
||||
icon: ShipmentIcon,
|
||||
actions: [
|
||||
{
|
||||
name: 'info',
|
||||
label: 'Info',
|
||||
default: true,
|
||||
row: true,
|
||||
icon: InfoCircleIcon,
|
||||
url: (_id) => `/dashboard/inventory/shipments/info?shipmentId=${_id}`
|
||||
}
|
||||
],
|
||||
group: ['vendor', 'purchaseOrder'],
|
||||
filters: ['vendor', 'purchaseOrder', 'state', 'courierService'],
|
||||
sorters: [
|
||||
'createdAt',
|
||||
'state',
|
||||
'updatedAt',
|
||||
'shippedDate',
|
||||
'expectedDeliveryDate'
|
||||
],
|
||||
columns: [
|
||||
'_id',
|
||||
'createdAt',
|
||||
'state',
|
||||
'updatedAt',
|
||||
'vendor',
|
||||
'purchaseOrder',
|
||||
'trackingNumber'
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
name: '_id',
|
||||
label: 'ID',
|
||||
type: 'id',
|
||||
columnFixed: 'left',
|
||||
objectType: 'shipment',
|
||||
columnWidth: 140,
|
||||
showCopy: true
|
||||
},
|
||||
{
|
||||
name: 'createdAt',
|
||||
label: 'Created At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{ name: 'state', label: 'State', type: 'state', readOnly: true },
|
||||
{
|
||||
name: 'updatedAt',
|
||||
label: 'Updated At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'purchaseOrder',
|
||||
label: 'Purchase Order',
|
||||
required: true,
|
||||
type: 'object',
|
||||
objectType: 'purchaseOrder',
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'vendor',
|
||||
label: 'Vendor',
|
||||
required: true,
|
||||
type: 'object',
|
||||
objectType: 'vendor',
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'courierService',
|
||||
label: 'Courier Service',
|
||||
required: false,
|
||||
type: 'object',
|
||||
objectType: 'courierService'
|
||||
},
|
||||
{
|
||||
name: 'trackingNumber',
|
||||
label: 'Tracking Number',
|
||||
type: 'string',
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'items',
|
||||
label: 'Shipment Items',
|
||||
type: 'objectChildren',
|
||||
required: true,
|
||||
properties: [
|
||||
{
|
||||
name: 'itemType',
|
||||
label: 'Item Type',
|
||||
type: 'objectType',
|
||||
masterFilter: ['part', 'packaging'],
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'item',
|
||||
label: 'Item',
|
||||
type: 'object',
|
||||
objectType: (objectData) => {
|
||||
return objectData?.itemType
|
||||
},
|
||||
required: true,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'itemCost',
|
||||
label: 'Item Cost',
|
||||
type: 'number',
|
||||
required: true,
|
||||
prefix: '£',
|
||||
min: 0,
|
||||
step: 0.01,
|
||||
columnWidth: 150,
|
||||
value: (objectData) => {
|
||||
if (objectData?.item) {
|
||||
return objectData?.item?.cost || undefined
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'quantity',
|
||||
label: 'Quantity',
|
||||
type: 'number',
|
||||
required: true,
|
||||
columnWidth: 150
|
||||
},
|
||||
{
|
||||
name: 'totalCost',
|
||||
label: 'Total Cost',
|
||||
type: 'number',
|
||||
required: true,
|
||||
prefix: '£',
|
||||
min: 0,
|
||||
step: 0.01,
|
||||
columnWidth: 150,
|
||||
value: (objectData) => {
|
||||
return (
|
||||
(objectData?.itemCost || 0) * (objectData?.quantity || 0) ||
|
||||
undefined
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'taxRate',
|
||||
label: 'Tax Rate',
|
||||
type: 'object',
|
||||
objectType: 'taxRate',
|
||||
showHyperlink: true,
|
||||
value: (objectData) => {
|
||||
if (objectData?.item) {
|
||||
return objectData?.item?.costTaxRate || undefined
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'totalCostWithTax',
|
||||
label: 'Total Cost w/ Tax',
|
||||
type: 'number',
|
||||
required: true,
|
||||
readOnly: true,
|
||||
prefix: '£',
|
||||
min: 0,
|
||||
step: 0.01,
|
||||
columnWidth: 175,
|
||||
value: (objectData) => {
|
||||
if (objectData?.taxRate?.rateType == 'percentage') {
|
||||
return (
|
||||
(
|
||||
(objectData?.totalCost || 0) *
|
||||
(1 + objectData?.taxRate?.rate / 100)
|
||||
).toFixed(2) || undefined
|
||||
)
|
||||
} else if (objectData?.taxRate?.rateType == 'amount') {
|
||||
return (
|
||||
(
|
||||
(objectData?.totalCost || 0) + objectData?.taxRate?.rate
|
||||
).toFixed(2) || undefined
|
||||
)
|
||||
} else {
|
||||
return objectData?.totalCost || undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
rollups: [
|
||||
{
|
||||
name: 'totalQuantity',
|
||||
label: 'Total',
|
||||
type: 'number',
|
||||
property: 'quantity',
|
||||
value: (objectData) => {
|
||||
return objectData?.items?.reduce(
|
||||
(acc, item) => acc + item.quantity,
|
||||
0
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'totalCost',
|
||||
label: 'Total',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
property: 'totalCost',
|
||||
value: (objectData) => {
|
||||
return objectData?.items
|
||||
?.reduce((acc, item) => acc + (item.totalCost || 0), 0)
|
||||
.toFixed(2)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'totalCostWithTax',
|
||||
label: 'Total',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
property: 'totalCostWithTax',
|
||||
value: (objectData) => {
|
||||
return objectData?.items
|
||||
?.reduce((acc, item) => acc + (item.totalCostWithTax || 0), 0)
|
||||
.toFixed(2)
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'cost',
|
||||
label: 'Cost',
|
||||
type: 'netGross',
|
||||
required: true,
|
||||
prefix: '£',
|
||||
min: 0,
|
||||
step: 0.01,
|
||||
value: (objectData) => {
|
||||
const net = objectData?.items?.reduce(
|
||||
(acc, item) => acc + (item.totalCost || 0),
|
||||
0
|
||||
)
|
||||
const gross = objectData?.items?.reduce(
|
||||
(acc, item) => acc + (item.totalCostWithTax || 0),
|
||||
0
|
||||
)
|
||||
return { net: net, gross: gross }
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'shippedDate',
|
||||
label: 'Shipped Date',
|
||||
type: 'dateTime',
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'expectedDeliveryDate',
|
||||
label: 'Expected Delivery Date',
|
||||
type: 'dateTime',
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'actualDeliveryDate',
|
||||
label: 'Actual Delivery Date',
|
||||
type: 'dateTime',
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'notes',
|
||||
label: 'Notes',
|
||||
type: 'textarea',
|
||||
required: false
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -6,8 +6,8 @@ export const StockEvent = {
|
||||
prefix: 'SEV',
|
||||
icon: StockEventIcon,
|
||||
actions: [],
|
||||
columns: ['_id', 'owner', 'owner._id', 'parent._id', 'value', 'createdAt'],
|
||||
filters: ['_id', 'owner._id', 'parent._id'],
|
||||
columns: ['_id', 'owner', 'parent', 'value', 'createdAt'],
|
||||
filters: ['_id', 'owner', 'parent'],
|
||||
sorters: ['createdAt'],
|
||||
properties: [
|
||||
{
|
||||
@ -42,18 +42,8 @@ export const StockEvent = {
|
||||
},
|
||||
columnFixed: 'left',
|
||||
value: null,
|
||||
showCopy: true
|
||||
},
|
||||
{
|
||||
name: 'owner._id',
|
||||
label: 'Owner ID',
|
||||
type: 'id',
|
||||
objectType: (objectData) => {
|
||||
return objectData.ownerType
|
||||
},
|
||||
columnFixed: 'left',
|
||||
showHyperlink: true,
|
||||
showCopy: true
|
||||
showCopy: true,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'parent',
|
||||
@ -63,17 +53,8 @@ export const StockEvent = {
|
||||
return objectData?.parentType
|
||||
},
|
||||
value: null,
|
||||
showCopy: true
|
||||
},
|
||||
{
|
||||
name: 'parent._id',
|
||||
label: 'Parent ID',
|
||||
type: 'id',
|
||||
objectType: (objectData) => {
|
||||
return objectData.parentType
|
||||
},
|
||||
showHyperlink: true,
|
||||
showCopy: true
|
||||
showCopy: true,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'value',
|
||||
|
||||
@ -28,8 +28,8 @@ export const SubJob = {
|
||||
}
|
||||
}
|
||||
],
|
||||
columns: ['_id', 'printer', 'printer._id', 'job._id', 'state', 'createdAt'],
|
||||
filters: ['state', '_id', 'job._id', 'printer._id'],
|
||||
columns: ['_id', 'printer', 'job', 'state', 'createdAt'],
|
||||
filters: ['state', '_id', 'job', 'printer'],
|
||||
sorters: ['createdAt', 'state'],
|
||||
group: ['job'],
|
||||
properties: [
|
||||
@ -102,15 +102,8 @@ export const SubJob = {
|
||||
name: 'job',
|
||||
label: 'Job',
|
||||
type: 'object',
|
||||
objectType: 'job'
|
||||
},
|
||||
{
|
||||
name: 'job._id',
|
||||
label: 'Job ID',
|
||||
type: 'id',
|
||||
columnWidth: 140,
|
||||
showHyperlink: true,
|
||||
objectType: 'job'
|
||||
objectType: 'job',
|
||||
showHyperlink: true
|
||||
},
|
||||
|
||||
{
|
||||
@ -118,16 +111,8 @@ export const SubJob = {
|
||||
label: 'Printer',
|
||||
type: 'object',
|
||||
columnFixed: 'left',
|
||||
objectType: 'printer'
|
||||
},
|
||||
{
|
||||
name: 'printer._id',
|
||||
label: 'Printer ID',
|
||||
type: 'id',
|
||||
columnWidth: 140,
|
||||
columnFixed: 'left',
|
||||
showHyperlink: true,
|
||||
objectType: 'printer'
|
||||
objectType: 'printer',
|
||||
showHyperlink: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -101,15 +101,8 @@ export const TaxRecord = {
|
||||
label: 'Tax Rate',
|
||||
required: true,
|
||||
type: 'object',
|
||||
objectType: 'taxRate'
|
||||
},
|
||||
{
|
||||
name: 'taxRate._id',
|
||||
label: 'Tax Rate ID',
|
||||
readOnly: true,
|
||||
type: 'id',
|
||||
showHyperlink: true,
|
||||
objectType: 'taxRate'
|
||||
objectType: 'taxRate',
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'transactionType',
|
||||
@ -129,17 +122,8 @@ export const TaxRecord = {
|
||||
type: 'object',
|
||||
objectType: (objectData) => {
|
||||
return objectData?.transactionType || 'purchaseOrder'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'transaction._id',
|
||||
label: 'Transaction ID',
|
||||
readOnly: true,
|
||||
type: 'id',
|
||||
showHyperlink: true,
|
||||
objectType: (objectData) => {
|
||||
return objectData?.transactionType || 'purchaseOrder'
|
||||
}
|
||||
},
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'amount',
|
||||
|
||||
@ -69,9 +69,34 @@ export const Vendor = {
|
||||
`/dashboard/management/vendors/info?vendorId=${_id}&action=delete`
|
||||
}
|
||||
],
|
||||
columns: ['name', '_id', 'country', 'email', 'website', 'createdAt'],
|
||||
filters: ['name', '_id', 'country', 'email'],
|
||||
sorters: ['name', 'country', 'email', 'createdAt', '_id'],
|
||||
columns: [
|
||||
'name',
|
||||
'_id',
|
||||
'country',
|
||||
'email',
|
||||
'website',
|
||||
'active',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
],
|
||||
filters: [
|
||||
'name',
|
||||
'_id',
|
||||
'country',
|
||||
'email',
|
||||
'active',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
],
|
||||
sorters: [
|
||||
'name',
|
||||
'country',
|
||||
'email',
|
||||
'active',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'_id'
|
||||
],
|
||||
group: ['country'],
|
||||
properties: [
|
||||
{
|
||||
@ -108,6 +133,13 @@ export const Vendor = {
|
||||
readOnly: false,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'active',
|
||||
label: 'Active',
|
||||
type: 'bool',
|
||||
readOnly: false,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'country',
|
||||
label: 'Country',
|
||||
@ -137,6 +169,55 @@ export const Vendor = {
|
||||
type: 'url',
|
||||
readOnly: false,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'address.building',
|
||||
label: 'Building',
|
||||
type: 'text',
|
||||
readOnly: false,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'address.addressLine1',
|
||||
label: 'Address Line 1',
|
||||
type: 'text',
|
||||
readOnly: false,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'address.addressLine2',
|
||||
label: 'Address Line 2',
|
||||
type: 'text',
|
||||
readOnly: false,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'address.city',
|
||||
label: 'City',
|
||||
type: 'text',
|
||||
readOnly: false,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'address.state',
|
||||
label: 'State',
|
||||
type: 'text',
|
||||
readOnly: false,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'address.postcode',
|
||||
label: 'Postcode',
|
||||
type: 'text',
|
||||
readOnly: false,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'address.country',
|
||||
label: 'Country',
|
||||
type: 'country',
|
||||
readOnly: false,
|
||||
required: false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -9,6 +9,10 @@ import StockAudits from '../components/Dashboard/Inventory/StockAudits.jsx'
|
||||
import StockAuditInfo from '../components/Dashboard/Inventory/StockAudits/StockAuditInfo.jsx'
|
||||
import PurchaseOrders from '../components/Dashboard/Inventory/PurchaseOrders.jsx'
|
||||
import PurchaseOrderInfo from '../components/Dashboard/Inventory/PurchaseOrders/PurchaseOrderInfo.jsx'
|
||||
import OrderItems from '../components/Dashboard/Inventory/OrderItems.jsx'
|
||||
import OrderItemInfo from '../components/Dashboard/Inventory/OrderItems/OrderItemInfo.jsx'
|
||||
import Shipments from '../components/Dashboard/Inventory/Shipments.jsx'
|
||||
import ShipmentInfo from '../components/Dashboard/Inventory/Shipments/ShipmentInfo.jsx'
|
||||
|
||||
const InventoryRoutes = [
|
||||
<Route
|
||||
@ -55,6 +59,22 @@ const InventoryRoutes = [
|
||||
key='purchaseorders-info'
|
||||
path='inventory/purchaseorders/info'
|
||||
element={<PurchaseOrderInfo />}
|
||||
/>,
|
||||
<Route
|
||||
key='orderitems'
|
||||
path='inventory/orderitems'
|
||||
element={<OrderItems />}
|
||||
/>,
|
||||
<Route
|
||||
key='orderitems-info'
|
||||
path='inventory/orderitems/info'
|
||||
element={<OrderItemInfo />}
|
||||
/>,
|
||||
<Route key='shipments' path='inventory/shipments' element={<Shipments />} />,
|
||||
<Route
|
||||
key='shipments-info'
|
||||
path='inventory/shipments/info'
|
||||
element={<ShipmentInfo />}
|
||||
/>
|
||||
]
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user