diff --git a/lib/api.dart b/lib/api.dart index 3abb40a4..1f07581c 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -192,12 +192,10 @@ class InvenTreeAPI { } } - // Minimum required API version for server. - // Deliberately kept low/permissive: individual features are gated by their - // own apiVersion checks below (up to 496 at last count), so this floor - // only needs to reject servers old enough to predate those checks - // entirely, not enforce feature parity. Last reviewed 2026-07-06. - static const _minApiVersion = 100; + // Minimum required API version for server + // 2024-03-02 (release 0.14.0) + // Ref: https://github.com/inventree/InvenTree/releases/tag/0.14.0 + static const _minApiVersion = 180; bool _strictHttps = false; @@ -294,45 +292,9 @@ class InvenTreeAPI { String get serverVersion => (serverInfo["version"] ?? "") as String; int get apiVersion => (serverInfo["apiVersion"] ?? 1) as int; - // Consolidated search request API v102 or newer - bool get supportsConsolidatedSearch => apiVersion >= 102; - - // ReturnOrder supports API v104 or newer - bool get supportsReturnOrders => apiVersion >= 104; - - // "Contact" model exposed to API - bool get supportsContactModel => apiVersion >= 104; - - // Status label endpoints API v105 or newer - bool get supportsStatusLabelEndpoints => apiVersion >= 105; - - // Regex search API v106 or newer - bool get supportsRegexSearch => apiVersion >= 106; - - // Order barcodes API v107 or newer - bool get supportsOrderBarcodes => apiVersion >= 107; - - // Project codes require v109 or newer - bool get supportsProjectCodes => apiVersion >= 109; - - // Does the server support extra fields on stock adjustment actions? - bool get supportsStockAdjustExtraFields => apiVersion >= 133; - - // Does the server support receiving items against a PO using barcodes? - bool get supportsBarcodePOReceiveEndpoint => apiVersion >= 139; - - // Does the server support adding line items to a PO using barcodes? - bool get supportsBarcodePOAddLineEndpoint => apiVersion >= 153; - - // Does the server support allocating stock to sales order using barcodes? - bool get supportsBarcodeSOAllocateEndpoint => apiVersion >= 160; - - // Does the server support the "modern" test results API - // Ref: https://github.com/inventree/InvenTree/pull/6430/ - bool get supportsModernTestResults => apiVersion >= 169; - - // Does the server support "null" top-level filtering for PartCategory and StockLocation endpoints? - bool get supportsNullTopLevelFiltering => apiVersion < 174; + /* API Version Checks + * These functions are used to determine if the server supports a particular feature + */ // Does the server support "active" status on Company and SupplierPart API endpoints? bool get supportsCompanyActiveStatus => apiVersion >= 189; diff --git a/lib/barcode/barcode.dart b/lib/barcode/barcode.dart index f9ad5b3f..64be1d46 100644 --- a/lib/barcode/barcode.dart +++ b/lib/barcode/barcode.dart @@ -272,14 +272,11 @@ class BarcodeScanHandler extends BarcodeHandler { InvenTreePart.MODEL_TYPE, InvenTreeStockLocation.MODEL_TYPE, InvenTreeCompany.MODEL_TYPE, + InvenTreeBuildOrder.MODEL_TYPE, + InvenTreePurchaseOrder.MODEL_TYPE, + InvenTreeSalesOrder.MODEL_TYPE, ]; - if (InvenTreeAPI().supportsOrderBarcodes) { - validModels.add(InvenTreeBuildOrder.MODEL_TYPE); - validModels.add(InvenTreePurchaseOrder.MODEL_TYPE); - validModels.add(InvenTreeSalesOrder.MODEL_TYPE); - } - for (var key in validModels) { if (data.containsKey(key)) { try { diff --git a/lib/inventree/purchase_order.dart b/lib/inventree/purchase_order.dart index 65af3610..cf82aff5 100644 --- a/lib/inventree/purchase_order.dart +++ b/lib/inventree/purchase_order.dart @@ -63,10 +63,6 @@ class InvenTreePurchaseOrder extends InvenTreeOrder { }, }; - if (!InvenTreeAPI().supportsProjectCodes) { - fields.remove("project_code"); - } - if (!InvenTreeAPI().supportsPurchaseOrderDestination) { fields.remove("destination"); } diff --git a/lib/inventree/sales_order.dart b/lib/inventree/sales_order.dart index 9e239a77..02786b19 100644 --- a/lib/inventree/sales_order.dart +++ b/lib/inventree/sales_order.dart @@ -62,14 +62,6 @@ class InvenTreeSalesOrder extends InvenTreeOrder { }, }; - if (!InvenTreeAPI().supportsProjectCodes) { - fields.remove("project_code"); - } - - if (!InvenTreeAPI().supportsContactModel) { - fields.remove("contact"); - } - if (!InvenTreeAPI().supportsStartDate) { fields.remove("start_date"); } diff --git a/lib/inventree/status_codes.dart b/lib/inventree/status_codes.dart index fb9bcf6f..fb3b5a1f 100644 --- a/lib/inventree/status_codes.dart +++ b/lib/inventree/status_codes.dart @@ -46,11 +46,6 @@ class InvenTreeStatusCode { return; } - // The server must support this feature! - if (!InvenTreeAPI().supportsStatusLabelEndpoints) { - return; - } - debug("Loading status codes from ${URL}"); APIResponse response = await InvenTreeAPI().get(URL); diff --git a/lib/inventree/stock.dart b/lib/inventree/stock.dart index 918ff8f0..0b7f7dac 100644 --- a/lib/inventree/stock.dart +++ b/lib/inventree/stock.dart @@ -34,7 +34,6 @@ class InvenTreeStockItemTestResult extends InvenTreeModel { Map> formFields() { Map> fields = { "stock_item": {"hidden": true}, - "test": {}, "template": { "filters": {"enabled": "true"}, }, @@ -44,12 +43,6 @@ class InvenTreeStockItemTestResult extends InvenTreeModel { "attachment": {}, }; - if (InvenTreeAPI().supportsModernTestResults) { - fields.remove("test"); - } else { - fields.remove("template"); - } - return fields; } @@ -168,12 +161,6 @@ class InvenTreeStockItem extends InvenTreeModel { fields["quantity"]?["hidden"] = true; } - // Old API does not support these fields - if (!api.supportsStockAdjustExtraFields) { - fields.remove("packaging"); - fields.remove("status"); - } - return fields; } diff --git a/lib/widget/order/purchase_order_detail.dart b/lib/widget/order/purchase_order_detail.dart index f149034e..a055ee15 100644 --- a/lib/widget/order/purchase_order_detail.dart +++ b/lib/widget/order/purchase_order_detail.dart @@ -241,7 +241,7 @@ class _PurchaseOrderDetailState List barcodeButtons(BuildContext context) { List actions = []; - if (api.supportsBarcodePOReceiveEndpoint && widget.order.isPlaced) { + if (widget.order.isPlaced) { actions.add( SpeedDialChild( child: Icon(Icons.barcode_reader), @@ -258,7 +258,7 @@ class _PurchaseOrderDetailState ); } - if (widget.order.isPending && api.supportsBarcodePOAddLineEndpoint) { + if (widget.order.isPending) { actions.add( SpeedDialChild( child: Icon(TablerIcons.circle_plus, color: COLOR_SUCCESS), @@ -288,12 +288,10 @@ class _PurchaseOrderDetailState INV_PO_SHOW_CAMERA, true, ); - supportProjectCodes = - api.supportsProjectCodes && - await api.getGlobalBooleanSetting( - "PROJECT_CODES_ENABLED", - backup: true, - ); + supportProjectCodes = await api.getGlobalBooleanSetting( + "PROJECT_CODES_ENABLED", + backup: true, + ); completedLines = 0; @@ -367,11 +365,6 @@ class _PurchaseOrderDetailState // Cannot edit supplier field from here fields.remove("supplier"); - // Contact model not supported by server - if (!api.supportsContactModel) { - fields.remove("contact"); - } - // ProjectCode model not supported by server if (!supportProjectCodes) { fields.remove("project_code"); diff --git a/lib/widget/order/purchase_order_list.dart b/lib/widget/order/purchase_order_list.dart index 85fc668c..0d75f1d8 100644 --- a/lib/widget/order/purchase_order_list.dart +++ b/lib/widget/order/purchase_order_list.dart @@ -79,17 +79,15 @@ class _PurchaseOrderListWidgetState List barcodeButtons(BuildContext context) { List actions = []; - if (api.supportsBarcodePOReceiveEndpoint) { - actions.add( - SpeedDialChild( - child: Icon(Icons.barcode_reader), - label: L10().scanReceivedParts, - onTap: () async { - scanBarcode(context, handler: POReceiveBarcodeHandler()); - }, - ), - ); - } + actions.add( + SpeedDialChild( + child: Icon(Icons.barcode_reader), + label: L10().scanReceivedParts, + onTap: () async { + scanBarcode(context, handler: POReceiveBarcodeHandler()); + }, + ), + ); return actions; } diff --git a/lib/widget/order/sales_order_detail.dart b/lib/widget/order/sales_order_detail.dart index 6a53293c..38fe225d 100644 --- a/lib/widget/order/sales_order_detail.dart +++ b/lib/widget/order/sales_order_detail.dart @@ -239,20 +239,18 @@ class _SalesOrderDetailState extends RefreshableState { ), ); - if (api.supportsBarcodeSOAllocateEndpoint) { - actions.add( - SpeedDialChild( - child: Icon(TablerIcons.transition_right), - label: L10().allocateStock, - onTap: () async { - scanBarcode( - context, - handler: SOAllocateStockHandler(salesOrder: widget.order), - ); - }, - ), - ); - } + actions.add( + SpeedDialChild( + child: Icon(TablerIcons.transition_right), + label: L10().allocateStock, + onTap: () async { + scanBarcode( + context, + handler: SOAllocateStockHandler(salesOrder: widget.order), + ); + }, + ), + ); } return actions; @@ -263,12 +261,10 @@ class _SalesOrderDetailState extends RefreshableState { await widget.order.reload(); await api.SalesOrderStatus.load(); - supportsProjectCodes = - api.supportsProjectCodes && - await api.getGlobalBooleanSetting( - "PROJECT_CODES_ENABLED", - backup: true, - ); + supportsProjectCodes = await api.getGlobalBooleanSetting( + "PROJECT_CODES_ENABLED", + backup: true, + ); showCameraShortcut = await InvenTreeSettingsManager().getBool( INV_SO_SHOW_CAMERA, true, @@ -312,11 +308,6 @@ class _SalesOrderDetailState extends RefreshableState { fields.remove("customer"); - // Contact model not supported by server - if (!api.supportsContactModel) { - fields.remove("contact"); - } - // ProjectCode model not supported by server if (!supportsProjectCodes) { fields.remove("project_code"); diff --git a/lib/widget/order/so_line_detail.dart b/lib/widget/order/so_line_detail.dart index d6e2a986..73092212 100644 --- a/lib/widget/order/so_line_detail.dart +++ b/lib/widget/order/so_line_detail.dart @@ -128,23 +128,21 @@ class _SOLineDetailWidgetState extends RefreshableState { List actions = []; if (order != null && order!.isOpen && InvenTreeSOLineItem().canCreate) { - if (api.supportsBarcodeSOAllocateEndpoint) { - actions.add( - SpeedDialChild( - child: Icon(TablerIcons.transition_right), - label: L10().allocateStock, - onTap: () async { - scanBarcode( - context, - handler: SOAllocateStockHandler( - salesOrder: order, - lineItem: widget.item, - ), - ); - }, - ), - ); - } + actions.add( + SpeedDialChild( + child: Icon(TablerIcons.transition_right), + label: L10().allocateStock, + onTap: () async { + scanBarcode( + context, + handler: SOAllocateStockHandler( + salesOrder: order, + lineItem: widget.item, + ), + ); + }, + ), + ); } return actions; diff --git a/lib/widget/part/category_display.dart b/lib/widget/part/category_display.dart index bb9464be..34214aca 100644 --- a/lib/widget/part/category_display.dart +++ b/lib/widget/part/category_display.dart @@ -192,7 +192,7 @@ class _CategoryDisplayState extends RefreshableState { if (parent != null) { filters["parent"] = parent.toString(); - } else if (api.supportsNullTopLevelFiltering) { + } else { filters["parent"] = "null"; } diff --git a/lib/widget/search.dart b/lib/widget/search.dart index 40c5a333..8a470277 100644 --- a/lib/widget/search.dart +++ b/lib/widget/search.dart @@ -255,124 +255,37 @@ class _SearchDisplayState extends RefreshableState { return; } - // Consolidated search allows us to perform *all* searches in a single query - if (api.supportsConsolidatedSearch) { - Map body = { - "limit": 1, - "search": term, + Map body = { + "limit": 1, + "search": term, - InvenTreePart.MODEL_TYPE: {}, - InvenTreePartCategory.MODEL_TYPE: {}, - InvenTreeStockItem.MODEL_TYPE: {}, - InvenTreeStockLocation.MODEL_TYPE: {}, - InvenTreePurchaseOrder.MODEL_TYPE: {}, - InvenTreeSalesOrder.MODEL_TYPE: {}, - InvenTreeSupplierPart.MODEL_TYPE: {}, - InvenTreeManufacturerPart.MODEL_TYPE: {}, - }; + InvenTreePart.MODEL_TYPE: {}, + InvenTreePartCategory.MODEL_TYPE: {}, + InvenTreeStockItem.MODEL_TYPE: {}, + InvenTreeStockLocation.MODEL_TYPE: {}, + InvenTreePurchaseOrder.MODEL_TYPE: {}, + InvenTreeSalesOrder.MODEL_TYPE: {}, + InvenTreeSupplierPart.MODEL_TYPE: {}, + InvenTreeManufacturerPart.MODEL_TYPE: {}, + }; - if (api.supportsSplitCompanySearch) { - body["supplier"] = {}; - body["manufacturer"] = {}; - body["customer"] = {}; - } else { - // All "company" results are returned in a single query - body[InvenTreeCompany.MODEL_TYPE] = {}; - } - - if (body.isNotEmpty) { - if (mounted) { - setState(() { - nPendingSearches = 1; - }); - - _search_query = CancelableOperation.fromFuture(_perform_search(body)); - } - } + if (api.supportsSplitCompanySearch) { + body["supplier"] = {}; + body["manufacturer"] = {}; + body["customer"] = {}; } else { - legacySearch(term); - } - } - - /* - * Perform "legacy" search (without consolidated search API endpoint - */ - Future legacySearch(String term) async { - // Search parts - if (InvenTreePart().canView) { - nPendingSearches++; - InvenTreePart().count(searchQuery: term).then((int n) { - if (term == searchController.text) { - if (mounted) { - decrementPendingSearches(); - setState(() { - nPartResults = n; - }); - } - } - }); + // All "company" results are returned in a single query + body[InvenTreeCompany.MODEL_TYPE] = {}; } - // Search part categories - if (InvenTreePartCategory().canView) { - nPendingSearches++; - InvenTreePartCategory().count(searchQuery: term).then((int n) { - if (term == searchController.text) { - if (mounted) { - decrementPendingSearches(); - setState(() { - nCategoryResults = n; - }); - } - } - }); - } + if (body.isNotEmpty) { + if (mounted) { + setState(() { + nPendingSearches = 1; + }); - // Search stock items - if (InvenTreeStockItem().canView) { - nPendingSearches++; - InvenTreeStockItem().count(searchQuery: term).then((int n) { - if (term == searchController.text) { - if (mounted) { - decrementPendingSearches(); - setState(() { - nStockResults = n; - }); - } - } - }); - } - - // Search stock locations - if (InvenTreeStockLocation().canView) { - nPendingSearches++; - InvenTreeStockLocation().count(searchQuery: term).then((int n) { - if (term == searchController.text) { - if (mounted) { - decrementPendingSearches(); - setState(() { - nLocationResults = n; - }); - } - } - }); - } - - // Search purchase orders - if (InvenTreePurchaseOrder().canView) { - nPendingSearches++; - InvenTreePurchaseOrder() - .count(searchQuery: term, filters: {"outstanding": "true"}) - .then((int n) { - if (term == searchController.text) { - if (mounted) { - decrementPendingSearches(); - setState(() { - nPurchaseOrderResults = n; - }); - } - } - }); + _search_query = CancelableOperation.fromFuture(_perform_search(body)); + } } } diff --git a/lib/widget/stock/location_display.dart b/lib/widget/stock/location_display.dart index f6f74db9..6a944abb 100644 --- a/lib/widget/stock/location_display.dart +++ b/lib/widget/stock/location_display.dart @@ -101,20 +101,18 @@ class _LocationDisplayState extends RefreshableState { ); } - if (api.supportsBarcodePOReceiveEndpoint) { - actions.add( - SpeedDialChild( - child: Icon(Icons.barcode_reader), - label: L10().scanReceivedParts, - onTap: () async { - scanBarcode( - context, - handler: POReceiveBarcodeHandler(location: location), - ); - }, - ), - ); - } + actions.add( + SpeedDialChild( + child: Icon(Icons.barcode_reader), + label: L10().scanReceivedParts, + onTap: () async { + scanBarcode( + context, + handler: POReceiveBarcodeHandler(location: location), + ); + }, + ), + ); // Scan this location into another one if (InvenTreeStockLocation().canEdit) { @@ -362,7 +360,7 @@ class _LocationDisplayState extends RefreshableState { if (parent != null) { filters["parent"] = parent.toString(); - } else if (api.supportsNullTopLevelFiltering) { + } else { filters["parent"] = "null"; } diff --git a/lib/widget/stock/stock_list.dart b/lib/widget/stock/stock_list.dart index 5163a127..a16e30e2 100644 --- a/lib/widget/stock/stock_list.dart +++ b/lib/widget/stock/stock_list.dart @@ -112,10 +112,6 @@ class _PaginatedStockItemListState }, }; - if (!InvenTreeAPI().supportsStatusLabelEndpoints) { - filters.remove("status"); - } - return filters; }