diff --git a/src/backend/InvenTree/stock/models.py b/src/backend/InvenTree/stock/models.py index b781176096..d0f21f6862 100644 --- a/src/backend/InvenTree/stock/models.py +++ b/src/backend/InvenTree/stock/models.py @@ -1985,9 +1985,18 @@ class StockItem( Returns: The new StockItem object + Raises: + ValidationError: If the stock item cannot be split + - The provided quantity will be subtracted from this item and given to the new one. - The new item will have a different StockItem ID, while this will remain the same. """ + # Run initial checks to test if the stock item can actually be "split" + + # Cannot split a stock item which is in production + if self.is_building: + raise ValidationError(_('Stock item is currently in production')) + notes = kwargs.get('notes', '') # Do not split a serialized part diff --git a/src/backend/InvenTree/stock/serializers.py b/src/backend/InvenTree/stock/serializers.py index fc91e16660..e8deab1bd8 100644 --- a/src/backend/InvenTree/stock/serializers.py +++ b/src/backend/InvenTree/stock/serializers.py @@ -1565,18 +1565,18 @@ class StockAdjustmentItemSerializer(serializers.Serializer): help_text=_('StockItem primary key value'), ) - def validate_pk(self, pk): + def validate_pk(self, stock_item: StockItem) -> StockItem: """Ensure the stock item is valid.""" allow_out_of_stock_transfer = get_global_setting( 'STOCK_ALLOW_OUT_OF_STOCK_TRANSFER', backup_value=False, cache=False ) - if not allow_out_of_stock_transfer and not pk.is_in_stock( + if not allow_out_of_stock_transfer and not stock_item.is_in_stock( check_status=False, check_quantity=False ): raise ValidationError(_('Stock item is not in stock')) - return pk + return stock_item quantity = serializers.DecimalField( max_digits=15, decimal_places=5, min_value=Decimal(0), required=True diff --git a/src/frontend/src/pages/stock/StockDetail.tsx b/src/frontend/src/pages/stock/StockDetail.tsx index 3b069e99f4..eab214acce 100644 --- a/src/frontend/src/pages/stock/StockDetail.tsx +++ b/src/frontend/src/pages/stock/StockDetail.tsx @@ -667,13 +667,15 @@ export default function StockDetail() { }); const stockActions = useMemo(() => { - const inStock = + // Can this stock item be transferred to a different location? + const canTransfer = user.hasChangeRole(UserRoles.stock) && !stockitem.sales_order && !stockitem.belongs_to && !stockitem.customer && - !stockitem.consumed_by && - !stockitem.is_building; + !stockitem.consumed_by; + + const isBuilding = stockitem.is_building; const serial = stockitem.serial; const serialized = @@ -704,7 +706,7 @@ export default function StockDetail() { { name: t`Count`, tooltip: t`Count stock`, - hidden: serialized || !inStock, + hidden: serialized || !canTransfer || isBuilding, icon: ( ), @@ -715,7 +717,7 @@ export default function StockDetail() { { name: t`Add`, tooltip: t`Add Stock`, - hidden: serialized || !inStock, + hidden: serialized || !canTransfer || isBuilding, icon: , onClick: () => { stockitem.pk && addStockItem.open(); @@ -724,7 +726,11 @@ export default function StockDetail() { { name: t`Remove`, tooltip: t`Remove Stock`, - hidden: serialized || !inStock || stockitem.quantity <= 0, + hidden: + serialized || + !canTransfer || + isBuilding || + stockitem.quantity <= 0, icon: , onClick: () => { stockitem.pk && removeStockItem.open(); @@ -733,7 +739,7 @@ export default function StockDetail() { { name: t`Transfer`, tooltip: t`Transfer Stock`, - hidden: !inStock, + hidden: !canTransfer, icon: ( ), @@ -745,8 +751,10 @@ export default function StockDetail() { name: t`Serialize`, tooltip: t`Serialize stock`, hidden: - !inStock || + !canTransfer || + isBuilding || serialized || + stockitem?.quantity != 1 || stockitem?.part_detail?.trackable != true, icon: , onClick: () => { diff --git a/src/frontend/src/tables/build/BuildOutputTable.tsx b/src/frontend/src/tables/build/BuildOutputTable.tsx index 594d9471c1..3adf96be82 100644 --- a/src/frontend/src/tables/build/BuildOutputTable.tsx +++ b/src/frontend/src/tables/build/BuildOutputTable.tsx @@ -575,6 +575,7 @@ export default function BuildOutputTable({ props={{ params: { part_detail: true, + location_detail: true, tests: true, is_building: true, build: buildId