mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 19:46:46 +00:00
[PUI] Sales order actions (#8086)
* [PUI] Add placeholder action - "Allocate Serials" action for sales order - No functionality yet * Implement form for allocating by serial numbers * Improve validation of serial numbers in back-end * Trim serial number string
This commit is contained in:
parent
3d9db2543d
commit
9f92475af0
@ -1517,37 +1517,45 @@ class SalesOrderSerialAllocationSerializer(serializers.Serializer):
|
|||||||
except DjangoValidationError as e:
|
except DjangoValidationError as e:
|
||||||
raise ValidationError({'serial_numbers': e.messages})
|
raise ValidationError({'serial_numbers': e.messages})
|
||||||
|
|
||||||
serials_not_exist = []
|
serials_not_exist = set()
|
||||||
serials_allocated = []
|
serials_unavailable = set()
|
||||||
stock_items_to_allocate = []
|
stock_items_to_allocate = []
|
||||||
|
|
||||||
for serial in data['serials']:
|
for serial in data['serials']:
|
||||||
|
serial = str(serial).strip()
|
||||||
|
|
||||||
items = stock.models.StockItem.objects.filter(
|
items = stock.models.StockItem.objects.filter(
|
||||||
part=part, serial=serial, quantity=1
|
part=part, serial=serial, quantity=1
|
||||||
)
|
)
|
||||||
|
|
||||||
if not items.exists():
|
if not items.exists():
|
||||||
serials_not_exist.append(str(serial))
|
serials_not_exist.add(str(serial))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
stock_item = items[0]
|
stock_item = items[0]
|
||||||
|
|
||||||
if stock_item.unallocated_quantity() == 1:
|
if not stock_item.in_stock:
|
||||||
|
serials_unavailable.add(str(serial))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if stock_item.unallocated_quantity() < 1:
|
||||||
|
serials_unavailable.add(str(serial))
|
||||||
|
continue
|
||||||
|
|
||||||
|
# At this point, the serial number is valid, and can be added to the list
|
||||||
stock_items_to_allocate.append(stock_item)
|
stock_items_to_allocate.append(stock_item)
|
||||||
else:
|
|
||||||
serials_allocated.append(str(serial))
|
|
||||||
|
|
||||||
if len(serials_not_exist) > 0:
|
if len(serials_not_exist) > 0:
|
||||||
error_msg = _('No match found for the following serial numbers')
|
error_msg = _('No match found for the following serial numbers')
|
||||||
error_msg += ': '
|
error_msg += ': '
|
||||||
error_msg += ','.join(serials_not_exist)
|
error_msg += ','.join(sorted(serials_not_exist))
|
||||||
|
|
||||||
raise ValidationError({'serial_numbers': error_msg})
|
raise ValidationError({'serial_numbers': error_msg})
|
||||||
|
|
||||||
if len(serials_allocated) > 0:
|
if len(serials_unavailable) > 0:
|
||||||
error_msg = _('The following serial numbers are already allocated')
|
error_msg = _('The following serial numbers are unavailable')
|
||||||
error_msg += ': '
|
error_msg += ': '
|
||||||
error_msg += ','.join(serials_allocated)
|
error_msg += ','.join(sorted(serials_unavailable))
|
||||||
|
|
||||||
raise ValidationError({'serial_numbers': error_msg})
|
raise ValidationError({'serial_numbers': error_msg})
|
||||||
|
|
||||||
|
@ -152,6 +152,7 @@ export enum ApiEndpoints {
|
|||||||
sales_order_extra_line_list = 'order/so-extra-line/',
|
sales_order_extra_line_list = 'order/so-extra-line/',
|
||||||
sales_order_allocation_list = 'order/so-allocation/',
|
sales_order_allocation_list = 'order/so-allocation/',
|
||||||
sales_order_shipment_list = 'order/so/shipment/',
|
sales_order_shipment_list = 'order/so/shipment/',
|
||||||
|
sales_order_allocate_serials = 'order/so/:id/allocate-serials/',
|
||||||
|
|
||||||
return_order_list = 'order/ro/',
|
return_order_list = 'order/ro/',
|
||||||
return_order_issue = 'order/ro/:id/issue/',
|
return_order_issue = 'order/ro/:id/issue/',
|
||||||
|
@ -84,6 +84,31 @@ export function useSalesOrderLineItemFields({
|
|||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useSalesOrderAllocateSerialsFields({
|
||||||
|
itemId,
|
||||||
|
orderId
|
||||||
|
}: {
|
||||||
|
itemId: number;
|
||||||
|
orderId: number;
|
||||||
|
}): ApiFormFieldSet {
|
||||||
|
return useMemo(() => {
|
||||||
|
return {
|
||||||
|
line_item: {
|
||||||
|
value: itemId,
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
quantity: {},
|
||||||
|
serial_numbers: {},
|
||||||
|
shipment: {
|
||||||
|
filters: {
|
||||||
|
order: orderId,
|
||||||
|
shipped: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [itemId, orderId]);
|
||||||
|
}
|
||||||
|
|
||||||
export function useSalesOrderShipmentFields(): ApiFormFieldSet {
|
export function useSalesOrderShipmentFields(): ApiFormFieldSet {
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
return {
|
return {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Text } from '@mantine/core';
|
import { Text } from '@mantine/core';
|
||||||
import {
|
import {
|
||||||
|
IconHash,
|
||||||
IconShoppingCart,
|
IconShoppingCart,
|
||||||
IconSquareArrowRight,
|
IconSquareArrowRight,
|
||||||
IconTools
|
IconTools
|
||||||
@ -14,7 +15,10 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
|||||||
import { ModelType } from '../../enums/ModelType';
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { UserRoles } from '../../enums/Roles';
|
import { UserRoles } from '../../enums/Roles';
|
||||||
import { useBuildOrderFields } from '../../forms/BuildForms';
|
import { useBuildOrderFields } from '../../forms/BuildForms';
|
||||||
import { useSalesOrderLineItemFields } from '../../forms/SalesOrderForms';
|
import {
|
||||||
|
useSalesOrderAllocateSerialsFields,
|
||||||
|
useSalesOrderLineItemFields
|
||||||
|
} from '../../forms/SalesOrderForms';
|
||||||
import { notYetImplemented } from '../../functions/notifications';
|
import { notYetImplemented } from '../../functions/notifications';
|
||||||
import {
|
import {
|
||||||
useCreateApiFormModal,
|
useCreateApiFormModal,
|
||||||
@ -223,6 +227,19 @@ export default function SalesOrderLineItemTable({
|
|||||||
table: table
|
table: table
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const allocateSerialFields = useSalesOrderAllocateSerialsFields({
|
||||||
|
itemId: selectedLine,
|
||||||
|
orderId: orderId
|
||||||
|
});
|
||||||
|
|
||||||
|
const allocateBySerials = useCreateApiFormModal({
|
||||||
|
url: ApiEndpoints.sales_order_allocate_serials,
|
||||||
|
pk: orderId,
|
||||||
|
title: t`Allocate Serial Numbers`,
|
||||||
|
fields: allocateSerialFields,
|
||||||
|
table: table
|
||||||
|
});
|
||||||
|
|
||||||
const buildOrderFields = useBuildOrderFields({ create: true });
|
const buildOrderFields = useBuildOrderFields({ create: true });
|
||||||
|
|
||||||
const newBuildOrder = useCreateApiFormModal({
|
const newBuildOrder = useCreateApiFormModal({
|
||||||
@ -264,6 +281,20 @@ export default function SalesOrderLineItemTable({
|
|||||||
color: 'green',
|
color: 'green',
|
||||||
onClick: notYetImplemented
|
onClick: notYetImplemented
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
hidden:
|
||||||
|
!record?.part_detail?.trackable ||
|
||||||
|
allocated ||
|
||||||
|
!editable ||
|
||||||
|
!user.hasChangeRole(UserRoles.sales_order),
|
||||||
|
title: t`Allocate Serials`,
|
||||||
|
icon: <IconHash />,
|
||||||
|
color: 'green',
|
||||||
|
onClick: () => {
|
||||||
|
setSelectedLine(record.pk);
|
||||||
|
allocateBySerials.open();
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
hidden:
|
hidden:
|
||||||
allocated ||
|
allocated ||
|
||||||
@ -323,6 +354,7 @@ export default function SalesOrderLineItemTable({
|
|||||||
{deleteLine.modal}
|
{deleteLine.modal}
|
||||||
{newLine.modal}
|
{newLine.modal}
|
||||||
{newBuildOrder.modal}
|
{newBuildOrder.modal}
|
||||||
|
{allocateBySerials.modal}
|
||||||
<InvenTreeTable
|
<InvenTreeTable
|
||||||
url={apiUrl(ApiEndpoints.sales_order_line_list)}
|
url={apiUrl(ApiEndpoints.sales_order_line_list)}
|
||||||
tableState={table}
|
tableState={table}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user