mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 11:36:44 +00:00
[PUI] BOM table updates (#7561)
* Update BOM table for PUI - Display "validated" column - Allow ordering by "validated" column * Update table rowActions * Add row action to validate BOM line * Enable bulk deletion of BOM items
This commit is contained in:
parent
13dbfd0b14
commit
58f12f5ce5
@ -1872,6 +1872,7 @@ class BomList(BomMixin, ListCreateDestroyAPIView):
|
|||||||
'inherited',
|
'inherited',
|
||||||
'optional',
|
'optional',
|
||||||
'consumable',
|
'consumable',
|
||||||
|
'validated',
|
||||||
'pricing_min',
|
'pricing_min',
|
||||||
'pricing_max',
|
'pricing_max',
|
||||||
'pricing_min_total',
|
'pricing_min_total',
|
||||||
|
@ -1458,7 +1458,7 @@ class BomItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
|||||||
- This saves a bunch of database requests
|
- This saves a bunch of database requests
|
||||||
"""
|
"""
|
||||||
part_detail = kwargs.pop('part_detail', False)
|
part_detail = kwargs.pop('part_detail', False)
|
||||||
sub_part_detail = kwargs.pop('sub_part_detail', False)
|
sub_part_detail = kwargs.pop('sub_part_detail', True)
|
||||||
pricing = kwargs.pop('pricing', True)
|
pricing = kwargs.pop('pricing', True)
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
@ -60,6 +60,7 @@ export enum ApiEndpoints {
|
|||||||
build_line_list = 'build/line/',
|
build_line_list = 'build/line/',
|
||||||
|
|
||||||
bom_list = 'bom/',
|
bom_list = 'bom/',
|
||||||
|
bom_item_validate = 'bom/:id/validate/',
|
||||||
|
|
||||||
// Part API endpoints
|
// Part API endpoints
|
||||||
part_list = 'part/',
|
part_list = 'part/',
|
||||||
|
@ -478,8 +478,8 @@ export function InvenTreeTable<T = any>({
|
|||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
// Callback function to delete the selected records in the table
|
// Callback function to delete the selected records in the table
|
||||||
const deleteSelectedRecords = useCallback(() => {
|
const deleteSelectedRecords = useCallback((ids: number[]) => {
|
||||||
if (tableState.selectedRecords.length == 0) {
|
if (ids.length == 0) {
|
||||||
// Ignore if no records are selected
|
// Ignore if no records are selected
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -502,15 +502,10 @@ export function InvenTreeTable<T = any>({
|
|||||||
color: 'red'
|
color: 'red'
|
||||||
},
|
},
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
// Delete the selected records
|
|
||||||
let selection = tableState.selectedRecords.map(
|
|
||||||
(record) => record.pk ?? record.id
|
|
||||||
);
|
|
||||||
|
|
||||||
api
|
api
|
||||||
.delete(url, {
|
.delete(url, {
|
||||||
data: {
|
data: {
|
||||||
items: selection
|
items: ids
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then((_response) => {
|
.then((_response) => {
|
||||||
@ -535,7 +530,7 @@ export function InvenTreeTable<T = any>({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [tableState.selectedRecords]);
|
}, []);
|
||||||
|
|
||||||
// Callback when a row is clicked
|
// Callback when a row is clicked
|
||||||
const handleRowClick = useCallback(
|
const handleRowClick = useCallback(
|
||||||
@ -609,7 +604,7 @@ export function InvenTreeTable<T = any>({
|
|||||||
icon={<IconTrash />}
|
icon={<IconTrash />}
|
||||||
color="red"
|
color="red"
|
||||||
tooltip={t`Delete selected records`}
|
tooltip={t`Delete selected records`}
|
||||||
onClick={deleteSelectedRecords}
|
onClick={() => deleteSelectedRecords(tableState.selectedIds)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{tableProps.tableActions?.map((group, idx) => (
|
{tableProps.tableActions?.map((group, idx) => (
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Group, Text } from '@mantine/core';
|
import { Group, Text } from '@mantine/core';
|
||||||
|
import { showNotification } from '@mantine/notifications';
|
||||||
import {
|
import {
|
||||||
IconArrowRight,
|
IconArrowRight,
|
||||||
IconCircleCheck,
|
IconCircleCheck,
|
||||||
@ -8,6 +9,7 @@ import {
|
|||||||
import { ReactNode, useCallback, useMemo, useState } from 'react';
|
import { ReactNode, useCallback, useMemo, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { api } from '../../App';
|
||||||
import { AddItemButton } from '../../components/buttons/AddItemButton';
|
import { AddItemButton } from '../../components/buttons/AddItemButton';
|
||||||
import { YesNoButton } from '../../components/buttons/YesNoButton';
|
import { YesNoButton } from '../../components/buttons/YesNoButton';
|
||||||
import { Thumbnail } from '../../components/images/Thumbnail';
|
import { Thumbnail } from '../../components/images/Thumbnail';
|
||||||
@ -33,7 +35,7 @@ import {
|
|||||||
} from '../ColumnRenderers';
|
} from '../ColumnRenderers';
|
||||||
import { TableFilter } from '../Filter';
|
import { TableFilter } from '../Filter';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
import { RowAction, RowDeleteAction, RowEditAction } from '../RowActions';
|
import { RowDeleteAction, RowEditAction } from '../RowActions';
|
||||||
import { TableHoverCard } from '../TableHoverCard';
|
import { TableHoverCard } from '../TableHoverCard';
|
||||||
|
|
||||||
// Calculate the total stock quantity available for a given BomItem
|
// Calculate the total stock quantity available for a given BomItem
|
||||||
@ -146,6 +148,9 @@ export function BomTable({
|
|||||||
// TODO: Custom renderer for this column
|
// TODO: Custom renderer for this column
|
||||||
// TODO: See bom.js for existing implementation
|
// TODO: See bom.js for existing implementation
|
||||||
}),
|
}),
|
||||||
|
BooleanColumn({
|
||||||
|
accessor: 'validated'
|
||||||
|
}),
|
||||||
{
|
{
|
||||||
accessor: 'price_range',
|
accessor: 'price_range',
|
||||||
title: t`Unit Price`,
|
title: t`Unit Price`,
|
||||||
@ -339,6 +344,29 @@ export function BomTable({
|
|||||||
table: table
|
table: table
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const validateBomItem = useCallback((record: any) => {
|
||||||
|
const url = apiUrl(ApiEndpoints.bom_item_validate, record.pk);
|
||||||
|
|
||||||
|
api
|
||||||
|
.patch(url, { valid: true })
|
||||||
|
.then((_response) => {
|
||||||
|
showNotification({
|
||||||
|
title: t`Success`,
|
||||||
|
message: t`BOM item validated`,
|
||||||
|
color: 'green'
|
||||||
|
});
|
||||||
|
|
||||||
|
table.refreshTable();
|
||||||
|
})
|
||||||
|
.catch((_error) => {
|
||||||
|
showNotification({
|
||||||
|
title: t`Error`,
|
||||||
|
message: t`Failed to validate BOM item`,
|
||||||
|
color: 'red'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const rowActions = useCallback(
|
const rowActions = useCallback(
|
||||||
(record: any) => {
|
(record: any) => {
|
||||||
// If this BOM item is defined for a *different* parent, then it cannot be edited
|
// If this BOM item is defined for a *different* parent, then it cannot be edited
|
||||||
@ -352,37 +380,27 @@ export function BomTable({
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
let actions: RowAction[] = [];
|
return [
|
||||||
|
{
|
||||||
// TODO: Enable BomItem validation
|
title: t`Validate BOM Line`,
|
||||||
actions.push({
|
color: 'green',
|
||||||
title: t`Validate BOM line`,
|
hidden: record.validated || !user.hasChangeRole(UserRoles.part),
|
||||||
color: 'green',
|
icon: <IconCircleCheck />,
|
||||||
hidden: record.validated || !user.hasChangeRole(UserRoles.part),
|
onClick: () => validateBomItem(record)
|
||||||
icon: <IconCircleCheck />
|
},
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: Enable editing of substitutes
|
|
||||||
actions.push({
|
|
||||||
title: t`Edit Substitutes`,
|
|
||||||
color: 'blue',
|
|
||||||
hidden: !user.hasChangeRole(UserRoles.part),
|
|
||||||
icon: <IconSwitch3 />
|
|
||||||
});
|
|
||||||
|
|
||||||
// Action on edit
|
|
||||||
actions.push(
|
|
||||||
RowEditAction({
|
RowEditAction({
|
||||||
hidden: !user.hasChangeRole(UserRoles.part),
|
hidden: !user.hasChangeRole(UserRoles.part),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
setSelectedBomItem(record.pk);
|
setSelectedBomItem(record.pk);
|
||||||
editBomItem.open();
|
editBomItem.open();
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
);
|
{
|
||||||
|
title: t`Edit Substitutes`,
|
||||||
// Action on delete
|
color: 'blue',
|
||||||
actions.push(
|
hidden: !user.hasChangeRole(UserRoles.part),
|
||||||
|
icon: <IconSwitch3 />
|
||||||
|
},
|
||||||
RowDeleteAction({
|
RowDeleteAction({
|
||||||
hidden: !user.hasDeleteRole(UserRoles.part),
|
hidden: !user.hasDeleteRole(UserRoles.part),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
@ -390,9 +408,7 @@ export function BomTable({
|
|||||||
deleteBomItem.open();
|
deleteBomItem.open();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
];
|
||||||
|
|
||||||
return actions;
|
|
||||||
},
|
},
|
||||||
[partId, user]
|
[partId, user]
|
||||||
);
|
);
|
||||||
@ -427,7 +443,9 @@ export function BomTable({
|
|||||||
tableFilters: tableFilters,
|
tableFilters: tableFilters,
|
||||||
modelType: ModelType.part,
|
modelType: ModelType.part,
|
||||||
modelField: 'sub_part',
|
modelField: 'sub_part',
|
||||||
rowActions: rowActions
|
rowActions: rowActions,
|
||||||
|
enableSelection: true,
|
||||||
|
enableBulkDelete: true
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user