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}
+
);
}
},