2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-10-23 09:27:39 +00:00

[UI] Hide actions for completed orders (#10617)

* Adjust error message target

* Hide UI actions if order is locked

* Refactor salesorderdetail page

* Refactor PurchaseOrderDetail page

* Refactor ReturnOrderDetail
This commit is contained in:
Oliver
2025-10-19 11:34:04 +11:00
committed by GitHub
parent 2182fe42db
commit 73ca468ff7
7 changed files with 90 additions and 38 deletions

View File

@@ -1687,7 +1687,7 @@ class OrderLineItem(InvenTree.models.InvenTreeMetadataModel):
"""
if self.order and self.order.check_locked():
raise ValidationError({
'reference': _('The order is locked and cannot be modified')
'non_field_errors': _('The order is locked and cannot be modified')
})
update_order = kwargs.pop('update_order', True)
@@ -1703,7 +1703,7 @@ class OrderLineItem(InvenTree.models.InvenTreeMetadataModel):
"""
if self.order and self.order.check_locked():
raise ValidationError({
'reference': _('The order is locked and cannot be modified')
'non_field_errors': _('The order is locked and cannot be modified')
})
super().delete(*args, **kwargs)

View File

@@ -93,6 +93,24 @@ export default function PurchaseOrderDetail() {
}
});
const poStatus = useStatusCodes({ modelType: ModelType.purchaseorder });
const orderOpen: boolean = useMemo(() => {
return (
order.status == poStatus.PENDING ||
order.status == poStatus.PLACED ||
order.status == poStatus.ON_HOLD
);
}, [order, poStatus]);
const lineItemsEditable: boolean = useMemo(() => {
if (orderOpen) {
return true;
} else {
return globalSettings.isSet('PURCHASEORDER_EDIT_COMPLETED_ORDERS');
}
}, [orderOpen, globalSettings]);
const duplicatePurchaseOrderInitialData = useMemo(() => {
const data = { ...order };
// if we set the reference to null/undefined, it will be left blank in the form
@@ -335,6 +353,7 @@ export default function PurchaseOrderDetail() {
orderDetailRefresh={refreshInstance}
currency={orderCurrency}
orderId={Number(id)}
editable={lineItemsEditable}
supplierId={Number(order.supplier)}
/>
</Accordion.Panel>
@@ -349,6 +368,7 @@ export default function PurchaseOrderDetail() {
orderId={order.pk}
orderDetailRefresh={refreshInstance}
currency={orderCurrency}
editable={lineItemsEditable}
role={UserRoles.purchase_order}
/>
</Accordion.Panel>
@@ -380,8 +400,6 @@ export default function PurchaseOrderDetail() {
];
}, [order, id, user]);
const poStatus = useStatusCodes({ modelType: ModelType.purchaseorder });
const issueOrder = useCreateApiFormModal({
url: apiUrl(ApiEndpoints.purchase_order_issue, order.pk),
title: t`Issue Purchase Order`,

View File

@@ -68,6 +68,25 @@ export default function ReturnOrderDetail() {
}
});
const roStatus = useStatusCodes({ modelType: ModelType.returnorder });
const orderOpen = useMemo(() => {
return (
order.status == roStatus.PENDING ||
order.status == roStatus.PLACED ||
order.status == roStatus.IN_PROGRESS ||
order.status == roStatus.ON_HOLD
);
}, [order, roStatus]);
const lineItemsEditable: boolean = useMemo(() => {
if (orderOpen) {
return true;
} else {
return globalSettings.isSet('RETURNORDER_EDIT_COMPLETED_ORDERS');
}
}, [orderOpen, globalSettings]);
const orderCurrency = useMemo(() => {
return (
order.order_currency ||
@@ -299,6 +318,7 @@ export default function ReturnOrderDetail() {
order={order}
orderDetailRefresh={refreshInstance}
customerId={order.customer}
editable={lineItemsEditable}
currency={orderCurrency}
/>
</Accordion.Panel>
@@ -313,6 +333,7 @@ export default function ReturnOrderDetail() {
orderId={order.pk}
orderDetailRefresh={refreshInstance}
currency={orderCurrency}
editable={lineItemsEditable}
role={UserRoles.return_order}
/>
</Accordion.Panel>
@@ -409,8 +430,6 @@ export default function ReturnOrderDetail() {
successMessage: t`Order completed`
});
const roStatus = useStatusCodes({ modelType: ModelType.returnorder });
const orderActions = useMemo(() => {
const canEdit: boolean = user.hasChangeRole(UserRoles.return_order);
@@ -488,7 +507,7 @@ export default function ReturnOrderDetail() {
]}
/>
];
}, [user, order, roStatus]);
}, [user, order, orderOpen, roStatus]);
const subtitle: string = useMemo(() => {
let t = order.customer_detail?.name || '';

View File

@@ -284,6 +284,17 @@ export default function SalesOrderDetail() {
const soStatus = useStatusCodes({ modelType: ModelType.salesorder });
const lineItemsEditable: boolean = useMemo(() => {
const orderOpen: boolean =
order.status != soStatus.COMPLETE && order.status != soStatus.CANCELLED;
if (orderOpen) {
return true;
} else {
return globalSettings.isSet('SALESORDER_EDIT_COMPLETED_ORDERS');
}
}, [globalSettings, order.status, soStatus]);
const salesOrderFields = useSalesOrderFields({});
const editSalesOrder = useEditApiFormModal({
@@ -345,10 +356,7 @@ export default function SalesOrderDetail() {
orderDetailRefresh={refreshInstance}
currency={orderCurrency}
customerId={order.customer}
editable={
order.status != soStatus.COMPLETE &&
order.status != soStatus.CANCELLED
}
editable={lineItemsEditable}
/>
</Accordion.Panel>
</Accordion.Item>
@@ -360,6 +368,7 @@ export default function SalesOrderDetail() {
<ExtraLineItemTable
endpoint={ApiEndpoints.sales_order_extra_line_list}
orderId={order.pk}
editable={lineItemsEditable}
orderDetailRefresh={refreshInstance}
currency={orderCurrency}
role={UserRoles.sales_order}

View File

@@ -34,10 +34,12 @@ export default function ExtraLineItemTable({
orderId,
orderDetailRefresh,
currency,
editable,
role
}: Readonly<{
endpoint: ApiEndpoints;
orderId: number;
editable: boolean;
orderDetailRefresh: () => void;
currency: string;
role: UserRoles;
@@ -119,21 +121,21 @@ export default function ExtraLineItemTable({
(record: any): RowAction[] => {
return [
RowEditAction({
hidden: !user.hasChangeRole(role),
hidden: !editable || !user.hasChangeRole(role),
onClick: () => {
setSelectedLine(record.pk);
editLineItem.open();
}
}),
RowDuplicateAction({
hidden: !user.hasAddRole(role),
hidden: !editable || !user.hasAddRole(role),
onClick: () => {
setInitialData({ ...record });
newLineItem.open();
}
}),
RowDeleteAction({
hidden: !user.hasDeleteRole(role),
hidden: !editable || !user.hasDeleteRole(role),
onClick: () => {
setSelectedLine(record.pk);
deleteLineItem.open();
@@ -141,7 +143,7 @@ export default function ExtraLineItemTable({
})
];
},
[user, role]
[editable, user, role]
);
const tableActions = useMemo(() => {
@@ -149,7 +151,7 @@ export default function ExtraLineItemTable({
<AddItemButton
key='add-line-item'
tooltip={t`Add Extra Line Item`}
hidden={!user.hasAddRole(role)}
hidden={!editable || !user.hasAddRole(role)}
onClick={() => {
setInitialData({
order: orderId
@@ -158,7 +160,7 @@ export default function ExtraLineItemTable({
}}
/>
];
}, [user, role]);
}, [editable, user, role]);
return (
<>

View File

@@ -36,6 +36,7 @@ import {
} from '../../hooks/UseForm';
import useStatusCodes from '../../hooks/UseStatusCodes';
import { useTable } from '../../hooks/UseTable';
import { useGlobalSettingsState } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState';
import {
CurrencyColumn,
@@ -59,6 +60,7 @@ export function PurchaseOrderLineItemTable({
orderId,
currency,
supplierId,
editable,
params
}: Readonly<{
order: any;
@@ -66,10 +68,12 @@ export function PurchaseOrderLineItemTable({
orderId: number;
currency: string;
supplierId?: number;
editable: boolean;
params?: any;
}>) {
const table = useTable('purchase-order-line-item');
const globalSettings = useGlobalSettingsState();
const navigate = useNavigate();
const user = useUserState();
@@ -327,14 +331,6 @@ export function PurchaseOrderLineItemTable({
const poStatus = useStatusCodes({ modelType: ModelType.purchaseorder });
const orderOpen: boolean = useMemo(() => {
return (
order.status == poStatus.PENDING ||
order.status == poStatus.PLACED ||
order.status == poStatus.ON_HOLD
);
}, [order, poStatus]);
const orderPlaced: boolean = useMemo(() => {
return order.status == poStatus.PLACED;
}, [order, poStatus]);
@@ -343,6 +339,9 @@ export function PurchaseOrderLineItemTable({
(record: any): RowAction[] => {
const received = (record?.received ?? 0) >= (record?.quantity ?? 0);
const canEdit: boolean =
editable && user.hasChangeRole(UserRoles.purchase_order);
return [
{
hidden: received || !orderPlaced,
@@ -362,21 +361,21 @@ export function PurchaseOrderLineItemTable({
navigate: navigate
}),
RowEditAction({
hidden: !user.hasChangeRole(UserRoles.purchase_order),
hidden: !canEdit,
onClick: () => {
setSelectedLine(record.pk);
editLine.open();
}
}),
RowDuplicateAction({
hidden: !orderOpen || !user.hasAddRole(UserRoles.purchase_order),
hidden: !canEdit || !user.hasAddRole(UserRoles.purchase_order),
onClick: () => {
setInitialData({ ...record });
newLine.open();
}
}),
RowDeleteAction({
hidden: !user.hasDeleteRole(UserRoles.purchase_order),
hidden: !canEdit || !user.hasDeleteRole(UserRoles.purchase_order),
onClick: () => {
setSelectedLine(record.pk);
deleteLine.open();
@@ -384,7 +383,7 @@ export function PurchaseOrderLineItemTable({
})
];
},
[orderId, user, orderOpen, orderPlaced]
[orderId, user, editable, orderPlaced]
);
// Custom table actions
@@ -392,7 +391,7 @@ export function PurchaseOrderLineItemTable({
return [
<ActionButton
key='import-line-items'
hidden={!orderOpen || !user.hasAddRole(UserRoles.purchase_order)}
hidden={!editable || !user.hasAddRole(UserRoles.purchase_order)}
tooltip={t`Import Line Items`}
icon={<IconFileArrowLeft />}
onClick={() => importLineItems.open()}
@@ -406,7 +405,7 @@ export function PurchaseOrderLineItemTable({
});
newLine.open();
}}
hidden={!orderOpen || !user?.hasAddRole(UserRoles.purchase_order)}
hidden={!editable || !user?.hasAddRole(UserRoles.purchase_order)}
/>,
<ActionButton
key='receive-items'
@@ -417,7 +416,7 @@ export function PurchaseOrderLineItemTable({
hidden={!orderPlaced || !user.hasChangeRole(UserRoles.purchase_order)}
/>
];
}, [orderId, user, table, orderOpen, orderPlaced]);
}, [orderId, user, table, editable, orderPlaced]);
return (
<>

View File

@@ -45,12 +45,14 @@ export default function ReturnOrderLineItemTable({
order,
orderDetailRefresh,
customerId,
editable,
currency
}: Readonly<{
orderId: number;
order: any;
orderDetailRefresh: () => void;
customerId: number;
editable: boolean;
currency: string;
}>) {
const table = useTable('return-order-line-item');
@@ -181,7 +183,7 @@ export default function ReturnOrderLineItemTable({
<AddItemButton
key='add-line-item'
tooltip={t`Add Line Item`}
hidden={!user.hasAddRole(UserRoles.return_order)}
hidden={!editable || !user.hasAddRole(UserRoles.return_order)}
onClick={() => {
newLine.open();
}}
@@ -190,7 +192,9 @@ export default function ReturnOrderLineItemTable({
key='receive-items'
tooltip={t`Receive selected items`}
icon={<IconSquareArrowRight />}
hidden={!inProgress || !user.hasChangeRole(UserRoles.return_order)}
hidden={
!editable || inProgress || !user.hasChangeRole(UserRoles.return_order)
}
onClick={() => {
setSelectedItems(
table.selectedRecords.filter((record: any) => !record.received_date)
@@ -200,7 +204,7 @@ export default function ReturnOrderLineItemTable({
disabled={table.selectedRecords.length == 0}
/>
];
}, [user, inProgress, orderId, table.selectedRecords]);
}, [user, editable, inProgress, orderId, table.selectedRecords]);
const [selectedItems, setSelectedItems] = useState<any[]>([]);
@@ -218,6 +222,7 @@ export default function ReturnOrderLineItemTable({
{
hidden:
received ||
!editable ||
!inProgress ||
!user.hasChangeRole(UserRoles.return_order),
title: t`Receive Item`,
@@ -228,14 +233,14 @@ export default function ReturnOrderLineItemTable({
}
},
RowEditAction({
hidden: !user.hasChangeRole(UserRoles.return_order),
hidden: !editable || !user.hasChangeRole(UserRoles.return_order),
onClick: () => {
setSelectedLine(record.pk);
editLine.open();
}
}),
RowDeleteAction({
hidden: !user.hasDeleteRole(UserRoles.return_order),
hidden: !editable || !user.hasDeleteRole(UserRoles.return_order),
onClick: () => {
setSelectedLine(record.pk);
deleteLine.open();
@@ -243,7 +248,7 @@ export default function ReturnOrderLineItemTable({
})
];
},
[user, inProgress]
[user, editable, inProgress]
);
return (