- Added 'additionalCost' and 'additionalCostWithTax' fields to CourierService model. - Introduced 'connection.port' field in DocumentPrinter model. - Updated Filament model with 'density' and 'emptySpoolWeight' fields. - Enhanced Part, PartSku, Product, and ProductSku models by adding 'cost', 'costWithTax', 'price', 'margin', and 'priceWithTax' fields for improved pricing structure.
479 lines
12 KiB
JavaScript
479 lines
12 KiB
JavaScript
import { createElement, lazy } from 'react'
|
|
|
|
const ProductSkuInfo = lazy(
|
|
() => import('../../components/Dashboard/Management/ProductSkus/ProductSkuInfo')
|
|
)
|
|
const NewProductSku = lazy(
|
|
() => import('../../components/Dashboard/Management/ProductSkus/NewProductSku')
|
|
)
|
|
const DeleteObject = lazy(
|
|
() => import('../../components/Dashboard/common/DeleteObject')
|
|
)
|
|
import ProductSkuIcon from '../../components/Icons/ProductSkuIcon'
|
|
import PlusIcon from '../../components/Icons/PlusIcon'
|
|
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
|
import EditIcon from '../../components/Icons/EditIcon'
|
|
import CheckIcon from '../../components/Icons/CheckIcon'
|
|
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
|
import BinIcon from '../../components/Icons/BinIcon'
|
|
import ListIcon from '../../components/Icons/ListIcon'
|
|
|
|
export const ProductSku = {
|
|
name: 'productSku',
|
|
label: 'Product SKU',
|
|
labelPlural: 'Product SKUs',
|
|
url: '/dashboard/management/productskus',
|
|
prefix: 'SKU',
|
|
icon: ProductSkuIcon,
|
|
actions: [
|
|
{
|
|
name: 'new',
|
|
type: 'modal',
|
|
pageName: 'list',
|
|
modalWidth: 700,
|
|
label: 'New Product SKU',
|
|
icon: PlusIcon,
|
|
content: (objectData, { onOk } = {}) => {
|
|
return createElement(NewProductSku, { defaultValues: objectData, onOk, reset: true })
|
|
}
|
|
},
|
|
{
|
|
name: 'list',
|
|
type: 'page',
|
|
pageName: 'list',
|
|
label: 'List',
|
|
icon: ListIcon
|
|
},
|
|
{
|
|
name: 'info',
|
|
type: 'page',
|
|
pageName: 'info',
|
|
label: 'Info',
|
|
default: true,
|
|
row: true,
|
|
icon: InfoCircleIcon
|
|
},
|
|
{
|
|
name: 'edit',
|
|
type: 'page',
|
|
pageName: 'info',
|
|
label: 'Edit',
|
|
row: true,
|
|
icon: EditIcon,
|
|
visible: (objectData) => {
|
|
return !(objectData?._isEditing && objectData?._isEditing == true)
|
|
}
|
|
},
|
|
{
|
|
name: 'cancelEdit',
|
|
label: 'Cancel Edits',
|
|
type: 'page',
|
|
pageName: 'info',
|
|
icon: XMarkIcon,
|
|
visible: (objectData) => {
|
|
return objectData?._isEditing && objectData?._isEditing == true
|
|
}
|
|
},
|
|
{
|
|
name: 'finishEdit',
|
|
label: 'Save Edits',
|
|
type: 'page',
|
|
pageName: 'info',
|
|
icon: CheckIcon,
|
|
visible: (objectData) => {
|
|
return objectData?._isEditing && objectData?._isEditing == true
|
|
}
|
|
},
|
|
{ type: 'divider' },
|
|
{
|
|
name: 'delete',
|
|
type: 'modal',
|
|
modalWidth: 520,
|
|
modalCentered: true,
|
|
label: 'Delete',
|
|
icon: BinIcon,
|
|
danger: true,
|
|
content: (objectData, { onOk } = {}) => {
|
|
return createElement(DeleteObject, { objectData, onOk })
|
|
}
|
|
}
|
|
],
|
|
pages: [
|
|
{
|
|
name: 'info',
|
|
content: () => createElement(ProductSkuInfo)
|
|
}
|
|
],
|
|
columns: [
|
|
'_reference',
|
|
'name',
|
|
'product',
|
|
'barcode',
|
|
'overrideCost',
|
|
'cost',
|
|
'costWithTax',
|
|
'overridePrice',
|
|
'price',
|
|
'priceWithTax',
|
|
'createdAt',
|
|
'updatedAt'
|
|
],
|
|
filters: [
|
|
'_id',
|
|
'barcode',
|
|
'product',
|
|
'name',
|
|
'cost',
|
|
'costWithTax',
|
|
'price',
|
|
'priceWithTax',
|
|
'margin',
|
|
'createdAt',
|
|
'updatedAt',
|
|
'_reference'
|
|
],
|
|
sorters: [
|
|
'barcode',
|
|
'product',
|
|
'name',
|
|
'cost',
|
|
'costWithTax',
|
|
'price',
|
|
'priceWithTax',
|
|
'margin',
|
|
'createdAt',
|
|
'updatedAt'
|
|
],
|
|
properties: [
|
|
{
|
|
name: '_id',
|
|
label: 'ID',
|
|
type: 'id',
|
|
objectType: 'productSku',
|
|
showCopy: true,
|
|
readOnly: true,
|
|
columnWidth: 140
|
|
},
|
|
{
|
|
name: 'createdAt',
|
|
label: 'Created At',
|
|
type: 'dateTime',
|
|
readOnly: true,
|
|
columnWidth: 175
|
|
},
|
|
{
|
|
name: '_reference',
|
|
label: 'Reference',
|
|
type: 'reference',
|
|
columnFixed: 'left',
|
|
objectType: 'productSku',
|
|
showCopy: true,
|
|
readOnly: true,
|
|
columnWidth: 180
|
|
},
|
|
|
|
{
|
|
name: 'name',
|
|
label: 'Name',
|
|
required: true,
|
|
type: 'text',
|
|
columnWidth: 200,
|
|
columnFixed: 'left'
|
|
},
|
|
{
|
|
name: 'updatedAt',
|
|
label: 'Updated At',
|
|
type: 'dateTime',
|
|
readOnly: true,
|
|
columnWidth: 175
|
|
},
|
|
{
|
|
name: 'product',
|
|
label: 'Product',
|
|
type: 'object',
|
|
objectType: 'product',
|
|
required: true,
|
|
showHyperlink: true,
|
|
columnWidth: 200
|
|
},
|
|
{
|
|
name: 'barcode',
|
|
label: 'Barcode',
|
|
required: false,
|
|
type: 'text',
|
|
columnWidth: 150
|
|
},
|
|
{
|
|
name: 'description',
|
|
label: 'Description',
|
|
required: false,
|
|
type: 'text',
|
|
columnWidth: 200
|
|
},
|
|
{
|
|
name: 'overrideCost',
|
|
label: 'Override Cost',
|
|
required: true,
|
|
type: 'bool',
|
|
columnWidth: 150
|
|
},
|
|
{
|
|
name: 'cost',
|
|
label: 'Cost',
|
|
required: true,
|
|
type: 'number',
|
|
prefix: '£',
|
|
min: 0,
|
|
step: 0.01,
|
|
visible: (objectData) => {
|
|
return objectData?.overrideCost
|
|
},
|
|
value: (objectData) =>
|
|
objectData?.overrideCost
|
|
? objectData?.cost
|
|
: objectData?.product?.cost,
|
|
columnWidth: 100
|
|
},
|
|
{
|
|
name: 'costWithTax',
|
|
label: 'Cost w/ Tax',
|
|
required: true,
|
|
readOnly: true,
|
|
type: 'number',
|
|
prefix: '£',
|
|
min: 0,
|
|
step: 0.01,
|
|
visible: (objectData) => {
|
|
return objectData?.overrideCost
|
|
},
|
|
value: (objectData) => {
|
|
if (!objectData?.overrideCost)
|
|
return objectData?.product?.costWithTax || undefined
|
|
const cost = objectData?.cost
|
|
const taxRate = objectData?.costTaxRate
|
|
if (!cost) return 0
|
|
if (taxRate?.rateType == 'percentage') {
|
|
return (cost * (1 + taxRate?.rate / 100)).toFixed(2) || 0
|
|
} else if (taxRate?.rateType == 'amount') {
|
|
return (cost + taxRate?.rate).toFixed(2) || 0
|
|
}
|
|
return cost
|
|
},
|
|
columnWidth: 150
|
|
},
|
|
{
|
|
name: 'costTaxRate',
|
|
label: 'Cost Tax Rate',
|
|
required: true,
|
|
type: 'object',
|
|
objectType: 'taxRate',
|
|
showHyperlink: true,
|
|
visible: (objectData) => {
|
|
return objectData?.overrideCost
|
|
},
|
|
value: (objectData) =>
|
|
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
|
|
},
|
|
{
|
|
name: 'price',
|
|
label: 'Price',
|
|
required: true,
|
|
type: 'number',
|
|
prefix: '£',
|
|
min: 0,
|
|
step: 0.1,
|
|
fixedNumber: 2,
|
|
visible: (objectData) => {
|
|
return objectData?.overridePrice
|
|
},
|
|
readOnly: (objectData) =>
|
|
objectData?.overridePrice && objectData?.priceMode == 'margin',
|
|
value: (objectData) => {
|
|
if (!objectData?.overridePrice)
|
|
return objectData?.product?.price || undefined
|
|
const priceMode =
|
|
objectData?.priceMode ?? objectData?.product?.priceMode
|
|
const cost = objectData?.overrideCost
|
|
? objectData?.cost
|
|
: objectData?.product?.cost
|
|
const margin = objectData?.margin ?? objectData?.product?.margin
|
|
if (
|
|
priceMode == 'margin' &&
|
|
margin !== undefined &&
|
|
margin !== null &&
|
|
cost != null
|
|
) {
|
|
return cost * (1 + margin / 100) || undefined
|
|
}
|
|
return objectData?.price
|
|
},
|
|
columnWidth: 100
|
|
},
|
|
{
|
|
name: 'priceWithTax',
|
|
label: 'Price w/ Tax',
|
|
required: true,
|
|
readOnly: true,
|
|
type: 'number',
|
|
prefix: '£',
|
|
min: 0,
|
|
step: 0.01,
|
|
visible: (objectData) => {
|
|
return objectData?.overridePrice
|
|
},
|
|
value: (objectData) => {
|
|
if (!objectData?.overridePrice)
|
|
return objectData.product?.priceWithTax || undefined
|
|
let price
|
|
const priceMode =
|
|
objectData?.priceMode ?? objectData?.product?.priceMode
|
|
const cost = objectData?.overrideCost
|
|
? objectData?.cost
|
|
: objectData?.product?.cost
|
|
const margin = objectData?.margin ?? objectData?.product?.margin
|
|
if (priceMode == 'margin' && margin != null && cost != null) {
|
|
price = cost * (1 + margin / 100)
|
|
} else {
|
|
price = objectData?.price
|
|
}
|
|
if (!price) return 0
|
|
const taxRate =
|
|
objectData?.priceTaxRate ?? objectData?.product?.priceTaxRate
|
|
if (taxRate?.rateType == 'percentage') {
|
|
return (price * (1 + taxRate?.rate / 100)).toFixed(2) || 0
|
|
} else if (taxRate?.rateType == 'amount') {
|
|
return (price + taxRate?.rate).toFixed(2) || 0
|
|
}
|
|
return price
|
|
},
|
|
columnWidth: 150
|
|
},
|
|
{
|
|
name: 'margin',
|
|
label: 'Margin',
|
|
required: true,
|
|
type: 'number',
|
|
visible: (objectData) => {
|
|
return objectData?.overridePrice
|
|
},
|
|
readOnly: (objectData) => {
|
|
const priceMode =
|
|
objectData?.priceMode ?? objectData?.product?.priceMode
|
|
return priceMode == 'amount'
|
|
},
|
|
suffix: '%',
|
|
min: 0,
|
|
max: 100,
|
|
step: 0.01,
|
|
value: (objectData) => {
|
|
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
|
|
},
|
|
{
|
|
name: 'priceTaxRate',
|
|
label: 'Price Tax Rate',
|
|
required: true,
|
|
type: 'object',
|
|
objectType: 'taxRate',
|
|
showHyperlink: true,
|
|
visible: (objectData) => {
|
|
return objectData?.overridePrice
|
|
},
|
|
value: (objectData) =>
|
|
objectData?.overridePrice
|
|
? objectData?.priceTaxRate
|
|
: objectData?.product?.priceTaxRate,
|
|
columnWidth: 150
|
|
},
|
|
{
|
|
name: 'parts',
|
|
label: 'Parts',
|
|
type: 'objectChildren',
|
|
size: 'medium',
|
|
properties: [
|
|
{
|
|
name: 'part',
|
|
label: 'Part',
|
|
type: 'object',
|
|
objectType: 'part',
|
|
required: true,
|
|
showHyperlink: true
|
|
},
|
|
{
|
|
name: 'partSku',
|
|
label: 'Part SKU',
|
|
type: 'object',
|
|
objectType: 'partSku',
|
|
required: true,
|
|
showHyperlink: true,
|
|
masterFilter: (objectData) => {
|
|
const partId = objectData?.part?._id
|
|
if (partId == null) return {}
|
|
return { part: partId }
|
|
}
|
|
},
|
|
{
|
|
name: 'quantity',
|
|
label: 'Quantity',
|
|
type: 'number',
|
|
required: true
|
|
}
|
|
],
|
|
rollups: [
|
|
{
|
|
name: 'totalQuantity',
|
|
label: 'Total',
|
|
type: 'number',
|
|
property: 'quantity',
|
|
value: (objectData) => {
|
|
return objectData?.parts?.reduce(
|
|
(acc, part) => acc + part.quantity,
|
|
0
|
|
)
|
|
}
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|