diff --git a/lib/api.dart b/lib/api.dart index 64a74682..7e751653 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -225,6 +225,8 @@ class InvenTreeAPI { // API version of the connected server int _apiVersion = 1; + int get apiVersion => _apiVersion; + // Getter for server version information String get version => _version; @@ -244,10 +246,14 @@ class InvenTreeAPI { // Ensure we only ever create a single instance of the API class static final InvenTreeAPI _api = InvenTreeAPI._internal(); + // API endpoint for receiving purchase order line items was introduced in v12 bool supportPoReceive() { + return apiVersion >= 12; + } - // API endpoint for receiving purchase order line items was introduced in v12 - return _apiVersion >= 12; + // "Modern" API transactions were implemented in API v14 + bool supportModernStockTransactions() { + return apiVersion >= 14; } /* diff --git a/lib/inventree/stock.dart b/lib/inventree/stock.dart index 29391b26..8ce457f5 100644 --- a/lib/inventree/stock.dart +++ b/lib/inventree/stock.dart @@ -115,6 +115,16 @@ class InvenTreeStockItem extends InvenTreeModel { @override String get URL => "stock/"; + // URLs for performing stock actions + + static String transferStockUrl() => "stock/transfer/"; + + static String countStockUrl() => "stock/count/"; + + static String addStockUrl() => "stock/add/"; + + static String removeStockUrl() => "stock/remove/"; + @override String get WEB_URL => "stock/item/"; @@ -474,7 +484,7 @@ class InvenTreeStockItem extends InvenTreeModel { return response.isValid(); } - // TODO: Refactor this once the server supports API metadata for this action + // TODO: Remove this function when we deprecate support for the old API Future countStock(BuildContext context, double q, {String? notes}) async { final bool result = await adjustStock(context, "/stock/count/", q, notes: notes); @@ -482,7 +492,7 @@ class InvenTreeStockItem extends InvenTreeModel { return result; } - // TODO: Refactor this once the server supports API metadata for this action + // TODO: Remove this function when we deprecate support for the old API Future addStock(BuildContext context, double q, {String? notes}) async { final bool result = await adjustStock(context, "/stock/add/", q, notes: notes); @@ -490,7 +500,7 @@ class InvenTreeStockItem extends InvenTreeModel { return result; } - // TODO: Refactor this once the server supports API metadata for this action + // TODO: Remove this function when we deprecate support for the old API Future removeStock(BuildContext context, double q, {String? notes}) async { final bool result = await adjustStock(context, "/stock/remove/", q, notes: notes); @@ -498,7 +508,7 @@ class InvenTreeStockItem extends InvenTreeModel { return result; } - // TODO: Refactor this once the server supports API metadata for this action + // TODO: Remove this function when we deprecate support for the old API Future transferStock(int location, {double? quantity, String? notes}) async { if ((quantity == null) || (quantity < 0) || (quantity > this.quantity)) { quantity = this.quantity; diff --git a/lib/widget/stock_detail.dart b/lib/widget/stock_detail.dart index 46e49cfb..bca258be 100644 --- a/lib/widget/stock_detail.dart +++ b/lib/widget/stock_detail.dart @@ -1,3 +1,9 @@ +import "package:flutter/cupertino.dart"; +import "package:flutter/material.dart"; + +import "package:dropdown_search/dropdown_search.dart"; +import "package:font_awesome_flutter/font_awesome_flutter.dart"; + import "package:inventree/app_colors.dart"; import "package:inventree/barcode.dart"; import "package:inventree/inventree/model.dart"; @@ -12,15 +18,11 @@ import "package:inventree/widget/refreshable_state.dart"; import "package:inventree/widget/snacks.dart"; import "package:inventree/widget/stock_item_test_results.dart"; import "package:inventree/widget/stock_notes.dart"; -import "package:flutter/cupertino.dart"; -import "package:flutter/material.dart"; - import "package:inventree/l10.dart"; import "package:inventree/helpers.dart"; import "package:inventree/api.dart"; +import "package:inventree/api_form.dart"; -import "package:dropdown_search/dropdown_search.dart"; -import "package:font_awesome_flutter/font_awesome_flutter.dart"; class StockDetailWidget extends StatefulWidget { @@ -140,6 +142,39 @@ class _StockItemDisplayState extends RefreshableState { Future _addStockDialog() async { + // TODO: In future, deprecate support for older API + if (InvenTreeAPI().supportModernStockTransactions()) { + + Map fields = { + "pk": { + "parent": "items", + "nested": true, + "hidden": true, + "value": item.pk, + }, + "quantity": { + "parent": "items", + "nested": true, + "value": 0, + }, + "notes": {}, + }; + + launchApiForm( + context, + L10().addStock, + InvenTreeStockItem.addStockUrl(), + fields, + method: "POST", + icon: FontAwesomeIcons.plusCircle, + onSuccess: (data) async { + refresh(); + } + ); + + return; + } + _quantityController.clear(); _notesController.clear(); @@ -186,6 +221,38 @@ class _StockItemDisplayState extends RefreshableState { void _removeStockDialog() { + // TODO: In future, deprecate support for the older API + if (InvenTreeAPI().supportModernStockTransactions()) { + Map fields = { + "pk": { + "parent": "items", + "nested": true, + "hidden": true, + "value": item.pk, + }, + "quantity": { + "parent": "items", + "nested": true, + "value": 0, + }, + "notes": {}, + }; + + launchApiForm( + context, + L10().addStock, + InvenTreeStockItem.removeStockUrl(), + fields, + method: "POST", + icon: FontAwesomeIcons.minusCircle, + onSuccess: (data) async { + refresh(); + } + ); + + return; + } + _quantityController.clear(); _notesController.clear(); @@ -225,6 +292,39 @@ class _StockItemDisplayState extends RefreshableState { Future _countStockDialog() async { + // TODO: In future, deprecate support for older API + if (InvenTreeAPI().supportModernStockTransactions()) { + + Map fields = { + "pk": { + "parent": "items", + "nested": true, + "hidden": true, + "value": item.pk, + }, + "quantity": { + "parent": "items", + "nested": true, + "value": item.quantity, + }, + "notes": {}, + }; + + launchApiForm( + context, + L10().addStock, + InvenTreeStockItem.countStockUrl(), + fields, + method: "POST", + icon: FontAwesomeIcons.plusCircle, + onSuccess: (data) async { + refresh(); + } + ); + + return; + } + _quantityController.text = item.quantity.toString(); _notesController.clear(); @@ -271,6 +371,7 @@ class _StockItemDisplayState extends RefreshableState { } + // TODO: Delete this function once support for old API is deprecated Future _transferStock(int locationId) async { double quantity = double.tryParse(_quantityController.text) ?? item.quantity; @@ -288,8 +389,45 @@ class _StockItemDisplayState extends RefreshableState { } } + /* + * Launches an API Form to transfer this stock item to a new location + */ Future _transferStockDialog(BuildContext context) async { + // TODO: In future, deprecate support for older API + if (InvenTreeAPI().supportModernStockTransactions()) { + + Map fields = { + "pk": { + "parent": "items", + "nested": true, + "hidden": true, + "value": item.pk, + }, + "quantity": { + "parent": "items", + "nested": true, + "value": item.quantity, + }, + "location": {}, + "notes": {}, + }; + + launchApiForm( + context, + L10().transferStock, + InvenTreeStockItem.transferStockUrl(), + fields, + method: "POST", + icon: FontAwesomeIcons.dolly, + onSuccess: (data) async { + refresh(); + } + ); + + return; + } + int? location_pk; _quantityController.text = "${item.quantity}";