mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 11:36:44 +00:00
Batch code backport (#9138)
* Batch code fix (#9123) * Fix batch code assignment when receiving items * Add playwright tests * Harden playwright tests * Refactoring * Handle undefined values * Fix conflicts
This commit is contained in:
parent
8a2fce9c36
commit
cd41ca2a87
@ -83,6 +83,26 @@ export function getStatusCodes(type: ModelType | string) {
|
|||||||
return statusCodes;
|
return statusCodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of status codes select options for a given model type
|
||||||
|
* returns an array of objects with keys "value" and "display_name"
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export function getStatusCodeOptions(type: ModelType | string): any[] {
|
||||||
|
const statusCodes = getStatusCodes(type);
|
||||||
|
|
||||||
|
if (!statusCodes) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.values(statusCodes?.values ?? []).map((entry) => {
|
||||||
|
return {
|
||||||
|
value: entry.key,
|
||||||
|
display_name: entry.label
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return the name of a status code, based on the key
|
* Return the name of a status code, based on the key
|
||||||
*/
|
*/
|
||||||
|
@ -22,10 +22,7 @@ import {
|
|||||||
IconUser,
|
IconUser,
|
||||||
IconUsers
|
IconUsers
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { api } from '../App';
|
|
||||||
import { ActionButton } from '../components/buttons/ActionButton';
|
import { ActionButton } from '../components/buttons/ActionButton';
|
||||||
import RemoveRowButton from '../components/buttons/RemoveRowButton';
|
import RemoveRowButton from '../components/buttons/RemoveRowButton';
|
||||||
import { StandaloneField } from '../components/forms/StandaloneField';
|
import { StandaloneField } from '../components/forms/StandaloneField';
|
||||||
@ -40,6 +37,7 @@ import {
|
|||||||
import { Thumbnail } from '../components/images/Thumbnail';
|
import { Thumbnail } from '../components/images/Thumbnail';
|
||||||
import { ProgressBar } from '../components/items/ProgressBar';
|
import { ProgressBar } from '../components/items/ProgressBar';
|
||||||
import { StylishText } from '../components/items/StylishText';
|
import { StylishText } from '../components/items/StylishText';
|
||||||
|
import { getStatusCodeOptions } from '../components/render/StatusRenderer';
|
||||||
import { ApiEndpoints } from '../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../enums/ApiEndpoints';
|
||||||
import { ModelType } from '../enums/ModelType';
|
import { ModelType } from '../enums/ModelType';
|
||||||
import { InvenTreeIcon } from '../functions/icons';
|
import { InvenTreeIcon } from '../functions/icons';
|
||||||
@ -291,10 +289,14 @@ function LineItemFormRow({
|
|||||||
order: record?.order
|
order: record?.order
|
||||||
});
|
});
|
||||||
// Generate new serial numbers
|
// Generate new serial numbers
|
||||||
serialNumberGenerator.update({
|
if (trackable) {
|
||||||
part: record?.supplier_part_detail?.part,
|
serialNumberGenerator.update({
|
||||||
quantity: props.item.quantity
|
part: record?.supplier_part_detail?.part,
|
||||||
});
|
quantity: props.item.quantity
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
props.changeFn(props.idx, 'serial_numbers', undefined);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -564,7 +566,10 @@ function LineItemFormRow({
|
|||||||
)}
|
)}
|
||||||
<TableFieldExtraRow
|
<TableFieldExtraRow
|
||||||
visible={batchOpen}
|
visible={batchOpen}
|
||||||
onValueChange={(value) => props.changeFn(props.idx, 'batch', value)}
|
onValueChange={(value) => {
|
||||||
|
props.changeFn(props.idx, 'batch_code', value);
|
||||||
|
}}
|
||||||
|
fieldName='batch_code'
|
||||||
fieldDefinition={{
|
fieldDefinition={{
|
||||||
field_type: 'string',
|
field_type: 'string',
|
||||||
label: t`Batch Code`,
|
label: t`Batch Code`,
|
||||||
@ -578,6 +583,7 @@ function LineItemFormRow({
|
|||||||
onValueChange={(value) =>
|
onValueChange={(value) =>
|
||||||
props.changeFn(props.idx, 'serial_numbers', value)
|
props.changeFn(props.idx, 'serial_numbers', value)
|
||||||
}
|
}
|
||||||
|
fieldName='serial_numbers'
|
||||||
fieldDefinition={{
|
fieldDefinition={{
|
||||||
field_type: 'string',
|
field_type: 'string',
|
||||||
label: t`Serial Numbers`,
|
label: t`Serial Numbers`,
|
||||||
@ -589,6 +595,7 @@ function LineItemFormRow({
|
|||||||
<TableFieldExtraRow
|
<TableFieldExtraRow
|
||||||
visible={packagingOpen}
|
visible={packagingOpen}
|
||||||
onValueChange={(value) => props.changeFn(props.idx, 'packaging', value)}
|
onValueChange={(value) => props.changeFn(props.idx, 'packaging', value)}
|
||||||
|
fieldName='packaging'
|
||||||
fieldDefinition={{
|
fieldDefinition={{
|
||||||
field_type: 'string',
|
field_type: 'string',
|
||||||
label: t`Packaging`
|
label: t`Packaging`
|
||||||
@ -599,6 +606,7 @@ function LineItemFormRow({
|
|||||||
<TableFieldExtraRow
|
<TableFieldExtraRow
|
||||||
visible={statusOpen}
|
visible={statusOpen}
|
||||||
defaultValue={10}
|
defaultValue={10}
|
||||||
|
fieldName='status'
|
||||||
onValueChange={(value) => props.changeFn(props.idx, 'status', value)}
|
onValueChange={(value) => props.changeFn(props.idx, 'status', value)}
|
||||||
fieldDefinition={{
|
fieldDefinition={{
|
||||||
field_type: 'choice',
|
field_type: 'choice',
|
||||||
@ -610,6 +618,7 @@ function LineItemFormRow({
|
|||||||
/>
|
/>
|
||||||
<TableFieldExtraRow
|
<TableFieldExtraRow
|
||||||
visible={noteOpen}
|
visible={noteOpen}
|
||||||
|
fieldName='note'
|
||||||
onValueChange={(value) => props.changeFn(props.idx, 'note', value)}
|
onValueChange={(value) => props.changeFn(props.idx, 'note', value)}
|
||||||
fieldDefinition={{
|
fieldDefinition={{
|
||||||
field_type: 'string',
|
field_type: 'string',
|
||||||
@ -634,23 +643,10 @@ type LineItemsForm = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function useReceiveLineItems(props: LineItemsForm) {
|
export function useReceiveLineItems(props: LineItemsForm) {
|
||||||
const { data } = useQuery({
|
const stockStatusCodes = useMemo(
|
||||||
queryKey: ['stock', 'status'],
|
() => getStatusCodeOptions(ModelType.stockitem),
|
||||||
queryFn: async () => {
|
[]
|
||||||
return api.get(apiUrl(ApiEndpoints.stock_status)).then((response) => {
|
);
|
||||||
if (response.status === 200) {
|
|
||||||
const entries = Object.values(response.data.values);
|
|
||||||
const mapped = entries.map((item: any) => {
|
|
||||||
return {
|
|
||||||
value: item.key,
|
|
||||||
display_name: item.label
|
|
||||||
};
|
|
||||||
});
|
|
||||||
return mapped;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const records = Object.fromEntries(
|
const records = Object.fromEntries(
|
||||||
props.items.map((item) => [item.pk, item])
|
props.items.map((item) => [item.pk, item])
|
||||||
@ -660,44 +656,46 @@ export function useReceiveLineItems(props: LineItemsForm) {
|
|||||||
(elem) => elem.quantity !== elem.received
|
(elem) => elem.quantity !== elem.received
|
||||||
);
|
);
|
||||||
|
|
||||||
const fields: ApiFormFieldSet = {
|
const fields: ApiFormFieldSet = useMemo(() => {
|
||||||
id: {
|
return {
|
||||||
value: props.orderPk,
|
id: {
|
||||||
hidden: true
|
value: props.orderPk,
|
||||||
},
|
hidden: true
|
||||||
items: {
|
|
||||||
field_type: 'table',
|
|
||||||
value: filteredItems.map((elem, idx) => {
|
|
||||||
return {
|
|
||||||
line_item: elem.pk,
|
|
||||||
location: elem.destination ?? elem.destination_detail?.pk ?? null,
|
|
||||||
quantity: elem.quantity - elem.received,
|
|
||||||
batch_code: '',
|
|
||||||
serial_numbers: '',
|
|
||||||
status: 10,
|
|
||||||
barcode: null
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
modelRenderer: (row: TableFieldRowProps) => {
|
|
||||||
const record = records[row.item.line_item];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<LineItemFormRow
|
|
||||||
props={row}
|
|
||||||
record={record}
|
|
||||||
statuses={data}
|
|
||||||
key={record.pk}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
headers: [t`Part`, t`SKU`, t`Received`, t`Quantity`, t`Actions`]
|
items: {
|
||||||
},
|
field_type: 'table',
|
||||||
location: {
|
value: filteredItems.map((elem, idx) => {
|
||||||
filters: {
|
return {
|
||||||
structural: false
|
line_item: elem.pk,
|
||||||
|
location: elem.destination ?? elem.destination_detail?.pk ?? null,
|
||||||
|
quantity: elem.quantity - elem.received,
|
||||||
|
batch_code: '',
|
||||||
|
serial_numbers: '',
|
||||||
|
status: 10,
|
||||||
|
barcode: null
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
modelRenderer: (row: TableFieldRowProps) => {
|
||||||
|
const record = records[row.item.line_item];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LineItemFormRow
|
||||||
|
props={row}
|
||||||
|
record={record}
|
||||||
|
statuses={stockStatusCodes}
|
||||||
|
key={record.pk}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
headers: [t`Part`, t`SKU`, t`Received`, t`Quantity`, t`Actions`]
|
||||||
|
},
|
||||||
|
location: {
|
||||||
|
filters: {
|
||||||
|
structural: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
};
|
}, [filteredItems, props, stockStatusCodes]);
|
||||||
|
|
||||||
return useCreateApiFormModal({
|
return useCreateApiFormModal({
|
||||||
...props.formProps,
|
...props.formProps,
|
||||||
@ -707,6 +705,7 @@ export function useReceiveLineItems(props: LineItemsForm) {
|
|||||||
initialData: {
|
initialData: {
|
||||||
location: props.destinationPk
|
location: props.destinationPk
|
||||||
},
|
},
|
||||||
size: '80%'
|
size: '80%',
|
||||||
|
successMessage: t`Items received`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user