Refactor ProductSku and PartSku models to enforce required fields for overrideCost, overridePrice, and related properties. Update visibility logic for cost and price fields based on overrides, ensuring consistent data handling across components. Adjust NewProductSku and ProductInfo components to utilize updated default values and improve user experience.
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good

This commit is contained in:
Tom Butcher 2026-06-21 21:19:49 +01:00
parent 6cd0dca365
commit 4501f9936f
4 changed files with 201 additions and 124 deletions

View File

@ -8,7 +8,11 @@ const NewProductSku = ({ onOk, reset, defaultValues }) => {
<NewObjectForm <NewObjectForm
type='productSku' type='productSku'
reset={reset} reset={reset}
defaultValues={defaultValues} defaultValues={{
overrideCost: false,
overridePrice: false,
...defaultValues
}}
> >
{({ handleSubmit, submitLoading, objectData, formValid }) => { {({ handleSubmit, submitLoading, objectData, formValid }) => {
const steps = [ const steps = [
@ -50,51 +54,18 @@ const NewProductSku = ({ onOk, reset, defaultValues }) => {
<ObjectInfo <ObjectInfo
type='productSku' type='productSku'
column={1} column={1}
labelWidth={100} labelWidth={120}
visibleProperties={{ visibleProperties={{
overrideCost: true, overrideCost: true,
cost: true, cost: true,
costTaxRate: true, costTaxRate: true,
costWithTax: true, costWithTax: true,
overridePrice: true, overridePrice: true,
priceMode: true,
price: true, price: true,
margin: true,
priceTaxRate: true, priceTaxRate: true,
priceWithTax: true, priceWithTax: true
priceMode: true
}}
required={true}
bordered={false}
isEditing={true}
objectData={objectData}
/>
)
},
{
title: 'Parts',
key: 'parts',
content: (
<ObjectInfo
type='productSku'
column={1}
labelWidth={100}
visibleProperties={{
_id: false,
createdAt: false,
updatedAt: false,
barcode: false,
product: false,
name: false,
description: false,
priceMode: false,
cost: false,
costWithTax: false,
costTaxRate: false,
price: false,
priceWithTax: false,
margin: false,
amount: false,
priceTaxRate: false,
vendor: false
}} }}
bordered={false} bordered={false}
isEditing={true} isEditing={true}
@ -131,9 +102,10 @@ const NewProductSku = ({ onOk, reset, defaultValues }) => {
createdAt: false, createdAt: false,
updatedAt: false, updatedAt: false,
_id: false, _id: false,
_reference: false _reference: false,
parts: false
}} }}
labelWidth={100} labelWidth={120}
bordered={false} bordered={false}
isEditing={false} isEditing={false}
objectData={objectData} objectData={objectData}

View File

@ -197,7 +197,7 @@ const ProductInfo = () => {
}} }}
reset={newProductSkuOpen} reset={newProductSkuOpen}
defaultValues={{ defaultValues={{
product: productId ? { _id: productId } : undefined product: objectFormState?.objectData || undefined
}} }}
/> />
</Modal> </Modal>

View File

@ -75,7 +75,17 @@ export const PartSku = {
'createdAt', 'createdAt',
'updatedAt' 'updatedAt'
], ],
filters: ['_id', 'barcode', 'part', 'part._id', 'name', 'cost', 'costWithTax', 'price', 'priceWithTax'], filters: [
'_id',
'barcode',
'part',
'part._id',
'name',
'cost',
'costWithTax',
'price',
'priceWithTax'
],
sorters: [ sorters: [
'barcode', 'barcode',
'part', 'part',
@ -152,54 +162,44 @@ export const PartSku = {
type: 'text', type: 'text',
columnWidth: 200 columnWidth: 200
}, },
{
name: 'priceMode',
label: 'Price Mode',
required: false,
type: 'priceMode',
columnWidth: 150
},
{ {
name: 'overrideCost', name: 'overrideCost',
label: 'Override Cost', label: 'Override Cost',
required: false, required: true,
type: 'bool', type: 'bool',
value: (objectData) => objectData?.overrideCost ?? false,
columnWidth: 150
},
{
name: 'overridePrice',
label: 'Override Price',
required: false,
type: 'bool',
value: (objectData) => objectData?.overridePrice ?? false,
columnWidth: 150 columnWidth: 150
}, },
{ {
name: 'cost', name: 'cost',
label: 'Cost', label: 'Cost',
required: false, required: true,
type: 'number', type: 'number',
prefix: '£', prefix: '£',
min: 0, min: 0,
step: 0.01, step: 0.01,
disabled: (objectData) => !objectData?.overrideCost, visible: (objectData) => {
return objectData?.overrideCost
},
value: (objectData) => value: (objectData) =>
objectData?.overrideCost ? objectData?.cost : undefined, objectData?.overrideCost ? objectData?.cost : objectData?.part?.cost,
columnWidth: 100 columnWidth: 100
}, },
{ {
name: 'costWithTax', name: 'costWithTax',
label: 'Cost w/ Tax', label: 'Cost w/ Tax',
required: false, required: true,
readOnly: true, readOnly: true,
type: 'number', type: 'number',
prefix: '£', prefix: '£',
min: 0, min: 0,
step: 0.01, step: 0.01,
disabled: (objectData) => !objectData?.overrideCost, visible: (objectData) => {
return objectData?.overrideCost
},
value: (objectData) => { value: (objectData) => {
if (!objectData?.overrideCost) return undefined if (!objectData?.overrideCost)
return objectData?.part?.costWithTax || undefined
const cost = objectData?.cost const cost = objectData?.cost
const taxRate = objectData?.costTaxRate const taxRate = objectData?.costTaxRate
if (!cost) return 0 if (!cost) return 0
@ -215,28 +215,58 @@ export const PartSku = {
{ {
name: 'costTaxRate', name: 'costTaxRate',
label: 'Cost Tax Rate', label: 'Cost Tax Rate',
required: false, required: true,
type: 'object', type: 'object',
objectType: 'taxRate', objectType: 'taxRate',
showHyperlink: true, showHyperlink: true,
disabled: (objectData) => !objectData?.overrideCost, visible: (objectData) => {
return objectData?.overrideCost
},
value: (objectData) => value: (objectData) =>
objectData?.overrideCost ? objectData?.costTaxRate : undefined, objectData?.overrideCost
? objectData?.costTaxRate
: objectData?.part?.costTaxRate,
columnWidth: 150
},
{
name: 'overridePrice',
label: 'Override Price',
required: true,
type: 'bool',
columnWidth: 150
},
{
name: 'priceMode',
label: 'Price Mode',
required: true,
visible: (objectData) => {
return objectData?.overridePrice
},
value: (objectData) => {
return objectData?.overridePrice
? objectData?.priceMode
: objectData?.part?.priceMode
},
type: 'priceMode',
columnWidth: 150 columnWidth: 150
}, },
{ {
name: 'price', name: 'price',
label: 'Price', label: 'Price',
required: false, required: true,
type: 'number', type: 'number',
prefix: '£', prefix: '£',
min: 0, min: 0,
step: 0.1, step: 0.1,
disabled: (objectData) => !objectData?.overridePrice, fixedNumber: 2,
visible: (objectData) => {
return objectData?.overridePrice
},
readOnly: (objectData) => readOnly: (objectData) =>
objectData?.overridePrice && objectData?.priceMode == 'margin', objectData?.overridePrice && objectData?.priceMode == 'margin',
value: (objectData) => { value: (objectData) => {
if (!objectData?.overridePrice) return undefined if (!objectData?.overridePrice)
return objectData?.part?.price || undefined
const priceMode = objectData?.priceMode ?? objectData?.part?.priceMode const priceMode = objectData?.priceMode ?? objectData?.part?.priceMode
const cost = objectData?.overrideCost const cost = objectData?.overrideCost
? objectData?.cost ? objectData?.cost
@ -248,7 +278,7 @@ export const PartSku = {
margin !== null && margin !== null &&
cost != null cost != null
) { ) {
return (cost * (1 + margin / 100)).toFixed(2) || undefined return cost * (1 + margin / 100) || undefined
} }
return objectData?.price return objectData?.price
}, },
@ -257,15 +287,18 @@ export const PartSku = {
{ {
name: 'priceWithTax', name: 'priceWithTax',
label: 'Price w/ Tax', label: 'Price w/ Tax',
required: false, required: true,
readOnly: true, readOnly: true,
type: 'number', type: 'number',
prefix: '£', prefix: '£',
min: 0, min: 0,
step: 0.01, step: 0.01,
disabled: (objectData) => !objectData?.overridePrice, visible: (objectData) => {
return objectData?.overridePrice
},
value: (objectData) => { value: (objectData) => {
if (!objectData?.overridePrice) return undefined if (!objectData?.overridePrice)
return objectData.part?.priceWithTax || undefined
let price let price
const priceMode = objectData?.priceMode ?? objectData?.part?.priceMode const priceMode = objectData?.priceMode ?? objectData?.part?.priceMode
const cost = objectData?.overrideCost const cost = objectData?.overrideCost
@ -292,28 +325,51 @@ export const PartSku = {
{ {
name: 'margin', name: 'margin',
label: 'Margin', label: 'Margin',
required: false, required: true,
type: 'number', type: 'number',
disabled: (objectData) => visible: (objectData) => {
!objectData?.overridePrice || objectData?.priceMode == 'amount', return objectData?.overridePrice
},
readOnly: (objectData) => {
const priceMode = objectData?.priceMode ?? objectData?.part?.priceMode
return priceMode == 'amount'
},
suffix: '%', suffix: '%',
min: 0, min: 0,
max: 100, max: 100,
step: 0.01, step: 0.01,
value: (objectData) => value: (objectData) => {
objectData?.overridePrice ? objectData?.margin : undefined, if (!objectData?.overridePrice)
return objectData?.part?.margin || undefined
const priceMode = objectData?.priceMode ?? objectData?.part?.priceMode
const cost = objectData?.overrideCost
? objectData?.cost
: objectData?.part?.cost
if (priceMode == 'amount') {
const price = objectData?.price
if (price != null && cost != null) {
return Number(((price / cost - 1) * 100).toFixed(2)) || undefined
}
return undefined
}
return objectData?.margin ?? objectData?.part?.margin
},
columnWidth: 85 columnWidth: 85
}, },
{ {
name: 'priceTaxRate', name: 'priceTaxRate',
label: 'Price Tax Rate', label: 'Price Tax Rate',
required: false, required: true,
type: 'object', type: 'object',
objectType: 'taxRate', objectType: 'taxRate',
showHyperlink: true, showHyperlink: true,
disabled: (objectData) => !objectData?.overridePrice, visible: (objectData) => {
return objectData?.overridePrice
},
value: (objectData) => value: (objectData) =>
objectData?.overridePrice ? objectData?.priceTaxRate : undefined, objectData?.overridePrice
? objectData?.priceTaxRate
: objectData?.part?.priceTaxRate,
columnWidth: 150 columnWidth: 150
} }
] ]

View File

@ -163,54 +163,45 @@ export const ProductSku = {
type: 'text', type: 'text',
columnWidth: 200 columnWidth: 200
}, },
{
name: 'priceMode',
label: 'Price Mode',
required: false,
type: 'priceMode',
columnWidth: 150
},
{ {
name: 'overrideCost', name: 'overrideCost',
label: 'Override Cost', label: 'Override Cost',
required: false, required: true,
type: 'bool', type: 'bool',
value: (objectData) => objectData?.overrideCost ?? false,
columnWidth: 150
},
{
name: 'overridePrice',
label: 'Override Price',
required: false,
type: 'bool',
value: (objectData) => objectData?.overridePrice ?? false,
columnWidth: 150 columnWidth: 150
}, },
{ {
name: 'cost', name: 'cost',
label: 'Cost', label: 'Cost',
required: false, required: true,
type: 'number', type: 'number',
prefix: '£', prefix: '£',
min: 0, min: 0,
step: 0.01, step: 0.01,
disabled: (objectData) => !objectData?.overrideCost, visible: (objectData) => {
return objectData?.overrideCost
},
value: (objectData) => value: (objectData) =>
objectData?.overrideCost ? objectData?.cost : undefined, objectData?.overrideCost
? objectData?.cost
: objectData?.product?.cost,
columnWidth: 100 columnWidth: 100
}, },
{ {
name: 'costWithTax', name: 'costWithTax',
label: 'Cost w/ Tax', label: 'Cost w/ Tax',
required: false, required: true,
readOnly: true, readOnly: true,
type: 'number', type: 'number',
prefix: '£', prefix: '£',
min: 0, min: 0,
step: 0.01, step: 0.01,
disabled: (objectData) => !objectData?.overrideCost, visible: (objectData) => {
return objectData?.overrideCost
},
value: (objectData) => { value: (objectData) => {
if (!objectData?.overrideCost) return undefined if (!objectData?.overrideCost)
return objectData?.product?.costWithTax || undefined
const cost = objectData?.cost const cost = objectData?.cost
const taxRate = objectData?.costTaxRate const taxRate = objectData?.costTaxRate
if (!cost) return 0 if (!cost) return 0
@ -226,28 +217,58 @@ export const ProductSku = {
{ {
name: 'costTaxRate', name: 'costTaxRate',
label: 'Cost Tax Rate', label: 'Cost Tax Rate',
required: false, required: true,
type: 'object', type: 'object',
objectType: 'taxRate', objectType: 'taxRate',
showHyperlink: true, showHyperlink: true,
disabled: (objectData) => !objectData?.overrideCost, visible: (objectData) => {
return objectData?.overrideCost
},
value: (objectData) => value: (objectData) =>
objectData?.overrideCost ? objectData?.costTaxRate : undefined, objectData?.overrideCost
? objectData?.costTaxRate
: objectData?.product?.costTaxRate,
columnWidth: 150
},
{
name: 'overridePrice',
label: 'Override Price',
required: true,
type: 'bool',
columnWidth: 150
},
{
name: 'priceMode',
label: 'Price Mode',
required: true,
visible: (objectData) => {
return objectData?.overridePrice
},
value: (objectData) => {
return objectData?.overridePrice
? objectData?.priceMode
: objectData?.product?.priceMode
},
type: 'priceMode',
columnWidth: 150 columnWidth: 150
}, },
{ {
name: 'price', name: 'price',
label: 'Price', label: 'Price',
required: false, required: true,
type: 'number', type: 'number',
prefix: '£', prefix: '£',
min: 0, min: 0,
step: 0.1, step: 0.1,
disabled: (objectData) => !objectData?.overridePrice, fixedNumber: 2,
visible: (objectData) => {
return objectData?.overridePrice
},
readOnly: (objectData) => readOnly: (objectData) =>
objectData?.overridePrice && objectData?.priceMode == 'margin', objectData?.overridePrice && objectData?.priceMode == 'margin',
value: (objectData) => { value: (objectData) => {
if (!objectData?.overridePrice) return undefined if (!objectData?.overridePrice)
return objectData?.product?.price || undefined
const priceMode = const priceMode =
objectData?.priceMode ?? objectData?.product?.priceMode objectData?.priceMode ?? objectData?.product?.priceMode
const cost = objectData?.overrideCost const cost = objectData?.overrideCost
@ -260,7 +281,7 @@ export const ProductSku = {
margin !== null && margin !== null &&
cost != null cost != null
) { ) {
return (cost * (1 + margin / 100)).toFixed(2) || undefined return cost * (1 + margin / 100) || undefined
} }
return objectData?.price return objectData?.price
}, },
@ -269,15 +290,18 @@ export const ProductSku = {
{ {
name: 'priceWithTax', name: 'priceWithTax',
label: 'Price w/ Tax', label: 'Price w/ Tax',
required: false, required: true,
readOnly: true, readOnly: true,
type: 'number', type: 'number',
prefix: '£', prefix: '£',
min: 0, min: 0,
step: 0.01, step: 0.01,
disabled: (objectData) => !objectData?.overridePrice, visible: (objectData) => {
return objectData?.overridePrice
},
value: (objectData) => { value: (objectData) => {
if (!objectData?.overridePrice) return undefined if (!objectData?.overridePrice)
return objectData.product?.priceWithTax || undefined
let price let price
const priceMode = const priceMode =
objectData?.priceMode ?? objectData?.product?.priceMode objectData?.priceMode ?? objectData?.product?.priceMode
@ -305,28 +329,53 @@ export const ProductSku = {
{ {
name: 'margin', name: 'margin',
label: 'Margin', label: 'Margin',
required: false, required: true,
type: 'number', type: 'number',
disabled: (objectData) => visible: (objectData) => {
!objectData?.overridePrice || objectData?.priceMode == 'amount', return objectData?.overridePrice
},
readOnly: (objectData) => {
const priceMode =
objectData?.priceMode ?? objectData?.product?.priceMode
return priceMode == 'amount'
},
suffix: '%', suffix: '%',
min: 0, min: 0,
max: 100, max: 100,
step: 0.01, step: 0.01,
value: (objectData) => value: (objectData) => {
objectData?.overridePrice ? objectData?.margin : undefined, if (!objectData?.overridePrice)
return objectData?.product?.margin || undefined
const priceMode =
objectData?.priceMode ?? objectData?.product?.priceMode
const cost = objectData?.overrideCost
? objectData?.cost
: objectData?.product?.cost
if (priceMode == 'amount') {
const price = objectData?.price
if (price != null && cost != null) {
return Number(((price / cost - 1) * 100).toFixed(2)) || undefined
}
return undefined
}
return objectData?.margin ?? objectData?.product?.margin
},
columnWidth: 85 columnWidth: 85
}, },
{ {
name: 'priceTaxRate', name: 'priceTaxRate',
label: 'Price Tax Rate', label: 'Price Tax Rate',
required: false, required: true,
type: 'object', type: 'object',
objectType: 'taxRate', objectType: 'taxRate',
showHyperlink: true, showHyperlink: true,
disabled: (objectData) => !objectData?.overridePrice, visible: (objectData) => {
return objectData?.overridePrice
},
value: (objectData) => value: (objectData) =>
objectData?.overridePrice ? objectData?.priceTaxRate : undefined, objectData?.overridePrice
? objectData?.priceTaxRate
: objectData?.product?.priceTaxRate,
columnWidth: 150 columnWidth: 150
}, },
{ {