From 9d2297da7de5272a34c6e3e2db78284d995c3a1c Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 16 Apr 2024 09:52:47 +1000 Subject: [PATCH] BOM pricing table fix (#7044) * Allow click-through from BOM pricing table * Allow sorting by price in BOM table * Add "Total Price" column to BOM table * Enable part table to be sorted by price range * Enable click-through on VariantPricing table * Update quantity columns for BOM tables * Improve rendering for UsedInTable --- .../pages/part/pricing/BomPricingPanel.tsx | 25 +++++++++++-- .../part/pricing/VariantPricingPanel.tsx | 6 ++-- src/frontend/src/tables/ColumnRenderers.tsx | 9 +++-- src/frontend/src/tables/bom/BomTable.tsx | 36 ++++++++++++++----- src/frontend/src/tables/bom/UsedInTable.tsx | 13 +++++-- src/frontend/src/tables/part/PartTable.tsx | 3 +- 6 files changed, 74 insertions(+), 18 deletions(-) diff --git a/src/frontend/src/pages/part/pricing/BomPricingPanel.tsx b/src/frontend/src/pages/part/pricing/BomPricingPanel.tsx index 4284e954df..98a561d6c0 100644 --- a/src/frontend/src/pages/part/pricing/BomPricingPanel.tsx +++ b/src/frontend/src/pages/part/pricing/BomPricingPanel.tsx @@ -1,5 +1,11 @@ import { t } from '@lingui/macro'; -import { SegmentedControl, SimpleGrid, Stack } from '@mantine/core'; +import { + Group, + SegmentedControl, + SimpleGrid, + Stack, + Text +} from '@mantine/core'; import { ReactNode, useMemo, useState } from 'react'; import { Bar, @@ -17,6 +23,7 @@ import { import { CHART_COLORS } from '../../../components/charts/colors'; import { formatDecimal, formatPriceRange } from '../../../defaults/formatters'; import { ApiEndpoints } from '../../../enums/ApiEndpoints'; +import { ModelType } from '../../../enums/ModelType'; import { useTable } from '../../../hooks/UseTable'; import { apiUrl } from '../../../states/ApiState'; import { TableColumn } from '../../../tables/Column'; @@ -110,7 +117,17 @@ export default function BomPricingPanel({ title: t`Quantity`, sortable: true, switchable: false, - render: (record: any) => formatDecimal(record.quantity) + render: (record: any) => { + let quantity = formatDecimal(record.quantity); + let units = record.sub_part_detail?.units; + + return ( + + {quantity} + {units && [{units}]} + + ); + } }, { accessor: 'unit_price', @@ -178,7 +195,9 @@ export default function BomPricingPanel({ sub_part_detail: true, has_pricing: true }, - enableSelection: false + enableSelection: false, + modelType: ModelType.part, + modelField: 'sub_part' }} /> {bomPricingData.length > 0 ? ( diff --git a/src/frontend/src/pages/part/pricing/VariantPricingPanel.tsx b/src/frontend/src/pages/part/pricing/VariantPricingPanel.tsx index 2c5c21a3c3..2aeea96bfb 100644 --- a/src/frontend/src/pages/part/pricing/VariantPricingPanel.tsx +++ b/src/frontend/src/pages/part/pricing/VariantPricingPanel.tsx @@ -14,6 +14,7 @@ import { import { CHART_COLORS } from '../../../components/charts/colors'; import { formatCurrency } from '../../../defaults/formatters'; import { ApiEndpoints } from '../../../enums/ApiEndpoints'; +import { ModelType } from '../../../enums/ModelType'; import { useTable } from '../../../hooks/UseTable'; import { apiUrl } from '../../../states/ApiState'; import { TableColumn } from '../../../tables/Column'; @@ -37,7 +38,7 @@ export default function VariantPricingPanel({ title: t`Variant Part`, sortable: true, switchable: false, - render: (record: any) => PartColumn(record) + render: (record: any) => PartColumn(record, true) }, { accessor: 'pricing_min', @@ -90,7 +91,8 @@ export default function VariantPricingPanel({ ancestor: part?.pk, has_pricing: true }, - enablePagination: false + enablePagination: true, + modelType: ModelType.part }} /> {variantPricingData.length > 0 ? ( diff --git a/src/frontend/src/tables/ColumnRenderers.tsx b/src/frontend/src/tables/ColumnRenderers.tsx index 5353a467b8..f1d39858cf 100644 --- a/src/frontend/src/tables/ColumnRenderers.tsx +++ b/src/frontend/src/tables/ColumnRenderers.tsx @@ -16,8 +16,13 @@ import { TableColumn } from './Column'; import { ProjectCodeHoverCard } from './TableHoverCard'; // Render a Part instance within a table -export function PartColumn(part: any) { - return ; +export function PartColumn(part: any, full_name?: boolean) { + return ( + + ); } export function BooleanColumn({ diff --git a/src/frontend/src/tables/bom/BomTable.tsx b/src/frontend/src/tables/bom/BomTable.tsx index b2101897dc..6e2cea5b57 100644 --- a/src/frontend/src/tables/bom/BomTable.tsx +++ b/src/frontend/src/tables/bom/BomTable.tsx @@ -1,5 +1,5 @@ import { t } from '@lingui/macro'; -import { Text } from '@mantine/core'; +import { Group, Text } from '@mantine/core'; import { IconArrowRight, IconCircleCheck, @@ -10,7 +10,7 @@ import { useNavigate } from 'react-router-dom'; import { YesNoButton } from '../../components/buttons/YesNoButton'; import { Thumbnail } from '../../components/images/Thumbnail'; -import { formatPriceRange } from '../../defaults/formatters'; +import { formatDecimal, formatPriceRange } from '../../defaults/formatters'; import { ApiEndpoints } from '../../enums/ApiEndpoints'; import { ModelType } from '../../enums/ModelType'; import { UserRoles } from '../../enums/Roles'; @@ -98,9 +98,19 @@ export function BomTable({ { accessor: 'quantity', switchable: false, - sortable: true - // TODO: Custom quantity renderer - // TODO: see bom.js for existing implementation + sortable: true, + render: (record: any) => { + let quantity = formatDecimal(record.quantity); + let units = record.sub_part_detail?.units; + + return ( + + {quantity} + {record.overage && +{record.overage}} + {units && {units}} + + ); + } }, { accessor: 'substitutes', @@ -131,12 +141,22 @@ export function BomTable({ }), { accessor: 'price_range', - title: t`Price Range`, - - sortable: false, + title: t`Unit Price`, + ordering: 'pricing_max', + sortable: true, + switchable: true, render: (record: any) => formatPriceRange(record.pricing_min, record.pricing_max) }, + { + accessor: 'total_price', + title: t`Total Price`, + ordering: 'pricing_max_total', + sortable: true, + switchable: true, + render: (record: any) => + formatPriceRange(record.pricing_min_total, record.pricing_max_total) + }, { accessor: 'available_stock', sortable: true, diff --git a/src/frontend/src/tables/bom/UsedInTable.tsx b/src/frontend/src/tables/bom/UsedInTable.tsx index 6f62a7ef31..69da8b651b 100644 --- a/src/frontend/src/tables/bom/UsedInTable.tsx +++ b/src/frontend/src/tables/bom/UsedInTable.tsx @@ -1,7 +1,9 @@ import { t } from '@lingui/macro'; +import { Group, Text } from '@mantine/core'; import { useMemo } from 'react'; import { PartHoverCard } from '../../components/images/Thumbnail'; +import { formatDecimal } from '../../defaults/formatters'; import { ApiEndpoints } from '../../enums/ApiEndpoints'; import { ModelType } from '../../enums/ModelType'; import { useTable } from '../../hooks/UseTable'; @@ -39,8 +41,15 @@ export function UsedInTable({ { accessor: 'quantity', render: (record: any) => { - // TODO: render units if appropriate - return record.quantity; + let quantity = formatDecimal(record.quantity); + let units = record.sub_part_detail?.units; + + return ( + + {quantity} + {units && {units}} + + ); } }, ReferenceColumn() diff --git a/src/frontend/src/tables/part/PartTable.tsx b/src/frontend/src/tables/part/PartTable.tsx index 856e88524b..f9d46cad23 100644 --- a/src/frontend/src/tables/part/PartTable.tsx +++ b/src/frontend/src/tables/part/PartTable.tsx @@ -158,7 +158,8 @@ function partTableColumns(): TableColumn[] { { accessor: 'price_range', title: t`Price Range`, - sortable: false, + sortable: true, + ordering: 'pricing_max', render: (record: any) => formatPriceRange(record.pricing_min, record.pricing_max) },