mirror of
https://github.com/inventree/InvenTree.git
synced 2025-10-29 20:30:39 +00:00
[UI] Shipments table (#10675)
* Display PendingShipments panel - Overview of all outstanding shipments * Update UI tests * Bump API version
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
"""InvenTree API version information."""
|
||||
|
||||
# InvenTree API version
|
||||
INVENTREE_API_VERSION = 419
|
||||
INVENTREE_API_VERSION = 420
|
||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||
|
||||
INVENTREE_API_TEXT = """
|
||||
|
||||
v420 -> 2025-10-26 : https://github.com/inventree/InvenTree/pull/10675
|
||||
- Adds optional "customer_detail" filter to SalesOrderShipment API endpoint
|
||||
|
||||
v419 -> 2025-10-24 : https://github.com/inventree/InvenTree/pull/10659
|
||||
- Fixes regression introduced in v417 which reverted the changes from v416
|
||||
|
||||
|
||||
@@ -1320,6 +1320,7 @@ class SalesOrderShipmentSerializer(
|
||||
'notes',
|
||||
# Extra detail fields
|
||||
'checked_by_detail',
|
||||
'customer_detail',
|
||||
'order_detail',
|
||||
'shipment_address_detail',
|
||||
]
|
||||
@@ -1352,6 +1353,13 @@ class SalesOrderShipmentSerializer(
|
||||
True,
|
||||
)
|
||||
|
||||
customer_detail = enable_filter(
|
||||
CompanyBriefSerializer(
|
||||
source='order.customer', many=False, read_only=True, allow_null=True
|
||||
),
|
||||
False,
|
||||
)
|
||||
|
||||
shipment_address_detail = enable_filter(
|
||||
AddressBriefSerializer(
|
||||
source='shipment_address', many=False, read_only=True, allow_null=True
|
||||
|
||||
@@ -378,7 +378,7 @@ export function useSalesOrderShipmentFields({
|
||||
customerId,
|
||||
pending
|
||||
}: {
|
||||
customerId: number;
|
||||
customerId?: number;
|
||||
pending?: boolean;
|
||||
}): ApiFormFieldSet {
|
||||
return useMemo(() => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Stack } from '@mantine/core';
|
||||
import {
|
||||
IconBuildingStore,
|
||||
IconCalendar,
|
||||
IconCubeSend,
|
||||
IconTable,
|
||||
IconTruckDelivery,
|
||||
IconTruckReturn
|
||||
@@ -20,6 +21,7 @@ import { PanelGroup } from '../../components/panels/PanelGroup';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
import { CompanyTable } from '../../tables/company/CompanyTable';
|
||||
import { ReturnOrderTable } from '../../tables/sales/ReturnOrderTable';
|
||||
import SalesOrderShipmentTable from '../../tables/sales/SalesOrderShipmentTable';
|
||||
import { SalesOrderTable } from '../../tables/sales/SalesOrderTable';
|
||||
|
||||
function SalesOrderOverview({
|
||||
@@ -98,6 +100,18 @@ export default function SalesIndex() {
|
||||
),
|
||||
hidden: !user.hasViewRole(UserRoles.sales_order)
|
||||
},
|
||||
{
|
||||
name: 'shipments',
|
||||
label: t`Pending Shipments`,
|
||||
icon: <IconCubeSend />,
|
||||
content: (
|
||||
<SalesOrderShipmentTable
|
||||
tableName={'sales-order-pending-shipment'}
|
||||
showOrderInfo
|
||||
filters={{ shipped: false, order_outstanding: true }}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: 'returnorders',
|
||||
label: t`Return Orders`,
|
||||
|
||||
@@ -29,19 +29,30 @@ import {
|
||||
} from '../../hooks/UseForm';
|
||||
import { useTable } from '../../hooks/UseTable';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
import { DateColumn, LinkColumn } from '../ColumnRenderers';
|
||||
import {
|
||||
CompanyColumn,
|
||||
DateColumn,
|
||||
LinkColumn,
|
||||
StatusColumn
|
||||
} from '../ColumnRenderers';
|
||||
import { InvenTreeTable } from '../InvenTreeTable';
|
||||
|
||||
export default function SalesOrderShipmentTable({
|
||||
showOrderInfo = false,
|
||||
tableName,
|
||||
customerId,
|
||||
orderId
|
||||
orderId,
|
||||
filters
|
||||
}: Readonly<{
|
||||
customerId: number;
|
||||
orderId: number;
|
||||
showOrderInfo?: boolean;
|
||||
tableName?: string;
|
||||
customerId?: number;
|
||||
orderId?: number;
|
||||
filters?: any;
|
||||
}>) {
|
||||
const user = useUserState();
|
||||
const navigate = useNavigate();
|
||||
const table = useTable('sales-order-shipment');
|
||||
const table = useTable(tableName ?? 'sales-order-shipment');
|
||||
|
||||
const [selectedShipment, setSelectedShipment] = useState<any>({});
|
||||
|
||||
@@ -95,6 +106,30 @@ export default function SalesOrderShipmentTable({
|
||||
|
||||
const tableColumns: TableColumn[] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
accessor: 'customer',
|
||||
title: t`Customer`,
|
||||
switchable: true,
|
||||
sortable: true,
|
||||
hidden: !showOrderInfo,
|
||||
render: (record: any) => (
|
||||
<CompanyColumn company={record.customer_detail} />
|
||||
)
|
||||
},
|
||||
{
|
||||
switchable: false,
|
||||
accessor: 'order_detail.reference',
|
||||
title: t`Sales Order`,
|
||||
hidden: !showOrderInfo,
|
||||
sortable: false
|
||||
},
|
||||
StatusColumn({
|
||||
switchable: true,
|
||||
model: ModelType.salesorder,
|
||||
accessor: 'order_detail.status',
|
||||
title: t`Order Status`,
|
||||
hidden: !showOrderInfo
|
||||
}),
|
||||
{
|
||||
accessor: 'reference',
|
||||
title: t`Shipment Reference`,
|
||||
@@ -146,19 +181,13 @@ export default function SalesOrderShipmentTable({
|
||||
accessor: 'link'
|
||||
})
|
||||
];
|
||||
}, []);
|
||||
}, [showOrderInfo]);
|
||||
|
||||
const rowActions = useCallback(
|
||||
(record: any): RowAction[] => {
|
||||
const shipped: boolean = !!record.shipment_date;
|
||||
|
||||
return [
|
||||
RowViewAction({
|
||||
title: t`View Shipment`,
|
||||
modelType: ModelType.salesordershipment,
|
||||
modelId: record.pk,
|
||||
navigate: navigate
|
||||
}),
|
||||
{
|
||||
hidden: shipped || !user.hasChangeRole(UserRoles.sales_order),
|
||||
title: t`Complete Shipment`,
|
||||
@@ -184,13 +213,28 @@ export default function SalesOrderShipmentTable({
|
||||
setSelectedShipment(record);
|
||||
deleteShipment.open();
|
||||
}
|
||||
}),
|
||||
RowViewAction({
|
||||
title: t`View Sales Order`,
|
||||
modelType: ModelType.salesorder,
|
||||
modelId: record.order,
|
||||
hidden:
|
||||
!record.order ||
|
||||
!showOrderInfo ||
|
||||
!user.hasViewRole(UserRoles.sales_order),
|
||||
navigate: navigate
|
||||
})
|
||||
];
|
||||
},
|
||||
[user]
|
||||
[showOrderInfo, user]
|
||||
);
|
||||
|
||||
const tableActions = useMemo(() => {
|
||||
// No actions possible if no order is specified
|
||||
if (!orderId) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
<AddItemButton
|
||||
key='add-shipment'
|
||||
@@ -201,7 +245,7 @@ export default function SalesOrderShipmentTable({
|
||||
}}
|
||||
/>
|
||||
];
|
||||
}, [user]);
|
||||
}, [orderId, user]);
|
||||
|
||||
const tableFilters: TableFilter[] = useMemo(() => {
|
||||
return [
|
||||
@@ -241,7 +285,10 @@ export default function SalesOrderShipmentTable({
|
||||
enableReports: true,
|
||||
rowActions: rowActions,
|
||||
params: {
|
||||
order: orderId
|
||||
order: orderId,
|
||||
order_detail: true,
|
||||
customer_detail: showOrderInfo,
|
||||
...filters
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -14,9 +14,18 @@ test('Sales Orders - Tabs', async ({ browser }) => {
|
||||
|
||||
await page.waitForURL('**/web/sales/**');
|
||||
|
||||
// Sales Orders panel
|
||||
await loadTab(page, 'Sales Orders');
|
||||
await page.waitForURL('**/web/sales/index/salesorders');
|
||||
|
||||
// Pending Shipments panel
|
||||
await loadTab(page, 'Pending Shipments');
|
||||
await page.getByRole('cell', { name: 'SO0007' }).waitFor();
|
||||
await page.getByRole('button', { name: 'Shipment Reference' }).waitFor();
|
||||
|
||||
// Return Orders panel
|
||||
await loadTab(page, 'Return Orders');
|
||||
await page.getByRole('cell', { name: 'NOISE-COMPLAINT' }).waitFor();
|
||||
|
||||
// Customers
|
||||
await loadTab(page, 'Customers');
|
||||
|
||||
Reference in New Issue
Block a user