diff --git a/src/backend/InvenTree/order/serializers.py b/src/backend/InvenTree/order/serializers.py index ea65d1c9be..feeb649d53 100644 --- a/src/backend/InvenTree/order/serializers.py +++ b/src/backend/InvenTree/order/serializers.py @@ -845,6 +845,20 @@ class PurchaseOrderLineItemReceiveSerializer(serializers.Serializer): except DjangoValidationError as e: raise ValidationError({'serial_numbers': e.messages}) + invalid_serials = [] + + # Check the serial numbers are valid + for serial in data['serials']: + try: + base_part.validate_serial_number(serial, raise_error=True) + except (ValidationError, DjangoValidationError): + invalid_serials.append(serial) + + if len(invalid_serials) > 0: + msg = _('The following serial numbers already exist or are invalid') + msg += ': ' + ', '.join(invalid_serials) + raise ValidationError({'serial_numbers': msg}) + return data diff --git a/src/backend/InvenTree/templates/js/translated/purchase_order.js b/src/backend/InvenTree/templates/js/translated/purchase_order.js index 170234b6bb..2ddfc71359 100644 --- a/src/backend/InvenTree/templates/js/translated/purchase_order.js +++ b/src/backend/InvenTree/templates/js/translated/purchase_order.js @@ -259,6 +259,7 @@ function poLineItemFields(options={}) { part: { icon: 'fa-shapes', filters: { + active: true, part_detail: true, supplier_detail: true, supplier: options.supplier, diff --git a/src/frontend/src/forms/PurchaseOrderForms.tsx b/src/frontend/src/forms/PurchaseOrderForms.tsx index e0f6b77c91..add7d92414 100644 --- a/src/frontend/src/forms/PurchaseOrderForms.tsx +++ b/src/frontend/src/forms/PurchaseOrderForms.tsx @@ -237,6 +237,12 @@ function LineItemFormRow({ onClose: () => props.changeFn(props.idx, 'location', undefined) }); + // Is this a trackable part? + const trackable: boolean = useMemo( + () => record.part_detail?.trackable ?? false, + [record] + ); + useEffect(() => { if (!!record.destination) { props.changeFn(props.idx, 'location', record.destination); @@ -303,6 +309,14 @@ function LineItemFormRow({ props.changeFn(props.idx, 'barcode', barcode); }, [barcode]); + const batchToolTip: string = useMemo(() => { + if (trackable) { + return t`Assign Batch Code and Serial Numbers`; + } else { + return t`Assign Batch Code`; + } + }, [trackable]); + // Update location field description on state change useEffect(() => { if (!opened) { @@ -418,9 +432,7 @@ function LineItemFormRow({ size="sm" onClick={() => batchHandlers.toggle()} icon={} - tooltip={t`Assign Batch Code${ - record.trackable && ' and Serial Numbers' - }`} + tooltip={batchToolTip} tooltipAlignment="top" variant={batchOpen ? 'filled' : 'transparent'} /> @@ -552,18 +564,20 @@ function LineItemFormRow({ fieldDefinition={{ field_type: 'string', label: t`Batch Code`, + description: t`Enter batch code for received items`, value: props.item.batch_code }} error={props.rowErrors?.batch_code?.message} /> props.changeFn(props.idx, 'serial_numbers', value) } fieldDefinition={{ field_type: 'string', - label: t`Serial numbers`, + label: t`Serial Numbers`, + description: t`Enter serial numbers for received items`, value: props.item.serial_numbers }} error={props.rowErrors?.serial_numbers?.message} diff --git a/src/frontend/src/tables/purchasing/PurchaseOrderLineItemTable.tsx b/src/frontend/src/tables/purchasing/PurchaseOrderLineItemTable.tsx index 02ffe97978..9a97a4c961 100644 --- a/src/frontend/src/tables/purchasing/PurchaseOrderLineItemTable.tsx +++ b/src/frontend/src/tables/purchasing/PurchaseOrderLineItemTable.tsx @@ -302,6 +302,10 @@ export function PurchaseOrderLineItemTable({ ); }, [order, poStatus]); + const orderPlaced: boolean = useMemo(() => { + return order.status == poStatus.PLACED; + }, [order, poStatus]); + const rowActions = useCallback( (record: any): RowAction[] => { let received = (record?.received ?? 0) >= (record?.quantity ?? 0); @@ -370,10 +374,10 @@ export function PurchaseOrderLineItemTable({ icon={} onClick={() => receiveLineItems.open()} disabled={table.selectedRecords.length === 0} - hidden={!orderOpen || !user.hasChangeRole(UserRoles.purchase_order)} + hidden={!orderPlaced || !user.hasChangeRole(UserRoles.purchase_order)} /> ]; - }, [orderId, user, table, orderOpen]); + }, [orderId, user, table, orderOpen, orderPlaced]); return ( <>