diff --git a/src/backend/InvenTree/InvenTree/api.py b/src/backend/InvenTree/InvenTree/api.py index f4d481048a..e4da445cb4 100644 --- a/src/backend/InvenTree/InvenTree/api.py +++ b/src/backend/InvenTree/InvenTree/api.py @@ -229,11 +229,11 @@ class InfoApiSerializer(serializers.Serializer): logo = serializers.CharField() splash = serializers.CharField() - login_message = serializers.CharField() - navbar_message = serializers + login_message = serializers.CharField(allow_null=True) + navbar_message = serializers.CharField(allow_null=True) server = serializers.CharField(read_only=True) - id = serializers.CharField(read_only=True) + id = serializers.CharField(read_only=True, allow_null=True) version = serializers.CharField(read_only=True) instance = serializers.CharField(read_only=True) apiVersion = serializers.IntegerField(read_only=True) # noqa: N815 @@ -246,15 +246,13 @@ class InfoApiSerializer(serializers.Serializer): email_configured = serializers.BooleanField(read_only=True) debug_mode = serializers.BooleanField(read_only=True) docker_mode = serializers.BooleanField(read_only=True) - default_locale = serializers.ChoiceField( - choices=settings.LOCALE_CODES, read_only=True - ) + default_locale = serializers.CharField(read_only=True) customize = CustomizeSerializer(read_only=True) system_health = serializers.BooleanField(read_only=True) database = serializers.CharField(read_only=True) platform = serializers.CharField(read_only=True) installer = serializers.CharField(read_only=True) - target = serializers.CharField(read_only=True) + target = serializers.CharField(read_only=True, allow_null=True) django_admin = serializers.CharField(read_only=True) settings = SettingsSerializer(read_only=True, many=False) diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index de04146469..bbf63ceff5 100644 --- a/src/backend/InvenTree/InvenTree/api_version.py +++ b/src/backend/InvenTree/InvenTree/api_version.py @@ -1,13 +1,16 @@ """InvenTree API version information.""" # InvenTree API version -INVENTREE_API_VERSION = 339 +INVENTREE_API_VERSION = 340 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ +v340 -> 2025-04-15 : https://github.com/inventree/InvenTree/pull/9546 + - Add nullable to various fields to make them not required + v339 -> 2025-04-15 : https://github.com/inventree/InvenTree/pull/9283 - Remove need for source in /plugins/ui/features diff --git a/src/backend/InvenTree/build/serializers.py b/src/backend/InvenTree/build/serializers.py index 84a5f1c517..aaef6bae81 100644 --- a/src/backend/InvenTree/build/serializers.py +++ b/src/backend/InvenTree/build/serializers.py @@ -1431,7 +1431,12 @@ class BuildLineSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali pricing=False, ) build_detail = BuildSerializer( - label=_('Build'), source='build', part_detail=False, many=False, read_only=True + label=_('Build'), + source='build', + part_detail=False, + many=False, + read_only=True, + allow_null=True, ) # Annotated (calculated) fields diff --git a/src/backend/InvenTree/common/serializers.py b/src/backend/InvenTree/common/serializers.py index c30ed95398..1931e35d72 100644 --- a/src/backend/InvenTree/common/serializers.py +++ b/src/backend/InvenTree/common/serializers.py @@ -812,6 +812,4 @@ class DataOutputSerializer(InvenTreeModelSerializer): user_detail = UserSerializer(source='user', read_only=True, many=False) - output = InvenTreeAttachmentSerializerField( - required=False, allow_null=True, read_only=True - ) + output = InvenTreeAttachmentSerializerField(allow_null=True, read_only=True) diff --git a/src/backend/InvenTree/company/serializers.py b/src/backend/InvenTree/company/serializers.py index 3214595901..fa8605bcc3 100644 --- a/src/backend/InvenTree/company/serializers.py +++ b/src/backend/InvenTree/company/serializers.py @@ -169,7 +169,7 @@ class CompanySerializer( read_only=True, ) - primary_address = AddressSerializer(required=False, allow_null=True, read_only=True) + primary_address = AddressSerializer(allow_null=True, read_only=True) image = InvenTreeImageSerializerField(required=False, allow_null=True) @@ -463,7 +463,7 @@ class SupplierPartSerializer( ) MPN = serializers.CharField( - source='manufacturer_part.MPN', read_only=True, label=_('MPN') + source='manufacturer_part.MPN', read_only=True, allow_null=True, label=_('MPN') ) # Date fields diff --git a/src/backend/InvenTree/importer/api.py b/src/backend/InvenTree/importer/api.py index 96e8bacaf8..45bda7e430 100644 --- a/src/backend/InvenTree/importer/api.py +++ b/src/backend/InvenTree/importer/api.py @@ -65,7 +65,7 @@ class DataImporterModelSerializer(serializers.Serializer): serializer = serializers.CharField(read_only=True) model_type = serializers.CharField(read_only=True) - api_url = serializers.URLField(read_only=True) + api_url = serializers.URLField(read_only=True, allow_null=True) class DataImporterModelList(APIView): diff --git a/src/backend/InvenTree/order/serializers.py b/src/backend/InvenTree/order/serializers.py index 8034c735f6..2b9d81363b 100644 --- a/src/backend/InvenTree/order/serializers.py +++ b/src/backend/InvenTree/order/serializers.py @@ -112,11 +112,13 @@ class AbstractOrderSerializer(DataImportExportSerializerMixin, serializers.Seria import_exclude_fields = ['notes', 'duplicate'] # Number of line items in this order - line_items = serializers.IntegerField(read_only=True, label=_('Line Items')) + line_items = serializers.IntegerField( + read_only=True, allow_null=True, label=_('Line Items') + ) # Number of completed line items (this is an annotated field) completed_lines = serializers.IntegerField( - read_only=True, label=_('Completed Lines') + read_only=True, allow_null=True, label=_('Completed Lines') ) # Human-readable status text (read-only) @@ -156,7 +158,7 @@ class AbstractOrderSerializer(DataImportExportSerializerMixin, serializers.Seria ) # Boolean field indicating if this order is overdue (Note: must be annotated) - overdue = serializers.BooleanField(required=False, read_only=True) + overdue = serializers.BooleanField(read_only=True, allow_null=True) barcode_hash = serializers.CharField(read_only=True) @@ -609,7 +611,7 @@ class PurchaseOrderLineItemSerializer( received = serializers.FloatField(default=0, read_only=True) - overdue = serializers.BooleanField(required=False, read_only=True) + overdue = serializers.BooleanField(read_only=True, allow_null=True) total_price = serializers.FloatField(read_only=True) @@ -632,7 +634,7 @@ class PurchaseOrderLineItemSerializer( ) destination_detail = stock.serializers.LocationBriefSerializer( - source='get_destination', read_only=True + source='get_destination', read_only=True, allow_null=True ) purchase_price_currency = InvenTreeCurrencySerializer( @@ -652,14 +654,22 @@ class PurchaseOrderLineItemSerializer( write_only=True, ) - sku = serializers.CharField(source='part.SKU', read_only=True, label=_('SKU')) + sku = serializers.CharField( + source='part.SKU', read_only=True, allow_null=True, label=_('SKU') + ) mpn = serializers.CharField( - source='part.manufacturer_part.MPN', read_only=True, label=_('MPN') + source='part.manufacturer_part.MPN', + read_only=True, + allow_null=True, + label=_('MPN'), ) ipn = serializers.CharField( - source='part.part.IPN', read_only=True, label=_('Internal Part Number') + source='part.part.IPN', + read_only=True, + allow_null=True, + label=_('Internal Part Number'), ) internal_part = serializers.PrimaryKeyRelatedField( @@ -1078,10 +1088,12 @@ class SalesOrderSerializer( source='customer', many=False, read_only=True, allow_null=True ) - shipments_count = serializers.IntegerField(read_only=True, label=_('Shipments')) + shipments_count = serializers.IntegerField( + read_only=True, allow_null=True, label=_('Shipments') + ) completed_shipments_count = serializers.IntegerField( - read_only=True, label=_('Completed Shipments') + read_only=True, allow_null=True, label=_('Completed Shipments') ) @@ -1253,7 +1265,7 @@ class SalesOrderLineItemSerializer( ) # Annotated fields - overdue = serializers.BooleanField(required=False, read_only=True) + overdue = serializers.BooleanField(read_only=True, allow_null=True) available_stock = serializers.FloatField(read_only=True) available_variant_stock = serializers.FloatField(read_only=True) on_order = serializers.FloatField(label=_('On Order'), read_only=True) @@ -1316,7 +1328,7 @@ class SalesOrderShipmentSerializer(NotesFieldMixin, InvenTreeModelSerializer): return queryset allocated_items = serializers.IntegerField( - read_only=True, label=_('Allocated Items') + read_only=True, allow_null=True, label=_('Allocated Items') ) order_detail = SalesOrderSerializer( @@ -1389,7 +1401,7 @@ class SalesOrderAllocationSerializer(InvenTreeModelSerializer): order = serializers.PrimaryKeyRelatedField( source='line.order', many=False, read_only=True ) - serial = serializers.CharField(source='get_serial', read_only=True) + serial = serializers.CharField(source='get_serial', read_only=True, allow_null=True) quantity = serializers.FloatField(read_only=False) location = serializers.PrimaryKeyRelatedField( source='item.location', many=False, read_only=True diff --git a/src/backend/InvenTree/part/serializers.py b/src/backend/InvenTree/part/serializers.py index 5c8b4870ca..1522304d7a 100644 --- a/src/backend/InvenTree/part/serializers.py +++ b/src/backend/InvenTree/part/serializers.py @@ -126,9 +126,13 @@ class CategorySerializer( help_text=_('Parent part category'), ) - part_count = serializers.IntegerField(read_only=True, label=_('Parts')) + part_count = serializers.IntegerField( + read_only=True, allow_null=True, label=_('Parts') + ) - subcategories = serializers.IntegerField(read_only=True, label=_('Subcategories')) + subcategories = serializers.IntegerField( + read_only=True, allow_null=True, label=_('Subcategories') + ) level = serializers.IntegerField(read_only=True) @@ -149,7 +153,7 @@ class CategorySerializer( max_length=100, ) - parent_default_location = serializers.IntegerField(read_only=True) + parent_default_location = serializers.IntegerField(read_only=True, allow_null=True) class CategoryTree(InvenTree.serializers.InvenTreeModelSerializer): @@ -335,6 +339,7 @@ class PartParameterTemplateSerializer( parts = serializers.IntegerField( read_only=True, + allow_null=True, label=_('Parts'), help_text=_('Number of parts using this template'), ) @@ -396,7 +401,9 @@ class PartBriefSerializer(InvenTree.serializers.InvenTreeModelSerializer): read_only=True, allow_null=True ) - image = InvenTree.serializers.InvenTreeImageSerializerField(read_only=True) + image = InvenTree.serializers.InvenTreeImageSerializerField( + read_only=True, allow_null=True + ) thumbnail = serializers.CharField(source='get_thumbnail_url', read_only=True) IPN = serializers.CharField( @@ -924,25 +931,47 @@ class PartSerializer( ) # Annotated fields - allocated_to_build_orders = serializers.FloatField(read_only=True) - allocated_to_sales_orders = serializers.FloatField(read_only=True) - building = serializers.FloatField(read_only=True, label=_('Building')) - in_stock = serializers.FloatField(read_only=True, label=_('In Stock')) - ordering = serializers.FloatField(read_only=True, label=_('On Order')) - required_for_build_orders = serializers.IntegerField(read_only=True) - required_for_sales_orders = serializers.IntegerField(read_only=True) - stock_item_count = serializers.IntegerField(read_only=True, label=_('Stock Items')) - revision_count = serializers.IntegerField(read_only=True, label=_('Revisions')) - suppliers = serializers.IntegerField(read_only=True, label=_('Suppliers')) - total_in_stock = serializers.FloatField(read_only=True, label=_('Total Stock')) - external_stock = serializers.FloatField(read_only=True, label=_('External Stock')) + allocated_to_build_orders = serializers.FloatField(read_only=True, allow_null=True) + allocated_to_sales_orders = serializers.FloatField(read_only=True, allow_null=True) + building = serializers.FloatField( + read_only=True, allow_null=True, label=_('Building') + ) + in_stock = serializers.FloatField( + read_only=True, allow_null=True, label=_('In Stock') + ) + ordering = serializers.FloatField( + read_only=True, allow_null=True, label=_('On Order') + ) + required_for_build_orders = serializers.IntegerField( + read_only=True, allow_null=True + ) + required_for_sales_orders = serializers.IntegerField( + read_only=True, allow_null=True + ) + stock_item_count = serializers.IntegerField( + read_only=True, allow_null=True, label=_('Stock Items') + ) + revision_count = serializers.IntegerField( + read_only=True, allow_null=True, label=_('Revisions') + ) + suppliers = serializers.IntegerField( + read_only=True, allow_null=True, label=_('Suppliers') + ) + total_in_stock = serializers.FloatField( + read_only=True, allow_null=True, label=_('Total Stock') + ) + external_stock = serializers.FloatField( + read_only=True, allow_null=True, label=_('External Stock') + ) unallocated_stock = serializers.FloatField( - read_only=True, label=_('Unallocated Stock') + read_only=True, allow_null=True, label=_('Unallocated Stock') ) category_default_location = serializers.IntegerField( read_only=True, allow_null=True ) - variant_stock = serializers.FloatField(read_only=True, label=_('Variant Stock')) + variant_stock = serializers.FloatField( + read_only=True, allow_null=True, label=_('Variant Stock') + ) minimum_stock = serializers.FloatField( required=False, label=_('Minimum Stock'), default=0 @@ -1657,11 +1686,17 @@ class BomItemSerializer( allow_null=True, ) - on_order = serializers.FloatField(label=_('On Order'), read_only=True) + on_order = serializers.FloatField( + label=_('On Order'), read_only=True, allow_null=True + ) - building = serializers.FloatField(label=_('In Production'), read_only=True) + building = serializers.FloatField( + label=_('In Production'), read_only=True, allow_null=True + ) - can_build = serializers.FloatField(label=_('Can Build'), read_only=True) + can_build = serializers.FloatField( + label=_('Can Build'), read_only=True, allow_null=True + ) # Cached pricing fields pricing_min = InvenTree.serializers.InvenTreeMoneySerializer( @@ -1684,12 +1719,14 @@ class BomItemSerializer( ) # Annotated fields for available stock - available_stock = serializers.FloatField(label=_('Available Stock'), read_only=True) + available_stock = serializers.FloatField( + label=_('Available Stock'), read_only=True, allow_null=True + ) - available_substitute_stock = serializers.FloatField(read_only=True) - available_variant_stock = serializers.FloatField(read_only=True) + available_substitute_stock = serializers.FloatField(read_only=True, allow_null=True) + available_variant_stock = serializers.FloatField(read_only=True, allow_null=True) - external_stock = serializers.FloatField(read_only=True) + external_stock = serializers.FloatField(read_only=True, allow_null=True) @staticmethod def annotate_queryset(queryset): diff --git a/src/backend/InvenTree/stock/serializers.py b/src/backend/InvenTree/stock/serializers.py index 8a82328a54..2c6b838394 100644 --- a/src/backend/InvenTree/stock/serializers.py +++ b/src/backend/InvenTree/stock/serializers.py @@ -655,20 +655,20 @@ class StockItemSerializer( # Annotated fields allocated = serializers.FloatField( - required=False, read_only=True, label=_('Allocated Quantity') + read_only=True, allow_null=True, label=_('Allocated Quantity') ) expired = serializers.BooleanField( - required=False, read_only=True, label=_('Expired') + read_only=True, allow_null=True, label=_('Expired') ) installed_items = serializers.IntegerField( - read_only=True, required=False, label=_('Installed Items') + read_only=True, allow_null=True, label=_('Installed Items') ) child_items = serializers.IntegerField( - read_only=True, required=False, label=_('Child Items') + read_only=True, allow_null=True, label=_('Child Items') ) - stale = serializers.BooleanField(required=False, read_only=True, label=_('Stale')) + stale = serializers.BooleanField(read_only=True, allow_null=True, label=_('Stale')) tracking_items = serializers.IntegerField( - read_only=True, required=False, label=_('Tracking Items') + read_only=True, allow_null=True, label=_('Tracking Items') ) purchase_price = InvenTree.serializers.InvenTreeMoneySerializer( @@ -1161,7 +1161,7 @@ class StockLocationTypeSerializer(InvenTree.serializers.InvenTreeModelSerializer read_only_fields = ['location_count'] - location_count = serializers.IntegerField(read_only=True) + location_count = serializers.IntegerField(read_only=True, allow_null=True) @staticmethod def annotate_queryset(queryset): @@ -1273,7 +1273,7 @@ class LocationSerializer( # Detail for location type location_type_detail = StockLocationTypeSerializer( - source='location_type', read_only=True, many=False + source='location_type', read_only=True, allow_null=True, many=False ) diff --git a/src/backend/InvenTree/users/serializers.py b/src/backend/InvenTree/users/serializers.py index 69ded40739..5950fb286d 100644 --- a/src/backend/InvenTree/users/serializers.py +++ b/src/backend/InvenTree/users/serializers.py @@ -283,9 +283,13 @@ class GroupSerializer(InvenTreeModelSerializer): """Return a list of permissions associated with the group.""" return generate_permission_dict(group.permissions.all()) - roles = RuleSetSerializer(source='rule_sets', many=True, read_only=True) + roles = RuleSetSerializer( + source='rule_sets', many=True, read_only=True, allow_null=True + ) - users = UserSerializer(source='user_set', many=True, read_only=True) + users = UserSerializer( + source='user_set', many=True, read_only=True, allow_null=True + ) class ExtendedUserSerializer(UserSerializer):