mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 19:46:46 +00:00
[PUI] Extra line table (#7889)
* Add generic "extra line item" table * Add "role" as parameter * Add placeholder actions * Fix price rendering * Add forms to create / edit / delete extra line items * Tweak type annotation
This commit is contained in:
parent
09c4710107
commit
8b44dfbc4e
@ -132,6 +132,7 @@ export enum ApiEndpoints {
|
|||||||
purchase_order_cancel = 'order/po/:id/cancel/',
|
purchase_order_cancel = 'order/po/:id/cancel/',
|
||||||
purchase_order_complete = 'order/po/:id/complete/',
|
purchase_order_complete = 'order/po/:id/complete/',
|
||||||
purchase_order_line_list = 'order/po-line/',
|
purchase_order_line_list = 'order/po-line/',
|
||||||
|
purchase_order_extra_line_list = 'order/po-extra-line/',
|
||||||
purchase_order_receive = 'order/po/:id/receive/',
|
purchase_order_receive = 'order/po/:id/receive/',
|
||||||
|
|
||||||
sales_order_list = 'order/so/',
|
sales_order_list = 'order/so/',
|
||||||
@ -141,6 +142,7 @@ export enum ApiEndpoints {
|
|||||||
sales_order_ship = 'order/so/:id/ship/',
|
sales_order_ship = 'order/so/:id/ship/',
|
||||||
sales_order_complete = 'order/so/:id/complete/',
|
sales_order_complete = 'order/so/:id/complete/',
|
||||||
sales_order_line_list = 'order/so-line/',
|
sales_order_line_list = 'order/so-line/',
|
||||||
|
sales_order_extra_line_list = 'order/so-extra-line/',
|
||||||
sales_order_allocation_list = 'order/so-allocation/',
|
sales_order_allocation_list = 'order/so-allocation/',
|
||||||
sales_order_shipment_list = 'order/so/shipment/',
|
sales_order_shipment_list = 'order/so/shipment/',
|
||||||
|
|
||||||
@ -150,6 +152,7 @@ export enum ApiEndpoints {
|
|||||||
return_order_cancel = 'order/ro/:id/cancel/',
|
return_order_cancel = 'order/ro/:id/cancel/',
|
||||||
return_order_complete = 'order/ro/:id/complete/',
|
return_order_complete = 'order/ro/:id/complete/',
|
||||||
return_order_line_list = 'order/ro-line/',
|
return_order_line_list = 'order/ro-line/',
|
||||||
|
return_order_extra_line_list = 'order/ro-extra-line/',
|
||||||
|
|
||||||
// Template API endpoints
|
// Template API endpoints
|
||||||
label_list = 'label/template/',
|
label_list = 'label/template/',
|
||||||
|
@ -19,3 +19,18 @@ export function customUnitsFields(): ApiFormFieldSet {
|
|||||||
symbol: {}
|
symbol: {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function extraLineItemFields(): ApiFormFieldSet {
|
||||||
|
return {
|
||||||
|
order: {
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
reference: {},
|
||||||
|
description: {},
|
||||||
|
quantity: {},
|
||||||
|
price: {},
|
||||||
|
price_currency: {},
|
||||||
|
notes: {},
|
||||||
|
link: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Grid, Skeleton, Stack } from '@mantine/core';
|
import { Accordion, Grid, Skeleton, Stack } from '@mantine/core';
|
||||||
import {
|
import {
|
||||||
IconDots,
|
IconDots,
|
||||||
IconInfoCircle,
|
IconInfoCircle,
|
||||||
@ -29,6 +29,7 @@ import {
|
|||||||
UnlinkBarcodeAction,
|
UnlinkBarcodeAction,
|
||||||
ViewBarcodeAction
|
ViewBarcodeAction
|
||||||
} from '../../components/items/ActionDropdown';
|
} from '../../components/items/ActionDropdown';
|
||||||
|
import { StylishText } from '../../components/items/StylishText';
|
||||||
import InstanceDetail from '../../components/nav/InstanceDetail';
|
import InstanceDetail from '../../components/nav/InstanceDetail';
|
||||||
import { PageDetail } from '../../components/nav/PageDetail';
|
import { PageDetail } from '../../components/nav/PageDetail';
|
||||||
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
||||||
@ -47,6 +48,7 @@ import useStatusCodes from '../../hooks/UseStatusCodes';
|
|||||||
import { apiUrl } from '../../states/ApiState';
|
import { apiUrl } from '../../states/ApiState';
|
||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
import { AttachmentTable } from '../../tables/general/AttachmentTable';
|
import { AttachmentTable } from '../../tables/general/AttachmentTable';
|
||||||
|
import ExtraLineItemTable from '../../tables/general/ExtraLineItemTable';
|
||||||
import { PurchaseOrderLineItemTable } from '../../tables/purchasing/PurchaseOrderLineItemTable';
|
import { PurchaseOrderLineItemTable } from '../../tables/purchasing/PurchaseOrderLineItemTable';
|
||||||
import { StockItemTable } from '../../tables/stock/StockItemTable';
|
import { StockItemTable } from '../../tables/stock/StockItemTable';
|
||||||
|
|
||||||
@ -245,11 +247,35 @@ export default function PurchaseOrderDetail() {
|
|||||||
label: t`Line Items`,
|
label: t`Line Items`,
|
||||||
icon: <IconList />,
|
icon: <IconList />,
|
||||||
content: (
|
content: (
|
||||||
<PurchaseOrderLineItemTable
|
<Accordion
|
||||||
order={order}
|
multiple={true}
|
||||||
orderId={Number(id)}
|
defaultValue={['line-items', 'extra-items']}
|
||||||
supplierId={Number(order.supplier)}
|
>
|
||||||
/>
|
<Accordion.Item value="line-items" key="lineitems">
|
||||||
|
<Accordion.Control>
|
||||||
|
<StylishText size="lg">{t`Line Items`}</StylishText>
|
||||||
|
</Accordion.Control>
|
||||||
|
<Accordion.Panel>
|
||||||
|
<PurchaseOrderLineItemTable
|
||||||
|
order={order}
|
||||||
|
orderId={Number(id)}
|
||||||
|
supplierId={Number(order.supplier)}
|
||||||
|
/>
|
||||||
|
</Accordion.Panel>
|
||||||
|
</Accordion.Item>
|
||||||
|
<Accordion.Item value="extra-items" key="extraitems">
|
||||||
|
<Accordion.Control>
|
||||||
|
<StylishText size="lg">{t`Extra Line Items`}</StylishText>
|
||||||
|
</Accordion.Control>
|
||||||
|
<Accordion.Panel>
|
||||||
|
<ExtraLineItemTable
|
||||||
|
endpoint={ApiEndpoints.purchase_order_extra_line_list}
|
||||||
|
orderId={order.pk}
|
||||||
|
role={UserRoles.purchase_order}
|
||||||
|
/>
|
||||||
|
</Accordion.Panel>
|
||||||
|
</Accordion.Item>
|
||||||
|
</Accordion>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Grid, Skeleton, Stack } from '@mantine/core';
|
import { Accordion, Grid, Skeleton, Stack } from '@mantine/core';
|
||||||
import {
|
import {
|
||||||
IconDots,
|
IconDots,
|
||||||
IconInfoCircle,
|
IconInfoCircle,
|
||||||
@ -28,6 +28,7 @@ import {
|
|||||||
UnlinkBarcodeAction,
|
UnlinkBarcodeAction,
|
||||||
ViewBarcodeAction
|
ViewBarcodeAction
|
||||||
} from '../../components/items/ActionDropdown';
|
} from '../../components/items/ActionDropdown';
|
||||||
|
import { StylishText } from '../../components/items/StylishText';
|
||||||
import InstanceDetail from '../../components/nav/InstanceDetail';
|
import InstanceDetail from '../../components/nav/InstanceDetail';
|
||||||
import { PageDetail } from '../../components/nav/PageDetail';
|
import { PageDetail } from '../../components/nav/PageDetail';
|
||||||
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
||||||
@ -46,6 +47,7 @@ import useStatusCodes from '../../hooks/UseStatusCodes';
|
|||||||
import { apiUrl } from '../../states/ApiState';
|
import { apiUrl } from '../../states/ApiState';
|
||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
import { AttachmentTable } from '../../tables/general/AttachmentTable';
|
import { AttachmentTable } from '../../tables/general/AttachmentTable';
|
||||||
|
import ExtraLineItemTable from '../../tables/general/ExtraLineItemTable';
|
||||||
import ReturnOrderLineItemTable from '../../tables/sales/ReturnOrderLineItemTable';
|
import ReturnOrderLineItemTable from '../../tables/sales/ReturnOrderLineItemTable';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -223,10 +225,34 @@ export default function ReturnOrderDetail() {
|
|||||||
label: t`Line Items`,
|
label: t`Line Items`,
|
||||||
icon: <IconList />,
|
icon: <IconList />,
|
||||||
content: (
|
content: (
|
||||||
<ReturnOrderLineItemTable
|
<Accordion
|
||||||
orderId={order.pk}
|
multiple={true}
|
||||||
customerId={order.customer}
|
defaultValue={['line-items', 'extra-items']}
|
||||||
/>
|
>
|
||||||
|
<Accordion.Item value="line-items" key="lineitems">
|
||||||
|
<Accordion.Control>
|
||||||
|
<StylishText size="lg">{t`Line Items`}</StylishText>
|
||||||
|
</Accordion.Control>
|
||||||
|
<Accordion.Panel>
|
||||||
|
<ReturnOrderLineItemTable
|
||||||
|
orderId={order.pk}
|
||||||
|
customerId={order.customer}
|
||||||
|
/>
|
||||||
|
</Accordion.Panel>
|
||||||
|
</Accordion.Item>
|
||||||
|
<Accordion.Item value="extra-items" key="extraitems">
|
||||||
|
<Accordion.Control>
|
||||||
|
<StylishText size="lg">{t`Extra Line Items`}</StylishText>
|
||||||
|
</Accordion.Control>
|
||||||
|
<Accordion.Panel>
|
||||||
|
<ExtraLineItemTable
|
||||||
|
endpoint={ApiEndpoints.return_order_extra_line_list}
|
||||||
|
orderId={order.pk}
|
||||||
|
role={UserRoles.return_order}
|
||||||
|
/>
|
||||||
|
</Accordion.Panel>
|
||||||
|
</Accordion.Item>
|
||||||
|
</Accordion>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Grid, Skeleton, Stack } from '@mantine/core';
|
import { Accordion, Grid, Skeleton, Stack } from '@mantine/core';
|
||||||
import {
|
import {
|
||||||
IconBook,
|
|
||||||
IconBookmark,
|
IconBookmark,
|
||||||
IconDots,
|
IconDots,
|
||||||
IconInfoCircle,
|
IconInfoCircle,
|
||||||
@ -32,6 +31,7 @@ import {
|
|||||||
UnlinkBarcodeAction,
|
UnlinkBarcodeAction,
|
||||||
ViewBarcodeAction
|
ViewBarcodeAction
|
||||||
} from '../../components/items/ActionDropdown';
|
} from '../../components/items/ActionDropdown';
|
||||||
|
import { StylishText } from '../../components/items/StylishText';
|
||||||
import InstanceDetail from '../../components/nav/InstanceDetail';
|
import InstanceDetail from '../../components/nav/InstanceDetail';
|
||||||
import { PageDetail } from '../../components/nav/PageDetail';
|
import { PageDetail } from '../../components/nav/PageDetail';
|
||||||
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
||||||
@ -51,6 +51,7 @@ import { apiUrl } from '../../states/ApiState';
|
|||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
import { BuildOrderTable } from '../../tables/build/BuildOrderTable';
|
import { BuildOrderTable } from '../../tables/build/BuildOrderTable';
|
||||||
import { AttachmentTable } from '../../tables/general/AttachmentTable';
|
import { AttachmentTable } from '../../tables/general/AttachmentTable';
|
||||||
|
import ExtraLineItemTable from '../../tables/general/ExtraLineItemTable';
|
||||||
import SalesOrderAllocationTable from '../../tables/sales/SalesOrderAllocationTable';
|
import SalesOrderAllocationTable from '../../tables/sales/SalesOrderAllocationTable';
|
||||||
import SalesOrderLineItemTable from '../../tables/sales/SalesOrderLineItemTable';
|
import SalesOrderLineItemTable from '../../tables/sales/SalesOrderLineItemTable';
|
||||||
import SalesOrderShipmentTable from '../../tables/sales/SalesOrderShipmentTable';
|
import SalesOrderShipmentTable from '../../tables/sales/SalesOrderShipmentTable';
|
||||||
@ -259,14 +260,38 @@ export default function SalesOrderDetail() {
|
|||||||
label: t`Line Items`,
|
label: t`Line Items`,
|
||||||
icon: <IconList />,
|
icon: <IconList />,
|
||||||
content: (
|
content: (
|
||||||
<SalesOrderLineItemTable
|
<Accordion
|
||||||
orderId={order.pk}
|
multiple={true}
|
||||||
customerId={order.customer}
|
defaultValue={['line-items', 'extra-items']}
|
||||||
editable={
|
>
|
||||||
order.status != soStatus.COMPLETE &&
|
<Accordion.Item value="line-items" key="lineitems">
|
||||||
order.status != soStatus.CANCELLED
|
<Accordion.Control>
|
||||||
}
|
<StylishText size="lg">{t`Line Items`}</StylishText>
|
||||||
/>
|
</Accordion.Control>
|
||||||
|
<Accordion.Panel>
|
||||||
|
<SalesOrderLineItemTable
|
||||||
|
orderId={order.pk}
|
||||||
|
customerId={order.customer}
|
||||||
|
editable={
|
||||||
|
order.status != soStatus.COMPLETE &&
|
||||||
|
order.status != soStatus.CANCELLED
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Accordion.Panel>
|
||||||
|
</Accordion.Item>
|
||||||
|
<Accordion.Item value="extra-items" key="extraitems">
|
||||||
|
<Accordion.Control>
|
||||||
|
<StylishText size="lg">{t`Extra Line Items`}</StylishText>
|
||||||
|
</Accordion.Control>
|
||||||
|
<Accordion.Panel>
|
||||||
|
<ExtraLineItemTable
|
||||||
|
endpoint={ApiEndpoints.sales_order_extra_line_list}
|
||||||
|
orderId={order.pk}
|
||||||
|
role={UserRoles.sales_order}
|
||||||
|
/>
|
||||||
|
</Accordion.Panel>
|
||||||
|
</Accordion.Item>
|
||||||
|
</Accordion>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
169
src/frontend/src/tables/general/ExtraLineItemTable.tsx
Normal file
169
src/frontend/src/tables/general/ExtraLineItemTable.tsx
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import { AddItemButton } from '../../components/buttons/AddItemButton';
|
||||||
|
import { ApiFormFieldSet } from '../../components/forms/fields/ApiFormField';
|
||||||
|
import { formatCurrency } from '../../defaults/formatters';
|
||||||
|
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||||
|
import { ModelType } from '../../enums/ModelType';
|
||||||
|
import { UserRoles } from '../../enums/Roles';
|
||||||
|
import { extraLineItemFields } from '../../forms/CommonForms';
|
||||||
|
import {
|
||||||
|
useCreateApiFormModal,
|
||||||
|
useDeleteApiFormModal,
|
||||||
|
useEditApiFormModal
|
||||||
|
} from '../../hooks/UseForm';
|
||||||
|
import { useTable } from '../../hooks/UseTable';
|
||||||
|
import { apiUrl } from '../../states/ApiState';
|
||||||
|
import { useUserState } from '../../states/UserState';
|
||||||
|
import { TableColumn } from '../Column';
|
||||||
|
import { LinkColumn, NoteColumn } from '../ColumnRenderers';
|
||||||
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
import {
|
||||||
|
RowDeleteAction,
|
||||||
|
RowDuplicateAction,
|
||||||
|
RowEditAction
|
||||||
|
} from '../RowActions';
|
||||||
|
|
||||||
|
export default function ExtraLineItemTable({
|
||||||
|
endpoint,
|
||||||
|
orderId,
|
||||||
|
role
|
||||||
|
}: {
|
||||||
|
endpoint: ApiEndpoints;
|
||||||
|
orderId: number;
|
||||||
|
role: UserRoles;
|
||||||
|
}) {
|
||||||
|
const table = useTable('extra-line-item');
|
||||||
|
const user = useUserState();
|
||||||
|
|
||||||
|
const tableColumns: TableColumn[] = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
accessor: 'reference',
|
||||||
|
switchable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'description'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'quantity',
|
||||||
|
switchable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'price',
|
||||||
|
title: t`Unit Price`,
|
||||||
|
render: (record: any) =>
|
||||||
|
formatCurrency(record.price, {
|
||||||
|
currency: record.price_currency
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'total_price',
|
||||||
|
title: t`Total Price`,
|
||||||
|
render: (record: any) =>
|
||||||
|
formatCurrency(record.price, {
|
||||||
|
currency: record.price_currency,
|
||||||
|
multiplier: record.quantity
|
||||||
|
})
|
||||||
|
},
|
||||||
|
NoteColumn({
|
||||||
|
accessor: 'notes'
|
||||||
|
}),
|
||||||
|
LinkColumn({
|
||||||
|
accessor: 'link'
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const [initialData, setInitialData] = useState<any>({});
|
||||||
|
|
||||||
|
const [selectedLine, setSelectedLine] = useState<number>(0);
|
||||||
|
|
||||||
|
const newLineItem = useCreateApiFormModal({
|
||||||
|
url: endpoint,
|
||||||
|
title: t`Add Line Item`,
|
||||||
|
fields: extraLineItemFields(),
|
||||||
|
initialData: initialData,
|
||||||
|
table: table
|
||||||
|
});
|
||||||
|
|
||||||
|
const editLineItem = useEditApiFormModal({
|
||||||
|
url: endpoint,
|
||||||
|
pk: selectedLine,
|
||||||
|
title: t`Edit Line Item`,
|
||||||
|
fields: extraLineItemFields(),
|
||||||
|
table: table
|
||||||
|
});
|
||||||
|
|
||||||
|
const deleteLineItem = useDeleteApiFormModal({
|
||||||
|
url: endpoint,
|
||||||
|
pk: selectedLine,
|
||||||
|
title: t`Delete Line Item`,
|
||||||
|
table: table
|
||||||
|
});
|
||||||
|
|
||||||
|
const rowActions = useCallback(
|
||||||
|
(record: any) => {
|
||||||
|
return [
|
||||||
|
RowEditAction({
|
||||||
|
hidden: !user.hasChangeRole(role),
|
||||||
|
onClick: () => {
|
||||||
|
setSelectedLine(record.pk);
|
||||||
|
editLineItem.open();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
RowDuplicateAction({
|
||||||
|
hidden: !user.hasAddRole(role),
|
||||||
|
onClick: () => {
|
||||||
|
setInitialData({ ...record });
|
||||||
|
newLineItem.open();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
RowDeleteAction({
|
||||||
|
hidden: !user.hasDeleteRole(role),
|
||||||
|
onClick: () => {
|
||||||
|
setSelectedLine(record.pk);
|
||||||
|
deleteLineItem.open();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
];
|
||||||
|
},
|
||||||
|
[user, role]
|
||||||
|
);
|
||||||
|
|
||||||
|
const tableActions = useMemo(() => {
|
||||||
|
return [
|
||||||
|
<AddItemButton
|
||||||
|
tooltip={t`Add Extra Line Item`}
|
||||||
|
hidden={!user.hasAddRole(role)}
|
||||||
|
onClick={() => {
|
||||||
|
setInitialData({
|
||||||
|
order: orderId
|
||||||
|
});
|
||||||
|
newLineItem.open();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
];
|
||||||
|
}, [user, role]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{newLineItem.modal}
|
||||||
|
{editLineItem.modal}
|
||||||
|
{deleteLineItem.modal}
|
||||||
|
<InvenTreeTable
|
||||||
|
tableState={table}
|
||||||
|
url={apiUrl(endpoint)}
|
||||||
|
columns={tableColumns}
|
||||||
|
props={{
|
||||||
|
params: {
|
||||||
|
order: orderId
|
||||||
|
},
|
||||||
|
rowActions: rowActions,
|
||||||
|
tableActions: tableActions
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -241,7 +241,7 @@ export function PurchaseOrderLineItemTable({
|
|||||||
supplierId: supplierId
|
supplierId: supplierId
|
||||||
});
|
});
|
||||||
|
|
||||||
const [initialData, setInitialData] = useState({});
|
const [initialData, setInitialData] = useState<any>({});
|
||||||
|
|
||||||
const newLine = useCreateApiFormModal({
|
const newLine = useCreateApiFormModal({
|
||||||
url: ApiEndpoints.purchase_order_line_list,
|
url: ApiEndpoints.purchase_order_line_list,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user