mirror of
https://github.com/inventree/inventree-app.git
synced 2025-04-28 05:26:47 +00:00
Merge pull request #140 from inventree/legacy-api
Remove support for legacy stock transfer API code (cherry picked from commit 333e5bb41d24466077d3f1c2de15cc03baab276e)
This commit is contained in:
parent
d796cd208d
commit
bf722d6b76
@ -5,6 +5,9 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
- Fixes issue which prevented text input in search window
|
- Fixes issue which prevented text input in search window
|
||||||
|
- Remove support for legacy stock adjustment API
|
||||||
|
- App now requires server API version 20 (or newer)
|
||||||
|
- Updated translation files
|
||||||
|
|
||||||
### 0.7.0 - May 2022
|
### 0.7.0 - May 2022
|
||||||
---
|
---
|
||||||
|
@ -144,7 +144,7 @@ class InvenTreeAPI {
|
|||||||
InvenTreeAPI._internal();
|
InvenTreeAPI._internal();
|
||||||
|
|
||||||
// Minimum required API version for server
|
// Minimum required API version for server
|
||||||
static const _minApiVersion = 7;
|
static const _minApiVersion = 20;
|
||||||
|
|
||||||
bool _strictHttps = false;
|
bool _strictHttps = false;
|
||||||
|
|
||||||
@ -294,9 +294,6 @@ class InvenTreeAPI {
|
|||||||
// API endpoint for receiving purchase order line items was introduced in v12
|
// API endpoint for receiving purchase order line items was introduced in v12
|
||||||
bool get supportsPoReceive => apiVersion >= 12;
|
bool get supportsPoReceive => apiVersion >= 12;
|
||||||
|
|
||||||
// "Modern" API transactions were implemented in API v14
|
|
||||||
bool get supportsModernStockTransactions => apiVersion >= 14;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Connect to the remote InvenTree server:
|
* Connect to the remote InvenTree server:
|
||||||
*
|
*
|
||||||
|
@ -1161,6 +1161,72 @@ class _APIFormWidgetState extends State<APIFormWidget> {
|
|||||||
nonFieldErrors = errors;
|
nonFieldErrors = errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check for errors relating to an *unhandled* field name
|
||||||
|
* These errors will not be displayed and potentially confuse the user
|
||||||
|
* So, we need to know if these are ever happening
|
||||||
|
*/
|
||||||
|
void checkInvalidErrors(APIResponse response) {
|
||||||
|
var errors = response.asMap();
|
||||||
|
|
||||||
|
for (String fieldName in errors.keys) {
|
||||||
|
|
||||||
|
bool match = false;
|
||||||
|
|
||||||
|
switch (fieldName) {
|
||||||
|
case "__all__":
|
||||||
|
case "non_field_errors":
|
||||||
|
case "errors":
|
||||||
|
// ignore these global fields
|
||||||
|
match = true;
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
for (var field in fields) {
|
||||||
|
|
||||||
|
// Hidden fields can't display errors, so we won't match
|
||||||
|
if (field.hidden) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field.name == fieldName) {
|
||||||
|
// Direct Match found!
|
||||||
|
match = true;
|
||||||
|
break;
|
||||||
|
} else if (field.parent == fieldName) {
|
||||||
|
|
||||||
|
var error = errors[fieldName];
|
||||||
|
|
||||||
|
if (error is List) {
|
||||||
|
for (var el in error) {
|
||||||
|
if (el is Map && el.containsKey(field.name)) {
|
||||||
|
match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (error is Map && error.containsKey(field.name)) {
|
||||||
|
match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
// Match for an unknown / unsupported field
|
||||||
|
sentryReportMessage(
|
||||||
|
"API form returned error for unsupported field",
|
||||||
|
context: {
|
||||||
|
"url": response.url,
|
||||||
|
"status_code": response.statusCode.toString(),
|
||||||
|
"field": fieldName,
|
||||||
|
"error_message": response.data.toString(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Submit the form data to the server, and handle the results
|
* Submit the form data to the server, and handle the results
|
||||||
*/
|
*/
|
||||||
@ -1234,8 +1300,6 @@ class _APIFormWidgetState extends State<APIFormWidget> {
|
|||||||
// Hide this form
|
// Hide this form
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
|
|
||||||
// TODO: Display a snackBar
|
|
||||||
|
|
||||||
if (successFunc != null) {
|
if (successFunc != null) {
|
||||||
|
|
||||||
// Ensure the response is a valid JSON structure
|
// Ensure the response is a valid JSON structure
|
||||||
@ -1263,7 +1327,7 @@ class _APIFormWidgetState extends State<APIFormWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extractNonFieldErrors(response);
|
extractNonFieldErrors(response);
|
||||||
|
checkInvalidErrors(response);
|
||||||
break;
|
break;
|
||||||
case 401:
|
case 401:
|
||||||
showSnackIcon(
|
showSnackIcon(
|
||||||
|
@ -517,7 +517,6 @@ class InvenTreeStockItem extends InvenTreeModel {
|
|||||||
* - Remove
|
* - Remove
|
||||||
* - Count
|
* - Count
|
||||||
*/
|
*/
|
||||||
// TODO: Remove this function when we deprecate support for the old API
|
|
||||||
Future<bool> adjustStock(BuildContext context, String endpoint, double q, {String? notes, int? location}) async {
|
Future<bool> adjustStock(BuildContext context, String endpoint, double q, {String? notes, int? location}) async {
|
||||||
|
|
||||||
// Serialized stock cannot be adjusted (unless it is a "transfer")
|
// Serialized stock cannot be adjusted (unless it is a "transfer")
|
||||||
@ -532,46 +531,29 @@ class InvenTreeStockItem extends InvenTreeModel {
|
|||||||
|
|
||||||
Map<String, dynamic> data = {};
|
Map<String, dynamic> data = {};
|
||||||
|
|
||||||
// Note: Format of adjustment API was updated in API v14
|
data = {
|
||||||
if (api.supportsModernStockTransactions) {
|
"items": [
|
||||||
// Modern (> 14) API
|
{
|
||||||
data = {
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"pk": "${pk}",
|
|
||||||
"quantity": "${quantity}",
|
|
||||||
}
|
|
||||||
],
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Legacy (<= 14) API
|
|
||||||
data = {
|
|
||||||
"item": {
|
|
||||||
"pk": "${pk}",
|
"pk": "${pk}",
|
||||||
"quantity": "${quantity}",
|
"quantity": "${quantity}",
|
||||||
},
|
}
|
||||||
};
|
],
|
||||||
}
|
"notes": notes ?? "",
|
||||||
|
};
|
||||||
data["notes"] = notes ?? "";
|
|
||||||
|
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
data["location"] = location;
|
data["location"] = location;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expected API return code depends on server API version
|
|
||||||
final int expected_response = api.supportsModernStockTransactions ? 201 : 200;
|
|
||||||
|
|
||||||
var response = await api.post(
|
var response = await api.post(
|
||||||
endpoint,
|
endpoint,
|
||||||
body: data,
|
body: data,
|
||||||
expectedStatusCode: expected_response,
|
expectedStatusCode: 200,
|
||||||
);
|
);
|
||||||
|
|
||||||
return response.isValid();
|
return response.isValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove this function when we deprecate support for the old API
|
|
||||||
Future<bool> countStock(BuildContext context, double q, {String? notes}) async {
|
Future<bool> countStock(BuildContext context, double q, {String? notes}) async {
|
||||||
|
|
||||||
final bool result = await adjustStock(context, "/stock/count/", q, notes: notes);
|
final bool result = await adjustStock(context, "/stock/count/", q, notes: notes);
|
||||||
@ -579,7 +561,6 @@ class InvenTreeStockItem extends InvenTreeModel {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove this function when we deprecate support for the old API
|
|
||||||
Future<bool> addStock(BuildContext context, double q, {String? notes}) async {
|
Future<bool> addStock(BuildContext context, double q, {String? notes}) async {
|
||||||
|
|
||||||
final bool result = await adjustStock(context, "/stock/add/", q, notes: notes);
|
final bool result = await adjustStock(context, "/stock/add/", q, notes: notes);
|
||||||
@ -587,7 +568,6 @@ class InvenTreeStockItem extends InvenTreeModel {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove this function when we deprecate support for the old API
|
|
||||||
Future<bool> removeStock(BuildContext context, double q, {String? notes}) async {
|
Future<bool> removeStock(BuildContext context, double q, {String? notes}) async {
|
||||||
|
|
||||||
final bool result = await adjustStock(context, "/stock/remove/", q, notes: notes);
|
final bool result = await adjustStock(context, "/stock/remove/", q, notes: notes);
|
||||||
@ -595,7 +575,6 @@ class InvenTreeStockItem extends InvenTreeModel {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove this function when we deprecate support for the old API
|
|
||||||
Future<bool> transferStock(BuildContext context, int location, {double? quantity, String? notes}) async {
|
Future<bool> transferStock(BuildContext context, int location, {double? quantity, String? notes}) async {
|
||||||
|
|
||||||
double q = this.quantity;
|
double q = this.quantity;
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
|
||||||
import "package:dropdown_search/dropdown_search.dart";
|
|
||||||
import "package:font_awesome_flutter/font_awesome_flutter.dart";
|
import "package:font_awesome_flutter/font_awesome_flutter.dart";
|
||||||
|
|
||||||
import "package:inventree/app_colors.dart";
|
import "package:inventree/app_colors.dart";
|
||||||
import "package:inventree/barcode.dart";
|
import "package:inventree/barcode.dart";
|
||||||
import "package:inventree/inventree/model.dart";
|
|
||||||
import "package:inventree/inventree/stock.dart";
|
import "package:inventree/inventree/stock.dart";
|
||||||
import "package:inventree/inventree/part.dart";
|
import "package:inventree/inventree/part.dart";
|
||||||
import "package:inventree/widget/dialogs.dart";
|
import "package:inventree/widget/dialogs.dart";
|
||||||
import "package:inventree/widget/fields.dart";
|
|
||||||
import "package:inventree/widget/location_display.dart";
|
import "package:inventree/widget/location_display.dart";
|
||||||
import "package:inventree/widget/part_detail.dart";
|
import "package:inventree/widget/part_detail.dart";
|
||||||
import "package:inventree/widget/progress.dart";
|
import "package:inventree/widget/progress.dart";
|
||||||
@ -42,14 +39,6 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
|||||||
@override
|
@override
|
||||||
String getAppBarTitle(BuildContext context) => L10().stockItem;
|
String getAppBarTitle(BuildContext context) => L10().stockItem;
|
||||||
|
|
||||||
final TextEditingController _quantityController = TextEditingController();
|
|
||||||
final TextEditingController _notesController = TextEditingController();
|
|
||||||
|
|
||||||
final _addStockKey = GlobalKey<FormState>();
|
|
||||||
final _removeStockKey = GlobalKey<FormState>();
|
|
||||||
final _countStockKey = GlobalKey<FormState>();
|
|
||||||
final _moveStockKey = GlobalKey<FormState>();
|
|
||||||
|
|
||||||
bool stockShowHistory = false;
|
bool stockShowHistory = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -295,76 +284,37 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future <void> _addStock() async {
|
/*
|
||||||
|
* Launch a dialog to 'add' quantity to this StockItem
|
||||||
double quantity = double.parse(_quantityController.text);
|
*/
|
||||||
_quantityController.clear();
|
|
||||||
|
|
||||||
final bool result = await item.addStock(context, quantity, notes: _notesController.text);
|
|
||||||
_notesController.clear();
|
|
||||||
|
|
||||||
_stockUpdateMessage(result);
|
|
||||||
|
|
||||||
refresh(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future <void> _addStockDialog() async {
|
Future <void> _addStockDialog() async {
|
||||||
|
|
||||||
// TODO: In future, deprecate support for older API
|
Map<String, dynamic> fields = {
|
||||||
if (InvenTreeAPI().supportsModernStockTransactions) {
|
"pk": {
|
||||||
|
"parent": "items",
|
||||||
Map<String, dynamic> fields = {
|
"nested": true,
|
||||||
"pk": {
|
"hidden": true,
|
||||||
"parent": "items",
|
"value": item.pk,
|
||||||
"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 {
|
|
||||||
_stockUpdateMessage(true);
|
|
||||||
refresh(context);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_quantityController.clear();
|
|
||||||
_notesController.clear();
|
|
||||||
|
|
||||||
showFormDialog( L10().addStock,
|
|
||||||
key: _addStockKey,
|
|
||||||
callback: () {
|
|
||||||
_addStock();
|
|
||||||
},
|
},
|
||||||
fields: <Widget> [
|
"quantity": {
|
||||||
Text("Current stock: ${item.quantity}"),
|
"parent": "items",
|
||||||
QuantityField(
|
"nested": true,
|
||||||
label: L10().addStock,
|
"value": 0,
|
||||||
controller: _quantityController,
|
},
|
||||||
),
|
"notes": {},
|
||||||
TextFormField(
|
};
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: L10().notes,
|
launchApiForm(
|
||||||
),
|
context,
|
||||||
controller: _notesController,
|
L10().addStock,
|
||||||
)
|
InvenTreeStockItem.addStockUrl(),
|
||||||
],
|
fields,
|
||||||
|
method: "POST",
|
||||||
|
icon: FontAwesomeIcons.plusCircle,
|
||||||
|
onSuccess: (data) async {
|
||||||
|
_stockUpdateMessage(true);
|
||||||
|
refresh(context);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,149 +325,68 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future <void> _removeStock() async {
|
/*
|
||||||
|
* Launch a dialog to 'remove' quantity from this StockItem
|
||||||
double quantity = double.parse(_quantityController.text);
|
*/
|
||||||
_quantityController.clear();
|
|
||||||
|
|
||||||
final bool result = await item.removeStock(context, quantity, notes: _notesController.text);
|
|
||||||
|
|
||||||
_stockUpdateMessage(result);
|
|
||||||
|
|
||||||
refresh(context);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void _removeStockDialog() {
|
void _removeStockDialog() {
|
||||||
|
|
||||||
// TODO: In future, deprecate support for the older API
|
Map<String, dynamic> fields = {
|
||||||
if (InvenTreeAPI().supportsModernStockTransactions) {
|
"pk": {
|
||||||
Map<String, dynamic> fields = {
|
"parent": "items",
|
||||||
"pk": {
|
"nested": true,
|
||||||
"parent": "items",
|
"hidden": true,
|
||||||
"nested": true,
|
"value": item.pk,
|
||||||
"hidden": true,
|
},
|
||||||
"value": item.pk,
|
"quantity": {
|
||||||
},
|
"parent": "items",
|
||||||
"quantity": {
|
"nested": true,
|
||||||
"parent": "items",
|
"value": 0,
|
||||||
"nested": true,
|
},
|
||||||
"value": 0,
|
"notes": {},
|
||||||
},
|
};
|
||||||
"notes": {},
|
|
||||||
};
|
|
||||||
|
|
||||||
launchApiForm(
|
launchApiForm(
|
||||||
context,
|
context,
|
||||||
L10().removeStock,
|
L10().removeStock,
|
||||||
InvenTreeStockItem.removeStockUrl(),
|
InvenTreeStockItem.removeStockUrl(),
|
||||||
fields,
|
fields,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
icon: FontAwesomeIcons.minusCircle,
|
icon: FontAwesomeIcons.minusCircle,
|
||||||
onSuccess: (data) async {
|
onSuccess: (data) async {
|
||||||
_stockUpdateMessage(true);
|
_stockUpdateMessage(true);
|
||||||
refresh(context);
|
refresh(context);
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_quantityController.clear();
|
|
||||||
_notesController.clear();
|
|
||||||
|
|
||||||
showFormDialog(L10().removeStock,
|
|
||||||
key: _removeStockKey,
|
|
||||||
callback: () {
|
|
||||||
_removeStock();
|
|
||||||
},
|
|
||||||
fields: <Widget>[
|
|
||||||
Text("Current stock: ${item.quantity}"),
|
|
||||||
QuantityField(
|
|
||||||
label: L10().removeStock,
|
|
||||||
controller: _quantityController,
|
|
||||||
max: item.quantity,
|
|
||||||
),
|
|
||||||
TextFormField(
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: L10().notes,
|
|
||||||
),
|
|
||||||
controller: _notesController,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future <void> _countStock() async {
|
|
||||||
|
|
||||||
double quantity = double.parse(_quantityController.text);
|
|
||||||
_quantityController.clear();
|
|
||||||
|
|
||||||
final bool result = await item.countStock(context, quantity, notes: _notesController.text);
|
|
||||||
|
|
||||||
_stockUpdateMessage(result);
|
|
||||||
|
|
||||||
refresh(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future <void> _countStockDialog() async {
|
Future <void> _countStockDialog() async {
|
||||||
|
|
||||||
// TODO: In future, deprecate support for older API
|
Map<String, dynamic> fields = {
|
||||||
if (InvenTreeAPI().supportsModernStockTransactions) {
|
"pk": {
|
||||||
|
"parent": "items",
|
||||||
Map<String, dynamic> fields = {
|
"nested": true,
|
||||||
"pk": {
|
"hidden": true,
|
||||||
"parent": "items",
|
"value": item.pk,
|
||||||
"nested": true,
|
|
||||||
"hidden": true,
|
|
||||||
"value": item.pk,
|
|
||||||
},
|
|
||||||
"quantity": {
|
|
||||||
"parent": "items",
|
|
||||||
"nested": true,
|
|
||||||
"value": item.quantity,
|
|
||||||
},
|
|
||||||
"notes": {},
|
|
||||||
};
|
|
||||||
|
|
||||||
launchApiForm(
|
|
||||||
context,
|
|
||||||
L10().countStock,
|
|
||||||
InvenTreeStockItem.countStockUrl(),
|
|
||||||
fields,
|
|
||||||
method: "POST",
|
|
||||||
icon: FontAwesomeIcons.clipboardCheck,
|
|
||||||
onSuccess: (data) async {
|
|
||||||
_stockUpdateMessage(true);
|
|
||||||
refresh(context);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_quantityController.text = item.quantity.toString();
|
|
||||||
_notesController.clear();
|
|
||||||
|
|
||||||
showFormDialog(L10().countStock,
|
|
||||||
key: _countStockKey,
|
|
||||||
callback: () {
|
|
||||||
_countStock();
|
|
||||||
},
|
},
|
||||||
acceptText: L10().count,
|
"quantity": {
|
||||||
fields: <Widget> [
|
"parent": "items",
|
||||||
QuantityField(
|
"nested": true,
|
||||||
label: L10().countStock,
|
"value": item.quantity,
|
||||||
hint: "${item.quantityString}",
|
},
|
||||||
controller: _quantityController,
|
"notes": {},
|
||||||
),
|
};
|
||||||
TextFormField(
|
|
||||||
decoration: InputDecoration(
|
launchApiForm(
|
||||||
labelText: L10().notes,
|
context,
|
||||||
),
|
L10().countStock,
|
||||||
controller: _notesController,
|
InvenTreeStockItem.countStockUrl(),
|
||||||
)
|
fields,
|
||||||
]
|
method: "POST",
|
||||||
|
icon: FontAwesomeIcons.clipboardCheck,
|
||||||
|
onSuccess: (data) async {
|
||||||
|
_stockUpdateMessage(true);
|
||||||
|
refresh(context);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -542,130 +411,43 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: Delete this function once support for old API is deprecated
|
|
||||||
Future <void> _transferStock(int locationId) async {
|
|
||||||
|
|
||||||
double quantity = double.tryParse(_quantityController.text) ?? item.quantity;
|
|
||||||
String notes = _notesController.text;
|
|
||||||
|
|
||||||
_quantityController.clear();
|
|
||||||
_notesController.clear();
|
|
||||||
|
|
||||||
var result = await item.transferStock(context, locationId, quantity: quantity, notes: notes);
|
|
||||||
|
|
||||||
refresh(context);
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
showSnackIcon(L10().stockItemTransferred, success: true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Launches an API Form to transfer this stock item to a new location
|
* Launches an API Form to transfer this stock item to a new location
|
||||||
*/
|
*/
|
||||||
Future <void> _transferStockDialog(BuildContext context) async {
|
Future <void> _transferStockDialog(BuildContext context) async {
|
||||||
|
|
||||||
// TODO: In future, deprecate support for older API
|
Map<String, dynamic> fields = {
|
||||||
if (InvenTreeAPI().supportsModernStockTransactions) {
|
"pk": {
|
||||||
|
"parent": "items",
|
||||||
|
"nested": true,
|
||||||
|
"hidden": true,
|
||||||
|
"value": item.pk,
|
||||||
|
},
|
||||||
|
"quantity": {
|
||||||
|
"parent": "items",
|
||||||
|
"nested": true,
|
||||||
|
"value": item.quantity,
|
||||||
|
},
|
||||||
|
"location": {},
|
||||||
|
"notes": {},
|
||||||
|
};
|
||||||
|
|
||||||
Map<String, dynamic> fields = {
|
if (item.isSerialized()) {
|
||||||
"pk": {
|
// Prevent editing of 'quantity' field if the item is serialized
|
||||||
"parent": "items",
|
fields["quantity"]["hidden"] = true;
|
||||||
"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 {
|
|
||||||
_stockUpdateMessage(true);
|
|
||||||
refresh(context);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int? location_pk;
|
launchApiForm(
|
||||||
|
context,
|
||||||
_quantityController.text = "${item.quantity}";
|
L10().transferStock,
|
||||||
|
InvenTreeStockItem.transferStockUrl(),
|
||||||
showFormDialog(L10().transferStock,
|
fields,
|
||||||
key: _moveStockKey,
|
method: "POST",
|
||||||
callback: () {
|
icon: FontAwesomeIcons.dolly,
|
||||||
var _pk = location_pk;
|
onSuccess: (data) async {
|
||||||
|
_stockUpdateMessage(true);
|
||||||
if (_pk != null) {
|
refresh(context);
|
||||||
_transferStock(_pk);
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
fields: <Widget>[
|
|
||||||
QuantityField(
|
|
||||||
label: L10().quantity,
|
|
||||||
controller: _quantityController,
|
|
||||||
max: item.quantity,
|
|
||||||
),
|
|
||||||
DropdownSearch<dynamic>(
|
|
||||||
mode: Mode.BOTTOM_SHEET,
|
|
||||||
showSelectedItem: false,
|
|
||||||
autoFocusSearchBox: true,
|
|
||||||
selectedItem: null,
|
|
||||||
errorBuilder: (context, entry, exception) {
|
|
||||||
print("entry: $entry");
|
|
||||||
print(exception.toString());
|
|
||||||
|
|
||||||
return Text(
|
|
||||||
exception.toString(),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 10,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onFind: (String filter) async {
|
|
||||||
|
|
||||||
final results = await InvenTreeStockLocation().search(filter);
|
|
||||||
|
|
||||||
List<dynamic> items = [];
|
|
||||||
|
|
||||||
for (InvenTreeModel loc in results) {
|
|
||||||
if (loc is InvenTreeStockLocation) {
|
|
||||||
items.add(loc.jsondata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return items;
|
|
||||||
},
|
|
||||||
label: L10().stockLocation,
|
|
||||||
hint: L10().searchLocation,
|
|
||||||
onChanged: null,
|
|
||||||
itemAsString: (dynamic location) {
|
|
||||||
return (location["pathstring"] ?? "") as String;
|
|
||||||
},
|
|
||||||
onSaved: (dynamic location) {
|
|
||||||
if (location == null) {
|
|
||||||
location_pk = null;
|
|
||||||
} else {
|
|
||||||
location_pk = location["pk"] as int;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isFilteredOnline: true,
|
|
||||||
showSearchBox: true,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +127,6 @@ void main() {
|
|||||||
assert(api.apiVersion >= 50);
|
assert(api.apiVersion >= 50);
|
||||||
assert(api.supportsSettings);
|
assert(api.supportsSettings);
|
||||||
assert(api.supportsNotifications);
|
assert(api.supportsNotifications);
|
||||||
assert(api.supportsModernStockTransactions);
|
|
||||||
assert(api.supportsPoReceive);
|
assert(api.supportsPoReceive);
|
||||||
|
|
||||||
// Check available permissions
|
// Check available permissions
|
||||||
|
Loading…
x
Reference in New Issue
Block a user