diff --git a/lib/api.dart b/lib/api.dart index 1dbdc191..e6d86e44 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -388,22 +388,96 @@ class InvenTreeAPI { return response; } - // Perform a POST request - Future post(String url, {Map body}) async { + /** + * Perform a HTTP POST request + * Returns a json object (or null if unsuccessful) + */ + Future post(String url, {Map body, int expectedStatusCode=201}) async { var _url = makeApiUrl(url); var _headers = jsonHeaders(); print("POST: ${_url} -> ${body.toString()}"); - var data = jsonEncode(body); + var client = createClient(true); - return http.post(_url, - headers: _headers, - body: data, - ); + HttpClientRequest request = await client.postUrl(Uri.parse(_url)); + + var data = json.encode(body); + + // Set headers + // Ref: https://stackoverflow.com/questions/59713003/body-not-sending-using-map-in-flutter + request.headers.set('Accept', 'application/json'); + request.headers.set('Content-type', 'application/json'); + request.headers.set('Content-Length', data.length.toString()); + + if (profile != null) { + request.headers.set( + HttpHeaders.authorizationHeader, + _authorizationHeader(profile.username, profile.password) + ); + } + + // Add JSON data to the request + request.add(utf8.encode(data)); + + HttpClientResponse response = await request.close() + .timeout(Duration(seconds: 30)) + .catchError((error) { + print("POST request returned error"); + print("URL: ${_url}"); + print("Error: ${error.toString()}"); + + var ctx = OneContext().context; + + if (error is SocketException) { + showServerError( + I18N.of(ctx).connectionRefused, + error.toString() + ); + } else if (error is TimeoutException) { + showTimeoutError(ctx); + } else { + showServerError( + I18N.of(ctx).serverError, + error.toString() + ); + } + + return null; + }); + + if (response == null) { + print("null response from POST ${_url}"); + return null; + } + + if (response.statusCode != expectedStatusCode) { + showStatusCodeError(response.statusCode); + return null; + } + + // Convert the body of the response to a JSON object + String responseData = await response.transform(utf8.decoder).join(); + + try { + var data = json.decode(responseData); + + return data; + + } on FormatException { + + print("JSON format exception!"); + print("${responseData}"); + + showServerError( + "Format Exception", + "JSON data format exception:\n${responseData}" + ); + return null; + } } - HttpClient _client(bool allowBadCert) { + HttpClient createClient(bool allowBadCert) { var client = new HttpClient(); @@ -433,7 +507,7 @@ class InvenTreeAPI { * Perform a HTTP GET request * Returns a json object (or null if did not complete) */ - Future get(String url, {Map params}) async { + Future get(String url, {Map params, int expectedStatusCode=200}) async { var _url = makeApiUrl(url); var _headers = defaultHeaders(); @@ -455,9 +529,7 @@ class InvenTreeAPI { print("GET: " + _url); - var client = _client(true); - - print("Created client"); + var client = createClient(true); HttpClientRequest request = await client.getUrl(Uri.parse(_url)); @@ -470,8 +542,6 @@ class InvenTreeAPI { ); } - print("Created request: ${request.uri}"); - HttpClientResponse response = await request.close() .timeout(Duration(seconds: 30)) .catchError((error) { @@ -505,7 +575,7 @@ class InvenTreeAPI { } // Check the status code of the response - if (response.statusCode != 200) { + if (response.statusCode != expectedStatusCode) { showStatusCodeError(response.statusCode); return null; } diff --git a/lib/barcode.dart b/lib/barcode.dart index 3997f088..6680bc7e 100644 --- a/lib/barcode.dart +++ b/lib/barcode.dart @@ -7,7 +7,6 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:one_context/one_context.dart'; -import 'package:device_info/device_info.dart'; import 'package:qr_code_scanner/qr_code_scanner.dart'; import 'package:InvenTree/inventree/stock.dart'; @@ -21,7 +20,6 @@ import 'package:InvenTree/widget/part_detail.dart'; import 'package:InvenTree/widget/stock_detail.dart'; import 'dart:io'; -import 'dart:convert'; class BarcodeHandler { @@ -88,49 +86,34 @@ class BarcodeHandler { _controller.resumeCamera(); } - Future processBarcode(BuildContext context, QRViewController _controller, String barcode, {String url = "barcode/"}) { + Future processBarcode(BuildContext context, QRViewController _controller, String barcode, {String url = "barcode/"}) async { this._context = context; this._controller = _controller; print("Scanned barcode data: ${barcode}"); - // Send barcode request to server - InvenTreeAPI().post( + var data = await InvenTreeAPI().post( url, body: { - "barcode": barcode - } - ).then((var response) { - - if (response.statusCode != 200) { - showStatusCodeError(response.statusCode); - _controller.resumeCamera(); - - return; - } - - // Decode the response - final Map data = json.decode(response.body); - - if (data.containsKey('error')) { - _controller.resumeCamera(); - onBarcodeUnknown(data); - } else if (data.containsKey('success')) { - _controller.resumeCamera(); - onBarcodeMatched(data); - } else { - _controller.resumeCamera(); - onBarcodeUnhandled(data); - } - }).timeout( - Duration(seconds: 5) - ).catchError((error) { - - showServerError(I18N.of(OneContext().context).error, error.toString()); - _controller.resumeCamera(); + "barcode": barcode, + }, + expectedStatusCode: 200 + ); + if (data == null) { return; - }); + } + + if (data.containsKey('error')) { + _controller.resumeCamera(); + onBarcodeUnknown(data); + } else if (data.containsKey('success')) { + _controller.resumeCamera(); + onBarcodeMatched(data); + } else { + _controller.resumeCamera(); + onBarcodeUnhandled(data); + } } } diff --git a/lib/inventree/model.dart b/lib/inventree/model.dart index 16d0451e..f37c11d0 100644 --- a/lib/inventree/model.dart +++ b/lib/inventree/model.dart @@ -262,36 +262,13 @@ class InvenTreeModel { InvenTreeModel _model; - await api.post(URL, body: data).timeout(Duration(seconds: 10)).catchError((e) { - print("Error during CREATE"); - print(e.toString()); - - if (e is SocketException) { - showServerError( - I18N.of(context).connectionRefused, - e.toString() - ); - } - else if (e is TimeoutException) { - showTimeoutError(context); - } else { - // Re-throw the error - throw e; - } + var response = await api.post(URL, body: data); + if (response == null) { return null; - }) - .then((http.Response response) { - // Server should return HTTP_201_CREATED - if (response.statusCode == 201) { - var decoded = json.decode(response.body); - _model = createFromJson(decoded); - } else { - showStatusCodeError(response.statusCode); - } - }); + } - return _model; + return createFromJson(response); } Future listPaginated(int limit, int offset, {Map filters}) async { diff --git a/lib/inventree/stock.dart b/lib/inventree/stock.dart index 4bc44663..1f3e7e09 100644 --- a/lib/inventree/stock.dart +++ b/lib/inventree/stock.dart @@ -437,31 +437,14 @@ class InvenTreeStockItem extends InvenTreeModel { endpoint, body: { "item": { - "pk": "${pk}", - "quantity": "${q}", + "pk": "${pk}", + "quantity": "${q}", }, "notes": notes ?? '', - }).timeout(Duration(seconds: 10)).catchError((error) { - if (error is TimeoutException) { - showTimeoutError(context); - } else if (error is SocketException) { - showServerError( - I18N.of(context).connectionRefused, - error.toString() - ); - } else { - // Re-throw the error - throw error; } + ); - // Null response if error - return null; - }); - - if (response == null) return false; - - if (response.statusCode != 200) { - showStatusCodeError(response.statusCode); + if (response == null) { return false; } @@ -508,9 +491,13 @@ class InvenTreeStockItem extends InvenTreeModel { data["item"]["quantity"] = "${quantity}"; } + print("transfer stock!"); + final response = await api.post("/stock/transfer/", body: data); - return (response.statusCode == 200); + print("transfer response: ${response}"); + + return response != null; } } diff --git a/lib/widget/dialogs.dart b/lib/widget/dialogs.dart index d22ff0b1..30a17259 100644 --- a/lib/widget/dialogs.dart +++ b/lib/widget/dialogs.dart @@ -208,8 +208,6 @@ void showFormDialog(String title, {String acceptText, String cancelText, GlobalK if (key.currentState.validate()) { key.currentState.save(); - print("Saving and closing the dialog"); - // Close the dialog Navigator.pop(dialogContext); diff --git a/lib/widget/stock_detail.dart b/lib/widget/stock_detail.dart index a1ff28fe..5075d435 100644 --- a/lib/widget/stock_detail.dart +++ b/lib/widget/stock_detail.dart @@ -247,7 +247,7 @@ class _StockItemDisplayState extends RefreshableState { void _transferStock(BuildContext context, InvenTreeStockLocation location) async { - double quantity = double.parse(_quantityController.text); + double quantity = double.tryParse(_quantityController.text) ?? item.quantity; String notes = _notesController.text; _quantityController.clear();