diff --git a/src/components/Dashboard/common/NewObjectForm.jsx b/src/components/Dashboard/common/NewObjectForm.jsx
index aba99983..0b11e1fc 100644
--- a/src/components/Dashboard/common/NewObjectForm.jsx
+++ b/src/components/Dashboard/common/NewObjectForm.jsx
@@ -3,6 +3,7 @@ import { Form } from 'antd'
import { ApiServerContext } from '../context/ApiServerContext'
import { useMessageContext } from '../context/MessageContext'
import PropTypes from 'prop-types'
+import merge from 'lodash/merge'
import mergeWith from 'lodash/mergeWith'
import set from 'lodash/set'
import { getModelByName } from '../../../database/ObjectModels'
@@ -18,6 +19,16 @@ const buildObjectFromEntries = (entries = []) => {
}, {})
}
+const applyComputedEntries = (base, entries = []) => {
+ const result = merge({}, base || {})
+ entries.forEach((entry) => {
+ const { namePath, value } = entry || {}
+ if (!Array.isArray(namePath) || value === undefined) return
+ set(result, namePath, value)
+ })
+ return result
+}
+
const arrayReplaceCustomizer = (objValue, srcValue, key) => {
if (Array.isArray(srcValue)) {
return srcValue
@@ -172,12 +183,9 @@ const NewObjectForm = ({ type, style, defaultValues = {}, children }) => {
if (Object.keys(defaultValues).length > 0) {
// Calculate computed values for initial data
const computedEntries = calculateComputedValues(defaultValues, model)
- const computedValuesObject = buildObjectFromEntries(computedEntries)
- const initialFormData = mergeWith(
- {},
+ const initialFormData = applyComputedEntries(
defaultValues,
- computedValuesObject,
- arrayReplaceCustomizer
+ computedEntries
)
form.setFieldsValue(initialFormData)
setObjectData((prev) =>
@@ -196,12 +204,9 @@ const NewObjectForm = ({ type, style, defaultValues = {}, children }) => {
try {
setSubmitLoading(true)
const computedEntries = calculateComputedValues(objectData, model)
- const computedValuesObject = buildObjectFromEntries(computedEntries)
- const computedObjectData = mergeWith(
- {},
+ const computedObjectData = applyComputedEntries(
objectData,
- computedValuesObject,
- arrayReplaceCustomizer
+ computedEntries
)
const newObject = await createObject(type, computedObjectData)
showSuccess('Object created successfully')
@@ -253,14 +258,7 @@ const NewObjectForm = ({ type, style, defaultValues = {}, children }) => {
})
}
- // Merge all values (user input + computed values)
- const computedValuesObject = buildObjectFromEntries(computedEntries)
- const allValues = mergeWith(
- {},
- allFormValues,
- computedValuesObject,
- arrayReplaceCustomizer
- )
+ const allValues = applyComputedEntries(allFormValues, computedEntries)
setObjectData((prev) => {
return mergeWith({}, prev, allValues, arrayReplaceCustomizer)
})
diff --git a/src/components/Dashboard/common/ObjectChildTable.jsx b/src/components/Dashboard/common/ObjectChildTable.jsx
index 6ef12fad..46cef44d 100644
--- a/src/components/Dashboard/common/ObjectChildTable.jsx
+++ b/src/components/Dashboard/common/ObjectChildTable.jsx
@@ -64,7 +64,7 @@ const ObjectChildTable = ({
additionalColumns = [],
emptyText = 'No items',
isEditing = false,
- value = [],
+ value,
rollups = [],
onChange,
hiddenPropertyWidth = '300px',
@@ -159,10 +159,13 @@ const ObjectChildTable = ({
)
}, [columns, properties])
- // When used inside antd Form.Item without Form.List, `value` will be the controlled array.
+ // Form.Item may not have hydrated yet; fall back to objectData the same way
+ // rollups already do.
const itemsSource = useMemo(() => {
- return value ?? []
- }, [value])
+ if (Array.isArray(value)) return value
+ if (name && Array.isArray(objectData?.[name])) return objectData[name]
+ return []
+ }, [value, name, objectData])
const handleRemoveItem = useCallback(
(record, index) => {
@@ -265,12 +268,11 @@ const ObjectChildTable = ({
)
@@ -384,10 +386,7 @@ const ObjectChildTable = ({
const rollupDataSource = useMemo(() => {
if (!rollups || rollups.length === 0) return []
- // Use value from form/props, or fall back to objectData when entering edit mode
- // (form may not have populated the field yet)
- const itemsForRollup =
- value ?? (name && objectData ? objectData[name] : null) ?? []
+ const itemsForRollup = itemsSource
// Build parent object with children array for rollup functions (e.g. objectData.parts)
const updatedObjectData = { ...objectData }
@@ -420,7 +419,7 @@ const ObjectChildTable = ({
})
return [summaryRow]
- }, [properties, rollups, objectData, value, name])
+ }, [properties, rollups, objectData, itemsSource, name])
const rollupColumns = useMemo(() => {
const propertyColumns = resolvedProperties.map((property, index) => {
@@ -634,7 +633,8 @@ const ObjectChildTable = ({
setMinimalModelOpen(true)
}}
>
- {value?.length || 0} {value?.length == 1 ? 'item' : 'items'}
+ {itemsSource?.length || 0}{' '}
+ {itemsSource?.length == 1 ? 'item' : 'items'}
{
}, {})
}
+// Patch computed fields onto a full object. Building them as a sparse tree and
+// merge-replacing arrays would wipe stored child-row fields (shipment, amount,
+// etc.) while leaving calculated columns intact.
+const applyComputedEntries = (base, entries = []) => {
+ const result = merge({}, base || {})
+ entries.forEach((entry) => {
+ const { namePath, value } = entry || {}
+ if (!Array.isArray(namePath) || value === undefined) return
+ set(result, namePath, value)
+ })
+ return result
+}
+
const getEditDisabled = (model, objectData, userProfile) => {
if (model?.readOnly === true) {
return true
@@ -356,12 +369,9 @@ const ObjectForm = forwardRef(
serverObjectData.current,
model
)
- const computedValuesObject = buildObjectFromEntries(computedEntries)
- const resetFormData = mergeWith(
- {},
+ const resetFormData = applyComputedEntries(
serverObjectData.current,
- computedValuesObject,
- arrayReplaceCustomizer
+ computedEntries
)
setIsEditing(false)
isEditingRef.current = false
@@ -498,13 +508,7 @@ const ObjectForm = forwardRef(
// Calculate and set computed values on initial load
const computedEntries = calculateComputedValues(data, model)
- const computedValuesObject = buildObjectFromEntries(computedEntries)
- const initialFormData = mergeWith(
- {},
- data,
- computedValuesObject,
- arrayReplaceCustomizer
- )
+ const initialFormData = applyComputedEntries(data, computedEntries)
setObjectData({ ...initialFormData, _isEditing: isEditingRef.current })
form.setFieldsValue(initialFormData)
setFetchLoading(false)
@@ -703,17 +707,11 @@ const ObjectForm = forwardRef(
setIsEditing(true)
const computedEntries = calculateComputedValues(objectData, model)
- const computedValuesObject = buildObjectFromEntries(computedEntries)
- setObjectData((prev) => ({
- ...prev,
- ...computedValuesObject,
- _isEditing: true
- }))
const nextObjectData = {
- ...objectData,
- ...computedValuesObject,
+ ...applyComputedEntries(objectData, computedEntries),
_isEditing: true
}
+ setObjectData(nextObjectData)
onStateChangeRef.current({
isEditing: true,
objectData: nextObjectData,
@@ -868,12 +866,9 @@ const ObjectForm = forwardRef(
})
}
- const computedValuesObject = buildObjectFromEntries(computedEntries)
- const mergedFormValues = mergeWith(
- {},
+ const mergedFormValues = applyComputedEntries(
allFormValues,
- computedValuesObject,
- arrayReplaceCustomizer
+ computedEntries
)
mergedFormValues._isEditing = isEditingRef.current