mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +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_complete = 'order/po/:id/complete/',
|
||||
purchase_order_line_list = 'order/po-line/',
|
||||
purchase_order_extra_line_list = 'order/po-extra-line/',
|
||||
purchase_order_receive = 'order/po/:id/receive/',
|
||||
|
||||
sales_order_list = 'order/so/',
|
||||
@ -141,6 +142,7 @@ export enum ApiEndpoints {
|
||||
sales_order_ship = 'order/so/:id/ship/',
|
||||
sales_order_complete = 'order/so/:id/complete/',
|
||||
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_shipment_list = 'order/so/shipment/',
|
||||
|
||||
@ -150,6 +152,7 @@ export enum ApiEndpoints {
|
||||
return_order_cancel = 'order/ro/:id/cancel/',
|
||||
return_order_complete = 'order/ro/:id/complete/',
|
||||
return_order_line_list = 'order/ro-line/',
|
||||
return_order_extra_line_list = 'order/ro-extra-line/',
|
||||
|
||||
// Template API endpoints
|
||||
label_list = 'label/template/',
|
||||
|
@ -19,3 +19,18 @@ export function customUnitsFields(): ApiFormFieldSet {
|
||||
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 { Grid, Skeleton, Stack } from '@mantine/core';
|
||||
import { Accordion, Grid, Skeleton, Stack } from '@mantine/core';
|
||||
import {
|
||||
IconDots,
|
||||
IconInfoCircle,
|
||||
@ -29,6 +29,7 @@ import {
|
||||
UnlinkBarcodeAction,
|
||||
ViewBarcodeAction
|
||||
} from '../../components/items/ActionDropdown';
|
||||
import { StylishText } from '../../components/items/StylishText';
|
||||
import InstanceDetail from '../../components/nav/InstanceDetail';
|
||||
import { PageDetail } from '../../components/nav/PageDetail';
|
||||
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
||||
@ -47,6 +48,7 @@ import useStatusCodes from '../../hooks/UseStatusCodes';
|
||||
import { apiUrl } from '../../states/ApiState';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
import { AttachmentTable } from '../../tables/general/AttachmentTable';
|
||||
import ExtraLineItemTable from '../../tables/general/ExtraLineItemTable';
|
||||
import { PurchaseOrderLineItemTable } from '../../tables/purchasing/PurchaseOrderLineItemTable';
|
||||
import { StockItemTable } from '../../tables/stock/StockItemTable';
|
||||
|
||||
@ -245,11 +247,35 @@ export default function PurchaseOrderDetail() {
|
||||
label: t`Line Items`,
|
||||
icon: <IconList />,
|
||||
content: (
|
||||
<PurchaseOrderLineItemTable
|
||||
order={order}
|
||||
orderId={Number(id)}
|
||||
supplierId={Number(order.supplier)}
|
||||
/>
|
||||
<Accordion
|
||||
multiple={true}
|
||||
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>
|
||||
<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 { Grid, Skeleton, Stack } from '@mantine/core';
|
||||
import { Accordion, Grid, Skeleton, Stack } from '@mantine/core';
|
||||
import {
|
||||
IconDots,
|
||||
IconInfoCircle,
|
||||
@ -28,6 +28,7 @@ import {
|
||||
UnlinkBarcodeAction,
|
||||
ViewBarcodeAction
|
||||
} from '../../components/items/ActionDropdown';
|
||||
import { StylishText } from '../../components/items/StylishText';
|
||||
import InstanceDetail from '../../components/nav/InstanceDetail';
|
||||
import { PageDetail } from '../../components/nav/PageDetail';
|
||||
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
||||
@ -46,6 +47,7 @@ import useStatusCodes from '../../hooks/UseStatusCodes';
|
||||
import { apiUrl } from '../../states/ApiState';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
import { AttachmentTable } from '../../tables/general/AttachmentTable';
|
||||
import ExtraLineItemTable from '../../tables/general/ExtraLineItemTable';
|
||||
import ReturnOrderLineItemTable from '../../tables/sales/ReturnOrderLineItemTable';
|
||||
|
||||
/**
|
||||
@ -223,10 +225,34 @@ export default function ReturnOrderDetail() {
|
||||
label: t`Line Items`,
|
||||
icon: <IconList />,
|
||||
content: (
|
||||
<ReturnOrderLineItemTable
|
||||
orderId={order.pk}
|
||||
customerId={order.customer}
|
||||
/>
|
||||
<Accordion
|
||||
multiple={true}
|
||||
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 { Grid, Skeleton, Stack } from '@mantine/core';
|
||||
import { Accordion, Grid, Skeleton, Stack } from '@mantine/core';
|
||||
import {
|
||||
IconBook,
|
||||
IconBookmark,
|
||||
IconDots,
|
||||
IconInfoCircle,
|
||||
@ -32,6 +31,7 @@ import {
|
||||
UnlinkBarcodeAction,
|
||||
ViewBarcodeAction
|
||||
} from '../../components/items/ActionDropdown';
|
||||
import { StylishText } from '../../components/items/StylishText';
|
||||
import InstanceDetail from '../../components/nav/InstanceDetail';
|
||||
import { PageDetail } from '../../components/nav/PageDetail';
|
||||
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
||||
@ -51,6 +51,7 @@ import { apiUrl } from '../../states/ApiState';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
import { BuildOrderTable } from '../../tables/build/BuildOrderTable';
|
||||
import { AttachmentTable } from '../../tables/general/AttachmentTable';
|
||||
import ExtraLineItemTable from '../../tables/general/ExtraLineItemTable';
|
||||
import SalesOrderAllocationTable from '../../tables/sales/SalesOrderAllocationTable';
|
||||
import SalesOrderLineItemTable from '../../tables/sales/SalesOrderLineItemTable';
|
||||
import SalesOrderShipmentTable from '../../tables/sales/SalesOrderShipmentTable';
|
||||
@ -259,14 +260,38 @@ export default function SalesOrderDetail() {
|
||||
label: t`Line Items`,
|
||||
icon: <IconList />,
|
||||
content: (
|
||||
<SalesOrderLineItemTable
|
||||
orderId={order.pk}
|
||||
customerId={order.customer}
|
||||
editable={
|
||||
order.status != soStatus.COMPLETE &&
|
||||
order.status != soStatus.CANCELLED
|
||||
}
|
||||
/>
|
||||
<Accordion
|
||||
multiple={true}
|
||||
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>
|
||||
<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
|
||||
});
|
||||
|
||||
const [initialData, setInitialData] = useState({});
|
||||
const [initialData, setInitialData] = useState<any>({});
|
||||
|
||||
const newLine = useCreateApiFormModal({
|
||||
url: ApiEndpoints.purchase_order_line_list,
|
||||
|
Loading…
Reference in New Issue
Block a user