diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index eb2b04d7cc..19dc675787 100644 --- a/src/backend/InvenTree/InvenTree/api_version.py +++ b/src/backend/InvenTree/InvenTree/api_version.py @@ -1,12 +1,15 @@ """InvenTree API version information.""" # InvenTree API version -INVENTREE_API_VERSION = 217 +INVENTREE_API_VERSION = 218 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ +v218 - 2024-07-11 : https://github.com/inventree/InvenTree/pull/7619 + - Adds "can_build" field to the BomItem API + v217 - 2024-07-09 : https://github.com/inventree/InvenTree/pull/7599 - Fixes bug in "project_code" field for order API endpoints diff --git a/src/backend/InvenTree/part/api.py b/src/backend/InvenTree/part/api.py index 95d0b5590a..07a8c56793 100644 --- a/src/backend/InvenTree/part/api.py +++ b/src/backend/InvenTree/part/api.py @@ -1841,7 +1841,6 @@ class BomList(BomMixin, DataExportViewMixin, ListCreateDestroyAPIView): """ filterset_class = BomFilter - filter_backends = SEARCH_ORDER_FILTER_ALIAS search_fields = [ @@ -1855,6 +1854,7 @@ class BomList(BomMixin, DataExportViewMixin, ListCreateDestroyAPIView): ] ordering_fields = [ + 'can_build', 'quantity', 'sub_part', 'available_stock', diff --git a/src/backend/InvenTree/part/serializers.py b/src/backend/InvenTree/part/serializers.py index 56c34bb5c3..c2aa330339 100644 --- a/src/backend/InvenTree/part/serializers.py +++ b/src/backend/InvenTree/part/serializers.py @@ -1506,6 +1506,8 @@ class BomItemSerializer( 'on_order', # Annotated field describing quantity being built 'building', + # Annotate the total potential quantity we can build + 'can_build', ] def __init__(self, *args, **kwargs): @@ -1560,6 +1562,8 @@ class BomItemSerializer( building = serializers.FloatField(label=_('In Production'), read_only=True) + can_build = serializers.FloatField(label=_('Can Build'), read_only=True) + # Cached pricing fields pricing_min = InvenTree.serializers.InvenTreeMoneySerializer( source='sub_part.pricing_data.overall_min', allow_null=True, read_only=True @@ -1733,6 +1737,20 @@ class BomItemSerializer( ) ) + # Annotate the "can_build" quantity + queryset = queryset.alias( + total_stock=ExpressionWrapper( + F('available_variant_stock') + + F('available_substitute_stock') + + F('available_stock'), + output_field=FloatField(), + ) + ).annotate( + can_build=ExpressionWrapper( + F('total_stock') / F('quantity'), output_field=FloatField() + ) + ) + return queryset diff --git a/src/frontend/src/tables/bom/BomTable.tsx b/src/frontend/src/tables/bom/BomTable.tsx index 495027e661..053b10d367 100644 --- a/src/frontend/src/tables/bom/BomTable.tsx +++ b/src/frontend/src/tables/bom/BomTable.tsx @@ -247,19 +247,38 @@ export function BomTable({ { accessor: 'can_build', title: t`Can Build`, - sortable: false, // TODO: Custom sorting via API + sortable: true, render: (record: any) => { + if (record.can_build === null || record.can_build === undefined) { + return '-'; + } + + if (!isFinite(record.can_build) || isNaN(record.can_build)) { + return '-'; + } + + let can_build = Math.trunc(record.can_build); + let value = ( + + {can_build} + + ); + + let extra = []; + if (record.consumable) { - return ( - {t`Consumable item`} + extra.push({t`Consumable item`}); + } else if (can_build <= 0) { + extra.push( + {t`No available stock`} ); } - let can_build = availableStockQuantity(record) / record.quantity; - can_build = Math.trunc(can_build); - return ( - {can_build} + ); } },