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