mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 13:15:43 +00:00 
			
		
		
		
	[PUI] Platform fixes (#8324)
* Add "IPN" column to build order allocated stock table * Allow sorting and searching by IPN * Handle allocations where allocated but required == 0 * Add "no info available" message to part scheduling * Adjust PartSchedulingTable * Icon fix * Add "latest serial number" information to PartDetail page * Cleanup code for serial-number placeholder in forms * Logic fix for displaying non-unity pack quantity * Fix description field on SupplierPart page * Fix duplicate table column
This commit is contained in:
		| @@ -737,10 +737,12 @@ class BuildItemList(DataExportViewMixin, BulkDeleteMixin, ListCreateAPI): | |||||||
|         'quantity', |         'quantity', | ||||||
|         'location', |         'location', | ||||||
|         'reference', |         'reference', | ||||||
|  |         'IPN', | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|     ordering_field_aliases = { |     ordering_field_aliases = { | ||||||
|         'part': 'stock_item__part__name', |         'part': 'stock_item__part__name', | ||||||
|  |         'IPN': 'stock_item__part__IPN', | ||||||
|         'sku': 'stock_item__supplier_part__SKU', |         'sku': 'stock_item__supplier_part__SKU', | ||||||
|         'location': 'stock_item__location__name', |         'location': 'stock_item__location__name', | ||||||
|         'reference': 'build_line__bom_item__reference', |         'reference': 'build_line__bom_item__reference', | ||||||
| @@ -749,6 +751,7 @@ class BuildItemList(DataExportViewMixin, BulkDeleteMixin, ListCreateAPI): | |||||||
|     search_fields = [ |     search_fields = [ | ||||||
|         'stock_item__supplier_part__SKU', |         'stock_item__supplier_part__SKU', | ||||||
|         'stock_item__part__name', |         'stock_item__part__name', | ||||||
|  |         'stock_item__part__IPN', | ||||||
|         'build_line__bom_item__reference', |         'build_line__bom_item__reference', | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|   | |||||||
| @@ -315,6 +315,10 @@ function TableAnchorValue(props: Readonly<FieldProps>) { | |||||||
| } | } | ||||||
|  |  | ||||||
| function ProgressBarValue(props: Readonly<FieldProps>) { | function ProgressBarValue(props: Readonly<FieldProps>) { | ||||||
|  |   if (props.field_data.total <= 0) { | ||||||
|  |     return <Text size="sm">{props.field_data.progress}</Text>; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <ProgressBar |     <ProgressBar | ||||||
|       value={props.field_data.progress} |       value={props.field_data.progress} | ||||||
|   | |||||||
| @@ -40,6 +40,7 @@ import { | |||||||
|   useBatchCodeGenerator, |   useBatchCodeGenerator, | ||||||
|   useSerialNumberGenerator |   useSerialNumberGenerator | ||||||
| } from '../hooks/UseGenerator'; | } from '../hooks/UseGenerator'; | ||||||
|  | import { useInstance } from '../hooks/UseInstance'; | ||||||
| import { useSerialNumberPlaceholder } from '../hooks/UsePlaceholder'; | import { useSerialNumberPlaceholder } from '../hooks/UsePlaceholder'; | ||||||
| import { apiUrl } from '../states/ApiState'; | import { apiUrl } from '../states/ApiState'; | ||||||
| import { useGlobalSettingsState } from '../states/SettingsState'; | import { useGlobalSettingsState } from '../states/SettingsState'; | ||||||
| @@ -48,65 +49,66 @@ import { useGlobalSettingsState } from '../states/SettingsState'; | |||||||
|  * Construct a set of fields for creating / editing a StockItem instance |  * Construct a set of fields for creating / editing a StockItem instance | ||||||
|  */ |  */ | ||||||
| export function useStockFields({ | export function useStockFields({ | ||||||
|   item_detail, |  | ||||||
|   part_detail, |  | ||||||
|   partId, |   partId, | ||||||
|  |   stockItem, | ||||||
|   create = false |   create = false | ||||||
| }: { | }: { | ||||||
|   partId?: number; |   partId?: number; | ||||||
|   item_detail?: any; |   stockItem?: any; | ||||||
|   part_detail?: any; |  | ||||||
|   create: boolean; |   create: boolean; | ||||||
| }): ApiFormFieldSet { | }): ApiFormFieldSet { | ||||||
|   const globalSettings = useGlobalSettingsState(); |   const globalSettings = useGlobalSettingsState(); | ||||||
|  |  | ||||||
|   const [part, setPart] = useState<number | null>(null); |   // Keep track of the "part" instance | ||||||
|  |   const [partInstance, setPartInstance] = useState<any>({}); | ||||||
|  |  | ||||||
|   const [supplierPart, setSupplierPart] = useState<number | null>(null); |   const [supplierPart, setSupplierPart] = useState<number | null>(null); | ||||||
|  |  | ||||||
|   const [batchCode, setBatchCode] = useState<string>(''); |   const [nextBatchCode, setNextBatchCode] = useState<string>(''); | ||||||
|   const [serialNumbers, setSerialNumbers] = useState<string>(''); |   const [nextSerialNumber, setNextSerialNumber] = useState<string>(''); | ||||||
|  |  | ||||||
|   const [trackable, setTrackable] = useState<boolean>(false); |  | ||||||
|  |  | ||||||
|   const batchGenerator = useBatchCodeGenerator((value: any) => { |   const batchGenerator = useBatchCodeGenerator((value: any) => { | ||||||
|     if (!batchCode) { |     if (value) { | ||||||
|       setBatchCode(value); |       setNextBatchCode(`Next batch code` + `: ${value}`); | ||||||
|  |     } else { | ||||||
|  |       setNextBatchCode(''); | ||||||
|     } |     } | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   const serialGenerator = useSerialNumberGenerator((value: any) => { |   const serialGenerator = useSerialNumberGenerator((value: any) => { | ||||||
|     if (!serialNumbers && create && trackable) { |     if (value) { | ||||||
|       setSerialNumbers(value); |       setNextSerialNumber(t`Next serial number` + `: ${value}`); | ||||||
|  |     } else { | ||||||
|  |       setNextSerialNumber(''); | ||||||
|     } |     } | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|  |   useEffect(() => { | ||||||
|  |     if (partInstance?.pk) { | ||||||
|  |       // Update the generators whenever the part ID changes | ||||||
|  |       batchGenerator.update({ part: partInstance.pk }); | ||||||
|  |       serialGenerator.update({ part: partInstance.pk }); | ||||||
|  |     } | ||||||
|  |   }, [partInstance.pk]); | ||||||
|  |  | ||||||
|   return useMemo(() => { |   return useMemo(() => { | ||||||
|     const fields: ApiFormFieldSet = { |     const fields: ApiFormFieldSet = { | ||||||
|       part: { |       part: { | ||||||
|         value: partId, |         value: partInstance.pk, | ||||||
|         disabled: !create, |         disabled: !create, | ||||||
|         filters: { |         filters: { | ||||||
|           active: create ? true : undefined |           active: create ? true : undefined | ||||||
|         }, |         }, | ||||||
|         onValueChange: (value, record) => { |         onValueChange: (value, record) => { | ||||||
|           setPart(value); |           // Update the tracked part instance | ||||||
|           // TODO: implement remaining functionality from old stock.py |           setPartInstance(record); | ||||||
|  |  | ||||||
|           setTrackable(record.trackable ?? false); |  | ||||||
|  |  | ||||||
|           batchGenerator.update({ part: value }); |  | ||||||
|           serialGenerator.update({ part: value }); |  | ||||||
|  |  | ||||||
|           if (!record.trackable) { |  | ||||||
|             setSerialNumbers(''); |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           // Clear the 'supplier_part' field if the part is changed |           // Clear the 'supplier_part' field if the part is changed | ||||||
|           setSupplierPart(null); |           setSupplierPart(null); | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|       supplier_part: { |       supplier_part: { | ||||||
|         hidden: part_detail?.purchaseable == false, |         hidden: partInstance?.purchaseable == false, | ||||||
|         value: supplierPart, |         value: supplierPart, | ||||||
|         onValueChange: (value) => { |         onValueChange: (value) => { | ||||||
|           setSupplierPart(value); |           setSupplierPart(value); | ||||||
| @@ -114,7 +116,7 @@ export function useStockFields({ | |||||||
|         filters: { |         filters: { | ||||||
|           part_detail: true, |           part_detail: true, | ||||||
|           supplier_detail: true, |           supplier_detail: true, | ||||||
|           ...(part ? { part } : {}) |           part: partId | ||||||
|         }, |         }, | ||||||
|         adjustFilters: (adjust: ApiFormAdjustFilterType) => { |         adjustFilters: (adjust: ApiFormAdjustFilterType) => { | ||||||
|           if (adjust.data.part) { |           if (adjust.data.part) { | ||||||
| @@ -148,22 +150,20 @@ export function useStockFields({ | |||||||
|       serial_numbers: { |       serial_numbers: { | ||||||
|         field_type: 'string', |         field_type: 'string', | ||||||
|         label: t`Serial Numbers`, |         label: t`Serial Numbers`, | ||||||
|  |         disabled: partInstance?.trackable == false, | ||||||
|         description: t`Enter serial numbers for new stock (or leave blank)`, |         description: t`Enter serial numbers for new stock (or leave blank)`, | ||||||
|         required: false, |         required: false, | ||||||
|         disabled: !trackable, |  | ||||||
|         hidden: !create, |         hidden: !create, | ||||||
|         value: serialNumbers, |         placeholder: nextSerialNumber | ||||||
|         onValueChange: (value) => setSerialNumbers(value) |  | ||||||
|       }, |       }, | ||||||
|       serial: { |       serial: { | ||||||
|         hidden: |         hidden: | ||||||
|           create || |           create || | ||||||
|           part_detail?.trackable == false || |           partInstance.trackable == false || | ||||||
|           (!item_detail?.quantity != undefined && item_detail?.quantity != 1) |           (!stockItem?.quantity != undefined && stockItem?.quantity != 1) | ||||||
|       }, |       }, | ||||||
|       batch: { |       batch: { | ||||||
|         value: batchCode, |         placeholder: nextBatchCode | ||||||
|         onValueChange: (value) => setBatchCode(value) |  | ||||||
|       }, |       }, | ||||||
|       status_custom_key: { |       status_custom_key: { | ||||||
|         label: t`Stock Status` |         label: t`Stock Status` | ||||||
| @@ -195,16 +195,14 @@ export function useStockFields({ | |||||||
|  |  | ||||||
|     return fields; |     return fields; | ||||||
|   }, [ |   }, [ | ||||||
|     item_detail, |     stockItem, | ||||||
|     part_detail, |     partInstance, | ||||||
|     part, |     partId, | ||||||
|     globalSettings, |     globalSettings, | ||||||
|     supplierPart, |     supplierPart, | ||||||
|     batchCode, |     nextSerialNumber, | ||||||
|     serialNumbers, |     nextBatchCode, | ||||||
|     trackable, |     create | ||||||
|     create, |  | ||||||
|     partId |  | ||||||
|   ]); |   ]); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -42,7 +42,13 @@ export function useInstance<T = any>({ | |||||||
|   const [requestStatus, setRequestStatus] = useState<number>(0); |   const [requestStatus, setRequestStatus] = useState<number>(0); | ||||||
|  |  | ||||||
|   const instanceQuery = useQuery<T>({ |   const instanceQuery = useQuery<T>({ | ||||||
|     queryKey: ['instance', endpoint, pk, params, pathParams], |     queryKey: [ | ||||||
|  |       'instance', | ||||||
|  |       endpoint, | ||||||
|  |       pk, | ||||||
|  |       JSON.stringify(params), | ||||||
|  |       JSON.stringify(pathParams) | ||||||
|  |     ], | ||||||
|     queryFn: async () => { |     queryFn: async () => { | ||||||
|       if (hasPrimaryKey) { |       if (hasPrimaryKey) { | ||||||
|         if ( |         if ( | ||||||
|   | |||||||
| @@ -96,9 +96,10 @@ export default function SupplierPartDetail() { | |||||||
|       { |       { | ||||||
|         type: 'string', |         type: 'string', | ||||||
|         name: 'part_detail.description', |         name: 'part_detail.description', | ||||||
|         label: t`Description`, |         label: t`Part Description`, | ||||||
|         copy: true, |         copy: true, | ||||||
|         icon: 'info' |         icon: 'info', | ||||||
|  |         hidden: !data.part_detail?.description | ||||||
|       }, |       }, | ||||||
|       { |       { | ||||||
|         type: 'link', |         type: 'link', | ||||||
| @@ -133,6 +134,13 @@ export default function SupplierPartDetail() { | |||||||
|         copy: true, |         copy: true, | ||||||
|         icon: 'reference' |         icon: 'reference' | ||||||
|       }, |       }, | ||||||
|  |       { | ||||||
|  |         type: 'string', | ||||||
|  |         name: 'description', | ||||||
|  |         label: t`Description`, | ||||||
|  |         copy: true, | ||||||
|  |         hidden: !data.description | ||||||
|  |       }, | ||||||
|       { |       { | ||||||
|         type: 'link', |         type: 'link', | ||||||
|         name: 'manufacturer', |         name: 'manufacturer', | ||||||
|   | |||||||
| @@ -118,6 +118,14 @@ export default function PartDetail() { | |||||||
|   const globalSettings = useGlobalSettingsState(); |   const globalSettings = useGlobalSettingsState(); | ||||||
|   const userSettings = useUserSettingsState(); |   const userSettings = useUserSettingsState(); | ||||||
|  |  | ||||||
|  |   const { instance: serials } = useInstance({ | ||||||
|  |     endpoint: ApiEndpoints.part_serial_numbers, | ||||||
|  |     pk: id, | ||||||
|  |     hasPrimaryKey: true, | ||||||
|  |     refetchOnMount: false, | ||||||
|  |     defaultValue: {} | ||||||
|  |   }); | ||||||
|  |  | ||||||
|   const { |   const { | ||||||
|     instance: part, |     instance: part, | ||||||
|     refreshInstance, |     refreshInstance, | ||||||
| @@ -132,15 +140,22 @@ export default function PartDetail() { | |||||||
|     refetchOnMount: true |     refetchOnMount: true | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   part.required = |  | ||||||
|     (part?.required_for_build_orders ?? 0) + |  | ||||||
|     (part?.required_for_sales_orders ?? 0); |  | ||||||
|  |  | ||||||
|   const detailsPanel = useMemo(() => { |   const detailsPanel = useMemo(() => { | ||||||
|     if (instanceQuery.isFetching) { |     if (instanceQuery.isFetching) { | ||||||
|       return <Skeleton />; |       return <Skeleton />; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     let data = { ...part }; | ||||||
|  |  | ||||||
|  |     data.required = | ||||||
|  |       (data?.required_for_build_orders ?? 0) + | ||||||
|  |       (data?.required_for_sales_orders ?? 0); | ||||||
|  |  | ||||||
|  |     // Provide latest serial number info | ||||||
|  |     if (!!serials.latest) { | ||||||
|  |       data.latest_serial_number = serials.latest; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // Construct the details tables |     // Construct the details tables | ||||||
|     let tl: DetailsField[] = [ |     let tl: DetailsField[] = [ | ||||||
|       { |       { | ||||||
| @@ -277,7 +292,10 @@ export default function PartDetail() { | |||||||
|         total: part.required_for_build_orders, |         total: part.required_for_build_orders, | ||||||
|         progress: part.allocated_to_build_orders, |         progress: part.allocated_to_build_orders, | ||||||
|         label: t`Allocated to Build Orders`, |         label: t`Allocated to Build Orders`, | ||||||
|         hidden: !part.component || part.required_for_build_orders <= 0 |         hidden: | ||||||
|  |           !part.component || | ||||||
|  |           (part.required_for_build_orders <= 0 && | ||||||
|  |             part.allocated_to_build_orders <= 0) | ||||||
|       }, |       }, | ||||||
|       { |       { | ||||||
|         type: 'progressbar', |         type: 'progressbar', | ||||||
| @@ -285,7 +303,10 @@ export default function PartDetail() { | |||||||
|         total: part.required_for_sales_orders, |         total: part.required_for_sales_orders, | ||||||
|         progress: part.allocated_to_sales_orders, |         progress: part.allocated_to_sales_orders, | ||||||
|         label: t`Allocated to Sales Orders`, |         label: t`Allocated to Sales Orders`, | ||||||
|         hidden: !part.salable || part.required_for_sales_orders <= 0 |         hidden: | ||||||
|  |           !part.salable || | ||||||
|  |           (part.required_for_sales_orders <= 0 && | ||||||
|  |             part.allocated_to_sales_orders <= 0) | ||||||
|       }, |       }, | ||||||
|       { |       { | ||||||
|         type: 'string', |         type: 'string', | ||||||
| @@ -349,6 +370,7 @@ export default function PartDetail() { | |||||||
|       { |       { | ||||||
|         type: 'boolean', |         type: 'boolean', | ||||||
|         name: 'salable', |         name: 'salable', | ||||||
|  |         icon: 'saleable', | ||||||
|         label: t`Saleable Part` |         label: t`Saleable Part` | ||||||
|       }, |       }, | ||||||
|       { |       { | ||||||
| @@ -434,6 +456,14 @@ export default function PartDetail() { | |||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     br.push({ | ||||||
|  |       type: 'string', | ||||||
|  |       name: 'latest_serial_number', | ||||||
|  |       label: t`Latest Serial Number`, | ||||||
|  |       hidden: !part.trackable || !data.latest_serial_number, | ||||||
|  |       icon: 'serial' | ||||||
|  |     }); | ||||||
|  |  | ||||||
|     // Add in stocktake information |     // Add in stocktake information | ||||||
|     if (id && part.last_stocktake) { |     if (id && part.last_stocktake) { | ||||||
|       br.push({ |       br.push({ | ||||||
| @@ -526,17 +556,17 @@ export default function PartDetail() { | |||||||
|             /> |             /> | ||||||
|           </Grid.Col> |           </Grid.Col> | ||||||
|           <Grid.Col span={8}> |           <Grid.Col span={8}> | ||||||
|             <DetailsTable fields={tl} item={part} /> |             <DetailsTable fields={tl} item={data} /> | ||||||
|           </Grid.Col> |           </Grid.Col> | ||||||
|         </Grid> |         </Grid> | ||||||
|         <DetailsTable fields={tr} item={part} /> |         <DetailsTable fields={tr} item={data} /> | ||||||
|         <DetailsTable fields={bl} item={part} /> |         <DetailsTable fields={bl} item={data} /> | ||||||
|         <DetailsTable fields={br} item={part} /> |         <DetailsTable fields={br} item={data} /> | ||||||
|       </ItemDetailsGrid> |       </ItemDetailsGrid> | ||||||
|     ) : ( |     ) : ( | ||||||
|       <Skeleton /> |       <Skeleton /> | ||||||
|     ); |     ); | ||||||
|   }, [globalSettings, part, instanceQuery]); |   }, [globalSettings, part, serials, instanceQuery]); | ||||||
|  |  | ||||||
|   // Part data panels (recalculate when part data changes) |   // Part data panels (recalculate when part data changes) | ||||||
|   const partPanels: PanelType[] = useMemo(() => { |   const partPanels: PanelType[] = useMemo(() => { | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| import { t } from '@lingui/macro'; | import { t } from '@lingui/macro'; | ||||||
| import { ChartTooltipProps, LineChart } from '@mantine/charts'; | import { ChartTooltipProps, LineChart } from '@mantine/charts'; | ||||||
| import { | import { | ||||||
|   Anchor, |   Alert, | ||||||
|   Center, |   Center, | ||||||
|   Divider, |   Divider, | ||||||
|   Loader, |   Loader, | ||||||
| @@ -65,23 +65,7 @@ export default function PartSchedulingDetail({ part }: { part: any }) { | |||||||
|       { |       { | ||||||
|         accessor: 'label', |         accessor: 'label', | ||||||
|         switchable: false, |         switchable: false, | ||||||
|         title: t`Order`, |         title: t`Order` | ||||||
|         render: (record: any) => { |  | ||||||
|           const url = getDetailUrl(record.model, record.model_id); |  | ||||||
|  |  | ||||||
|           if (url) { |  | ||||||
|             return ( |  | ||||||
|               <Anchor |  | ||||||
|                 href="#" |  | ||||||
|                 onClick={(event: any) => navigateToLink(url, navigate, event)} |  | ||||||
|               > |  | ||||||
|                 {record.label} |  | ||||||
|               </Anchor> |  | ||||||
|             ); |  | ||||||
|           } else { |  | ||||||
|             return record.label; |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       }, |       }, | ||||||
|       DescriptionColumn({ |       DescriptionColumn({ | ||||||
|         accessor: 'title', |         accessor: 'title', | ||||||
| @@ -245,15 +229,32 @@ export default function PartSchedulingDetail({ part }: { part: any }) { | |||||||
|     return [min_date.valueOf(), max_date.valueOf()]; |     return [min_date.valueOf(), max_date.valueOf()]; | ||||||
|   }, [chartData]); |   }, [chartData]); | ||||||
|  |  | ||||||
|  |   const hasSchedulingInfo: boolean = useMemo( | ||||||
|  |     () => table.recordCount > 0, | ||||||
|  |     [table.recordCount] | ||||||
|  |   ); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|  |       {!table.isLoading && !hasSchedulingInfo && ( | ||||||
|  |         <Alert color="blue" title={t`No information available`}> | ||||||
|  |           <Text>{t`There is no scheduling information available for the selected part`}</Text> | ||||||
|  |         </Alert> | ||||||
|  |       )} | ||||||
|       <SimpleGrid cols={2}> |       <SimpleGrid cols={2}> | ||||||
|         <InvenTreeTable |         <InvenTreeTable | ||||||
|           url={apiUrl(ApiEndpoints.part_scheduling, part.pk)} |           url={apiUrl(ApiEndpoints.part_scheduling, part.pk)} | ||||||
|           tableState={table} |           tableState={table} | ||||||
|           columns={tableColumns} |           columns={tableColumns} | ||||||
|           props={{ |           props={{ | ||||||
|             enableSearch: false |             enableSearch: false, | ||||||
|  |             onRowClick: (record: any, index: number, event: any) => { | ||||||
|  |               const url = getDetailUrl(record.model, record.model_id); | ||||||
|  |  | ||||||
|  |               if (url) { | ||||||
|  |                 navigateToLink(url, navigate, event); | ||||||
|  |               } | ||||||
|  |             } | ||||||
|           }} |           }} | ||||||
|         /> |         /> | ||||||
|         {table.isLoading ? ( |         {table.isLoading ? ( | ||||||
|   | |||||||
| @@ -525,7 +525,7 @@ export default function StockDetail() { | |||||||
|  |  | ||||||
|   const editStockItemFields = useStockFields({ |   const editStockItemFields = useStockFields({ | ||||||
|     create: false, |     create: false, | ||||||
|     part_detail: stockitem.part_detail |     partId: stockitem.part | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   const editStockItem = useEditApiFormModal({ |   const editStockItem = useEditApiFormModal({ | ||||||
|   | |||||||
| @@ -97,6 +97,14 @@ export default function BuildAllocatedStockTable({ | |||||||
|         switchable: false, |         switchable: false, | ||||||
|         render: (record: any) => PartColumn({ part: record.part_detail }) |         render: (record: any) => PartColumn({ part: record.part_detail }) | ||||||
|       }, |       }, | ||||||
|  |       { | ||||||
|  |         accessor: 'part_detail.IPN', | ||||||
|  |         ordering: 'IPN', | ||||||
|  |         hidden: !showPartInfo, | ||||||
|  |         title: t`IPN`, | ||||||
|  |         sortable: true, | ||||||
|  |         switchable: true | ||||||
|  |       }, | ||||||
|       { |       { | ||||||
|         hidden: !showPartInfo, |         hidden: !showPartInfo, | ||||||
|         accessor: 'bom_reference', |         accessor: 'bom_reference', | ||||||
|   | |||||||
| @@ -218,8 +218,8 @@ export default function BuildOutputTable({ | |||||||
|  |  | ||||||
|   const editStockItemFields = useStockFields({ |   const editStockItemFields = useStockFields({ | ||||||
|     create: false, |     create: false, | ||||||
|     item_detail: selectedOutputs[0], |     partId: partId, | ||||||
|     part_detail: selectedOutputs[0]?.part_detail |     stockItem: selectedOutputs[0] | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   const editBuildOutput = useEditApiFormModal({ |   const editBuildOutput = useEditApiFormModal({ | ||||||
|   | |||||||
| @@ -95,10 +95,6 @@ export default function PartPurchaseOrdersTable({ | |||||||
|           ); |           ); | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|       DateColumn({ |  | ||||||
|         accessor: 'order_detail.complete_date', |  | ||||||
|         title: t`Order Completed Date` |  | ||||||
|       }), |  | ||||||
|       DateColumn({ |       DateColumn({ | ||||||
|         accessor: 'target_date', |         accessor: 'target_date', | ||||||
|         title: t`Target Date` |         title: t`Target Date` | ||||||
|   | |||||||
| @@ -149,7 +149,10 @@ export function PurchaseOrderLineItemTable({ | |||||||
|           let part = record?.part_detail ?? supplier_part?.part_detail ?? {}; |           let part = record?.part_detail ?? supplier_part?.part_detail ?? {}; | ||||||
|           let extra = []; |           let extra = []; | ||||||
|  |  | ||||||
|           if (supplier_part.pack_quantity_native != 1) { |           if ( | ||||||
|  |             supplier_part?.pack_quantity_native != undefined && | ||||||
|  |             supplier_part.pack_quantity_native != 1 | ||||||
|  |           ) { | ||||||
|             let total = record.quantity * supplier_part.pack_quantity_native; |             let total = record.quantity * supplier_part.pack_quantity_native; | ||||||
|  |  | ||||||
|             extra.push( |             extra.push( | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user