From 4444884afa2304b98d304feafba1ac7953cc48c6 Mon Sep 17 00:00:00 2001 From: Ben Hagen Date: Tue, 24 Jun 2025 01:55:01 +0200 Subject: [PATCH] Format Code and Add Format Checks to CI (#643) * Remove unused lib/generated/i18n.dart * Use `fvm dart format .` * Add contributing guidelines * Enforce dart format * Add `dart format off` directive to generated files --- .github/workflows/ci.yaml | 1 + CONTRIBUTING.md | 50 ++ find_dart_files.py | 1 + lib/api.dart | 534 ++++++++-------- lib/api_form.dart | 441 ++++++------- lib/app_colors.dart | 1 - lib/barcode/barcode.dart | 204 +++--- lib/barcode/camera_controller.dart | 5 +- lib/barcode/controller.dart | 18 +- lib/barcode/handler.dart | 42 +- lib/barcode/purchase_order.dart | 56 +- lib/barcode/sales_order.dart | 49 +- lib/barcode/stock.dart | 90 +-- lib/barcode/tones.dart | 14 +- lib/barcode/wedge_controller.dart | 29 +- lib/dsn.dart | 4 +- lib/generated/i18n.dart | 76 --- lib/helpers.dart | 42 +- lib/inventree/bom.dart | 11 +- lib/inventree/company.dart | 127 ++-- lib/inventree/model.dart | 309 +++++----- lib/inventree/notification.dart | 15 +- lib/inventree/orders.dart | 22 +- lib/inventree/part.dart | 348 +++++------ lib/inventree/project_code.dart | 13 +- lib/inventree/purchase_order.dart | 138 ++--- lib/inventree/sales_order.dart | 127 ++-- lib/inventree/sentry.dart | 59 +- lib/inventree/status_codes.dart | 8 +- lib/inventree/stock.dart | 580 ++++++++---------- lib/l10.dart | 5 +- lib/l10n/collect_translations.py | 1 + lib/labels.dart | 112 ++-- lib/main.dart | 135 ++-- lib/preferences.dart | 10 +- lib/settings/about.dart | 120 ++-- lib/settings/app_settings.dart | 118 ++-- lib/settings/barcode_settings.dart | 93 +-- lib/settings/home_settings.dart | 221 ++++--- lib/settings/login.dart | 96 ++- lib/settings/part_settings.dart | 78 ++- lib/settings/purchase_order_settings.dart | 133 ++-- lib/settings/release.dart | 15 +- lib/settings/sales_order_settings.dart | 97 +-- lib/settings/select_server.dart | 164 +++-- lib/settings/settings.dart | 180 +++--- lib/user_profile.dart | 34 +- lib/widget/attachment_widget.dart | 146 ++--- lib/widget/back.dart | 3 +- lib/widget/company/company_detail.dart | 355 ++++++----- lib/widget/company/company_list.dart | 46 +- .../company/manufacturer_part_detail.dart | 146 ++--- lib/widget/company/supplier_part_detail.dart | 170 ++--- lib/widget/company/supplier_part_list.dart | 38 +- lib/widget/dialogs.dart | 140 ++--- lib/widget/drawer.dart | 89 ++- lib/widget/fields.dart | 176 +++--- lib/widget/home.dart | 231 ++++--- lib/widget/notes_widget.dart | 23 +- lib/widget/notifications.dart | 37 +- lib/widget/order/extra_line_detail.dart | 54 +- lib/widget/order/po_extra_line_list.dart | 47 +- lib/widget/order/po_line_detail.dart | 84 +-- lib/widget/order/po_line_list.dart | 42 +- lib/widget/order/purchase_order_detail.dart | 422 +++++++------ lib/widget/order/purchase_order_list.dart | 65 +- lib/widget/order/sales_order_detail.dart | 373 ++++++----- lib/widget/order/sales_order_list.dart | 86 +-- lib/widget/order/so_extra_line_list.dart | 69 ++- lib/widget/order/so_line_detail.dart | 114 ++-- lib/widget/order/so_line_list.dart | 44 +- lib/widget/order/so_shipment_list.dart | 35 +- lib/widget/paginator.dart | 177 +++--- lib/widget/part/bom_list.dart | 68 +- lib/widget/part/category_display.dart | 146 ++--- lib/widget/part/category_list.dart | 42 +- lib/widget/part/part_detail.dart | 490 +++++++-------- lib/widget/part/part_image_widget.dart | 15 +- lib/widget/part/part_list.dart | 38 +- lib/widget/part/part_parameter_widget.dart | 97 ++- lib/widget/part/part_pricing.dart | 85 ++- lib/widget/part/part_suppliers.dart | 8 +- lib/widget/progress.dart | 23 +- lib/widget/refreshable_state.dart | 100 ++- lib/widget/search.dart | 295 ++++----- lib/widget/snacks.dart | 55 +- lib/widget/spinner.dart | 16 +- lib/widget/stock/location_display.dart | 316 +++++----- lib/widget/stock/location_list.dart | 37 +- lib/widget/stock/stock_detail.dart | 504 ++++++++------- lib/widget/stock/stock_item_history.dart | 29 +- lib/widget/stock/stock_item_test_results.dart | 97 +-- lib/widget/stock/stock_list.dart | 33 +- test/api_test.dart | 14 +- test/barcode_test.dart | 28 +- test/models_test.dart | 30 +- test/preferences_test.dart | 38 +- test/setup.dart | 38 +- test/user_profile_test.dart | 33 +- test/wedge_scanner_test.dart | 11 +- 100 files changed, 5332 insertions(+), 5592 deletions(-) create mode 100644 CONTRIBUTING.md delete mode 100644 lib/generated/i18n.dart diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0dddcf8f..b61677de 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -60,6 +60,7 @@ jobs: python3 find_dart_files.py flutter pub get flutter analyze + dart format --output=none --set-exit-if-changed . - name: Install Python uses: actions/setup-python@v4 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..248d20c8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,50 @@ +# Contributing to InvenTree App + +Thank you for considering contributing to the InvenTree App! This document outlines some guidelines to ensure smooth collaboration. + +## Code Style and Formatting + +### Dart Formatting + +We enforce consistent code formatting using Dart's built-in formatter. Before submitting a pull request: + +1. Run the formatter on your code: + ```bash + fvm dart format . + ``` + +2. Our CI pipeline will verify that all code follows the standard Flutter/Dart formatting rules. Pull requests with improper formatting will fail CI checks. + +### General Guidelines + +- Write clear, readable, and maintainable code +- Include comments where necessary +- Follow Flutter/Dart best practices +- Write tests for new features when applicable + +## Pull Request Process + +1. Fork the repository and create a feature branch +2. Make your changes +3. Ensure your code passes all tests and linting +4. Format your code using `fvm dart format` +5. Submit a pull request with a clear description of the changes +6. Address any review comments + +## Development Setup + +1. Ensure you have Flutter installed (we use Flutter Version Management) +2. Check the required Flutter version in the `.fvmrc` file +3. Install dependencies with `fvm flutter pub get` +4. Run tests with `fvm flutter test` + +## Reporting Issues + +When reporting issues, please include: +- Clear steps to reproduce the issue +- Expected behavior +- Actual behavior +- Screenshots if applicable +- Device/environment information + +Thank you for contributing to the InvenTree App! diff --git a/find_dart_files.py b/find_dart_files.py index e71babd5..dbb1479b 100644 --- a/find_dart_files.py +++ b/find_dart_files.py @@ -15,6 +15,7 @@ if __name__ == "__main__": with open("test/coverage_helper_test.dart", "w") as f: f.write("// ignore_for_file: unused_import\n\n") + f.write("// dart format off\n\n") skips = [ "generated", diff --git a/lib/api.dart b/lib/api.dart index e8619adf..d8639f36 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -27,13 +27,17 @@ import "package:inventree/user_profile.dart"; import "package:inventree/widget/dialogs.dart"; import "package:inventree/widget/snacks.dart"; - /* * Class representing an API response from the server */ class APIResponse { - - APIResponse({this.url = "", this.method = "", this.statusCode = -1, this.error = "", this.data = const {}}); + APIResponse({ + this.url = "", + this.method = "", + this.statusCode = -1, + this.error = "", + this.data = const {}, + }); int statusCode = -1; @@ -88,7 +92,6 @@ class APIResponse { * Handles case where the response is paginated, or a complete set of results */ List resultsList() { - if (isList()) { return asList(); } else if (isMap()) { @@ -104,14 +107,12 @@ class APIResponse { } } - /* * Custom FileService for caching network images * Requires a custom badCertificateCallback, * so we can accept "dodgy" (e.g. self-signed) certificates */ class InvenTreeFileService extends FileService { - InvenTreeFileService({HttpClient? client, bool strictHttps = false}) { _client = client ?? HttpClient(); @@ -126,8 +127,10 @@ class InvenTreeFileService extends FileService { HttpClient? _client; @override - Future get(String url, - {Map? headers}) async { + Future get( + String url, { + Map? headers, + }) async { final Uri resolved = Uri.base.resolve(url); final HttpClientRequest req = await _client!.getUrl(resolved); @@ -141,8 +144,11 @@ class InvenTreeFileService extends FileService { final HttpClientResponse httpResponse = await req.close(); final http.StreamedResponse _response = http.StreamedResponse( - httpResponse.timeout(Duration(seconds: 60)), httpResponse.statusCode, - contentLength: httpResponse.contentLength < 0 ? 0 : httpResponse.contentLength, + httpResponse.timeout(Duration(seconds: 60)), + httpResponse.statusCode, + contentLength: httpResponse.contentLength < 0 + ? 0 + : httpResponse.contentLength, reasonPhrase: httpResponse.reasonPhrase, isRedirect: httpResponse.isRedirect, ); @@ -158,12 +164,10 @@ class InvenTreeFileService extends FileService { * initialised using a username:password combination. */ - /* * API class which manages all communication with the InvenTree server */ class InvenTreeAPI { - factory InvenTreeAPI() { return _api; } @@ -209,7 +213,6 @@ class InvenTreeAPI { } String _makeUrl(String url) { - // Strip leading slash if (url.startsWith("/")) { url = url.substring(1, url.length); @@ -257,14 +260,12 @@ class InvenTreeAPI { * Useful as a precursor check before performing operations. */ bool checkConnection() { - // Is the server connected? if (!isConnected()) { - showSnackIcon( L10().notConnected, success: false, - icon: TablerIcons.server + icon: TablerIcons.server, ); return false; @@ -388,7 +389,6 @@ class InvenTreeAPI { return !isConnected() && _connecting; } - /* * Perform the required login steps, in sequence. * Internal function, called by connectToServer() @@ -403,7 +403,6 @@ class InvenTreeAPI { * 5. Request information on available plugins */ Future _connectToServer() async { - if (!await _checkServer()) { return false; } @@ -413,7 +412,11 @@ class InvenTreeAPI { } if (!await _checkAuth()) { - showServerError(_URL_ME, L10().serverNotConnected, L10().serverAuthenticationError); + showServerError( + _URL_ME, + L10().serverNotConnected, + L10().serverAuthenticationError, + ); // Invalidate the token if (profile != null) { @@ -436,20 +439,18 @@ class InvenTreeAPI { return true; } - /* * Check that the remote server is available. * Ping the api/ endpoint, which does not require user authentication */ Future _checkServer() async { - String address = profile?.server ?? ""; if (address.isEmpty) { showSnackIcon( - L10().incompleteDetails, - icon: TablerIcons.exclamation_circle, - success: false + L10().incompleteDetails, + icon: TablerIcons.exclamation_circle, + success: false, ); return false; } @@ -459,7 +460,9 @@ class InvenTreeAPI { } // Cache the "strictHttps" setting, so we can use it later without async requirement - _strictHttps = await InvenTreeSettingsManager().getValue(INV_STRICT_HTTPS, false) as bool; + _strictHttps = + await InvenTreeSettingsManager().getValue(INV_STRICT_HTTPS, false) + as bool; debug("Connecting to ${apiUrl}"); @@ -467,7 +470,11 @@ class InvenTreeAPI { if (!response.successful()) { debug("Server returned invalid response: ${response.statusCode}"); - showStatusCodeError(apiUrl, response.statusCode, details: response.data.toString()); + showStatusCodeError( + apiUrl, + response.statusCode, + details: response.data.toString(), + ); return false; } @@ -476,17 +483,12 @@ class InvenTreeAPI { serverInfo = {..._data}; if (serverVersion.isEmpty) { - showServerError( - apiUrl, - L10().missingData, - L10().serverMissingData, - ); + showServerError(apiUrl, L10().missingData, L10().serverMissingData); return false; } if (apiVersion < _minApiVersion) { - String message = L10().serverApiVersion + ": ${apiVersion}"; message += "\n"; @@ -496,11 +498,7 @@ class InvenTreeAPI { message += "Ensure your InvenTree server version is up to date!"; - showServerError( - apiUrl, - L10().serverOld, - message, - ); + showServerError(apiUrl, L10().serverOld, message); return false; } @@ -509,7 +507,6 @@ class InvenTreeAPI { return true; } - /* * Check that the user is authenticated * Fetch the user information @@ -525,7 +522,9 @@ class InvenTreeAPI { userInfo = response.asMap(); return true; } else { - debug("Auth request failed: Server returned status ${response.statusCode}"); + debug( + "Auth request failed: Server returned status ${response.statusCode}", + ); if (response.data != null) { debug("Server response: ${response.data.toString()}"); } @@ -538,8 +537,11 @@ class InvenTreeAPI { * Fetch a token from the server, * with a temporary authentication header */ - Future fetchToken(UserProfile userProfile, String username, String password) async { - + Future fetchToken( + UserProfile userProfile, + String username, + String password, + ) async { debug("Fetching user token from ${userProfile.server}"); profile = userProfile; @@ -574,13 +576,14 @@ class InvenTreeAPI { } // Construct auth header from username and password - String authHeader = "Basic " + base64Encode(utf8.encode("${username}:${password}")); + String authHeader = + "Basic " + base64Encode(utf8.encode("${username}:${password}")); // Perform request to get a token final response = await get( - _URL_TOKEN, - params: { "name": platform_name}, - headers: { HttpHeaders.authorizationHeader: authHeader} + _URL_TOKEN, + params: {"name": platform_name}, + headers: {HttpHeaders.authorizationHeader: authHeader}, ); // Invalid response @@ -641,11 +644,9 @@ class InvenTreeAPI { _connectionStatusChanged(); } - /* Public facing connection function. */ Future connectToServer(UserProfile prf) async { - // Ensure server is first disconnected disconnectFromServer(); @@ -653,9 +654,9 @@ class InvenTreeAPI { if (profile == null) { showSnackIcon( - L10().profileSelect, - success: false, - icon: TablerIcons.exclamation_circle + L10().profileSelect, + success: false, + icon: TablerIcons.exclamation_circle, ); return false; } @@ -679,11 +680,9 @@ class InvenTreeAPI { if (_notification_timer == null) { debug("starting notification timer"); - _notification_timer = Timer.periodic( - Duration(seconds: 60), - (timer) { - _refreshNotifications(); - }); + _notification_timer = Timer.periodic(Duration(seconds: 60), (timer) { + _refreshNotifications(); + }); } } @@ -698,7 +697,6 @@ class InvenTreeAPI { * Request the user roles (permissions) from the InvenTree server */ Future _fetchRoles() async { - roles.clear(); debug("API: Requesting user role data"); @@ -712,15 +710,10 @@ class InvenTreeAPI { var data = response.asMap(); if (!data.containsKey("roles")) { - roles = {}; permissions = {}; - showServerError( - apiUrl, - L10().serverError, - L10().errorUserRoles, - ); + showServerError(apiUrl, L10().serverError, L10().errorUserRoles); return false; } @@ -737,7 +730,6 @@ class InvenTreeAPI { // Request plugin information from the server Future _fetchPlugins() async { - _plugins.clear(); debug("API: getPluginInformation()"); @@ -762,7 +754,6 @@ class InvenTreeAPI { * e.g. "sales_order", "change" */ bool checkRole(String role, String permission) { - if (!_connected) { return false; } @@ -793,12 +784,13 @@ class InvenTreeAPI { // Unknown error - report it! sentryReportError( "api.checkRole", - error, stackTrace, + error, + stackTrace, context: { "role": role, "permission": permission, "error": error.toString(), - } + }, ); } @@ -840,13 +832,14 @@ class InvenTreeAPI { } else { // Unknown error - report it! sentryReportError( - "api.checkPermission", - error, stackTrace, - context: { - "model": model, - "permission": permission, - "error": error.toString(), - } + "api.checkPermission", + error, + stackTrace, + context: { + "model": model, + "permission": permission, + "error": error.toString(), + }, ); } @@ -855,10 +848,12 @@ class InvenTreeAPI { } } - // Perform a PATCH request - Future patch(String url, {Map body = const {}, int? expectedStatusCode}) async { - + Future patch( + String url, { + Map body = const {}, + int? expectedStatusCode, + }) async { Map _body = body; HttpClientRequest? request = await apiRequest(url, "PATCH"); @@ -868,14 +863,14 @@ class InvenTreeAPI { return APIResponse( url: url, method: "PATCH", - error: "HttpClientRequest is null" + error: "HttpClientRequest is null", ); } return completeRequest( request, data: json.encode(_body), - statusCode: expectedStatusCode + statusCode: expectedStatusCode, ); } @@ -883,7 +878,6 @@ class InvenTreeAPI { * Download a file from the given URL */ Future downloadFile(String url, {bool openOnDownload = true}) async { - if (url.isEmpty) { // No URL provided for download return; @@ -910,19 +904,22 @@ class InvenTreeAPI { HttpClientRequest? _request; - final bool strictHttps = await InvenTreeSettingsManager().getValue(INV_STRICT_HTTPS, false) as bool; + final bool strictHttps = + await InvenTreeSettingsManager().getValue(INV_STRICT_HTTPS, false) + as bool; var client = createClient(url, strictHttps: strictHttps); // Attempt to open a connection to the server try { - _request = await client.openUrl("GET", _uri).timeout(Duration(seconds: 10)); + _request = await client + .openUrl("GET", _uri) + .timeout(Duration(seconds: 10)); // Set headers defaultHeaders().forEach((key, value) { _request?.headers.set(key, value); }); - } on SocketException catch (error) { debug("SocketException at ${url}: ${error.toString()}"); showServerError(url, L10().connectionRefused, error.toString()); @@ -939,10 +936,7 @@ class InvenTreeAPI { } catch (error, stackTrace) { debug("Server error at ${url}: ${error.toString()}"); showServerError(url, L10().serverError, error.toString()); - sentryReportError( - "api.downloadFile : client.openUrl", - error, stackTrace, - ); + sentryReportError("api.downloadFile : client.openUrl", error, stackTrace); return; } @@ -972,7 +966,8 @@ class InvenTreeAPI { showServerError(url, L10().downloadError, error.toString()); sentryReportError( "api.downloadFile : client.closeRequest", - error, stackTrace, + error, + stackTrace, ); } } @@ -980,8 +975,13 @@ class InvenTreeAPI { /* * Upload a file to the given URL */ - Future uploadFile(String url, File f, - {String name = "attachment", String method="POST", Map? fields}) async { + Future uploadFile( + String url, + File f, { + String name = "attachment", + String method = "POST", + Map? fields, + }) async { var _url = makeApiUrl(url); var request = http.MultipartRequest(method, Uri.parse(_url)); @@ -990,7 +990,6 @@ class InvenTreeAPI { if (fields != null) { fields.forEach((String key, dynamic value) { - if (value == null) { request.fields[key] = ""; } else { @@ -1003,10 +1002,7 @@ class InvenTreeAPI { request.files.add(_file); - APIResponse response = APIResponse( - url: url, - method: method, - ); + APIResponse response = APIResponse(url: url, method: method); String jsondata = ""; @@ -1022,15 +1018,15 @@ class InvenTreeAPI { // Report a server-side error if (response.statusCode == 500) { sentryReportMessage( - "Server error in uploadFile()", - context: { - "url": url, - "method": request.method, - "name": name, - "statusCode": response.statusCode.toString(), - "requestHeaders": request.headers.toString(), - "responseHeaders": httpResponse.headers.toString(), - } + "Server error in uploadFile()", + context: { + "url": url, + "method": request.method, + "name": name, + "statusCode": response.statusCode.toString(), + "requestHeaders": request.headers.toString(), + "responseHeaders": httpResponse.headers.toString(), + }, ); } } on SocketException catch (error) { @@ -1041,28 +1037,24 @@ class InvenTreeAPI { showServerError( url, L10().formatException, - L10().formatExceptionJson + ":\n${jsondata}" + L10().formatExceptionJson + ":\n${jsondata}", ); sentryReportMessage( - "Error decoding JSON response from server", - context: { - "method": "uploadFile", - "url": url, - "statusCode": response.statusCode.toString(), - "data": jsondata, - } + "Error decoding JSON response from server", + context: { + "method": "uploadFile", + "url": url, + "statusCode": response.statusCode.toString(), + "data": jsondata, + }, ); - } on TimeoutException { showTimeoutError(url); response.error = "TimeoutException"; } catch (error, stackTrace) { showServerError(url, L10().serverError, error.toString()); - sentryReportError( - "api.uploadFile", - error, stackTrace - ); + sentryReportError("api.uploadFile", error, stackTrace); response.error = "UnknownError"; response.errorDetail = error.toString(); } @@ -1077,15 +1069,11 @@ class InvenTreeAPI { * so that (hopefully) the field messages are correctly translated */ Future options(String url) async { - HttpClientRequest? request = await apiRequest(url, "OPTIONS"); if (request == null) { // Return an "invalid" APIResponse - return APIResponse( - url: url, - method: "OPTIONS" - ); + return APIResponse(url: url, method: "OPTIONS"); } return completeRequest(request); @@ -1095,22 +1083,22 @@ class InvenTreeAPI { * Perform a HTTP POST request * Returns a json object (or null if unsuccessful) */ - Future post(String url, {Map body = const {}, int? expectedStatusCode=201}) async { - + Future post( + String url, { + Map body = const {}, + int? expectedStatusCode = 201, + }) async { HttpClientRequest? request = await apiRequest(url, "POST"); if (request == null) { // Return an "invalid" APIResponse - return APIResponse( - url: url, - method: "POST" - ); + return APIResponse(url: url, method: "POST"); } return completeRequest( request, data: json.encode(body), - statusCode: expectedStatusCode + statusCode: expectedStatusCode, ); } @@ -1118,28 +1106,25 @@ class InvenTreeAPI { * Perform a request to link a custom barcode to a particular item */ Future linkBarcode(Map body) async { + HttpClientRequest? request = await apiRequest("/barcode/link/", "POST"); - HttpClientRequest? request = await apiRequest("/barcode/link/", "POST"); + if (request == null) { + return false; + } - if (request == null) { - return false; - } - - final response = await completeRequest( - request, - data: json.encode(body), - statusCode: 200 - ); - - return response.isValid() && response.statusCode == 200; + final response = await completeRequest( + request, + data: json.encode(body), + statusCode: 200, + ); + return response.isValid() && response.statusCode == 200; } /* * Perform a request to unlink a custom barcode from a particular item */ Future unlinkBarcode(Map body) async { - HttpClientRequest? request = await apiRequest("/barcode/unlink/", "POST"); if (request == null) { @@ -1147,33 +1132,31 @@ class InvenTreeAPI { } final response = await completeRequest( - request, - data: json.encode(body), - statusCode: 200, + request, + data: json.encode(body), + statusCode: 200, ); return response.isValid() && response.statusCode == 200; } - HttpClient createClient(String url, {bool strictHttps = false}) { - var client = HttpClient(); - client.badCertificateCallback = (X509Certificate cert, String host, int port) { + client.badCertificateCallback = + (X509Certificate cert, String host, int port) { + if (strictHttps) { + showServerError( + url, + L10().serverCertificateError, + L10().serverCertificateInvalid, + ); + return false; + } - if (strictHttps) { - showServerError( - url, - L10().serverCertificateError, - L10().serverCertificateInvalid, - ); - return false; - } - - // Strict HTTPs not enforced, so we'll ignore the bad cert - return true; - }; + // Strict HTTPs not enforced, so we'll ignore the bad cert + return true; + }; // Set the connection timeout client.connectionTimeout = Duration(seconds: 30); @@ -1189,22 +1172,15 @@ class InvenTreeAPI { * @param params is the request parameters */ Future apiRequest( - String url, - String method, - { - Map urlParams = const {}, - Map headers = const {}, - } - ) async { - + String url, + String method, { + Map urlParams = const {}, + Map headers = const {}, + }) async { var _url = makeApiUrl(url); if (_url.isEmpty) { - showServerError( - url, - L10().invalidHost, - L10().invalidHostDetails - ); + showServerError(url, L10().invalidHost, L10().invalidHostDetails); return null; } @@ -1225,23 +1201,23 @@ class InvenTreeAPI { Uri? _uri = Uri.tryParse(_url); if (_uri == null || _uri.host.isEmpty) { - showServerError( - _url, - L10().invalidHost, - L10().invalidHostDetails - ); + showServerError(_url, L10().invalidHost, L10().invalidHostDetails); return null; } HttpClientRequest? _request; - final bool strictHttps = await InvenTreeSettingsManager().getValue(INV_STRICT_HTTPS, false) as bool; + final bool strictHttps = + await InvenTreeSettingsManager().getValue(INV_STRICT_HTTPS, false) + as bool; var client = createClient(url, strictHttps: strictHttps); // Attempt to open a connection to the server try { - _request = await client.openUrl(method, _uri).timeout(Duration(seconds: 10)); + _request = await client + .openUrl(method, _uri) + .timeout(Duration(seconds: 10)); // Default headers defaultHeaders().forEach((key, value) { @@ -1281,40 +1257,45 @@ class InvenTreeAPI { showServerError(url, L10().serverError, error.toString()); sentryReportError( "api.apiRequest : openUrl", - error, stackTrace, - context: { - "url": url, - "method": method, - } + error, + stackTrace, + context: {"url": url, "method": method}, ); return null; } } - /* * Complete an API request, and return an APIResponse object */ - Future completeRequest(HttpClientRequest request, {String? data, int? statusCode, bool ignoreResponse = false}) async { - + Future completeRequest( + HttpClientRequest request, { + String? data, + int? statusCode, + bool ignoreResponse = false, + }) async { if (data != null && data.isNotEmpty) { - var encoded_data = utf8.encode(data); - request.headers.set(HttpHeaders.contentLengthHeader, encoded_data.length.toString()); + request.headers.set( + HttpHeaders.contentLengthHeader, + encoded_data.length.toString(), + ); request.add(encoded_data); } APIResponse response = APIResponse( method: request.method, - url: request.uri.toString() + url: request.uri.toString(), ); String url = request.uri.toString(); try { - HttpClientResponse? _response = await request.close().timeout(Duration(seconds: 10)); + HttpClientResponse? _response = await request.close().timeout( + Duration(seconds: 10), + ); response.statusCode = _response.statusCode; @@ -1324,30 +1305,35 @@ class InvenTreeAPI { // Some server errors are not ones for us to worry about! switch (_response.statusCode) { - case 502: // Bad gateway - case 503: // Service unavailable - case 504: // Gateway timeout + case 502: // Bad gateway + case 503: // Service unavailable + case 504: // Gateway timeout break; - default: // Any other error code + default: // Any other error code sentryReportMessage( - "Server error", - context: { - "url": request.uri.toString(), - "method": request.method, - "statusCode": _response.statusCode.toString(), - "requestHeaders": request.headers.toString(), - "responseHeaders": _response.headers.toString(), - "responseData": response.data.toString(), - } + "Server error", + context: { + "url": request.uri.toString(), + "method": request.method, + "statusCode": _response.statusCode.toString(), + "requestHeaders": request.headers.toString(), + "responseHeaders": _response.headers.toString(), + "responseData": response.data.toString(), + }, ); } } else { - - response.data = ignoreResponse ? {} : await responseToJson(url, _response) ?? {}; + response.data = ignoreResponse + ? {} + : await responseToJson(url, _response) ?? {}; // First check that the returned status code is what we expected if (statusCode != null && statusCode != _response.statusCode) { - showStatusCodeError(url, _response.statusCode, details: response.data.toString()); + showStatusCodeError( + url, + _response.statusCode, + details: response.data.toString(), + ); } } } on HttpException catch (error) { @@ -1373,14 +1359,12 @@ class InvenTreeAPI { } return response; - } /* * Convert a HttpClientResponse response object to JSON */ dynamic responseToJson(String url, HttpClientResponse response) async { - String body = await response.transform(utf8.decoder).join(); try { @@ -1388,7 +1372,6 @@ class InvenTreeAPI { return data ?? {}; } on FormatException { - switch (response.statusCode) { case 400: case 401: @@ -1403,34 +1386,37 @@ class InvenTreeAPI { break; default: sentryReportMessage( - "Error decoding JSON response from server", - context: { - "headers": response.headers.toString(), - "statusCode": response.statusCode.toString(), - "data": body.toString(), - "endpoint": url, - } + "Error decoding JSON response from server", + context: { + "headers": response.headers.toString(), + "statusCode": response.statusCode.toString(), + "data": body.toString(), + "endpoint": url, + }, ); } showServerError( url, L10().formatException, - L10().formatExceptionJson + ":\n${body}" + L10().formatExceptionJson + ":\n${body}", ); // Return an empty map return {}; } - } /* * Perform a HTTP GET request * Returns a json object (or null if did not complete) */ - Future get(String url, {Map params = const {}, Map headers = const {}, int? expectedStatusCode=200}) async { - + Future get( + String url, { + Map params = const {}, + Map headers = const {}, + int? expectedStatusCode = 200, + }) async { HttpClientRequest? request = await apiRequest( url, "GET", @@ -1438,7 +1424,6 @@ class InvenTreeAPI { headers: headers, ); - if (request == null) { // Return an "invalid" APIResponse return APIResponse( @@ -1455,11 +1440,7 @@ class InvenTreeAPI { * Perform a HTTP DELETE request */ Future delete(String url) async { - - HttpClientRequest? request = await apiRequest( - url, - "DELETE", - ); + HttpClientRequest? request = await apiRequest(url, "DELETE"); if (request == null) { // Return an "invalid" APIResponse object @@ -1470,23 +1451,17 @@ class InvenTreeAPI { ); } - return completeRequest( - request, - ignoreResponse: true, - ); + return completeRequest(request, ignoreResponse: true); } // Find the current locale code for the running app String get currentLocale { - if (hasContext()) { // Try to get app context BuildContext? context = OneContext().context; if (context != null) { - Locale? locale = InvenTreeApp - .of(context) - ?.locale; + Locale? locale = InvenTreeApp.of(context)?.locale; if (locale != null) { return locale.languageCode; //.toString(); @@ -1526,8 +1501,11 @@ class InvenTreeAPI { static String get staticThumb => "/static/img/blank_image.thumbnail.png"; - CachedNetworkImage? getThumbnail(String imageUrl, {double size = 40, bool hideIfNull = false}) { - + CachedNetworkImage? getThumbnail( + String imageUrl, { + double size = 40, + bool hideIfNull = false, + }) { if (hideIfNull) { if (imageUrl.isEmpty) { return null; @@ -1535,11 +1513,7 @@ class InvenTreeAPI { } try { - return getImage( - imageUrl, - width: size, - height: size - ); + return getImage(imageUrl, width: size, height: size); } catch (error, stackTrace) { sentryReportError("_getThumbnail", error, stackTrace); return null; @@ -1550,7 +1524,11 @@ class InvenTreeAPI { * Load image from the InvenTree server, * or from local cache (if it has been cached!) */ - CachedNetworkImage getImage(String imageUrl, {double? height, double? width}) { + CachedNetworkImage getImage( + String imageUrl, { + double? height, + double? width, + }) { if (imageUrl.isEmpty) { imageUrl = staticImage; } @@ -1560,18 +1538,14 @@ class InvenTreeAPI { const key = "inventree_network_image"; CacheManager manager = CacheManager( - Config( - key, - fileService: InvenTreeFileService( - strictHttps: _strictHttps, - ), - ) + Config(key, fileService: InvenTreeFileService(strictHttps: _strictHttps)), ); return CachedNetworkImage( imageUrl: url, placeholder: (context, url) => CircularProgressIndicator(), - errorWidget: (context, url, error) => Icon(TablerIcons.circle_x, color: COLOR_DANGER), + errorWidget: (context, url, error) => + Icon(TablerIcons.circle_x, color: COLOR_DANGER), httpHeaders: defaultHeaders(), height: height, width: width, @@ -1584,7 +1558,6 @@ class InvenTreeAPI { Map _userSettings = {}; Future getGlobalSetting(String key) async { - InvenTreeGlobalSetting? setting = _globalSettings[key]; if ((setting != null) && setting.reloadedWithin(Duration(minutes: 5))) { @@ -1603,7 +1576,10 @@ class InvenTreeAPI { } // Return a boolean global setting value - Future getGlobalBooleanSetting(String key, { bool backup = false }) async { + Future getGlobalBooleanSetting( + String key, { + bool backup = false, + }) async { String value = await getGlobalSetting(key); if (value.isEmpty) { @@ -1614,7 +1590,6 @@ class InvenTreeAPI { } Future getUserSetting(String key) async { - InvenTreeUserSetting? setting = _userSettings[key]; if ((setting != null) && setting.reloadedWithin(Duration(minutes: 5))) { @@ -1641,8 +1616,11 @@ class InvenTreeAPI { /* * Send a request to the server to locate / identify either a StockItem or StockLocation */ - Future locateItemOrLocation(BuildContext context, {int? item, int? location}) async { - + Future locateItemOrLocation( + BuildContext context, { + int? item, + int? location, + }) async { var plugins = getPlugins(mixin: "locate"); if (plugins.isEmpty) { @@ -1672,24 +1650,22 @@ class InvenTreeAPI { "value": plugins.first.key, "choices": plugin_options, "required": true, - } + }, }; await launchApiForm( - context, - L10().locateLocation, - "", - fields, - icon: TablerIcons.location_search, - onSuccess: (Map data) async { - plugin_name = (data["plugin"] ?? "") as String; - } + context, + L10().locateLocation, + "", + fields, + icon: TablerIcons.location_search, + onSuccess: (Map data) async { + plugin_name = (data["plugin"] ?? "") as String; + }, ); } - Map body = { - "plugin": plugin_name, - }; + Map body = {"plugin": plugin_name}; if (item != null) { body["item"] = item.toString(); @@ -1699,16 +1675,11 @@ class InvenTreeAPI { body["location"] = location.toString(); } - post( - "/api/locate/", - body: body, - expectedStatusCode: 200, - ).then((APIResponse response) { + post("/api/locate/", body: body, expectedStatusCode: 200).then(( + APIResponse response, + ) { if (response.successful()) { - showSnackIcon( - L10().requestSuccessful, - success: true, - ); + showSnackIcon(L10().requestSuccessful, success: true); } }); } @@ -1726,10 +1697,13 @@ class InvenTreeAPI { } // Accessors methods for various status code classes - InvenTreeStatusCode get StockHistoryStatus => _get_status_class("stock/track/status/"); + InvenTreeStatusCode get StockHistoryStatus => + _get_status_class("stock/track/status/"); InvenTreeStatusCode get StockStatus => _get_status_class("stock/status/"); - InvenTreeStatusCode get PurchaseOrderStatus => _get_status_class("order/po/status/"); - InvenTreeStatusCode get SalesOrderStatus => _get_status_class("order/so/status/"); + InvenTreeStatusCode get PurchaseOrderStatus => + _get_status_class("order/po/status/"); + InvenTreeStatusCode get SalesOrderStatus => + _get_status_class("order/so/status/"); void clearStatusCodeData() { StockHistoryStatus.data.clear(); @@ -1762,5 +1736,3 @@ class InvenTreeAPI { }); } } - - diff --git a/lib/api_form.dart b/lib/api_form.dart index 75ea5d95..e227289a 100644 --- a/lib/api_form.dart +++ b/lib/api_form.dart @@ -1,4 +1,3 @@ - import "dart:io"; import "package:intl/intl.dart"; @@ -27,13 +26,11 @@ import "package:inventree/widget/fields.dart"; import "package:inventree/widget/progress.dart"; import "package:inventree/widget/snacks.dart"; - /* * Class that represents a single "form field", * defined by the InvenTree API */ class APIFormField { - // Constructor APIFormField(this.name, this.data); @@ -53,7 +50,6 @@ class APIFormField { // Return the "lookup path" for this field, within the server data String get lookupPath { - // Simple top-level case if (parent.isEmpty && !nested) { return name; @@ -133,19 +129,16 @@ class APIFormField { // Construct a set of "filters" for this field (e.g. related field) Map get filters { - Map _filters = {}; // Start with the field "definition" (provided by the server) if (definition.containsKey("filters")) { - try { var fDef = definition["filters"] as Map; fDef.forEach((String key, dynamic value) { _filters[key] = value.toString(); }); - } catch (error) { // pass } @@ -153,7 +146,6 @@ class APIFormField { // Next, look at any "instance_filters" provided by the server if (definition.containsKey("instance_filters")) { - try { var fIns = definition["instance_filters"] as Map; @@ -163,7 +155,6 @@ class APIFormField { } catch (error) { // pass } - } // Finally, augment or override with any filters provided by the calling function @@ -180,14 +171,12 @@ class APIFormField { } return _filters; - } bool hasErrors() => errorMessages().isNotEmpty; // Extract error messages from the server response void extractErrorMessages(APIResponse response) { - dynamic errors; if (isSimple) { @@ -213,7 +202,6 @@ class APIFormField { // Return the error message associated with this field List errorMessages() { - dynamic errors = data["errors"] ?? []; // Handle the case where a single error message is returned @@ -246,7 +234,6 @@ class APIFormField { List get choices => (getParameter("choices") ?? []) as List; Future loadInitialData() async { - // Only for "related fields" if (type != "related field") { return; @@ -265,10 +252,7 @@ class APIFormField { String url = api_url + "/" + pk.toString() + "/"; - final APIResponse response = await InvenTreeAPI().get( - url, - params: filters, - ); + final APIResponse response = await InvenTreeAPI().get(url, params: filters); if (response.successful()) { initial_data = response.data; @@ -277,7 +261,6 @@ class APIFormField { // Construct a widget for this input Widget constructField(BuildContext context) { - switch (type) { case "string": case "url": @@ -302,17 +285,14 @@ class APIFormField { return ListTile( title: Text( "Unsupported field type: '${type}' for field '${name}'", - style: TextStyle( - color: COLOR_DANGER, - fontStyle: FontStyle.italic), - ) + style: TextStyle(color: COLOR_DANGER, fontStyle: FontStyle.italic), + ), ); } } // Field for capturing a barcode Widget _constructBarcodeField(BuildContext context) { - TextEditingController controller = TextEditingController(); String barcode = (value ?? "").toString(); @@ -332,10 +312,7 @@ class APIFormField { hintText: placeholderText, ), child: ListTile( - title: TextField( - readOnly: true, - controller: controller, - ), + title: TextField(readOnly: true, controller: controller), trailing: IconButton( icon: Icon(TablerIcons.qrcode), onPressed: () async { @@ -349,15 +326,13 @@ class APIFormField { scanBarcode(context, handler: handler); }, ), - ) + ), ); - } // Field for displaying and selecting dates Widget _constructDateField() { - - DateTime? currentDate = DateTime.tryParse((value ?? "")as String); + DateTime? currentDate = DateTime.tryParse((value ?? "") as String); return InputDecorator( decoration: InputDecoration( @@ -387,18 +362,17 @@ class APIFormField { return time; }, - ) + ), ); - } - // Field for selecting and uploading files Widget _constructFileField() { - TextEditingController controller = TextEditingController(); - controller.text = (attachedfile?.path ?? L10().attachmentSelect).split("/").last; + controller.text = (attachedfile?.path ?? L10().attachmentSelect) + .split("/") + .last; return InputDecorator( decoration: InputDecoration( @@ -406,10 +380,7 @@ class APIFormField { labelStyle: TextStyle(fontWeight: FontWeight.bold, fontSize: 18), ), child: ListTile( - title: TextField( - readOnly: true, - controller: controller, - ), + title: TextField(readOnly: true, controller: controller), trailing: IconButton( icon: Icon(TablerIcons.circle_plus), onPressed: () async { @@ -421,17 +392,16 @@ class APIFormField { // Save the file attachedfile = file; - } + }, ); }, - ) - ) + ), + ), ); } // Field for selecting from multiple choice options Widget _constructChoiceField() { - dynamic initial; // Check if the current value is within the allowed values @@ -445,17 +415,16 @@ class APIFormField { return DropdownSearch( popupProps: PopupProps.bottomSheet( showSelectedItems: false, - searchFieldProps: TextFieldProps( - autofocus: true - ) + searchFieldProps: TextFieldProps(autofocus: true), ), selectedItem: initial, items: choices, dropdownDecoratorProps: DropDownDecoratorProps( - dropdownSearchDecoration: InputDecoration( - labelText: label, - hintText: helpText, - )), + dropdownSearchDecoration: InputDecoration( + labelText: label, + hintText: helpText, + ), + ), onChanged: null, clearButtonProps: ClearButtonProps(isVisible: !required), itemAsString: (dynamic item) { @@ -467,12 +436,12 @@ class APIFormField { } else { data["value"] = item["value"]; } - }); + }, + ); } // Construct a floating point numerical input field Widget _constructFloatField() { - // Initial value: try to cast to a valid number String initial = ""; @@ -491,7 +460,10 @@ class APIFormField { hintText: placeholderText, ), initialValue: initial, - keyboardType: TextInputType.numberWithOptions(signed: true, decimal: true), + keyboardType: TextInputType.numberWithOptions( + signed: true, + decimal: true, + ), validator: (value) { value = value?.trim() ?? ""; @@ -512,7 +484,6 @@ class APIFormField { data["value"] = val; }, ); - } // Construct an input for a related field @@ -528,22 +499,20 @@ class APIFormField { emptyBuilder: (context, item) { return _renderEmptyResult(); }, - searchFieldProps: TextFieldProps( - autofocus: true - ) + searchFieldProps: TextFieldProps(autofocus: true), ), selectedItem: initial_data, asyncItems: (String filter) async { - Map _filters = { - ..._relatedFieldFilters(), - ...filters, - }; + Map _filters = {..._relatedFieldFilters(), ...filters}; _filters["search"] = filter; _filters["offset"] = "0"; _filters["limit"] = "25"; - final APIResponse response = await InvenTreeAPI().get(api_url, params: _filters); + final APIResponse response = await InvenTreeAPI().get( + api_url, + params: _filters, + ); if (response.isValid()) { return response.resultsList(); @@ -551,14 +520,13 @@ class APIFormField { return []; } }, - clearButtonProps: ClearButtonProps( - isVisible: !required - ), + clearButtonProps: ClearButtonProps(isVisible: !required), dropdownDecoratorProps: DropDownDecoratorProps( - dropdownSearchDecoration: InputDecoration( - labelText: label, - hintText: helpText, - )), + dropdownSearchDecoration: InputDecoration( + labelText: label, + hintText: helpText, + ), + ), onChanged: null, itemAsString: (dynamic item) { Map data = item as Map; @@ -607,12 +575,12 @@ class APIFormField { } return result; - }); + }, + ); } // Construct a set of custom filters for the dropdown search Map _relatedFieldFilters() { - switch (model) { case InvenTreeSupplierPart.MODEL_TYPE: return InvenTreeSupplierPart().defaultListFilters(); @@ -626,8 +594,12 @@ class APIFormField { } // Render a "related field" based on the "model" type - Widget _renderRelatedField(String fieldName, dynamic item, bool selected, bool extended) { - + Widget _renderRelatedField( + String fieldName, + dynamic item, + bool selected, + bool extended, + ) { // Convert to JSON Map data = {}; @@ -641,14 +613,16 @@ class APIFormField { data = {}; sentryReportError( - "_renderRelatedField", error, stackTrace, + "_renderRelatedField", + error, + stackTrace, context: { "method": "_renderRelateField", "field_name": fieldName, "item": item.toString(), "selected": selected.toString(), "extended": extended.toString(), - } + }, ); } @@ -658,52 +632,71 @@ class APIFormField { return ListTile( title: Text( - part.fullname, - style: TextStyle(fontWeight: selected && extended ? FontWeight.bold : FontWeight.normal) + part.fullname, + style: TextStyle( + fontWeight: selected && extended + ? FontWeight.bold + : FontWeight.normal, + ), ), - subtitle: extended ? Text( - part.description, - style: TextStyle(fontWeight: selected ? FontWeight.bold : FontWeight.normal), - ) : null, - leading: extended ? InvenTreeAPI().getThumbnail(part.thumbnail) : null, + subtitle: extended + ? Text( + part.description, + style: TextStyle( + fontWeight: selected ? FontWeight.bold : FontWeight.normal, + ), + ) + : null, + leading: extended + ? InvenTreeAPI().getThumbnail(part.thumbnail) + : null, ); case InvenTreePartTestTemplate.MODEL_TYPE: - var template = InvenTreePartTestTemplate.fromJson(data); + var template = InvenTreePartTestTemplate.fromJson(data); - return ListTile( - title: Text(template.testName), - subtitle: Text(template.description), - ); + return ListTile( + title: Text(template.testName), + subtitle: Text(template.description), + ); case InvenTreeSupplierPart.MODEL_TYPE: var part = InvenTreeSupplierPart.fromJson(data); return ListTile( title: Text(part.SKU), subtitle: Text(part.partName), - leading: extended ? InvenTreeAPI().getThumbnail(part.partImage) : null, - trailing: extended && part.supplierImage.isNotEmpty ? InvenTreeAPI().getThumbnail(part.supplierImage) : null, + leading: extended + ? InvenTreeAPI().getThumbnail(part.partImage) + : null, + trailing: extended && part.supplierImage.isNotEmpty + ? InvenTreeAPI().getThumbnail(part.supplierImage) + : null, ); case InvenTreePartCategory.MODEL_TYPE: - var cat = InvenTreePartCategory.fromJson(data); return ListTile( title: Text( - cat.pathstring, - style: TextStyle(fontWeight: selected && extended ? FontWeight.bold : FontWeight.normal) + cat.pathstring, + style: TextStyle( + fontWeight: selected && extended + ? FontWeight.bold + : FontWeight.normal, + ), ), - subtitle: extended ? Text( - cat.description, - style: TextStyle(fontWeight: selected ? FontWeight.bold : FontWeight.normal), - ) : null, + subtitle: extended + ? Text( + cat.description, + style: TextStyle( + fontWeight: selected ? FontWeight.bold : FontWeight.normal, + ), + ) + : null, ); case InvenTreeStockItem.MODEL_TYPE: var item = InvenTreeStockItem.fromJson(data); return ListTile( - title: Text( - item.partName, - ), + title: Text(item.partName), leading: InvenTreeAPI().getThumbnail(item.partThumbnail), trailing: Text(item.quantityString()), ); @@ -712,13 +705,21 @@ class APIFormField { return ListTile( title: Text( - loc.pathstring, - style: TextStyle(fontWeight: selected && extended ? FontWeight.bold : FontWeight.normal) + loc.pathstring, + style: TextStyle( + fontWeight: selected && extended + ? FontWeight.bold + : FontWeight.normal, + ), ), - subtitle: extended ? Text( - loc.description, - style: TextStyle(fontWeight: selected ? FontWeight.bold : FontWeight.normal), - ) : null, + subtitle: extended + ? Text( + loc.description, + style: TextStyle( + fontWeight: selected ? FontWeight.bold : FontWeight.normal, + ), + ) + : null, ); case InvenTreeSalesOrderShipment.MODEL_TYPE: var shipment = InvenTreeSalesOrderShipment.fromJson(data); @@ -738,32 +739,26 @@ class APIFormField { case "contact": String name = (data["name"] ?? "") as String; String role = (data["role"] ?? "") as String; - return ListTile( - title: Text(name), - subtitle: Text(role), - ); + return ListTile(title: Text(name), subtitle: Text(role)); case InvenTreeCompany.MODEL_TYPE: var company = InvenTreeCompany.fromJson(data); return ListTile( - title: Text(company.name), - subtitle: extended ? Text(company.description) : null, - leading: InvenTreeAPI().getThumbnail(company.thumbnail) + title: Text(company.name), + subtitle: extended ? Text(company.description) : null, + leading: InvenTreeAPI().getThumbnail(company.thumbnail), ); case InvenTreeProjectCode.MODEL_TYPE: var project_code = InvenTreeProjectCode.fromJson(data); return ListTile( - title: Text(project_code.code), - subtitle: Text(project_code.description), - leading: Icon(TablerIcons.list) + title: Text(project_code.code), + subtitle: Text(project_code.description), + leading: Icon(TablerIcons.list), ); default: return ListTile( title: Text( - "Unsupported model", - style: TextStyle( - fontWeight: FontWeight.bold, - color: COLOR_DANGER - ) + "Unsupported model", + style: TextStyle(fontWeight: FontWeight.bold, color: COLOR_DANGER), ), subtitle: Text("Model '${model}' rendering not supported"), ); @@ -782,10 +777,8 @@ class APIFormField { ); } - // Construct a string input element Widget _constructString() { - if (readOnly) { return ListTile( title: Text(label), @@ -821,7 +814,6 @@ class APIFormField { // Construct a boolean input element Widget _constructBoolean() { - bool? initial_value; if (value is bool || value == null) { @@ -860,15 +852,12 @@ class APIFormField { color: hasErrors() ? COLOR_DANGER : null, ); } - } - /* * Extract field options from a returned OPTIONS request */ Map extractFields(APIResponse response) { - if (!response.isValid()) { return {}; } @@ -896,8 +885,10 @@ Map extractFields(APIResponse response) { * The map "tree" is traversed based on the provided lookup string, which can use dotted notation. * This allows complex paths to be used to lookup field information. */ -Map extractFieldDefinition(Map data, String lookup) { - +Map extractFieldDefinition( + Map data, + String lookup, +) { List path = lookup.split("."); // Shadow copy the data for path traversal @@ -905,7 +896,6 @@ Map extractFieldDefinition(Map data, String lo // Iterate through all but the last element of the path for (int ii = 0; ii < (path.length - 1); ii++) { - String el = path[ii]; if (!_data.containsKey(el)) { @@ -923,11 +913,9 @@ Map extractFieldDefinition(Map data, String lo // Report the error sentryReportError( "apiForm.extractFieldDefinition : path traversal", - error, stackTrace, - context: { - "path": path.toString(), - "el": el, - } + error, + stackTrace, + context: {"path": path.toString(), "el": el}, ); return {}; } @@ -938,7 +926,6 @@ Map extractFieldDefinition(Map data, String lo if (!_data.containsKey(el)) { return {}; } else { - try { Map definition = _data[el] as Map; @@ -950,19 +937,16 @@ Map extractFieldDefinition(Map data, String lo // Report the error sentryReportError( "apiForm.extractFieldDefinition : as map", - error, stacktrace, - context: { - "el": el.toString(), - } + error, + stacktrace, + context: {"el": el.toString()}, ); return {}; } - } } - /* * Launch an API-driven form, * which uses the OPTIONS metadata (at the provided URL) @@ -976,24 +960,24 @@ Map extractFieldDefinition(Map data, String lo */ Future launchApiForm( - BuildContext context, String title, String url, Map fields, - { - String fileField = "", - Map modelData = const {}, - String method = "PATCH", - Function(Map)? onSuccess, - bool Function(Map)? validate, - Function? onCancel, - IconData icon = TablerIcons.device_floppy - }) async { - + BuildContext context, + String title, + String url, + Map fields, { + String fileField = "", + Map modelData = const {}, + String method = "PATCH", + Function(Map)? onSuccess, + bool Function(Map)? validate, + Function? onCancel, + IconData icon = TablerIcons.device_floppy, +}) async { showLoadingOverlay(); // List of fields defined by the server Map serverFields = {}; if (url.isNotEmpty) { - var options = await InvenTreeAPI().options(url); // Invalid response from server @@ -1006,10 +990,7 @@ Future launchApiForm( if (serverFields.isEmpty) { // User does not have permission to perform this action - showSnackIcon( - L10().response403, - icon: TablerIcons.user_x, - ); + showSnackIcon(L10().response403, icon: TablerIcons.user_x); hideLoadingOverlay(); return; @@ -1022,7 +1003,6 @@ Future launchApiForm( APIFormField field; for (String fieldName in fields.keys) { - dynamic data = fields[fieldName]; Map fieldData = {}; @@ -1066,35 +1046,33 @@ Future launchApiForm( // Now, launch a new widget! Navigator.push( context, - MaterialPageRoute(builder: (context) => APIFormWidget( - title, - url, - formFields, - method, - onSuccess: onSuccess, - validate: validate, - fileField: fileField, - icon: icon, - )) + MaterialPageRoute( + builder: (context) => APIFormWidget( + title, + url, + formFields, + method, + onSuccess: onSuccess, + validate: validate, + fileField: fileField, + icon: icon, + ), + ), ); } - class APIFormWidget extends StatefulWidget { - const APIFormWidget( - this.title, - this.url, - this.fields, - this.method, - { - Key? key, - this.onSuccess, - this.validate, - this.fileField = "", - this.icon = TablerIcons.device_floppy, - } - ) : super(key: key); + this.title, + this.url, + this.fields, + this.method, { + Key? key, + this.onSuccess, + this.validate, + this.fileField = "", + this.icon = TablerIcons.device_floppy, + }) : super(key: key); //! Form title to display final String title; @@ -1118,12 +1096,9 @@ class APIFormWidget extends StatefulWidget { @override _APIFormWidgetState createState() => _APIFormWidgetState(); - } - class _APIFormWidgetState extends State { - _APIFormWidgetState() : super(); final _formKey = GlobalKey(); @@ -1133,7 +1108,6 @@ class _APIFormWidgetState extends State { bool spacerRequired = false; List _buildForm() { - List widgets = []; // Display non-field errors first @@ -1141,26 +1115,16 @@ class _APIFormWidgetState extends State { for (String error in nonFieldErrors) { widgets.add( ListTile( - title: Text( - error, - style: TextStyle( - color: COLOR_DANGER, - ), - ), - leading: Icon( - TablerIcons.exclamation_circle, - color: COLOR_DANGER - ), - ) + title: Text(error, style: TextStyle(color: COLOR_DANGER)), + leading: Icon(TablerIcons.exclamation_circle, color: COLOR_DANGER), + ), ); } widgets.add(Divider(height: 5)); - } for (var field in widget.fields) { - if (field.hidden) { continue; } @@ -1189,8 +1153,8 @@ class _APIFormWidgetState extends State { fontStyle: FontStyle.italic, fontSize: 16, ), - ) - ) + ), + ), ); } } @@ -1210,20 +1174,16 @@ class _APIFormWidgetState extends State { } Future _submit(Map data) async { - // If a file upload is required, we have to handle the submission differently if (widget.fileField.isNotEmpty) { - // Pop the "file" field data.remove(widget.fileField); for (var field in widget.fields) { if (field.name == widget.fileField) { - File? file = field.attachedfile; if (file != null) { - // A valid file has been supplied final response = await InvenTreeAPI().uploadFile( widget.url, @@ -1239,23 +1199,21 @@ class _APIFormWidgetState extends State { } if (widget.method == "POST") { - showLoadingOverlay(); - final response = await InvenTreeAPI().post( + final response = await InvenTreeAPI().post( widget.url, body: data, - expectedStatusCode: null + expectedStatusCode: null, ); hideLoadingOverlay(); return response; - } else { showLoadingOverlay(); final response = await InvenTreeAPI().patch( widget.url, body: data, - expectedStatusCode: null + expectedStatusCode: null, ); hideLoadingOverlay(); @@ -1264,17 +1222,12 @@ class _APIFormWidgetState extends State { } void extractNonFieldErrors(APIResponse response) { - List errors = []; Map data = response.asMap(); // Potential keys representing non-field errors - List keys = [ - "__all__", - "non_field_errors", - "errors", - ]; + List keys = ["__all__", "non_field_errors", "errors"]; for (String key in keys) { if (data.containsKey(key)) { @@ -1301,7 +1254,6 @@ class _APIFormWidgetState extends State { var errors = response.asMap(); for (String fieldName in errors.keys) { - bool match = false; switch (fieldName) { @@ -1313,7 +1265,6 @@ class _APIFormWidgetState extends State { continue; default: for (var field in widget.fields) { - // Hidden fields can't display errors, so we won't match if (field.hidden) { continue; @@ -1324,7 +1275,6 @@ class _APIFormWidgetState extends State { match = true; break; } else if (field.parent == fieldName) { - var error = errors[fieldName]; if (error is List) { @@ -1340,7 +1290,6 @@ class _APIFormWidgetState extends State { } } } - } if (!match) { @@ -1352,7 +1301,7 @@ class _APIFormWidgetState extends State { "status_code": response.statusCode.toString(), "field": fieldName, "error_message": response.data.toString(), - } + }, ); } } @@ -1362,14 +1311,12 @@ class _APIFormWidgetState extends State { * Submit the form data to the server, and handle the results */ Future _save(BuildContext context) async { - // Package up the form data Map data = {}; // Iterate through and find "simple" top-level fields for (var field in widget.fields) { - if (field.readOnly) { continue; } @@ -1380,7 +1327,6 @@ class _APIFormWidgetState extends State { } else { // Not so simple... (WHY DID I MAKE THE API SO COMPLEX?) if (field.parent.isNotEmpty) { - // TODO: This is a dirty hack, there *must* be a cleaner way?! dynamic parent = data[field.parent] ?? {}; @@ -1402,7 +1348,7 @@ class _APIFormWidgetState extends State { } } } - + final bool isValid = widget.validate?.call(data) ?? true; if (!isValid) { @@ -1442,7 +1388,6 @@ class _APIFormWidgetState extends State { Navigator.pop(context); if (successFunc != null) { - // Ensure the response is a valid JSON structure Map json = {}; @@ -1457,10 +1402,7 @@ class _APIFormWidgetState extends State { return; case 400: // Form submission / validation error - showSnackIcon( - L10().formError, - success: false, - ); + showSnackIcon(L10().formError, success: false); // Update field errors for (var field in widget.fields) { @@ -1470,30 +1412,15 @@ class _APIFormWidgetState extends State { extractNonFieldErrors(response); checkInvalidErrors(response); case 401: - showSnackIcon( - "401: " + L10().response401, - success: false - ); + showSnackIcon("401: " + L10().response401, success: false); case 403: - showSnackIcon( - "403: " + L10().response403, - success: false, - ); + showSnackIcon("403: " + L10().response403, success: false); case 404: - showSnackIcon( - "404: " + L10().response404, - success: false, - ); + showSnackIcon("404: " + L10().response404, success: false); case 405: - showSnackIcon( - "405: " + L10().response405, - success: false, - ); + showSnackIcon("405: " + L10().response405, success: false); case 500: - showSnackIcon( - "500: " + L10().response500, - success: false, - ); + showSnackIcon("500: " + L10().response500, success: false); default: showSnackIcon( "${response.statusCode}: " + L10().responseInvalid, @@ -1504,12 +1431,10 @@ class _APIFormWidgetState extends State { setState(() { // Refresh the form }); - } @override Widget build(BuildContext context) { - return Scaffold( appBar: AppBar( title: Text(widget.title), @@ -1518,15 +1443,14 @@ class _APIFormWidgetState extends State { IconButton( icon: Icon(widget.icon), onPressed: () { - if (_formKey.currentState!.validate()) { _formKey.currentState!.save(); _save(context); } }, - ) - ] + ), + ], ), body: Form( key: _formKey, @@ -1538,9 +1462,8 @@ class _APIFormWidgetState extends State { children: _buildForm(), ), padding: EdgeInsets.all(16), - ) - ) + ), + ), ); - } } diff --git a/lib/app_colors.dart b/lib/app_colors.dart index 5ed9e20a..aeda838c 100644 --- a/lib/app_colors.dart +++ b/lib/app_colors.dart @@ -4,7 +4,6 @@ import "package:inventree/helpers.dart"; import "package:one_context/one_context.dart"; bool isDarkMode() { - if (!hasContext()) { return false; } diff --git a/lib/barcode/barcode.dart b/lib/barcode/barcode.dart index 7cd2f0f0..ddbb319e 100644 --- a/lib/barcode/barcode.dart +++ b/lib/barcode/barcode.dart @@ -10,7 +10,6 @@ import "package:inventree/widget/company/manufacturer_part_detail.dart"; import "package:inventree/widget/order/sales_order_detail.dart"; import "package:one_context/one_context.dart"; - import "package:inventree/api.dart"; import "package:inventree/l10.dart"; @@ -35,10 +34,8 @@ import "package:inventree/widget/stock/stock_detail.dart"; import "package:inventree/widget/company/company_detail.dart"; import "package:inventree/widget/company/supplier_part_detail.dart"; - // Signal a barcode scan success to the user Future barcodeSuccess(String msg) async { - barcodeSuccessTone(); showSnackIcon(msg, success: true); } @@ -47,24 +44,23 @@ Future barcodeSuccess(String msg) async { Future barcodeFailure(String msg, dynamic extra) async { barcodeFailureTone(); showSnackIcon( - msg, - success: false, + msg, + success: false, onAction: () { - if (hasContext()) { - OneContext().showDialog( - builder: (BuildContext context) => - SimpleDialog( - title: Text(L10().barcodeError), - children: [ - ListTile( - title: Text(L10().responseData), - subtitle: Text(extra.toString()) - ) - ] - ) - ); - } - } + if (hasContext()) { + OneContext().showDialog( + builder: (BuildContext context) => SimpleDialog( + title: Text(L10().barcodeError), + children: [ + ListTile( + title: Text(L10().responseData), + subtitle: Text(extra.toString()), + ), + ], + ), + ); + } + }, ); } @@ -75,15 +71,22 @@ Future barcodeFailure(String msg, dynamic extra) async { * - Returns a Future which resolves when the scanner is dismissed * - The provided BarcodeHandler instance is used to handle the scanned barcode */ -Future scanBarcode(BuildContext context, {BarcodeHandler? handler}) async { - +Future scanBarcode( + BuildContext context, { + BarcodeHandler? handler, +}) async { // Default to generic scan handler handler ??= BarcodeScanHandler(); - + InvenTreeBarcodeController controller = CameraBarcodeController(handler); // Select barcode controller based on user preference - final int barcodeControllerType = await InvenTreeSettingsManager().getValue(INV_BARCODE_SCAN_TYPE, BARCODE_CONTROLLER_CAMERA) as int; + final int barcodeControllerType = + await InvenTreeSettingsManager().getValue( + INV_BARCODE_SCAN_TYPE, + BARCODE_CONTROLLER_CAMERA, + ) + as int; switch (barcodeControllerType) { case BARCODE_CONTROLLER_WEDGE: @@ -95,14 +98,10 @@ Future scanBarcode(BuildContext context, {BarcodeHandler? handler}) asy } return Navigator.of(context).push( - PageRouteBuilder( - pageBuilder: (context, _, _) => controller, - opaque: false, - ) + PageRouteBuilder(pageBuilder: (context, _, _) => controller, opaque: false), ); } - /* * Class for general barcode scanning. * Scan *any* barcode without context, and then redirect app to correct view. @@ -116,19 +115,17 @@ Future scanBarcode(BuildContext context, {BarcodeHandler? handler}) asy * - PurchaseOrder */ class BarcodeScanHandler extends BarcodeHandler { - @override String getOverlayText(BuildContext context) => L10().barcodeScanGeneral; @override Future onBarcodeUnknown(Map data) async { - barcodeFailureTone(); showSnackIcon( - L10().barcodeNoMatch, - icon: TablerIcons.exclamation_circle, - success: false, + L10().barcodeNoMatch, + icon: TablerIcons.exclamation_circle, + success: false, ); } @@ -136,12 +133,13 @@ class BarcodeScanHandler extends BarcodeHandler { * Response when a "Part" instance is scanned */ Future handlePart(int pk) async { - var part = await InvenTreePart().get(pk); if (part is InvenTreePart) { OneContext().pop(); - OneContext().push(MaterialPageRoute(builder: (context) => PartDetailWidget(part))); + OneContext().push( + MaterialPageRoute(builder: (context) => PartDetailWidget(part)), + ); } } @@ -149,13 +147,13 @@ class BarcodeScanHandler extends BarcodeHandler { * Response when a "StockItem" instance is scanned */ Future handleStockItem(int pk) async { - var item = await InvenTreeStockItem().get(pk); if (item is InvenTreeStockItem) { OneContext().pop(); - OneContext().push(MaterialPageRoute( - builder: (context) => StockDetailWidget(item))); + OneContext().push( + MaterialPageRoute(builder: (context) => StockDetailWidget(item)), + ); } } @@ -163,13 +161,13 @@ class BarcodeScanHandler extends BarcodeHandler { * Response when a "StockLocation" instance is scanned */ Future handleStockLocation(int pk) async { - var loc = await InvenTreeStockLocation().get(pk); if (loc is InvenTreeStockLocation) { OneContext().pop(); - OneContext().navigator.push(MaterialPageRoute( - builder: (context) => LocationDisplayWidget(loc))); + OneContext().navigator.push( + MaterialPageRoute(builder: (context) => LocationDisplayWidget(loc)), + ); } } @@ -177,13 +175,15 @@ class BarcodeScanHandler extends BarcodeHandler { * Response when a "SupplierPart" instance is scanned */ Future handleSupplierPart(int pk) async { - var supplierPart = await InvenTreeSupplierPart().get(pk); if (supplierPart is InvenTreeSupplierPart) { OneContext().pop(); - OneContext().push(MaterialPageRoute( - builder: (context) => SupplierPartDetailWidget(supplierPart))); + OneContext().push( + MaterialPageRoute( + builder: (context) => SupplierPartDetailWidget(supplierPart), + ), + ); } } @@ -195,8 +195,11 @@ class BarcodeScanHandler extends BarcodeHandler { if (manufacturerPart is InvenTreeManufacturerPart) { OneContext().pop(); - OneContext().push(MaterialPageRoute( - builder: (context) => ManufacturerPartDetailWidget(manufacturerPart))); + OneContext().push( + MaterialPageRoute( + builder: (context) => ManufacturerPartDetailWidget(manufacturerPart), + ), + ); } } @@ -205,8 +208,9 @@ class BarcodeScanHandler extends BarcodeHandler { if (company is InvenTreeCompany) { OneContext().pop(); - OneContext().push(MaterialPageRoute( - builder: (context) => CompanyDetailWidget(company))); + OneContext().push( + MaterialPageRoute(builder: (context) => CompanyDetailWidget(company)), + ); } } @@ -218,8 +222,11 @@ class BarcodeScanHandler extends BarcodeHandler { if (order is InvenTreePurchaseOrder) { OneContext().pop(); - OneContext().push(MaterialPageRoute( - builder: (context) => PurchaseOrderDetailWidget(order))); + OneContext().push( + MaterialPageRoute( + builder: (context) => PurchaseOrderDetailWidget(order), + ), + ); } } @@ -229,8 +236,9 @@ class BarcodeScanHandler extends BarcodeHandler { if (order is InvenTreeSalesOrder) { OneContext().pop(); - OneContext().push(MaterialPageRoute( - builder: (context) => SalesOrderDetailWidget(order))); + OneContext().push( + MaterialPageRoute(builder: (context) => SalesOrderDetailWidget(order)), + ); } } @@ -250,7 +258,6 @@ class BarcodeScanHandler extends BarcodeHandler { InvenTreeManufacturerPart.MODEL_TYPE, ]; - if (InvenTreeAPI().supportsOrderBarcodes) { validModels.add(InvenTreePurchaseOrder.MODEL_TYPE); validModels.add(InvenTreeSalesOrder.MODEL_TYPE); @@ -274,7 +281,6 @@ class BarcodeScanHandler extends BarcodeHandler { // A valid result has been found if (pk > 0 && model.isNotEmpty) { - barcodeSuccessTone(); switch (model) { @@ -312,35 +318,31 @@ class BarcodeScanHandler extends BarcodeHandler { barcodeFailureTone(); showSnackIcon( - L10().barcodeUnknown, - success: false, - onAction: () { - - if (hasContext()) { - OneContext().showDialog( - builder: (BuildContext context) => - SimpleDialog( - title: Text(L10().unknownResponse), - children: [ - ListTile( - title: Text(L10().responseData), - subtitle: Text(data.toString()), - ) - ], - ) - ); - } + L10().barcodeUnknown, + success: false, + onAction: () { + if (hasContext()) { + OneContext().showDialog( + builder: (BuildContext context) => SimpleDialog( + title: Text(L10().unknownResponse), + children: [ + ListTile( + title: Text(L10().responseData), + subtitle: Text(data.toString()), + ), + ], + ), + ); } + }, ); } } - /* * Barcode handler for finding a "unique" barcode (one that does not match an item in the database) */ class UniqueBarcodeHandler extends BarcodeHandler { - UniqueBarcodeHandler(this.callback, {this.overlayText = ""}); // Callback function when a "unique" barcode hash is found @@ -360,11 +362,7 @@ class UniqueBarcodeHandler extends BarcodeHandler { @override Future onBarcodeMatched(Map data) async { if (!data.containsKey("hash") && !data.containsKey("barcode_hash")) { - showServerError( - "barcode/", - L10().missingData, - L10().barcodeMissingHash, - ); + showServerError("barcode/", L10().missingData, L10().barcodeMissingHash); } else { String barcode; @@ -373,12 +371,8 @@ class UniqueBarcodeHandler extends BarcodeHandler { if (barcode.isEmpty) { barcodeFailureTone(); - showSnackIcon( - L10().barcodeError, - success: false, - ); + showSnackIcon(L10().barcodeError, success: false); } else { - barcodeSuccessTone(); // Close the barcode scanner @@ -395,41 +389,43 @@ class UniqueBarcodeHandler extends BarcodeHandler { Future onBarcodeUnknown(Map data) async { await onBarcodeMatched(data); } - } - -SpeedDialChild customBarcodeAction(BuildContext context, RefreshableState state, String barcode, String model, int pk) { - +SpeedDialChild customBarcodeAction( + BuildContext context, + RefreshableState state, + String barcode, + String model, + int pk, +) { if (barcode.isEmpty) { return SpeedDialChild( label: L10().barcodeAssign, child: Icon(Icons.barcode_reader), onTap: () { var handler = UniqueBarcodeHandler((String barcode) { - InvenTreeAPI().linkBarcode({ - model: pk.toString(), - "barcode": barcode, - }).then((bool result) { - showSnackIcon( - result ? L10().barcodeAssigned : L10().barcodeNotAssigned, - success: result - ); + InvenTreeAPI() + .linkBarcode({model: pk.toString(), "barcode": barcode}) + .then((bool result) { + showSnackIcon( + result ? L10().barcodeAssigned : L10().barcodeNotAssigned, + success: result, + ); - state.refresh(context); - }); + state.refresh(context); + }); }); scanBarcode(context, handler: handler); - } + }, ); } else { return SpeedDialChild( child: Icon(Icons.barcode_reader), label: L10().barcodeUnassign, onTap: () { - InvenTreeAPI().unlinkBarcode({ - model: pk.toString() - }).then((bool result) { + InvenTreeAPI().unlinkBarcode({model: pk.toString()}).then(( + bool result, + ) { showSnackIcon( result ? L10().requestSuccessful : L10().requestFailed, success: result, @@ -437,7 +433,7 @@ SpeedDialChild customBarcodeAction(BuildContext context, RefreshableState state, state.refresh(context); }); - } + }, ); } } diff --git a/lib/barcode/camera_controller.dart b/lib/barcode/camera_controller.dart index d6bfa6a0..9e2fe501 100644 --- a/lib/barcode/camera_controller.dart +++ b/lib/barcode/camera_controller.dart @@ -273,8 +273,9 @@ class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState { } Widget bottomCenterOverlay() { - String info_text = - scanning_paused ? L10().barcodeScanPaused : L10().barcodeScanPause; + String info_text = scanning_paused + ? L10().barcodeScanPaused + : L10().barcodeScanPause; String text = scanned_code.isNotEmpty ? scanned_code : info_text; diff --git a/lib/barcode/controller.dart b/lib/barcode/controller.dart index 3a8fa2c5..70a79862 100644 --- a/lib/barcode/controller.dart +++ b/lib/barcode/controller.dart @@ -11,7 +11,6 @@ import "package:inventree/widget/progress.dart"; * which is used to process the scanned barcode. */ class InvenTreeBarcodeController extends StatefulWidget { - const InvenTreeBarcodeController(this.handler, {Key? key}) : super(key: key); final BarcodeHandler handler; @@ -20,16 +19,17 @@ class InvenTreeBarcodeController extends StatefulWidget { State createState() => InvenTreeBarcodeControllerState(); } - /* * Base state widget for the barcode controller. * This defines the basic interface for the barcode controller. */ -class InvenTreeBarcodeControllerState extends State { - +class InvenTreeBarcodeControllerState + extends State { InvenTreeBarcodeControllerState() : super(); - final GlobalKey barcodeControllerKey = GlobalKey(debugLabel: "barcodeController"); + final GlobalKey barcodeControllerKey = GlobalKey( + debugLabel: "barcodeController", + ); // Internal state flag to test if we are currently processing a barcode bool processingBarcode = false; @@ -40,7 +40,6 @@ class InvenTreeBarcodeControllerState extends State * Barcode data should be passed as a string */ Future handleBarcodeData(String? data) async { - // Check that the data is valid, and this view is still mounted if (!mounted || data == null || data.isEmpty) { return; @@ -66,7 +65,9 @@ class InvenTreeBarcodeControllerState extends State return; } - int delay = await InvenTreeSettingsManager().getValue(INV_BARCODE_SCAN_DELAY, 500) as int; + int delay = + await InvenTreeSettingsManager().getValue(INV_BARCODE_SCAN_DELAY, 500) + as int; Future.delayed(Duration(milliseconds: delay), () { hideLoadingOverlay(); @@ -99,5 +100,4 @@ class InvenTreeBarcodeControllerState extends State Widget build(BuildContext context) { return Container(); } - -} \ No newline at end of file +} diff --git a/lib/barcode/handler.dart b/lib/barcode/handler.dart index 48831f9a..3b9a72ca 100644 --- a/lib/barcode/handler.dart +++ b/lib/barcode/handler.dart @@ -1,4 +1,3 @@ - import "package:flutter/material.dart"; import "package:flutter_tabler_icons/flutter_tabler_icons.dart"; @@ -13,7 +12,6 @@ import "package:inventree/inventree/sentry.dart"; import "package:inventree/widget/dialogs.dart"; import "package:inventree/widget/snacks.dart"; - /* Generic class which "handles" a barcode, by communicating with the InvenTree server, * and handling match / unknown / error cases. * @@ -21,7 +19,6 @@ import "package:inventree/widget/snacks.dart"; * based on the response returned from the InvenTree server */ class BarcodeHandler { - BarcodeHandler(); // Return the text to display on the barcode overlay @@ -57,23 +54,23 @@ class BarcodeHandler { * * Returns true only if the barcode scanner should remain open */ - Future processBarcode(String barcode, - {String url = "barcode/", - Map extra_data = const {}}) async { - + Future processBarcode( + String barcode, { + String url = "barcode/", + Map extra_data = const {}, + }) async { debug("Scanned barcode data: '${barcode}'"); barcode = barcode.trim(); // Empty barcode is invalid if (barcode.isEmpty) { - barcodeFailureTone(); showSnackIcon( L10().barcodeError, icon: TablerIcons.exclamation_circle, - success: false + success: false, ); return; @@ -84,10 +81,7 @@ class BarcodeHandler { try { response = await InvenTreeAPI().post( url, - body: { - "barcode": barcode, - ...extra_data, - }, + body: {"barcode": barcode, ...extra_data}, expectedStatusCode: null, // Do not show an error on "unexpected code" ); } catch (error, stackTrace) { @@ -113,17 +107,17 @@ class BarcodeHandler { // We want to know about this one! await sentryReportMessage( - "BarcodeHandler.processBarcode returned unexpected value", - context: { - "data": response.data?.toString() ?? "null", - "barcode": barcode, - "url": url, - "statusCode": response.statusCode.toString(), - "valid": response.isValid().toString(), - "error": response.error, - "errorDetail": response.errorDetail, - "className": "${this}", - } + "BarcodeHandler.processBarcode returned unexpected value", + context: { + "data": response.data?.toString() ?? "null", + "barcode": barcode, + "url": url, + "statusCode": response.statusCode.toString(), + "valid": response.isValid().toString(), + "error": response.error, + "errorDetail": response.errorDetail, + "className": "${this}", + }, ); } else if (data.containsKey("success")) { await onBarcodeMatched(data); diff --git a/lib/barcode/purchase_order.dart b/lib/barcode/purchase_order.dart index 8b2f984b..65f4913c 100644 --- a/lib/barcode/purchase_order.dart +++ b/lib/barcode/purchase_order.dart @@ -20,7 +20,6 @@ import "package:inventree/widget/snacks.dart"; * - If location or quantity information wasn't provided, show a form to fill it in */ class POReceiveBarcodeHandler extends BarcodeHandler { - POReceiveBarcodeHandler({this.purchaseOrder, this.location, this.lineItem}); InvenTreePurchaseOrder? purchaseOrder; @@ -31,11 +30,15 @@ class POReceiveBarcodeHandler extends BarcodeHandler { String getOverlayText(BuildContext context) => L10().barcodeReceivePart; @override - Future processBarcode(String barcode, - {String url = "barcode/po-receive/", - Map extra_data = const {}}) async { - - final bool confirm = await InvenTreeSettingsManager().getBool(INV_PO_CONFIRM_SCAN, true); + Future processBarcode( + String barcode, { + String url = "barcode/po-receive/", + Map extra_data = const {}, + }) async { + final bool confirm = await InvenTreeSettingsManager().getBool( + INV_PO_CONFIRM_SCAN, + true, + ); final po_extra_data = { "purchase_order": purchaseOrder?.pk, @@ -50,7 +53,6 @@ class POReceiveBarcodeHandler extends BarcodeHandler { @override Future onBarcodeMatched(Map data) async { - if (data.containsKey("lineitem") || data.containsKey("success")) { barcodeSuccess(L10().receivedItem); return; @@ -66,7 +68,8 @@ class POReceiveBarcodeHandler extends BarcodeHandler { } final lineItemData = data["lineitem"] as Map; - if (!lineItemData.containsKey("pk") || !lineItemData.containsKey("purchase_order")) { + if (!lineItemData.containsKey("pk") || + !lineItemData.containsKey("purchase_order")) { barcodeFailureTone(); showSnackIcon(L10().missingData, success: false); } @@ -79,7 +82,8 @@ class POReceiveBarcodeHandler extends BarcodeHandler { return; } - InvenTreePOLineItem? lineItem = await InvenTreePOLineItem().get(lineItemId) as InvenTreePOLineItem?; + InvenTreePOLineItem? lineItem = + await InvenTreePOLineItem().get(lineItemId) as InvenTreePOLineItem?; if (lineItem == null) { barcodeFailureTone(); @@ -89,7 +93,9 @@ class POReceiveBarcodeHandler extends BarcodeHandler { // Next, extract the "optional" fields // Extract information from the returned server response - double? quantity = double.tryParse((lineItemData["quantity"] ?? "0").toString()); + double? quantity = double.tryParse( + (lineItemData["quantity"] ?? "0").toString(), + ); int? destination = lineItemData["location"] as int?; String? barcode = data["barcode_data"] as String?; @@ -105,7 +111,7 @@ class POReceiveBarcodeHandler extends BarcodeHandler { barcode: barcode, onSuccess: () { showSnackIcon(L10().receivedItem, success: true); - } + }, ); } @@ -113,18 +119,16 @@ class POReceiveBarcodeHandler extends BarcodeHandler { Future onBarcodeUnknown(Map data) async { barcodeFailureTone(); showSnackIcon( - data["error"] as String? ?? L10().barcodeError, - success: false + data["error"] as String? ?? L10().barcodeError, + success: false, ); } } - /* * Barcode handler to add a line item to a purchase order */ class POAllocateBarcodeHandler extends BarcodeHandler { - POAllocateBarcodeHandler({this.purchaseOrder}); InvenTreePurchaseOrder? purchaseOrder; @@ -133,21 +137,14 @@ class POAllocateBarcodeHandler extends BarcodeHandler { String getOverlayText(BuildContext context) => L10().scanSupplierPart; @override - Future processBarcode(String barcode, { + Future processBarcode( + String barcode, { String url = "barcode/po-allocate/", - Map extra_data = const {}} - ) { + Map extra_data = const {}, + }) { + final po_extra_data = {"purchase_order": purchaseOrder?.pk, ...extra_data}; - final po_extra_data = { - "purchase_order": purchaseOrder?.pk, - ...extra_data, - }; - - return super.processBarcode( - barcode, - url: url, - extra_data: po_extra_data, - ); + return super.processBarcode(barcode, url: url, extra_data: po_extra_data); } @override @@ -189,10 +186,9 @@ class POAllocateBarcodeHandler extends BarcodeHandler { @override Future onBarcodeUnhandled(Map data) async { - print("onBarcodeUnhandled:"); print(data.toString()); super.onBarcodeUnhandled(data); } -} \ No newline at end of file +} diff --git a/lib/barcode/sales_order.dart b/lib/barcode/sales_order.dart index e0e79ad0..75fbdc06 100644 --- a/lib/barcode/sales_order.dart +++ b/lib/barcode/sales_order.dart @@ -14,13 +14,11 @@ import "package:inventree/barcode/tones.dart"; import "package:inventree/widget/snacks.dart"; - /* * Barcode handler class for scanning a new part into a SalesOrder */ class SOAddItemBarcodeHandler extends BarcodeHandler { - SOAddItemBarcodeHandler({this.salesOrder}); InvenTreeSalesOrder? salesOrder; @@ -30,7 +28,6 @@ class SOAddItemBarcodeHandler extends BarcodeHandler { @override Future onBarcodeMatched(Map data) async { - // Extract the part ID from the returned data int part_id = -1; @@ -46,7 +43,6 @@ class SOAddItemBarcodeHandler extends BarcodeHandler { var part = await InvenTreePart().get(part_id); if (part is InvenTreePart) { - if (part.isSalable) { // Dispose of the barcode scanner if (OneContext.hasContext) { @@ -68,23 +64,18 @@ class SOAddItemBarcodeHandler extends BarcodeHandler { L10().lineItemAdd, fields: fields, ); - } else { barcodeFailureTone(); showSnackIcon(L10().partNotSalable, success: false); } - } else { // Failed to fetch part return onBarcodeUnknown(data); } - } } - class SOAllocateStockHandler extends BarcodeHandler { - SOAllocateStockHandler({this.salesOrder, this.lineItem, this.shipment}); InvenTreeSalesOrder? salesOrder; @@ -95,16 +86,16 @@ class SOAllocateStockHandler extends BarcodeHandler { String getOverlayText(BuildContext context) => L10().allocateStock; @override - Future processBarcode(String barcode, - { + Future processBarcode( + String barcode, { String url = "barcode/so-allocate/", - Map extra_data = const {}}) { - + Map extra_data = const {}, + }) { final so_extra_data = { "sales_order": salesOrder?.pk, "shipment": shipment?.pk, "line": lineItem?.pk, - ...extra_data + ...extra_data, }; return super.processBarcode(barcode, url: url, extra_data: so_extra_data); @@ -121,8 +112,8 @@ class SOAllocateStockHandler extends BarcodeHandler { @override Future onBarcodeUnhandled(Map data) async { - - if (!data.containsKey("action_required") || !data.containsKey("line_item")) { + if (!data.containsKey("action_required") || + !data.containsKey("line_item")) { return super.onBarcodeUnhandled(data); } @@ -132,10 +123,7 @@ class SOAllocateStockHandler extends BarcodeHandler { // Update fields with data gathered from the API response fields["line_item"]?["value"] = data["line_item"]; - Map stock_filters = { - "in_stock": true, - "available": true, - }; + Map stock_filters = {"in_stock": true, "available": true}; if (data.containsKey("part")) { stock_filters["part"] = data["part"]; @@ -147,9 +135,7 @@ class SOAllocateStockHandler extends BarcodeHandler { fields["quantity"]?["value"] = data["quantity"]; fields["shipment"]?["value"] = data["shipment"]; - fields["shipment"]?["filters"] = { - "order": salesOrder!.pk.toString() - }; + fields["shipment"]?["filters"] = {"order": salesOrder!.pk.toString()}; final context = OneContext().context!; @@ -157,20 +143,21 @@ class SOAllocateStockHandler extends BarcodeHandler { context, L10().allocateStock, salesOrder!.allocate_url, - fields, - method: "POST", - icon: TablerIcons.transition_right, - onSuccess: (data) async { + fields, + method: "POST", + icon: TablerIcons.transition_right, + onSuccess: (data) async { showSnackIcon(L10().allocated, success: true); - }); + }, + ); } @override Future onBarcodeUnknown(Map data) async { barcodeFailureTone(); showSnackIcon( - data["error"] as String? ?? L10().barcodeError, - success: false + data["error"] as String? ?? L10().barcodeError, + success: false, ); } -} \ No newline at end of file +} diff --git a/lib/barcode/stock.dart b/lib/barcode/stock.dart index 126d1d06..09826f7d 100644 --- a/lib/barcode/stock.dart +++ b/lib/barcode/stock.dart @@ -16,7 +16,6 @@ import "package:inventree/inventree/stock.dart"; import "package:inventree/widget/dialogs.dart"; import "package:inventree/widget/snacks.dart"; - /* * Generic class for scanning a StockLocation. * @@ -24,20 +23,17 @@ import "package:inventree/widget/snacks.dart"; * - Runs a "callback" function if a valid StockLocation is found */ class BarcodeScanStockLocationHandler extends BarcodeHandler { - @override String getOverlayText(BuildContext context) => L10().barcodeScanLocation; @override Future onBarcodeMatched(Map data) async { - // We expect that the barcode points to a 'stocklocation' if (data.containsKey("stocklocation")) { int _loc = (data["stocklocation"]?["pk"] ?? -1) as int; // A valid stock location! if (_loc > 0) { - debug("Scanned stock location ${_loc}"); final bool result = await onLocationScanned(_loc); @@ -52,10 +48,7 @@ class BarcodeScanStockLocationHandler extends BarcodeHandler { // If we get to this point, something went wrong during the scan process barcodeFailureTone(); - showSnackIcon( - L10().invalidStockLocation, - success: false, - ); + showSnackIcon(L10().invalidStockLocation, success: false); } // Callback function which runs when a valid StockLocation is scanned @@ -64,10 +57,8 @@ class BarcodeScanStockLocationHandler extends BarcodeHandler { // Re-implement this for particular subclass return false; } - } - /* * Generic class for scanning a StockItem * @@ -75,7 +66,6 @@ class BarcodeScanStockLocationHandler extends BarcodeHandler { * - Runs a "callback" function if a valid StockItem is found */ class BarcodeScanStockItemHandler extends BarcodeHandler { - @override String getOverlayText(BuildContext context) => L10().barcodeScanItem; @@ -87,7 +77,6 @@ class BarcodeScanStockItemHandler extends BarcodeHandler { // A valid stock location! if (_item > 0) { - barcodeSuccessTone(); bool result = await onItemScanned(_item); @@ -102,10 +91,7 @@ class BarcodeScanStockItemHandler extends BarcodeHandler { // If we get to this point, something went wrong during the scan process barcodeFailureTone(); - showSnackIcon( - L10().invalidStockItem, - success: false, - ); + showSnackIcon(L10().invalidStockItem, success: false); } // Callback function which runs when a valid StockItem is scanned @@ -115,7 +101,6 @@ class BarcodeScanStockItemHandler extends BarcodeHandler { } } - /* * Barcode handler for scanning a provided StockItem into a scanned StockLocation. * @@ -124,20 +109,20 @@ class BarcodeScanStockItemHandler extends BarcodeHandler { * - The StockItem is transferred into the scanned location */ class StockItemScanIntoLocationHandler extends BarcodeScanStockLocationHandler { - StockItemScanIntoLocationHandler(this.item); final InvenTreeStockItem item; @override Future onLocationScanned(int locationId) async { - - final bool confirm = await InvenTreeSettingsManager().getBool(INV_STOCK_CONFIRM_SCAN, false); + final bool confirm = await InvenTreeSettingsManager().getBool( + INV_STOCK_CONFIRM_SCAN, + false, + ); bool result = false; if (confirm) { - Map fields = item.transferFields(); // Override location with scanned value @@ -152,7 +137,7 @@ class StockItemScanIntoLocationHandler extends BarcodeScanStockLocationHandler { icon: TablerIcons.transfer, onSuccess: (data) async { showSnackIcon(L10().stockItemUpdated, success: true); - } + }, ); return true; @@ -171,7 +156,6 @@ class StockItemScanIntoLocationHandler extends BarcodeScanStockLocationHandler { } } - /* * Barcode handler for scanning stock item(s) into the specified StockLocation. * @@ -180,7 +164,6 @@ class StockItemScanIntoLocationHandler extends BarcodeScanStockLocationHandler { * - The scanned StockItem is transferred into the provided StockLocation */ class StockLocationScanInItemsHandler extends BarcodeScanStockItemHandler { - StockLocationScanInItemsHandler(this.location); final InvenTreeStockLocation location; @@ -190,14 +173,16 @@ class StockLocationScanInItemsHandler extends BarcodeScanStockItemHandler { @override Future onItemScanned(int itemId) async { - - final InvenTreeStockItem? item = await InvenTreeStockItem().get(itemId) as InvenTreeStockItem?; - final bool confirm = await InvenTreeSettingsManager().getBool(INV_STOCK_CONFIRM_SCAN, false); + final InvenTreeStockItem? item = + await InvenTreeStockItem().get(itemId) as InvenTreeStockItem?; + final bool confirm = await InvenTreeSettingsManager().getBool( + INV_STOCK_CONFIRM_SCAN, + false, + ); bool result = false; if (item != null) { - // Item is already *in* the specified location if (item.locationId == location.pk) { barcodeFailureTone(); @@ -211,25 +196,26 @@ class StockLocationScanInItemsHandler extends BarcodeScanStockItemHandler { fields["location"]?["value"] = location.pk; launchApiForm( - OneContext().context!, - L10().transferStock, - InvenTreeStockItem.transferStockUrl(), - fields, - method: "POST", - icon: TablerIcons.transfer, - onSuccess: (data) async { - showSnackIcon(L10().stockItemUpdated, success: true); - } + OneContext().context!, + L10().transferStock, + InvenTreeStockItem.transferStockUrl(), + fields, + method: "POST", + icon: TablerIcons.transfer, + onSuccess: (data) async { + showSnackIcon(L10().stockItemUpdated, success: true); + }, ); return true; - } else { result = await item.transferStock(location.pk); showSnackIcon( - result ? L10().barcodeScanIntoLocationSuccess : L10().barcodeScanIntoLocationFailure, - success: result + result + ? L10().barcodeScanIntoLocationSuccess + : L10().barcodeScanIntoLocationFailure, + success: result, ); } } @@ -240,7 +226,6 @@ class StockLocationScanInItemsHandler extends BarcodeScanStockItemHandler { } } - /* * Barcode handler class for scanning a StockLocation into another StockLocation * @@ -249,18 +234,14 @@ class StockLocationScanInItemsHandler extends BarcodeScanStockItemHandler { * - The scanned StockLocation is set as the "parent" of the provided StockLocation */ class ScanParentLocationHandler extends BarcodeScanStockLocationHandler { - ScanParentLocationHandler(this.location); final InvenTreeStockLocation location; @override Future onLocationScanned(int locationId) async { - final response = await location.update( - values: { - "parent": locationId.toString(), - }, + values: {"parent": locationId.toString()}, expectedStatusCode: null, ); @@ -269,22 +250,19 @@ class ScanParentLocationHandler extends BarcodeScanStockLocationHandler { case 201: barcodeSuccess(L10().barcodeScanIntoLocationSuccess); return true; - case 400: // Invalid parent location chosen + case 400: // Invalid parent location chosen barcodeFailureTone(); showSnackIcon(L10().invalidStockLocation, success: false); return false; default: barcodeFailureTone(); showSnackIcon( - L10().barcodeScanIntoLocationFailure, - success: false, - actionText: L10().details, - onAction: () { - showErrorDialog( - L10().barcodeError, - response: response, - ); - } + L10().barcodeScanIntoLocationFailure, + success: false, + actionText: L10().details, + onAction: () { + showErrorDialog(L10().barcodeError, response: response); + }, ); return false; } diff --git a/lib/barcode/tones.dart b/lib/barcode/tones.dart index a6e4b775..0d8fddcd 100644 --- a/lib/barcode/tones.dart +++ b/lib/barcode/tones.dart @@ -5,19 +5,21 @@ import "package:inventree/preferences.dart"; * Play an audible 'success' alert to the user. */ Future barcodeSuccessTone() async { - - final bool en = await InvenTreeSettingsManager().getValue(INV_SOUNDS_BARCODE, true) as bool; + final bool en = + await InvenTreeSettingsManager().getValue(INV_SOUNDS_BARCODE, true) + as bool; if (en) { playAudioFile("sounds/barcode_scan.mp3"); } } -Future barcodeFailureTone() async { - - final bool en = await InvenTreeSettingsManager().getValue(INV_SOUNDS_BARCODE, true) as bool; +Future barcodeFailureTone() async { + final bool en = + await InvenTreeSettingsManager().getValue(INV_SOUNDS_BARCODE, true) + as bool; if (en) { playAudioFile("sounds/barcode_error.mp3"); } -} \ No newline at end of file +} diff --git a/lib/barcode/wedge_controller.dart b/lib/barcode/wedge_controller.dart index b3147e5c..5edf0f5c 100644 --- a/lib/barcode/wedge_controller.dart +++ b/lib/barcode/wedge_controller.dart @@ -1,4 +1,3 @@ - import "package:flutter/material.dart"; import "package:flutter/services.dart"; import "package:flutter_tabler_icons/flutter_tabler_icons.dart"; @@ -15,17 +14,14 @@ import "package:inventree/helpers.dart"; * intercepting barcode data which is entered as rapid keyboard presses */ class WedgeBarcodeController extends InvenTreeBarcodeController { - - const WedgeBarcodeController(BarcodeHandler handler, {Key? key}) : super(handler, key: key); + const WedgeBarcodeController(BarcodeHandler handler, {Key? key}) + : super(handler, key: key); @override State createState() => _WedgeBarcodeControllerState(); - } - class _WedgeBarcodeControllerState extends InvenTreeBarcodeControllerState { - _WedgeBarcodeControllerState() : super(); bool canScan = true; @@ -40,7 +36,6 @@ class _WedgeBarcodeControllerState extends InvenTreeBarcodeControllerState { @override Future pauseScan() async { - if (mounted) { setState(() { canScan = false; @@ -50,7 +45,6 @@ class _WedgeBarcodeControllerState extends InvenTreeBarcodeControllerState { @override Future resumeScan() async { - if (mounted) { setState(() { canScan = true; @@ -60,7 +54,6 @@ class _WedgeBarcodeControllerState extends InvenTreeBarcodeControllerState { // Callback for a single key press / scan void handleKeyEvent(KeyEvent event) { - if (!scanning) { return; } @@ -78,7 +71,8 @@ class _WedgeBarcodeControllerState extends InvenTreeBarcodeControllerState { DateTime now = DateTime.now(); // Throw away old characters - if (_lastScanTime == null || _lastScanTime!.isBefore(now.subtract(Duration(milliseconds: 250)))) { + if (_lastScanTime == null || + _lastScanTime!.isBefore(now.subtract(Duration(milliseconds: 250)))) { _scannedCharacters.clear(); } @@ -99,7 +93,6 @@ class _WedgeBarcodeControllerState extends InvenTreeBarcodeControllerState { @override Widget build(BuildContext context) { - return Scaffold( appBar: AppBar( backgroundColor: COLOR_APP_BAR, @@ -118,7 +111,7 @@ class _WedgeBarcodeControllerState extends InvenTreeBarcodeControllerState { focusNode: _focusNode, child: SizedBox( child: CircularProgressIndicator( - color: scanning ? COLOR_ACTION : COLOR_PROGRESS + color: scanning ? COLOR_ACTION : COLOR_PROGRESS, ), width: 64, height: 64, @@ -140,14 +133,14 @@ class _WedgeBarcodeControllerState extends InvenTreeBarcodeControllerState { widget.handler.getOverlayText(context), style: TextStyle( fontWeight: FontWeight.bold, - color: Colors.white) + color: Colors.white, + ), ), padding: EdgeInsets.all(20), - ) + ), ], - ) - ) + ), + ), ); } - -} \ No newline at end of file +} diff --git a/lib/dsn.dart b/lib/dsn.dart index 39092c82..87eb6eba 100644 --- a/lib/dsn.dart +++ b/lib/dsn.dart @@ -1,7 +1,7 @@ - /* * For integration with sentry.io, fill out the SENTRY_DSN_KEY value below. * This should be set to a valid DSN key, from your sentry.io account * */ -String SENTRY_DSN_KEY = "https://fea705aa4b8e4c598dcf9b146b3d1b86@o378676.ingest.sentry.io/5202450"; \ No newline at end of file +String SENTRY_DSN_KEY = + "https://fea705aa4b8e4c598dcf9b146b3d1b86@o378676.ingest.sentry.io/5202450"; diff --git a/lib/generated/i18n.dart b/lib/generated/i18n.dart deleted file mode 100644 index ad002d5a..00000000 --- a/lib/generated/i18n.dart +++ /dev/null @@ -1,76 +0,0 @@ - -import "dart:async'; - -import "package:flutter/foundation.dart'; -import "package:flutter/material.dart'; -// ignore_for_file: non_constant_identifier_names -// ignore_for_file: camel_case_types -// ignore_for_file: prefer_single_quotes - -//This file is automatically generated. DO NOT EDIT, all your changes would be lost. - -class S implements WidgetsLocalizations { - const S(); - - static const GeneratedLocalizationsDelegate delegate = GeneratedLocalizationsDelegate(); - - static S of(BuildContext context) => Localizations.of(context, WidgetsLocalizations); - - @override - TextDirection get textDirection => TextDirection.ltr; - -} - -class en extends S { - const en(); -} - - -class GeneratedLocalizationsDelegate extends LocalizationsDelegate { - const GeneratedLocalizationsDelegate(); - - List get supportedLocales { - return const [ - - const Locale("en", ""), - - ]; - } - - LocaleResolutionCallback resolution({Locale fallback}) { - return (Locale locale, Iterable supported) { - final Locale languageLocale = new Locale(locale.languageCode, ""); - if (supported.contains(locale)) - return locale; - else if (supported.contains(languageLocale)) - return languageLocale; - else { - final Locale fallbackLocale = fallback ?? supported.first; - return fallbackLocale; - } - }; - } - - @override - Future load(Locale locale) { - final String lang = getLang(locale); - switch (lang) { - - case "en": - return new SynchronousFuture(const en()); - - default: - return new SynchronousFuture(const S()); - } - } - - @override - bool isSupported(Locale locale) => supportedLocales.contains(locale); - - @override - bool shouldReload(GeneratedLocalizationsDelegate old) => false; -} - -String getLang(Locale l) => l.countryCode != null && l.countryCode.isEmpty - ? l.languageCode - : l.toString(); diff --git a/lib/helpers.dart b/lib/helpers.dart index 349a821d..5f74c008 100644 --- a/lib/helpers.dart +++ b/lib/helpers.dart @@ -17,8 +17,6 @@ import "package:audioplayers/audioplayers.dart"; import "package:inventree/l10.dart"; import "package:inventree/widget/snacks.dart"; - - List debug_messages = []; void clearDebugMessage() => debug_messages.clear(); @@ -44,14 +42,12 @@ bool debugContains(String msg, {bool raiseAssert = true}) { } if (raiseAssert) { - assert(result); } return result; } - bool isTesting() { return Platform.environment.containsKey("FLUTTER_TEST"); } @@ -64,12 +60,10 @@ bool hasContext() { } } - /* * Display a debug message if we are in testing mode, or running in debug mode */ void debug(dynamic msg) { - if (Platform.environment.containsKey("FLUTTER_TEST")) { debug_messages.add(msg.toString()); } @@ -77,13 +71,11 @@ void debug(dynamic msg) { print("DEBUG: ${msg.toString()}"); } - /* * Simplify string representation of a floating point value * Basically, don't display fractional component if it is an integer */ String simpleNumberString(double number) { - if (number.toInt() == number) { return number.toInt().toString(); } else { @@ -98,7 +90,6 @@ String simpleNumberString(double number) { * we will not attempt to play the sound */ Future playAudioFile(String path) async { - // Debug message for unit testing debug("Playing audio file: '${path}'"); @@ -110,21 +101,21 @@ Future playAudioFile(String path) async { // Specify context options for the audio player // Ref: https://github.com/inventree/inventree-app/issues/582 - player.setAudioContext(AudioContext( - android: AudioContextAndroid( - usageType: AndroidUsageType.notification, - audioFocus: AndroidAudioFocus.none, + player.setAudioContext( + AudioContext( + android: AudioContextAndroid( + usageType: AndroidUsageType.notification, + audioFocus: AndroidAudioFocus.none, + ), + iOS: AudioContextIOS(), ), - iOS: AudioContextIOS() - )); + ); player.play(AssetSource(path)); } - // Open an external URL Future openLink(String url) async { - final link = Uri.parse(url); try { @@ -134,24 +125,20 @@ Future openLink(String url) async { } } - /* * Helper function for rendering a money / currency object as a String */ String renderCurrency(double? amount, String currency, {int decimals = 2}) { - if (amount == null || amount.isInfinite || amount.isNaN) return "-"; currency = currency.trim(); if (currency.isEmpty) return "-"; - CurrencyFormat fmt = CurrencyFormat.fromCode(currency.toLowerCase()) ?? CurrencyFormat.usd; + CurrencyFormat fmt = + CurrencyFormat.fromCode(currency.toLowerCase()) ?? CurrencyFormat.usd; - String value = CurrencyFormatter.format( - amount, - fmt - ); + String value = CurrencyFormatter.format(amount, fmt); return value; } @@ -163,8 +150,11 @@ bool isValidNumber(double? value) { /* * Render a "range" of prices between two values. */ -String formatPriceRange(double? minPrice, double? maxPrice, { String? currency }) { - +String formatPriceRange( + double? minPrice, + double? maxPrice, { + String? currency, +}) { // Account for empty or null values if (!isValidNumber(minPrice) && !isValidNumber(maxPrice)) { return "-"; diff --git a/lib/inventree/bom.dart b/lib/inventree/bom.dart index 8019ecf0..7c89376d 100644 --- a/lib/inventree/bom.dart +++ b/lib/inventree/bom.dart @@ -1,4 +1,3 @@ - import "package:inventree/inventree/model.dart"; import "package:inventree/inventree/part.dart"; @@ -6,13 +5,13 @@ import "package:inventree/inventree/part.dart"; * Class representing the BomItem database model */ class InvenTreeBomItem extends InvenTreeModel { - InvenTreeBomItem() : super(); InvenTreeBomItem.fromJson(Map json) : super.fromJson(json); @override - InvenTreeModel createFromJson(Map json) => InvenTreeBomItem.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreeBomItem.fromJson(json); @override String get URL => "bom/"; @@ -28,7 +27,7 @@ class InvenTreeBomItem extends InvenTreeModel { // Extract the 'reference' value associated with this BomItem String get reference => getString("reference"); - + // Extract the 'quantity' value associated with this BomItem double get quantity => getDouble("quantity"); @@ -57,8 +56,8 @@ class InvenTreeBomItem extends InvenTreeModel { } return null; -} + } // Extract the ID of the related sub-part int get subPartId => getInt("sub_part"); -} \ No newline at end of file +} diff --git a/lib/inventree/company.dart b/lib/inventree/company.dart index 072f0fe2..43f7d51b 100644 --- a/lib/inventree/company.dart +++ b/lib/inventree/company.dart @@ -6,13 +6,11 @@ import "package:inventree/inventree/model.dart"; import "package:inventree/inventree/purchase_order.dart"; import "package:inventree/widget/company/company_detail.dart"; - /* * The InvenTreeCompany class represents the Company model in the InvenTree database. */ class InvenTreeCompany extends InvenTreeModel { - InvenTreeCompany() : super(); InvenTreeCompany.fromJson(Map json) : super.fromJson(json); @@ -26,14 +24,16 @@ class InvenTreeCompany extends InvenTreeModel { Future goToDetailPage(BuildContext context) async { return Navigator.push( context, - MaterialPageRoute( - builder: (context) => CompanyDetailWidget(this) - ) + MaterialPageRoute(builder: (context) => CompanyDetailWidget(this)), ); } @override - List get rolesRequired => ["purchase_order", "sales_order", "return_order"]; + List get rolesRequired => [ + "purchase_order", + "sales_order", + "return_order", + ]; @override Map> formFields() { @@ -54,12 +54,16 @@ class InvenTreeCompany extends InvenTreeModel { return fields; } - String get image => (jsondata["image"] ?? jsondata["thumbnail"] ?? InvenTreeAPI.staticImage) as String; + String get image => + (jsondata["image"] ?? jsondata["thumbnail"] ?? InvenTreeAPI.staticImage) + as String; - String get thumbnail => (jsondata["thumbnail"] ?? jsondata["image"] ?? InvenTreeAPI.staticThumb) as String; + String get thumbnail => + (jsondata["thumbnail"] ?? jsondata["image"] ?? InvenTreeAPI.staticThumb) + as String; String get website => getString("website"); - + String get phone => getString("phone"); String get email => getString("email"); @@ -73,22 +77,21 @@ class InvenTreeCompany extends InvenTreeModel { bool get active => getBool("active", backup: true); int get partSuppliedCount => getInt("part_supplied"); - - int get partManufacturedCount => getInt("parts_manufactured"); - - // Request a list of purchase orders against this company - Future> getPurchaseOrders({bool? outstanding}) async { - Map filters = { - "supplier": "${pk}" - }; + int get partManufacturedCount => getInt("parts_manufactured"); + + // Request a list of purchase orders against this company + Future> getPurchaseOrders({ + bool? outstanding, + }) async { + Map filters = {"supplier": "${pk}"}; if (outstanding != null) { filters["outstanding"] = outstanding ? "true" : "false"; } final List results = await InvenTreePurchaseOrder().list( - filters: filters + filters: filters, ); List orders = []; @@ -103,18 +106,18 @@ class InvenTreeCompany extends InvenTreeModel { } @override - InvenTreeModel createFromJson(Map json) => InvenTreeCompany.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreeCompany.fromJson(json); } - /* * Class representing an attachment file against a Company object */ class InvenTreeCompanyAttachment extends InvenTreeAttachment { - InvenTreeCompanyAttachment() : super(); - InvenTreeCompanyAttachment.fromJson(Map json) : super.fromJson(json); + InvenTreeCompanyAttachment.fromJson(Map json) + : super.fromJson(json); @override String get REFERENCE_FIELD => "company"; @@ -123,21 +126,23 @@ class InvenTreeCompanyAttachment extends InvenTreeAttachment { String get REF_MODEL_TYPE => "company"; @override - String get URL => InvenTreeAPI().supportsModernAttachments ? "attachment/" : "company/attachment/"; + String get URL => InvenTreeAPI().supportsModernAttachments + ? "attachment/" + : "company/attachment/"; @override - InvenTreeModel createFromJson(Map json) => InvenTreeCompanyAttachment.fromJson(json); - + InvenTreeModel createFromJson(Map json) => + InvenTreeCompanyAttachment.fromJson(json); } /* * The InvenTreeSupplierPart class represents the SupplierPart model in the InvenTree database */ class InvenTreeSupplierPart extends InvenTreeModel { - InvenTreeSupplierPart() : super(); - InvenTreeSupplierPart.fromJson(Map json) : super.fromJson(json); + InvenTreeSupplierPart.fromJson(Map json) + : super.fromJson(json); @override String get URL => "company/part/"; @@ -180,37 +185,47 @@ class InvenTreeSupplierPart extends InvenTreeModel { }; } - int get manufacturerId => getInt("pk", subKey: "manufacturer_detail"); - - String get manufacturerName => getString("name", subKey: "manufacturer_detail"); - + + String get manufacturerName => + getString("name", subKey: "manufacturer_detail"); + String get MPN => getString("MPN", subKey: "manufacturer_part_detail"); - - String get manufacturerImage => (jsondata["manufacturer_detail"]?["image"] ?? jsondata["manufacturer_detail"]?["thumbnail"] ?? InvenTreeAPI.staticThumb) as String; + + String get manufacturerImage => + (jsondata["manufacturer_detail"]?["image"] ?? + jsondata["manufacturer_detail"]?["thumbnail"] ?? + InvenTreeAPI.staticThumb) + as String; int get manufacturerPartId => getInt("manufacturer_part"); - + int get supplierId => getInt("supplier"); - + String get supplierName => getString("name", subKey: "supplier_detail"); - - String get supplierImage => (jsondata["supplier_detail"]?["image"] ?? jsondata["supplier_detail"]?["thumbnail"] ?? InvenTreeAPI.staticThumb) as String; + + String get supplierImage => + (jsondata["supplier_detail"]?["image"] ?? + jsondata["supplier_detail"]?["thumbnail"] ?? + InvenTreeAPI.staticThumb) + as String; String get SKU => getString("SKU"); bool get active => getBool("active", backup: true); - + int get partId => getInt("part"); - String get partImage => (jsondata["part_detail"]?["thumbnail"] ?? InvenTreeAPI.staticThumb) as String; + String get partImage => + (jsondata["part_detail"]?["thumbnail"] ?? InvenTreeAPI.staticThumb) + as String; String get partName => getString("name", subKey: "part_detail"); Map get partDetail => getMap("part_detail"); String get partDescription => getString("description", subKey: "part_detail"); - + String get note => getString("note"); String get packaging => getString("packaging"); @@ -224,15 +239,15 @@ class InvenTreeSupplierPart extends InvenTreeModel { } @override - InvenTreeModel createFromJson(Map json) => InvenTreeSupplierPart.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreeSupplierPart.fromJson(json); } - class InvenTreeManufacturerPart extends InvenTreeModel { - InvenTreeManufacturerPart() : super(); - InvenTreeManufacturerPart.fromJson(Map json) : super.fromJson(json); + InvenTreeManufacturerPart.fromJson(Map json) + : super.fromJson(json); @override String URL = "company/part/manufacturer/"; @@ -255,10 +270,7 @@ class InvenTreeManufacturerPart extends InvenTreeModel { @override Map defaultFilters() { - return { - "manufacturer_detail": "true", - "part_detail": "true", - }; + return {"manufacturer_detail": "true", "part_detail": "true"}; } int get partId => getInt("part"); @@ -269,18 +281,27 @@ class InvenTreeManufacturerPart extends InvenTreeModel { String get partIPN => getString("IPN", subKey: "part_detail"); - String get partImage => (jsondata["part_detail"]?["thumbnail"] ?? InvenTreeAPI.staticThumb) as String; + String get partImage => + (jsondata["part_detail"]?["thumbnail"] ?? InvenTreeAPI.staticThumb) + as String; int get manufacturerId => getInt("manufacturer"); - String get manufacturerName => getString("name", subKey: "manufacturer_detail"); + String get manufacturerName => + getString("name", subKey: "manufacturer_detail"); - String get manufacturerDescription => getString("description", subKey: "manufacturer_detail"); + String get manufacturerDescription => + getString("description", subKey: "manufacturer_detail"); - String get manufacturerImage => (jsondata["manufacturer_detail"]?["image"] ?? jsondata["manufacturer_detail"]?["thumbnail"] ?? InvenTreeAPI.staticThumb) as String; + String get manufacturerImage => + (jsondata["manufacturer_detail"]?["image"] ?? + jsondata["manufacturer_detail"]?["thumbnail"] ?? + InvenTreeAPI.staticThumb) + as String; String get MPN => getString("MPN"); @override - InvenTreeModel createFromJson(Map json) => InvenTreeManufacturerPart.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreeManufacturerPart.fromJson(json); } diff --git a/lib/inventree/model.dart b/lib/inventree/model.dart index 6df483af..c3fc8b68 100644 --- a/lib/inventree/model.dart +++ b/lib/inventree/model.dart @@ -17,10 +17,8 @@ import "package:inventree/inventree/sentry.dart"; import "package:inventree/widget/dialogs.dart"; import "package:inventree/widget/fields.dart"; - // Paginated response object class InvenTreePageResponse { - InvenTreePageResponse() { results = []; } @@ -31,7 +29,7 @@ class InvenTreePageResponse { // Total number of results in the dataset int count = 0; - + int get length => results.length; List results = []; @@ -42,7 +40,6 @@ class InvenTreePageResponse { * for interacting with InvenTree data. */ class InvenTreeModel { - InvenTreeModel(); // Construct an InvenTreeModel from a JSON data object @@ -87,7 +84,6 @@ class InvenTreeModel { // If a subKey is specified, we need to dig deeper into the JSON data if (subKey.isNotEmpty) { - if (!data.containsKey(subKey)) { debug("JSON data does not contain subKey '$subKey' for key '$key'"); return backup; @@ -98,7 +94,6 @@ class InvenTreeModel { if (sub_data is Map) { data = (data[subKey] ?? {}) as Map; } - } if (data.containsKey(key)) { @@ -109,7 +104,11 @@ class InvenTreeModel { } // Helper function to get sub-map from JSON data - Map getMap(String key, {Map backup = const {}, String subKey = ""}) { + Map getMap( + String key, { + Map backup = const {}, + String subKey = "", + }) { dynamic value = getValue(key, backup: backup, subKey: subKey); if (value == null) { @@ -152,7 +151,7 @@ class InvenTreeModel { return double.tryParse(value.toString()) ?? backup; } - double getDouble(String key, {double backup = 0.0, String subkey = "" }) { + double getDouble(String key, {double backup = 0.0, String subkey = ""}) { double? value = getDoubleOrNull(key, backup: backup, subKey: subkey); return value ?? backup; } @@ -194,7 +193,6 @@ class InvenTreeModel { // Return the InvenTree web server URL for this object String get webUrl { - if (api.isConnected()) { String web = InvenTreeAPI().baseUrl; @@ -205,7 +203,6 @@ class InvenTreeModel { web = web.replaceAll("//", "/"); return web; - } else { return ""; } @@ -216,7 +213,9 @@ class InvenTreeModel { */ List get rolesRequired { // Default implementation should not be called - debug("rolesRequired() not implemented for model ${URL} - returning empty list"); + debug( + "rolesRequired() not implemented for model ${URL} - returning empty list", + ); return []; } @@ -271,12 +270,17 @@ class InvenTreeModel { // Fields for editing / creating this model // Override per-model Map> formFields() { - return {}; } - Future createForm(BuildContext context, String title, {String fileField = "", Map fields=const{}, Map data=const {}, Function(dynamic)? onSuccess}) async { - + Future createForm( + BuildContext context, + String title, { + String fileField = "", + Map fields = const {}, + Map data = const {}, + Function(dynamic)? onSuccess, + }) async { if (fields.isEmpty) { fields = formFields(); } @@ -291,14 +295,17 @@ class InvenTreeModel { method: "POST", fileField: fileField, ); - } /* * Launch a modal form to edit the fields available to this model instance. */ - Future editForm(BuildContext context, String title, {Map fields=const {}, Function(dynamic)? onSuccess}) async { - + Future editForm( + BuildContext context, + String title, { + Map fields = const {}, + Function(dynamic)? onSuccess, + }) async { if (fields.isEmpty) { fields = formFields(); } @@ -310,9 +317,8 @@ class InvenTreeModel { fields, modelData: jsondata, onSuccess: onSuccess, - method: "PATCH" + method: "PATCH", ); - } // JSON data which defines this object @@ -324,12 +330,12 @@ class InvenTreeModel { int get pk => getInt("pk"); String get pkString => pk.toString(); - + // Some common accessors String get name => getString("name"); String get description => getString("description"); - + String get notes => getString("notes"); int get parentId => getInt("parent"); @@ -387,8 +393,7 @@ class InvenTreeModel { return ""; } - Future goToInvenTreePage() async { - + Future goToInvenTreePage() async { var uri = Uri.tryParse(webUrl); if (uri != null && await canLaunchUrl(uri)) { await launchUrl(uri); @@ -397,8 +402,7 @@ class InvenTreeModel { } } - Future openLink() async { - + Future openLink() async { if (link.isNotEmpty) { var uri = Uri.tryParse(link); if (uri != null && await canLaunchUrl(uri)) { @@ -408,16 +412,21 @@ class InvenTreeModel { } String get keywords => getString("keywords"); - + // Create a new object from JSON data (not a constructor!) - InvenTreeModel createFromJson(Map json) => InvenTreeModel.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreeModel.fromJson(json); // Return the API detail endpoint for this Model object String get url => "${URL}/${pk}/".replaceAll("//", "/"); // Search this Model type in the database - Future> search(String searchTerm, {Map filters = const {}, int offset = 0, int limit = 25}) async { - + Future> search( + String searchTerm, { + Map filters = const {}, + int offset = 0, + int limit = 25, + }) async { Map searchFilters = {}; for (String key in filters.keys) { @@ -431,12 +440,13 @@ class InvenTreeModel { final results = list(filters: searchFilters); return results; - } // Return the number of results that would meet a particular "query" - Future count({Map filters = const {}, String searchQuery = ""} ) async { - + Future count({ + Map filters = const {}, + String searchQuery = "", + }) async { var params = defaultListFilters(); filters.forEach((String key, String value) { @@ -458,7 +468,7 @@ class InvenTreeModel { } else { return 0; } -} + } Map defaultFilters() { return {}; @@ -476,8 +486,11 @@ class InvenTreeModel { /* * Report error information to sentry, when a model operation fails. */ - Future reportModelError(String title, APIResponse response, {Map context = const {}}) async { - + Future reportModelError( + String title, + APIResponse response, { + Map context = const {}, + }) async { String dataString = response.data?.toString() ?? "null"; // If the response has "errorDetail" set, then the error has already been handled, and there is no need to continue @@ -506,16 +519,12 @@ class InvenTreeModel { context["dataType"] = response.data?.runtimeType.toString() ?? "null"; context["model"] = URL; - await sentryReportMessage( - title, - context: context, - ); + await sentryReportMessage(title, context: context); } /// Delete the instance on the remote server /// Returns true if the operation was successful, else false Future delete() async { - // Return if we do not have a valid pk if (pk < 0) { return false; @@ -523,18 +532,15 @@ class InvenTreeModel { var response = await api.delete(url); - if (!response.isValid() || response.data == null || (response.data is! Map)) { - + if (!response.isValid() || + response.data == null || + (response.data is! Map)) { reportModelError( "InvenTreeModel.delete() returned invalid response", response, ); - showServerError( - url, - L10().serverError, - L10().errorDelete, - ); + showServerError(url, L10().serverError, L10().errorDelete); return false; } @@ -547,52 +553,40 @@ class InvenTreeModel { * Reload this object, by requesting data from the server */ Future reload() async { - // If we do not have a valid pk (for some reason), exit immediately if (pk < 0) { return false; } - var response = await api.get(url, params: defaultGetFilters(), expectedStatusCode: 200); + var response = await api.get( + url, + params: defaultGetFilters(), + expectedStatusCode: 200, + ); // A valid response has been returned if (response.isValid() && response.statusCode == 200) { - // Returned data was not a valid JSON object if (response.data == null || response.data is! Map) { reportModelError( - "InvenTreeModel.reload() returned invalid response", - response, - context: { - "pk": pk.toString(), - } + "InvenTreeModel.reload() returned invalid response", + response, + context: {"pk": pk.toString()}, ); - showServerError( - url, - L10().serverError, - L10().responseInvalid, - ); + showServerError(url, L10().serverError, L10().responseInvalid); return false; } } else { - switch (response.statusCode) { case 404: // Object has been deleted - showSnackIcon( - L10().itemDeleted, - success: false, - ); + showSnackIcon(L10().itemDeleted, success: false); default: String detail = L10().errorFetch; detail += "\n${L10().statusCode}: ${response.statusCode}"; - showServerError( - url, - L10().serverError, - detail - ); + showServerError(url, L10().serverError, detail); } return false; @@ -606,15 +600,15 @@ class InvenTreeModel { } // POST data to update the model - Future update({Map values = const {}, int? expectedStatusCode = 200}) async { - + Future update({ + Map values = const {}, + int? expectedStatusCode = 200, + }) async { var url = path.join(URL, pk.toString()); // Return if we do not have a valid pk if (pk < 0) { - return APIResponse( - url: url, - ); + return APIResponse(url: url); } if (!url.endsWith("/")) { @@ -631,8 +625,10 @@ class InvenTreeModel { } // Return the detail view for the associated pk - Future getModel(String pk, {Map filters = const {}}) async { - + Future getModel( + String pk, { + Map filters = const {}, + }) async { var url = path.join(URL, pk.toString()); if (!url.endsWith("/")) { @@ -649,27 +645,18 @@ class InvenTreeModel { var response = await api.get(url, params: params); if (!response.isValid() || response.data == null || response.data is! Map) { - if (response.statusCode != -1) { // Report error reportModelError( - "InvenTreeModel.getModel() returned invalid response", - response, - context: { - "filters": filters.toString(), - "pk": pk, - } + "InvenTreeModel.getModel() returned invalid response", + response, + context: {"filters": filters.toString(), "pk": pk}, ); } - showServerError( - url, - L10().serverError, - L10().errorFetch, - ); + showServerError(url, L10().serverError, L10().errorFetch); return null; - } lastReload = DateTime.now(); @@ -677,8 +664,10 @@ class InvenTreeModel { return createFromJson(response.asMap()); } - Future get(int pk, {Map filters = const {}}) async { - + Future get( + int pk, { + Map filters = const {}, + }) async { if (pk < 0) { return null; } @@ -687,7 +676,6 @@ class InvenTreeModel { } Future create(Map data) async { - if (data.containsKey("pk")) { data.remove("pk"); } @@ -700,20 +688,13 @@ class InvenTreeModel { // Invalid response returned from server if (!response.isValid() || response.data == null || response.data is! Map) { - reportModelError( - "InvenTreeModel.create() returned invalid response", - response, - context: { - "pk": pk.toString(), - } + "InvenTreeModel.create() returned invalid response", + response, + context: {"pk": pk.toString()}, ); - showServerError( - URL, - L10().serverError, - L10().errorCreate, - ); + showServerError(URL, L10().serverError, L10().errorCreate); return null; } @@ -721,7 +702,11 @@ class InvenTreeModel { return createFromJson(response.asMap()); } - Future listPaginated(int limit, int offset, {Map filters = const {}}) async { + Future listPaginated( + int limit, + int offset, { + Map filters = const {}, + }) async { var params = defaultListFilters(); for (String key in filters.keys) { @@ -737,7 +722,6 @@ class InvenTreeModel { * - In such a case, we want to concatenate them together */ if (params.containsKey("original_search")) { - String search = params["search"] ?? ""; String original = params["original_search"] ?? ""; @@ -759,18 +743,20 @@ class InvenTreeModel { // First attempt is to look for paginated data, returned as a map - if (dataMap.isNotEmpty && dataMap.containsKey("count") && dataMap.containsKey("results")) { + if (dataMap.isNotEmpty && + dataMap.containsKey("count") && + dataMap.containsKey("results")) { page.count = (dataMap["count"] ?? 0) as int; - page.results = []; + page.results = []; - List results = dataMap["results"] as List; + List results = dataMap["results"] as List; - for (dynamic result in results) { - page.addResult(createFromJson(result as Map)); - } + for (dynamic result in results) { + page.addResult(createFromJson(result as Map)); + } - return page; + return page; } // Second attempt is to look for a list of data (not paginated) @@ -782,7 +768,7 @@ class InvenTreeModel { for (var result in dataList) { page.addResult(createFromJson(result as Map)); - } + } return page; } @@ -792,7 +778,9 @@ class InvenTreeModel { } // Return list of objects from the database, with optional filters - Future> list({Map filters = const {}}) async { + Future> list({ + Map filters = const {}, + }) async { var params = defaultListFilters(); for (String key in filters.keys) { @@ -821,7 +809,6 @@ class InvenTreeModel { } for (var d in data) { - // Create a new object (of the current class type InvenTreeModel obj = createFromJson(d as Map); @@ -847,7 +834,6 @@ class InvenTreeModel { // Each filter must be matched // Used for (e.g.) filtering returned results bool filter(String filterString) { - List filters = filterString.trim().toLowerCase().split(" "); for (var f in filters) { @@ -860,22 +846,20 @@ class InvenTreeModel { } } - /* * Class representing a single plugin instance */ class InvenTreePlugin extends InvenTreeModel { - InvenTreePlugin() : super(); InvenTreePlugin.fromJson(Map json) : super.fromJson(json); @override - InvenTreeModel createFromJson(Map json) => InvenTreePlugin.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreePlugin.fromJson(json); @override String get URL { - /* Note: The plugin API endpoint changed at API version 90, * < 90 = 'plugin' * >= 90 = 'plugins' @@ -889,23 +873,24 @@ class InvenTreePlugin extends InvenTreeModel { } String get key => getString("key"); - + bool get active => getBool("active"); - + // Return the metadata struct for this plugin - Map get _meta => (jsondata["meta"] ?? {}) as Map; + Map get _meta => + (jsondata["meta"] ?? {}) as Map; String get humanName => (_meta["human_name"] ?? "") as String; // Return the mixins struct for this plugin - Map get _mixins => (jsondata["mixins"] ?? {}) as Map; + Map get _mixins => + (jsondata["mixins"] ?? {}) as Map; bool supportsMixin(String mixin) { return _mixins.containsKey(mixin); } } - /* * Class representing a 'setting' object on the InvenTree server. * There are two sorts of settings available from the server, via the API: @@ -913,10 +898,10 @@ class InvenTreePlugin extends InvenTreeModel { * - UserSetting (applicable only to the current user) */ class InvenTreeGlobalSetting extends InvenTreeModel { - InvenTreeGlobalSetting() : super(); - InvenTreeGlobalSetting.fromJson(Map json) : super.fromJson(json); + InvenTreeGlobalSetting.fromJson(Map json) + : super.fromJson(json); @override InvenTreeGlobalSetting createFromJson(Map json) { @@ -927,18 +912,17 @@ class InvenTreeGlobalSetting extends InvenTreeModel { String get URL => "settings/global/"; String get key => getString("key"); - - String get value => getString("value"); - - String get type => getString("type"); + String get value => getString("value"); + + String get type => getString("type"); } class InvenTreeUserSetting extends InvenTreeGlobalSetting { - InvenTreeUserSetting() : super(); - InvenTreeUserSetting.fromJson(Map json) : super.fromJson(json); + InvenTreeUserSetting.fromJson(Map json) + : super.fromJson(json); @override InvenTreeGlobalSetting createFromJson(Map json) { @@ -949,22 +933,19 @@ class InvenTreeUserSetting extends InvenTreeGlobalSetting { String get URL => "settings/user/"; } - class InvenTreeAttachment extends InvenTreeModel { // Class representing an "attachment" file InvenTreeAttachment() : super(); - InvenTreeAttachment.fromJson(Map json) : super.fromJson(json); + InvenTreeAttachment.fromJson(Map json) + : super.fromJson(json); @override String get URL => "attachment/"; @override Map> formFields() { - Map> fields = { - "link": {}, - "comment": {} - }; + Map> fields = {"link": {}, "comment": {}}; if (!hasLink) { fields.remove("link"); @@ -1004,13 +985,7 @@ class InvenTreeAttachment extends InvenTreeModel { } // Image formats - final List img_formats = [ - ".png", - ".jpg", - ".gif", - ".bmp", - ".svg", - ]; + final List img_formats = [".png", ".jpg", ".gif", ".bmp", ".svg"]; for (String fmt in img_formats) { if (fn.endsWith(fmt)) { @@ -1022,7 +997,7 @@ class InvenTreeAttachment extends InvenTreeModel { } String get comment => getString("comment"); - + DateTime? get uploadDate { if (jsondata.containsKey("upload_date")) { return DateTime.tryParse((jsondata["upload_date"] ?? "") as String); @@ -1033,7 +1008,6 @@ class InvenTreeAttachment extends InvenTreeModel { // Return a count of how many attachments exist against the specified model ID Future countAttachments(int modelId) { - Map filters = {}; if (InvenTreeAPI().supportsModernAttachments) { @@ -1046,8 +1020,12 @@ class InvenTreeAttachment extends InvenTreeModel { return count(filters: filters); } - Future uploadAttachment(File attachment, int modelId, {String comment = "", Map fields = const {}}) async { - + Future uploadAttachment( + File attachment, + int modelId, { + String comment = "", + Map fields = const {}, + }) async { // Ensure that the correct reference field is set Map data = Map.from(fields); @@ -1058,15 +1036,14 @@ class InvenTreeAttachment extends InvenTreeModel { } if (InvenTreeAPI().supportsModernAttachments) { - url = "attachment/"; data["model_id"] = modelId.toString(); data["model_type"] = REF_MODEL_TYPE; - } else { - if (REFERENCE_FIELD.isEmpty) { - sentryReportMessage("uploadAttachment called with empty 'REFERENCE_FIELD'"); + sentryReportMessage( + "uploadAttachment called with empty 'REFERENCE_FIELD'", + ); return false; } @@ -1074,24 +1051,21 @@ class InvenTreeAttachment extends InvenTreeModel { } final APIResponse response = await InvenTreeAPI().uploadFile( - url, - attachment, - method: "POST", - name: "attachment", - fields: data + url, + attachment, + method: "POST", + name: "attachment", + fields: data, ); return response.successful(); } - Future uploadImage(int modelId, {String prefix = "InvenTree"}) async { - bool result = false; await FilePickerDialog.pickImageFromCamera().then((File? file) { if (file != null) { - String dir = path.dirname(file.path); String ext = path.extension(file.path); String now = DateTime.now().toIso8601String().replaceAll(":", "-"); @@ -1104,8 +1078,9 @@ class InvenTreeAttachment extends InvenTreeModel { uploadAttachment(renamed, modelId).then((success) { result = success; showSnackIcon( - result ? L10().imageUploadSuccess : L10().imageUploadFailure, - success: result); + result ? L10().imageUploadSuccess : L10().imageUploadFailure, + success: result, + ); }); }); } catch (error, stackTrace) { @@ -1118,14 +1093,10 @@ class InvenTreeAttachment extends InvenTreeModel { return result; } - /* * Download this attachment file */ Future downloadAttachment() async { await InvenTreeAPI().downloadFile(attachment); } - } - - diff --git a/lib/inventree/notification.dart b/lib/inventree/notification.dart index ae79fa2c..eee28723 100644 --- a/lib/inventree/notification.dart +++ b/lib/inventree/notification.dart @@ -5,10 +5,10 @@ import "package:inventree/inventree/model.dart"; */ class InvenTreeNotification extends InvenTreeModel { - InvenTreeNotification() : super(); - InvenTreeNotification.fromJson(Map json) : super.fromJson(json); + InvenTreeNotification.fromJson(Map json) + : super.fromJson(json); @override InvenTreeNotification createFromJson(Map json) { @@ -20,15 +20,12 @@ class InvenTreeNotification extends InvenTreeModel { @override Map defaultListFilters() { - // By default, only return 'unread' notifications - return { - "read": "false", - }; + return {"read": "false"}; } String get message => getString("message"); - + DateTime? get creationDate { if (jsondata.containsKey("creation")) { return DateTime.tryParse((jsondata["creation"] ?? "") as String); @@ -41,7 +38,6 @@ class InvenTreeNotification extends InvenTreeModel { * Dismiss this notification (mark as read) */ Future dismiss() async { - if (api.apiVersion >= 82) { // "Modern" API endpoint operates a little differently await update(values: {"read": "true"}); @@ -49,5 +45,4 @@ class InvenTreeNotification extends InvenTreeModel { await api.post("${url}read/"); } } - -} \ No newline at end of file +} diff --git a/lib/inventree/orders.dart b/lib/inventree/orders.dart index d06ddc86..a558092e 100644 --- a/lib/inventree/orders.dart +++ b/lib/inventree/orders.dart @@ -2,16 +2,13 @@ * Base model for various "orders" which share common properties */ - import "package:inventree/inventree/model.dart"; import "package:inventree/inventree/part.dart"; - /* * Generic class representing an "order" */ class InvenTreeOrder extends InvenTreeModel { - InvenTreeOrder() : super(); InvenTreeOrder.fromJson(Map json) : super.fromJson(json); @@ -34,7 +31,8 @@ class InvenTreeOrder extends InvenTreeModel { int get shipmentCount => getInt("shipments_count", backup: 0); - int get completedShipmentCount => getInt("completed_shipments_count", backup: 0); + int get completedShipmentCount => + getInt("completed_shipments_count", backup: 0); bool get complete => completedLineItemCount >= lineItemCount; @@ -46,14 +44,16 @@ class InvenTreeOrder extends InvenTreeModel { String get responsibleName => getString("name", subKey: "responsible_detail"); - String get responsibleLabel => getString("label", subKey: "responsible_detail"); + String get responsibleLabel => + getString("label", subKey: "responsible_detail"); // Project code information int get projectCodeId => getInt("project_code"); String get projectCode => getString("code", subKey: "project_code_detail"); - String get projectCodeDescription => getString("description", subKey: "project_code_detail"); + String get projectCodeDescription => + getString("description", subKey: "project_code_detail"); bool get hasProjectCode => projectCode.isNotEmpty; @@ -84,12 +84,10 @@ class InvenTreeOrder extends InvenTreeModel { } } - /* * Generic class representing an "order line" */ class InvenTreeOrderLine extends InvenTreeModel { - InvenTreeOrderLine() : super(); InvenTreeOrderLine.fromJson(Map json) : super.fromJson(json); @@ -121,15 +119,14 @@ class InvenTreeOrderLine extends InvenTreeModel { String get targetDate => getDateString("target_date"); } - /* * Generic class representing an "ExtraLineItem" */ class InvenTreeExtraLineItem extends InvenTreeModel { - InvenTreeExtraLineItem() : super(); - InvenTreeExtraLineItem.fromJson(Map json) : super.fromJson(json); + InvenTreeExtraLineItem.fromJson(Map json) + : super.fromJson(json); int get orderId => getInt("order"); @@ -157,5 +154,4 @@ class InvenTreeExtraLineItem extends InvenTreeModel { "notes": {}, }; } - -} \ No newline at end of file +} diff --git a/lib/inventree/part.dart b/lib/inventree/part.dart index 0aeec560..c0050ece 100644 --- a/lib/inventree/part.dart +++ b/lib/inventree/part.dart @@ -14,15 +14,14 @@ import "package:inventree/inventree/model.dart"; import "package:inventree/widget/part/category_display.dart"; import "package:inventree/widget/part/part_detail.dart"; - /* * Class representing the PartCategory database model */ class InvenTreePartCategory extends InvenTreeModel { - InvenTreePartCategory() : super(); - InvenTreePartCategory.fromJson(Map json) : super.fromJson(json); + InvenTreePartCategory.fromJson(Map json) + : super.fromJson(json); @override String get URL => "part/category/"; @@ -37,16 +36,13 @@ class InvenTreePartCategory extends InvenTreeModel { Future goToDetailPage(BuildContext context) async { // Default implementation does not do anything... return Navigator.push( - context, - MaterialPageRoute( - builder: (context) => CategoryDisplayWidget(this) - ) + context, + MaterialPageRoute(builder: (context) => CategoryDisplayWidget(this)), ); } @override Map> formFields() { - Map> fields = { "name": {}, "description": {}, @@ -58,9 +54,8 @@ class InvenTreePartCategory extends InvenTreeModel { } String get pathstring => getString("pathstring"); - - String get parentPathString { + String get parentPathString { List psplit = pathstring.split("/"); if (psplit.isNotEmpty) { @@ -78,21 +73,22 @@ class InvenTreePartCategory extends InvenTreeModel { // Return the number of parts in this category // Note that the API changed from 'parts' to 'part_count' (v69) - int get partcount => (jsondata["part_count"] ?? jsondata["parts"] ?? 0) as int; + int get partcount => + (jsondata["part_count"] ?? jsondata["parts"] ?? 0) as int; @override - InvenTreeModel createFromJson(Map json) => InvenTreePartCategory.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreePartCategory.fromJson(json); } - /* * Class representing the PartTestTemplate database model */ class InvenTreePartTestTemplate extends InvenTreeModel { - InvenTreePartTestTemplate() : super(); - InvenTreePartTestTemplate.fromJson(Map json) : super.fromJson(json); + InvenTreePartTestTemplate.fromJson(Map json) + : super.fromJson(json); @override String get URL => "part/test-template/"; @@ -104,16 +100,16 @@ class InvenTreePartTestTemplate extends InvenTreeModel { String get testName => getString("test_name"); bool get required => getBool("required"); - + bool get requiresValue => getBool("requires_value"); bool get requiresAttachment => getBool("requires_attachment"); @override - InvenTreeModel createFromJson(Map json) => InvenTreePartTestTemplate.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreePartTestTemplate.fromJson(json); bool passFailStatus() { - var result = latestResult(); if (result == null) { @@ -134,17 +130,16 @@ class InvenTreePartTestTemplate extends InvenTreeModel { return results.last; } - } /* Class representing the PartParameter database model */ class InvenTreePartParameter extends InvenTreeModel { - InvenTreePartParameter() : super(); - InvenTreePartParameter.fromJson(Map json) : super.fromJson(json); + InvenTreePartParameter.fromJson(Map json) + : super.fromJson(json); @override String get URL => "part/parameter/"; @@ -153,11 +148,11 @@ class InvenTreePartParameter extends InvenTreeModel { List get rolesRequired => ["part"]; @override - InvenTreeModel createFromJson(Map json) => InvenTreePartParameter.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreePartParameter.fromJson(json); @override Map> formFields() { - Map> fields = { "header": { "type": "string", @@ -166,9 +161,7 @@ class InvenTreePartParameter extends InvenTreeModel { "help_text": description, "value": "", }, - "data": { - "type": "string", - } + "data": {"type": "string"}, }; return fields; @@ -179,9 +172,9 @@ class InvenTreePartParameter extends InvenTreeModel { @override String get description => getString("description", subKey: "template_detail"); - + String get value => getString("data"); - + String get valueString { String v = value; @@ -196,15 +189,15 @@ class InvenTreePartParameter extends InvenTreeModel { bool get as_bool => value.toLowerCase() == "true"; String get units => getString("units", subKey: "template_detail"); - - bool get is_checkbox => getBool("checkbox", subKey: "template_detail", backup: false); + + bool get is_checkbox => + getBool("checkbox", subKey: "template_detail", backup: false); } /* * Class representing the Part database model */ class InvenTreePart extends InvenTreeModel { - InvenTreePart() : super(); InvenTreePart.fromJson(Map json) : super.fromJson(json); @@ -222,10 +215,8 @@ class InvenTreePart extends InvenTreeModel { Future goToDetailPage(BuildContext context) async { // Default implementation does not do anything... return Navigator.push( - context, - MaterialPageRoute( - builder: (context) => PartDetailWidget(this) - ) + context, + MaterialPageRoute(builder: (context) => PartDetailWidget(this)), ); } @@ -259,9 +250,7 @@ class InvenTreePart extends InvenTreeModel { @override Map defaultFilters() { - return { - "category_detail": "true", - }; + return {"category_detail": "true"}; } // Cached list of stock items @@ -270,27 +259,25 @@ class InvenTreePart extends InvenTreeModel { int get stockItemCount => stockItems.length; // Request stock items for this part - Future getStockItems(BuildContext context, {bool showDialog=false}) async { + Future getStockItems( + BuildContext context, { + bool showDialog = false, + }) async { + await InvenTreeStockItem() + .list(filters: {"part": "${pk}", "in_stock": "true"}) + .then((var items) { + stockItems.clear(); - await InvenTreeStockItem().list( - filters: { - "part": "${pk}", - "in_stock": "true", - }, - ).then((var items) { - stockItems.clear(); - - for (var item in items) { - if (item is InvenTreeStockItem) { - stockItems.add(item); - } - } - }); + for (var item in items) { + if (item is InvenTreeStockItem) { + stockItems.add(item); + } + } + }); } // Request pricing data for this part Future getPricing() async { - print("REQUEST PRICING FOR: ${pk}"); try { @@ -311,15 +298,13 @@ class InvenTreePart extends InvenTreeModel { } int get supplierCount => getInt("suppliers", backup: 0); - + // Request supplier parts for this part Future> getSupplierParts() async { List _supplierParts = []; final parts = await InvenTreeSupplierPart().list( - filters: { - "part": "${pk}", - } + filters: {"part": "${pk}"}, ); for (var result in parts) { @@ -338,13 +323,9 @@ class InvenTreePart extends InvenTreeModel { // Request test templates from the serve Future getTestTemplates() async { - - InvenTreePartTestTemplate().list( - filters: { - "part": "${pk}", - }, - ).then((var templates) { - + InvenTreePartTestTemplate().list(filters: {"part": "${pk}"}).then(( + var templates, + ) { testingTemplates.clear(); for (var t in templates) { @@ -373,12 +354,12 @@ class InvenTreePart extends InvenTreeModel { // Get the 'available stock' for this Part double get unallocatedStock { - double unallocated = 0; // Note that the 'available_stock' was not added until API v35 if (jsondata.containsKey("unallocated_stock")) { - unallocated = double.tryParse(jsondata["unallocated_stock"].toString()) ?? 0; + unallocated = + double.tryParse(jsondata["unallocated_stock"].toString()) ?? 0; } else { unallocated = inStock; } @@ -386,148 +367,150 @@ class InvenTreePart extends InvenTreeModel { return max(0, unallocated); } - String get unallocatedStockString => simpleNumberString(unallocatedStock); + String get unallocatedStockString => simpleNumberString(unallocatedStock); - String stockString({bool includeUnits = true}) { - String q = unallocatedStockString; + String stockString({bool includeUnits = true}) { + String q = unallocatedStockString; - if (unallocatedStock != inStock) { - q += " / ${inStockString}"; - } - - if (includeUnits && units.isNotEmpty) { - q += " ${units}"; - } - - return q; + if (unallocatedStock != inStock) { + q += " / ${inStockString}"; } - String get units => getString("units"); - - // Get the ID of the Part that this part is a variant of (or null) - int? get variantOf => jsondata["variant_of"] as int?; - - // Get the number of units being build for this Part - double get building => getDouble("building"); - - // Get the number of BOMs this Part is used in (if it is a component) - int get usedInCount => jsondata.containsKey("used_in") ? getInt("used_in", backup: 0) : 0; - - bool get isAssembly => getBool("assembly"); - - bool get isComponent => getBool("component"); - - bool get isPurchaseable => getBool("purchaseable"); - - bool get isSalable => getBool("salable"); - - bool get isActive => getBool("active"); - - bool get isVirtual => getBool("virtual"); - - bool get isTemplate => getBool("is_template"); - - bool get isTrackable => getBool("trackable"); - - // Get the IPN (internal part number) for the Part instance - String get IPN => getString("IPN"); - - // Get the revision string for the Part instance - String get revision => getString("revision"); - - // Get the category ID for the Part instance (or "null" if does not exist) - int get categoryId => getInt("category"); - - // Get the category name for the Part instance - String get categoryName { - // Inavlid category ID - if (categoryId <= 0) return ""; - - if (!jsondata.containsKey("category_detail")) return ""; - - return (jsondata["category_detail"]?["name"] ?? "") as String; + if (includeUnits && units.isNotEmpty) { + q += " ${units}"; } - // Get the category description for the Part instance - String get categoryDescription { - // Invalid category ID - if (categoryId <= 0) return ""; + return q; + } - if (!jsondata.containsKey("category_detail")) return ""; + String get units => getString("units"); - return (jsondata["category_detail"]?["description"] ?? "") as String; - } - // Get the image URL for the Part instance - String get _image => getString("image"); + // Get the ID of the Part that this part is a variant of (or null) + int? get variantOf => jsondata["variant_of"] as int?; - // Get the thumbnail URL for the Part instance - String get _thumbnail => getString("thumbnail"); + // Get the number of units being build for this Part + double get building => getDouble("building"); - // Return the fully-qualified name for the Part instance - String get fullname { + // Get the number of BOMs this Part is used in (if it is a component) + int get usedInCount => + jsondata.containsKey("used_in") ? getInt("used_in", backup: 0) : 0; - String fn = getString("full_name"); + bool get isAssembly => getBool("assembly"); - if (fn.isNotEmpty) return fn; + bool get isComponent => getBool("component"); - List elements = []; + bool get isPurchaseable => getBool("purchaseable"); - if (IPN.isNotEmpty) elements.add(IPN); + bool get isSalable => getBool("salable"); - elements.add(name); + bool get isActive => getBool("active"); - if (revision.isNotEmpty) elements.add(revision); + bool get isVirtual => getBool("virtual"); - return elements.join(" | "); - } + bool get isTemplate => getBool("is_template"); - // Return a path to the image for this Part - String get image { - // Use thumbnail as a backup - String img = _image.isNotEmpty ? _image : _thumbnail; + bool get isTrackable => getBool("trackable"); - return img.isNotEmpty ? img : InvenTreeAPI.staticImage; - } + // Get the IPN (internal part number) for the Part instance + String get IPN => getString("IPN"); - // Return a path to the thumbnail for this part - String get thumbnail { - // Use image as a backup - String img = _thumbnail.isNotEmpty ? _thumbnail : _image; + // Get the revision string for the Part instance + String get revision => getString("revision"); - return img.isNotEmpty ? img : InvenTreeAPI.staticThumb; - } + // Get the category ID for the Part instance (or "null" if does not exist) + int get categoryId => getInt("category"); - Future uploadImage(File image) async { - // Upload file against this part - final APIResponse response = await InvenTreeAPI().uploadFile( - url, - image, - method: "PATCH", - name: "image", - ); + // Get the category name for the Part instance + String get categoryName { + // Inavlid category ID + if (categoryId <= 0) return ""; - return response.successful(); - } + if (!jsondata.containsKey("category_detail")) return ""; - // Return the "starred" status of this part - bool get starred => getBool("starred"); + return (jsondata["category_detail"]?["name"] ?? "") as String; + } + + // Get the category description for the Part instance + String get categoryDescription { + // Invalid category ID + if (categoryId <= 0) return ""; + + if (!jsondata.containsKey("category_detail")) return ""; + + return (jsondata["category_detail"]?["description"] ?? "") as String; + } + + // Get the image URL for the Part instance + String get _image => getString("image"); + + // Get the thumbnail URL for the Part instance + String get _thumbnail => getString("thumbnail"); + + // Return the fully-qualified name for the Part instance + String get fullname { + String fn = getString("full_name"); + + if (fn.isNotEmpty) return fn; + + List elements = []; + + if (IPN.isNotEmpty) elements.add(IPN); + + elements.add(name); + + if (revision.isNotEmpty) elements.add(revision); + + return elements.join(" | "); + } + + // Return a path to the image for this Part + String get image { + // Use thumbnail as a backup + String img = _image.isNotEmpty ? _image : _thumbnail; + + return img.isNotEmpty ? img : InvenTreeAPI.staticImage; + } + + // Return a path to the thumbnail for this part + String get thumbnail { + // Use image as a backup + String img = _thumbnail.isNotEmpty ? _thumbnail : _image; + + return img.isNotEmpty ? img : InvenTreeAPI.staticThumb; + } + + Future uploadImage(File image) async { + // Upload file against this part + final APIResponse response = await InvenTreeAPI().uploadFile( + url, + image, + method: "PATCH", + name: "image", + ); + + return response.successful(); + } + + // Return the "starred" status of this part + bool get starred => getBool("starred"); @override - InvenTreeModel createFromJson(Map json) => InvenTreePart.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreePart.fromJson(json); } - class InvenTreePartPricing extends InvenTreeModel { - InvenTreePartPricing() : super(); - InvenTreePartPricing.fromJson(Map json) : super.fromJson(json); + InvenTreePartPricing.fromJson(Map json) + : super.fromJson(json); @override List get rolesRequired => ["part"]; @override - InvenTreeModel createFromJson(Map json) => InvenTreePartPricing.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreePartPricing.fromJson(json); // Price data accessors String get currency => getString("currency", backup: "USD"); @@ -538,8 +521,10 @@ class InvenTreePartPricing extends InvenTreeModel { double? get overrideMin => getDoubleOrNull("override_min"); double? get overrideMax => getDoubleOrNull("override_max"); - String get overrideMinCurrency => getString("override_min_currency", backup: currency); - String get overrideMaxCurrency => getString("override_max_currency", backup: currency); + String get overrideMinCurrency => + getString("override_min_currency", backup: currency); + String get overrideMaxCurrency => + getString("override_max_currency", backup: currency); double? get bomCostMin => getDoubleOrNull("bom_cost_min"); double? get bomCostMax => getDoubleOrNull("bom_cost_max"); @@ -563,15 +548,14 @@ class InvenTreePartPricing extends InvenTreeModel { double? get saleHistoryMax => getDoubleOrNull("sale_history_max"); } - /* * Class representing an attachment file against a Part object */ class InvenTreePartAttachment extends InvenTreeAttachment { - InvenTreePartAttachment() : super(); - InvenTreePartAttachment.fromJson(Map json) : super.fromJson(json); + InvenTreePartAttachment.fromJson(Map json) + : super.fromJson(json); @override String get REFERENCE_FIELD => "part"; @@ -580,9 +564,11 @@ class InvenTreePartAttachment extends InvenTreeAttachment { String get REF_MODEL_TYPE => "part"; @override - String get URL => InvenTreeAPI().supportsModernAttachments ? "attachment/" : "part/attachment/"; + String get URL => InvenTreeAPI().supportsModernAttachments + ? "attachment/" + : "part/attachment/"; @override - InvenTreeModel createFromJson(Map json) => InvenTreePartAttachment.fromJson(json); - + InvenTreeModel createFromJson(Map json) => + InvenTreePartAttachment.fromJson(json); } diff --git a/lib/inventree/project_code.dart b/lib/inventree/project_code.dart index e5d45289..3d8793b9 100644 --- a/lib/inventree/project_code.dart +++ b/lib/inventree/project_code.dart @@ -1,17 +1,17 @@ import "package:inventree/inventree/model.dart"; - /* * Class representing the ProjectCode database model */ class InvenTreeProjectCode extends InvenTreeModel { - InvenTreeProjectCode() : super(); - InvenTreeProjectCode.fromJson(Map json) : super.fromJson(json); + InvenTreeProjectCode.fromJson(Map json) + : super.fromJson(json); @override - InvenTreeModel createFromJson(Map json) => InvenTreeProjectCode.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreeProjectCode.fromJson(json); @override String get URL => "project-code/"; @@ -20,10 +20,7 @@ class InvenTreeProjectCode extends InvenTreeModel { @override Map> formFields() { - return { - "code": {}, - "description": {}, - }; + return {"code": {}, "description": {}}; } String get code => getString("code"); diff --git a/lib/inventree/purchase_order.dart b/lib/inventree/purchase_order.dart index 1b8bc7c5..59874dc1 100644 --- a/lib/inventree/purchase_order.dart +++ b/lib/inventree/purchase_order.dart @@ -12,18 +12,18 @@ import "package:inventree/widget/progress.dart"; import "package:inventree/api_form.dart"; import "package:inventree/l10.dart"; - /* * Class representing an individual PurchaseOrder instance */ class InvenTreePurchaseOrder extends InvenTreeOrder { - InvenTreePurchaseOrder() : super(); - InvenTreePurchaseOrder.fromJson(Map json) : super.fromJson(json); + InvenTreePurchaseOrder.fromJson(Map json) + : super.fromJson(json); @override - InvenTreeModel createFromJson(Map json) => InvenTreePurchaseOrder.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreePurchaseOrder.fromJson(json); @override String get URL => "order/po/"; @@ -31,10 +31,8 @@ class InvenTreePurchaseOrder extends InvenTreeOrder { @override Future goToDetailPage(BuildContext context) async { return Navigator.push( - context, - MaterialPageRoute( - builder: (context) => PurchaseOrderDetailWidget(this) - ) + context, + MaterialPageRoute(builder: (context) => PurchaseOrderDetailWidget(this)), ); } @@ -50,9 +48,7 @@ class InvenTreePurchaseOrder extends InvenTreeOrder { Map> fields = { "reference": {}, "supplier": { - "filters": { - "is_supplier": true, - }, + "filters": {"is_supplier": true}, }, "supplier_reference": {}, "description": {}, @@ -63,9 +59,7 @@ class InvenTreePurchaseOrder extends InvenTreeOrder { "link": {}, "responsible": {}, "contact": { - "filters": { - "company": supplierId, - } + "filters": {"company": supplierId}, }, }; @@ -82,20 +76,16 @@ class InvenTreePurchaseOrder extends InvenTreeOrder { } return fields; - } @override Map defaultFilters() { - return { - "supplier_detail": "true", - }; + return {"supplier_detail": "true"}; } int get supplierId => getInt("supplier"); InvenTreeCompany? get supplier { - dynamic supplier_detail = jsondata["supplier_detail"]; if (supplier_detail == null) { @@ -109,20 +99,26 @@ class InvenTreePurchaseOrder extends InvenTreeOrder { int get destinationId => getInt("destination"); - bool get isOpen => api.PurchaseOrderStatus.isNameIn(status, ["PENDING", "PLACED", "ON_HOLD"]); + bool get isOpen => api.PurchaseOrderStatus.isNameIn(status, [ + "PENDING", + "PLACED", + "ON_HOLD", + ]); - bool get isPending => api.PurchaseOrderStatus.isNameIn(status, ["PENDING", "ON_HOLD"]); + bool get isPending => + api.PurchaseOrderStatus.isNameIn(status, ["PENDING", "ON_HOLD"]); bool get isPlaced => api.PurchaseOrderStatus.isNameIn(status, ["PLACED"]); - bool get isFailed => api.PurchaseOrderStatus.isNameIn(status, ["CANCELLED", "LOST", "RETURNED"]); + bool get isFailed => api.PurchaseOrderStatus.isNameIn(status, [ + "CANCELLED", + "LOST", + "RETURNED", + ]); Future> getLineItems() async { - final results = await InvenTreePOLineItem().list( - filters: { - "order": "${pk}", - } + filters: {"order": "${pk}"}, ); List items = []; @@ -161,13 +157,14 @@ class InvenTreePurchaseOrder extends InvenTreeOrder { } class InvenTreePOLineItem extends InvenTreeOrderLine { - InvenTreePOLineItem() : super(); - InvenTreePOLineItem.fromJson(Map json) : super.fromJson(json); + InvenTreePOLineItem.fromJson(Map json) + : super.fromJson(json); @override - InvenTreeModel createFromJson(Map json) => InvenTreePOLineItem.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreePOLineItem.fromJson(json); @override String get URL => "order/po-line/"; @@ -198,10 +195,7 @@ class InvenTreePOLineItem extends InvenTreeOrderLine { @override Map defaultFilters() { - return { - "part_detail": "true", - "order_detail": "true", - }; + return {"part_detail": "true", "order_detail": "true"}; } double get received => getDouble("received"); @@ -216,14 +210,14 @@ class InvenTreePOLineItem extends InvenTreeOrderLine { return received / quantity; } - String get progressString => simpleNumberString(received) + " / " + simpleNumberString(quantity); + String get progressString => + simpleNumberString(received) + " / " + simpleNumberString(quantity); double get outstanding => quantity - received; int get supplierPartId => getInt("part"); InvenTreeSupplierPart? get supplierPart { - dynamic detail = jsondata["supplier_part_detail"]; if (detail == null) { @@ -246,7 +240,7 @@ class InvenTreePOLineItem extends InvenTreeOrderLine { String get SKU => getString("SKU", subKey: "supplier_part_detail"); double get purchasePrice => getDouble("purchase_price"); - + String get purchasePriceCurrency => getString("purchase_price_currency"); int get destinationId => getInt("destination"); @@ -256,7 +250,13 @@ class InvenTreePOLineItem extends InvenTreeOrderLine { Map get destinationDetail => getMap("destination_detail"); // Receive this line item into stock - Future receive(BuildContext context, {int? destination, double? quantity, String? barcode, Function? onSuccess}) async { + Future receive( + BuildContext context, { + int? destination, + double? quantity, + String? barcode, + Function? onSuccess, + }) async { // Infer the destination location from the line item if not provided if (destinationId > 0) { destination = destinationId; @@ -274,20 +274,10 @@ class InvenTreePOLineItem extends InvenTreeOrderLine { "hidden": true, "value": pk, }, - "quantity": { - "parent": "items", - "nested": true, - "value": quantity, - }, + "quantity": {"parent": "items", "nested": true, "value": quantity}, "location": {}, - "status": { - "parent": "items", - "nested": true, - }, - "batch_code": { - "parent": "items", - "nested": true, - }, + "status": {"parent": "items", "nested": true}, + "batch_code": {"parent": "items", "nested": true}, "barcode": { "parent": "items", "nested": true, @@ -295,7 +285,7 @@ class InvenTreePOLineItem extends InvenTreeOrderLine { "label": L10().barcodeAssign, "value": barcode, "required": false, - } + }, }; if (destination != null && destination > 0) { @@ -306,31 +296,31 @@ class InvenTreePOLineItem extends InvenTreeOrderLine { if (order != null) { await launchApiForm( - context, - L10().receiveItem, - order.receive_url, - fields, - method: "POST", - icon: TablerIcons.transition_right, - onSuccess: (data) { - if (onSuccess != null) { - onSuccess(); - } + context, + L10().receiveItem, + order.receive_url, + fields, + method: "POST", + icon: TablerIcons.transition_right, + onSuccess: (data) { + if (onSuccess != null) { + onSuccess(); } + }, ); } } } - class InvenTreePOExtraLineItem extends InvenTreeExtraLineItem { - InvenTreePOExtraLineItem() : super(); - InvenTreePOExtraLineItem.fromJson(Map json) : super.fromJson(json); + InvenTreePOExtraLineItem.fromJson(Map json) + : super.fromJson(json); @override - InvenTreeModel createFromJson(Map json) => InvenTreePOExtraLineItem.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreePOExtraLineItem.fromJson(json); @override String get URL => "order/po-extra-line/"; @@ -342,23 +332,19 @@ class InvenTreePOExtraLineItem extends InvenTreeExtraLineItem { Future goToDetailPage(BuildContext context) async { return Navigator.push( context, - MaterialPageRoute( - builder: (context) => ExtraLineDetailWidget(this) - ) + MaterialPageRoute(builder: (context) => ExtraLineDetailWidget(this)), ); } - } - /* * Class representing an attachment file against a PurchaseOrder object */ class InvenTreePurchaseOrderAttachment extends InvenTreeAttachment { - InvenTreePurchaseOrderAttachment() : super(); - InvenTreePurchaseOrderAttachment.fromJson(Map json) : super.fromJson(json); + InvenTreePurchaseOrderAttachment.fromJson(Map json) + : super.fromJson(json); @override String get REFERENCE_FIELD => "order"; @@ -367,9 +353,11 @@ class InvenTreePurchaseOrderAttachment extends InvenTreeAttachment { String get REF_MODEL_TYPE => "purchaseorder"; @override - String get URL => InvenTreeAPI().supportsModernAttachments ? "attachment/" : "order/po/attachment/"; + String get URL => InvenTreeAPI().supportsModernAttachments + ? "attachment/" + : "order/po/attachment/"; @override - InvenTreeModel createFromJson(Map json) => InvenTreePurchaseOrderAttachment.fromJson(json); - + InvenTreeModel createFromJson(Map json) => + InvenTreePurchaseOrderAttachment.fromJson(json); } diff --git a/lib/inventree/sales_order.dart b/lib/inventree/sales_order.dart index babf2a25..51412dbd 100644 --- a/lib/inventree/sales_order.dart +++ b/lib/inventree/sales_order.dart @@ -1,5 +1,3 @@ - - import "package:flutter/material.dart"; import "package:inventree/api.dart"; import "package:inventree/helpers.dart"; @@ -11,18 +9,18 @@ import "package:inventree/widget/progress.dart"; import "package:inventree/widget/order/extra_line_detail.dart"; import "package:inventree/widget/order/sales_order_detail.dart"; - /* * Class representing an individual SalesOrder */ class InvenTreeSalesOrder extends InvenTreeOrder { - InvenTreeSalesOrder() : super(); - InvenTreeSalesOrder.fromJson(Map json) : super.fromJson(json); + InvenTreeSalesOrder.fromJson(Map json) + : super.fromJson(json); @override - InvenTreeModel createFromJson(Map json) => InvenTreeSalesOrder.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreeSalesOrder.fromJson(json); @override String get URL => "order/so/"; @@ -38,9 +36,7 @@ class InvenTreeSalesOrder extends InvenTreeOrder { Future goToDetailPage(BuildContext context) async { return Navigator.push( context, - MaterialPageRoute( - builder: (context) => SalesOrderDetailWidget(this) - ) + MaterialPageRoute(builder: (context) => SalesOrderDetailWidget(this)), ); } @@ -49,9 +45,7 @@ class InvenTreeSalesOrder extends InvenTreeOrder { Map> fields = { "reference": {}, "customer": { - "filters": { - "is_customer": true, - } + "filters": {"is_customer": true}, }, "customer_reference": {}, "description": {}, @@ -61,10 +55,8 @@ class InvenTreeSalesOrder extends InvenTreeOrder { "link": {}, "responsible": {}, "contact": { - "filters": { - "company": customerId, - } - } + "filters": {"company": customerId}, + }, }; if (!InvenTreeAPI().supportsProjectCodes) { @@ -84,9 +76,7 @@ class InvenTreeSalesOrder extends InvenTreeOrder { @override Map defaultFilters() { - return { - "customer_detail": "true", - }; + return {"customer_detail": "true"}; } Future issueOrder() async { @@ -124,28 +114,33 @@ class InvenTreeSalesOrder extends InvenTreeOrder { String get customerReference => getString("customer_reference"); - bool get isOpen => api.SalesOrderStatus.isNameIn(status, ["PENDING", "IN_PROGRESS", "ON_HOLD"]); + bool get isOpen => api.SalesOrderStatus.isNameIn(status, [ + "PENDING", + "IN_PROGRESS", + "ON_HOLD", + ]); - bool get isPending => api.SalesOrderStatus.isNameIn(status, ["PENDING", "ON_HOLD"]); + bool get isPending => + api.SalesOrderStatus.isNameIn(status, ["PENDING", "ON_HOLD"]); - bool get isInProgress => api.SalesOrderStatus.isNameIn(status, ["IN_PROGRESS"]); + bool get isInProgress => + api.SalesOrderStatus.isNameIn(status, ["IN_PROGRESS"]); bool get isComplete => api.SalesOrderStatus.isNameIn(status, ["SHIPPED"]); - } - /* * Class representing an individual line item in a SalesOrder */ class InvenTreeSOLineItem extends InvenTreeOrderLine { - InvenTreeSOLineItem() : super(); - InvenTreeSOLineItem.fromJson(Map json) : super.fromJson(json); + InvenTreeSOLineItem.fromJson(Map json) + : super.fromJson(json); @override - InvenTreeModel createFromJson(Map json) => InvenTreeSOLineItem.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreeSOLineItem.fromJson(json); @override String get URL => "order/so-line/"; @@ -156,13 +151,9 @@ class InvenTreeSOLineItem extends InvenTreeOrderLine { @override Map> formFields() { return { - "order": { - "hidden": true, - }, + "order": {"hidden": true}, "part": { - "filters": { - "salable": true, - } + "filters": {"salable": true}, }, "quantity": {}, "reference": {}, @@ -172,33 +163,17 @@ class InvenTreeSOLineItem extends InvenTreeOrderLine { } Map> allocateFormFields() { - return { - "line_item": { - "parent": "items", - "nested": true, - "hidden": true, - }, - "stock_item": { - "parent": "items", - "nested": true, - "filters": {}, - }, - "quantity": { - "parent": "items", - "nested": true, - }, - "shipment": { - "filters": {} - } + "line_item": {"parent": "items", "nested": true, "hidden": true}, + "stock_item": {"parent": "items", "nested": true, "filters": {}}, + "quantity": {"parent": "items", "nested": true}, + "shipment": {"filters": {}}, }; } @override Map defaultFilters() { - return { - "part_detail": "true", - }; + return {"part_detail": "true"}; } double get allocated => getDouble("allocated"); @@ -223,7 +198,8 @@ class InvenTreeSOLineItem extends InvenTreeOrderLine { return unallocated; } - String get allocatedString => simpleNumberString(allocated) + " / " + simpleNumberString(quantity); + String get allocatedString => + simpleNumberString(allocated) + " / " + simpleNumberString(quantity); double get shipped => getDouble("shipped"); @@ -239,26 +215,28 @@ class InvenTreeSOLineItem extends InvenTreeOrderLine { return shipped / quantity; } - String get progressString => simpleNumberString(shipped) + " / " + simpleNumberString(quantity); + String get progressString => + simpleNumberString(shipped) + " / " + simpleNumberString(quantity); bool get isComplete => shipped >= quantity; - double get available => getDouble("available_stock") + getDouble("available_variant_stock"); + double get available => + getDouble("available_stock") + getDouble("available_variant_stock"); double get salePrice => getDouble("sale_price"); String get salePriceCurrency => getString("sale_price_currency"); - } - class InvenTreeSOExtraLineItem extends InvenTreeExtraLineItem { InvenTreeSOExtraLineItem() : super(); - InvenTreeSOExtraLineItem.fromJson(Map json) : super.fromJson(json); + InvenTreeSOExtraLineItem.fromJson(Map json) + : super.fromJson(json); @override - InvenTreeModel createFromJson(Map json) => InvenTreeSOExtraLineItem.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreeSOExtraLineItem.fromJson(json); @override String get URL => "order/so-extra-line/"; @@ -269,10 +247,8 @@ class InvenTreeSOExtraLineItem extends InvenTreeExtraLineItem { @override Future goToDetailPage(BuildContext context) async { return Navigator.push( - context, - MaterialPageRoute( - builder: (context) => ExtraLineDetailWidget(this) - ) + context, + MaterialPageRoute(builder: (context) => ExtraLineDetailWidget(this)), ); } } @@ -281,13 +257,14 @@ class InvenTreeSOExtraLineItem extends InvenTreeExtraLineItem { * Class representing a sales order shipment */ class InvenTreeSalesOrderShipment extends InvenTreeModel { - InvenTreeSalesOrderShipment() : super(); - InvenTreeSalesOrderShipment.fromJson(Map json) : super.fromJson(json); + InvenTreeSalesOrderShipment.fromJson(Map json) + : super.fromJson(json); @override - InvenTreeModel createFromJson(Map json) => InvenTreeSalesOrderShipment.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreeSalesOrderShipment.fromJson(json); @override String get URL => "/order/so/shipment/"; @@ -318,19 +295,18 @@ class InvenTreeSalesOrderShipment extends InvenTreeModel { bool get shipped => shipment_date != null && shipment_date!.isNotEmpty; } - - /* * Class representing an attachment file against a SalesOrder object */ class InvenTreeSalesOrderAttachment extends InvenTreeAttachment { - InvenTreeSalesOrderAttachment() : super(); - InvenTreeSalesOrderAttachment.fromJson(Map json) : super.fromJson(json); + InvenTreeSalesOrderAttachment.fromJson(Map json) + : super.fromJson(json); @override - InvenTreeModel createFromJson(Map json) => InvenTreeSalesOrderAttachment.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreeSalesOrderAttachment.fromJson(json); @override String get REFERENCE_FIELD => "order"; @@ -339,6 +315,7 @@ class InvenTreeSalesOrderAttachment extends InvenTreeAttachment { String get REF_MODEL_TYPE => "salesorder"; @override - String get URL => InvenTreeAPI().supportsModernAttachments ? "attachment/" : "order/so/attachment/"; - + String get URL => InvenTreeAPI().supportsModernAttachments + ? "attachment/" + : "order/so/attachment/"; } diff --git a/lib/inventree/sentry.dart b/lib/inventree/sentry.dart index a052ed57..132974a1 100644 --- a/lib/inventree/sentry.dart +++ b/lib/inventree/sentry.dart @@ -11,7 +11,6 @@ import "package:inventree/dsn.dart"; import "package:inventree/preferences.dart"; Future> getDeviceInfo() async { - // Extract device information final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); @@ -31,7 +30,6 @@ Future> getDeviceInfo() async { "identifierForVendor": iosDeviceInfo.identifierForVendor, "isPhysicalDevice": iosDeviceInfo.isPhysicalDevice, }; - } else if (Platform.isAndroid) { final androidDeviceInfo = await deviceInfo.androidInfo; @@ -57,13 +55,11 @@ Future> getDeviceInfo() async { return device_info; } - Map getServerInfo() => { "version": InvenTreeAPI().serverVersion, "apiVersion": InvenTreeAPI().apiVersion, }; - Future> getAppInfo() async { // Add app info final package_info = await PackageInfo.fromPlatform(); @@ -76,7 +72,6 @@ Future> getAppInfo() async { }; } - bool isInDebugMode() { bool inDebugMode = false; @@ -85,8 +80,10 @@ bool isInDebugMode() { return inDebugMode; } -Future sentryReportMessage(String message, {Map? context}) async { - +Future sentryReportMessage( + String message, { + Map? context, +}) async { if (SENTRY_DSN_KEY.isEmpty) { return false; } @@ -106,23 +103,22 @@ Future sentryReportMessage(String message, {Map? context}) // We don't care about the server address, only the path and query parameters! // Overwrite the provided URL context["url"] = uri.path + "?" + uri.query; - } catch (error) { // Ignore if any errors are thrown here } - } } print("Sending user message to Sentry: ${message}, ${context}"); if (isInDebugMode()) { - print("----- In dev mode. Not sending message to Sentry.io -----"); return true; } - final upload = await InvenTreeSettingsManager().getValue(INV_REPORT_ERRORS, true) as bool; + final upload = + await InvenTreeSettingsManager().getValue(INV_REPORT_ERRORS, true) + as bool; if (!upload) { print("----- Error reporting disabled -----"); @@ -152,12 +148,15 @@ Future sentryReportMessage(String message, {Map? context}) } } - /* * Report an error message to sentry.io */ -Future sentryReportError(String source, dynamic error, StackTrace? stackTrace, {Map context = const {}}) async { - +Future sentryReportError( + String source, + dynamic error, + StackTrace? stackTrace, { + Map context = const {}, +}) async { if (sentryIgnoreError(error)) { // No action on this error return; @@ -170,7 +169,6 @@ Future sentryReportError(String source, dynamic error, StackTrace? stackTr // check if you are running in dev mode using an assertion and omit sending // the report. if (isInDebugMode()) { - print("----- In dev mode. Not sending report to Sentry.io -----"); return; } @@ -179,7 +177,9 @@ Future sentryReportError(String source, dynamic error, StackTrace? stackTr return; } - final upload = await InvenTreeSettingsManager().getValue(INV_REPORT_ERRORS, true) as bool; + final upload = + await InvenTreeSettingsManager().getValue(INV_REPORT_ERRORS, true) + as bool; if (!upload) { print("----- Error reporting disabled -----"); @@ -188,11 +188,12 @@ Future sentryReportError(String source, dynamic error, StackTrace? stackTr // Some errors are outside our control, and we do not want to "pollute" the uploaded data if (source == "FlutterError.onError") { - String errorString = error.toString(); // Missing media file - if (errorString.contains("HttpException") && errorString.contains("404") && errorString.contains("/media/")) { + if (errorString.contains("HttpException") && + errorString.contains("404") && + errorString.contains("/media/")) { return; } @@ -225,26 +226,28 @@ Future sentryReportError(String source, dynamic error, StackTrace? stackTr scope.setContexts("context", context); }); - Sentry.captureException(error, stackTrace: stackTrace).catchError((error) { - print("Error uploading information to Sentry.io:"); - print(error); - return SentryId.empty(); - }).then((response) { - print("Uploaded information to Sentry.io : ${response.toString()}"); - }); + Sentry.captureException(error, stackTrace: stackTrace) + .catchError((error) { + print("Error uploading information to Sentry.io:"); + print(error); + return SentryId.empty(); + }) + .then((response) { + print("Uploaded information to Sentry.io : ${response.toString()}"); + }); } - /* * Test if a certain error should be ignored by Sentry */ bool sentryIgnoreError(dynamic error) { // Ignore 404 errors for media files if (error is HttpException) { - if (error.uri.toString().contains("/media/") && error.message.contains("404")) { + if (error.uri.toString().contains("/media/") && + error.message.contains("404")) { return true; } } return false; -} \ No newline at end of file +} diff --git a/lib/inventree/status_codes.dart b/lib/inventree/status_codes.dart index 9b75280d..0058fc8a 100644 --- a/lib/inventree/status_codes.dart +++ b/lib/inventree/status_codes.dart @@ -11,12 +11,10 @@ import "package:inventree/api.dart"; import "package:inventree/app_colors.dart"; import "package:inventree/helpers.dart"; - /* * Base class definition for a "status code" definition. */ class InvenTreeStatusCode { - InvenTreeStatusCode(this.URL); final String URL; @@ -34,10 +32,7 @@ class InvenTreeStatusCode { dynamic _entry = data[key]; if (_entry is Map) { - _choices.add({ - "value": _entry["key"], - "display_name": _entry["label"] - }); + _choices.add({"value": _entry["key"], "display_name": _entry["label"]}); } } @@ -46,7 +41,6 @@ class InvenTreeStatusCode { // Load status code information from the server Future load({bool forceReload = false}) async { - // Return internally cached data if (data.isNotEmpty && !forceReload) { return; diff --git a/lib/inventree/stock.dart b/lib/inventree/stock.dart index 96b2a2fa..1a45e73e 100644 --- a/lib/inventree/stock.dart +++ b/lib/inventree/stock.dart @@ -10,16 +10,14 @@ import "package:inventree/inventree/model.dart"; import "package:inventree/widget/stock/location_display.dart"; import "package:inventree/widget/stock/stock_detail.dart"; - - /* * Class representing a test result for a single stock item */ class InvenTreeStockItemTestResult extends InvenTreeModel { - InvenTreeStockItemTestResult() : super(); - InvenTreeStockItemTestResult.fromJson(Map json) : super.fromJson(json); + InvenTreeStockItemTestResult.fromJson(Map json) + : super.fromJson(json); @override String get URL => "stock/test/"; @@ -29,22 +27,16 @@ class InvenTreeStockItemTestResult extends InvenTreeModel { @override Map defaultFilters() { - return { - "user_detail": "true", - "template_detail": "true", - }; + return {"user_detail": "true", "template_detail": "true"}; } @override Map> formFields() { - Map> fields = { "stock_item": {"hidden": true}, "test": {}, "template": { - "filters": { - "enabled": "true", - } + "filters": {"enabled": "true"}, }, "result": {}, "value": {}, @@ -68,44 +60,39 @@ class InvenTreeStockItemTestResult extends InvenTreeModel { String get testName => getString("test"); bool get result => getBool("result"); - + String get value => getString("value"); - + String get attachment => getString("attachment"); String get username => getString("username", subKey: "user_detail"); String get date => getString("date"); - + @override InvenTreeStockItemTestResult createFromJson(Map json) { var result = InvenTreeStockItemTestResult.fromJson(json); return result; } - } - class InvenTreeStockItemHistory extends InvenTreeModel { - InvenTreeStockItemHistory() : super(); - InvenTreeStockItemHistory.fromJson(Map json) : super.fromJson(json); + InvenTreeStockItemHistory.fromJson(Map json) + : super.fromJson(json); @override - InvenTreeModel createFromJson(Map json) => InvenTreeStockItemHistory.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreeStockItemHistory.fromJson(json); @override String get URL => "stock/track/"; @override Map defaultFilters() { - // By default, order by decreasing date - return { - "ordering": "-date", - "user_detail": "true", - }; + return {"ordering": "-date", "user_detail": "true"}; } DateTime? get date => getDate("date"); @@ -113,7 +100,7 @@ class InvenTreeStockItemHistory extends InvenTreeModel { String get dateString => getDateString("date"); String get label => getString("label"); - + // Return the "deltas" associated with this historical object Map get deltas => getMap("deltas"); @@ -133,7 +120,6 @@ class InvenTreeStockItemHistory extends InvenTreeModel { int? get user => getValue("user") as int?; String get userString { - if (user != null) { return getString("username", subKey: "user_detail"); } else { @@ -142,12 +128,10 @@ class InvenTreeStockItemHistory extends InvenTreeModel { } } - /* * Class representing a StockItem database instance */ class InvenTreeStockItem extends InvenTreeModel { - InvenTreeStockItem() : super(); InvenTreeStockItem.fromJson(Map json) : super.fromJson(json); @@ -164,39 +148,18 @@ class InvenTreeStockItem extends InvenTreeModel { Future goToDetailPage(BuildContext context) async { return Navigator.push( context, - MaterialPageRoute( - builder: (context) => StockDetailWidget(this) - ) + MaterialPageRoute(builder: (context) => StockDetailWidget(this)), ); } // Return a set of fields to transfer this stock item via dialog Map transferFields() { Map fields = { - "pk": { - "parent": "items", - "nested": true, - "hidden": true, - "value": pk, - }, - "quantity": { - "parent": "items", - "nested": true, - "value": quantity, - }, - "location": { - "value": locationId, - }, - "status": { - "parent": "items", - "nested": true, - "value": status, - }, - "packaging": { - "parent": "items", - "nested": true, - "value": packaging, - }, + "pk": {"parent": "items", "nested": true, "hidden": true, "value": pk}, + "quantity": {"parent": "items", "nested": true, "value": quantity}, + "location": {"value": locationId}, + "status": {"parent": "items", "nested": true, "value": status}, + "packaging": {"parent": "items", "nested": true, "value": packaging}, "notes": {}, }; @@ -233,10 +196,7 @@ class InvenTreeStockItem extends InvenTreeModel { "location": {}, "quantity": {}, "serial": {}, - "serial_numbers": { - "label": L10().serialNumbers, - "type": "string", - }, + "serial_numbers": {"label": L10().serialNumbers, "type": "string"}, "status": {}, "batch": {}, "purchase_price": {}, @@ -250,13 +210,12 @@ class InvenTreeStockItem extends InvenTreeModel { @override Map defaultFilters() { - return { "part_detail": "true", "location_detail": "true", "supplier_detail": "true", "supplier_part_detail": "true", - "cascade": "false" + "cascade": "false", }; } @@ -265,21 +224,18 @@ class InvenTreeStockItem extends InvenTreeModel { int get testTemplateCount => testTemplates.length; // Get all the test templates associated with this StockItem - Future getTestTemplates({bool showDialog=false}) async { - await InvenTreePartTestTemplate().list( - filters: { - "part": "${partId}", - "enabled": "true", - }, - ).then((var templates) { - testTemplates.clear(); + Future getTestTemplates({bool showDialog = false}) async { + await InvenTreePartTestTemplate() + .list(filters: {"part": "${partId}", "enabled": "true"}) + .then((var templates) { + testTemplates.clear(); - for (var t in templates) { - if (t is InvenTreePartTestTemplate) { - testTemplates.add(t); - } - } - }); + for (var t in templates) { + if (t is InvenTreePartTestTemplate) { + testTemplates.add(t); + } + } + }); } List testResults = []; @@ -287,21 +243,17 @@ class InvenTreeStockItem extends InvenTreeModel { int get testResultCount => testResults.length; Future getTestResults() async { + await InvenTreeStockItemTestResult() + .list(filters: {"stock_item": "${pk}", "user_detail": "true"}) + .then((var results) { + testResults.clear(); - await InvenTreeStockItemTestResult().list( - filters: { - "stock_item": "${pk}", - "user_detail": "true", - }, - ).then((var results) { - testResults.clear(); - - for (var r in results) { - if (r is InvenTreeStockItemTestResult) { - testResults.add(r); - } - } - }); + for (var r in results) { + if (r is InvenTreeStockItemTestResult) { + testResults.add(r); + } + } + }); } int get status => getInt("status"); @@ -313,7 +265,7 @@ class InvenTreeStockItem extends InvenTreeModel { String get batch => getString("batch"); int get partId => getInt("part"); - + double? get purchasePrice { String pp = getString("purchase_price"); @@ -334,7 +286,7 @@ class InvenTreeStockItem extends InvenTreeModel { int get purchaseOrderId => getInt("purchase_order"); int get trackingItemCount => getInt("tracking_items", backup: 0); - + bool get isBuilding => getBool("is_building"); int get salesOrderId => getInt("sales_order"); @@ -362,274 +314,275 @@ class InvenTreeStockItem extends InvenTreeModel { String get stocktakeDateString => getDateString("stocktake_date"); - String get partName { + String get partName { + String nm = ""; - String nm = ""; - - // Use the detailed part information as priority - if (jsondata.containsKey("part_detail")) { - nm = (jsondata["part_detail"]?["full_name"] ?? "") as String; - } - - // Backup if first value fails - if (nm.isEmpty) { - nm = getString("part__name"); - } - - return nm; + // Use the detailed part information as priority + if (jsondata.containsKey("part_detail")) { + nm = (jsondata["part_detail"]?["full_name"] ?? "") as String; } - String get partDescription { - String desc = ""; - - // Use the detailed part description as priority - if (jsondata.containsKey("part_detail")) { - desc = (jsondata["part_detail"]?["description"] ?? "") as String; - } - - if (desc.isEmpty) { - desc = getString("part__description"); - } - - return desc; + // Backup if first value fails + if (nm.isEmpty) { + nm = getString("part__name"); } - String get partImage { - String img = ""; + return nm; + } - if (jsondata.containsKey("part_detail")) { - img = (jsondata["part_detail"]?["thumbnail"] ?? "") as String; - } + String get partDescription { + String desc = ""; - if (img.isEmpty) { - img = getString("part__thumbnail"); - } - - return img; + // Use the detailed part description as priority + if (jsondata.containsKey("part_detail")) { + desc = (jsondata["part_detail"]?["description"] ?? "") as String; } - /* + if (desc.isEmpty) { + desc = getString("part__description"); + } + + return desc; + } + + String get partImage { + String img = ""; + + if (jsondata.containsKey("part_detail")) { + img = (jsondata["part_detail"]?["thumbnail"] ?? "") as String; + } + + if (img.isEmpty) { + img = getString("part__thumbnail"); + } + + return img; + } + + /* * Return the Part thumbnail for this stock item. */ - String get partThumbnail { + String get partThumbnail { + String thumb = ""; - String thumb = ""; + thumb = (jsondata["part_detail"]?["thumbnail"] ?? "") as String; - thumb = (jsondata["part_detail"]?["thumbnail"] ?? "") as String; - - // Use "image" as a backup - if (thumb.isEmpty) { - thumb = (jsondata["part_detail"]?["image"] ?? "") as String; - } - - // Try a different approach - if (thumb.isEmpty) { - thumb = getString("part__thumbnail"); - } - - // Still no thumbnail? Use the "no image" image - if (thumb.isEmpty) thumb = InvenTreeAPI.staticThumb; - - return thumb; + // Use "image" as a backup + if (thumb.isEmpty) { + thumb = (jsondata["part_detail"]?["image"] ?? "") as String; } - int get supplierPartId => getInt("supplier_part"); - - String get supplierImage { - String thumb = ""; - - if (jsondata.containsKey("supplier_part_detail")) { - thumb = (jsondata["supplier_part_detail"]?["supplier_detail"]?["image"] ?? "") as String; - } else if (jsondata.containsKey("supplier_detail")) { - thumb = (jsondata["supplier_detail"]?["image"] ?? "") as String; - } - - return thumb; + // Try a different approach + if (thumb.isEmpty) { + thumb = getString("part__thumbnail"); } - String get supplierName => getString("supplier_name", subKey: "supplier_detail"); + // Still no thumbnail? Use the "no image" image + if (thumb.isEmpty) thumb = InvenTreeAPI.staticThumb; - String get units => getString("units", subKey: "part_detail"); + return thumb; + } - String get supplierSKU => getString("SKU", subKey: "supplier_part_detail"); + int get supplierPartId => getInt("supplier_part"); - String get serialNumber => getString("serial"); + String get supplierImage { + String thumb = ""; - double get quantity => getDouble("quantity"); + if (jsondata.containsKey("supplier_part_detail")) { + thumb = + (jsondata["supplier_part_detail"]?["supplier_detail"]?["image"] ?? "") + as String; + } else if (jsondata.containsKey("supplier_detail")) { + thumb = (jsondata["supplier_detail"]?["image"] ?? "") as String; + } - String quantityString({bool includeUnits = true}){ + return thumb; + } - String q = ""; + String get supplierName => + getString("supplier_name", subKey: "supplier_detail"); - if (allocated > 0) { - q += simpleNumberString(available); - q += " / "; - } + String get units => getString("units", subKey: "part_detail"); - q += simpleNumberString(quantity); + String get supplierSKU => getString("SKU", subKey: "supplier_part_detail"); - if (includeUnits && units.isNotEmpty) { + String get serialNumber => getString("serial"); + + double get quantity => getDouble("quantity"); + + String quantityString({bool includeUnits = true}) { + String q = ""; + + if (allocated > 0) { + q += simpleNumberString(available); + q += " / "; + } + + q += simpleNumberString(quantity); + + if (includeUnits && units.isNotEmpty) { + q += " ${units}"; + } + + return q; + } + + double get allocated => getDouble("allocated"); + + double get available => quantity - allocated; + + int get locationId => getInt("location"); + + bool isSerialized() => serialNumber.isNotEmpty && quantity.toInt() == 1; + + String serialOrQuantityDisplay() { + if (isSerialized()) { + return "SN ${serialNumber}"; + } else if (allocated > 0) { + return "${available} / ${quantity}"; + } else { + return simpleNumberString(quantity); + } + } + + String get locationName { + if (locationId == -1 || !jsondata.containsKey("location_detail")) { + return "Unknown Location"; + } + + String loc = getString("name", subKey: "location_detail"); + + // Old-style name + if (loc.isEmpty) { + loc = getString("location__name"); + } + + return loc; + } + + String get locationPathString { + if (locationId == -1 || !jsondata.containsKey("location_detail")) { + return L10().locationNotSet; + } + + String _loc = getString("pathstring", subKey: "location_detail"); + if (_loc.isNotEmpty) { + return _loc; + } else { + return locationName; + } + } + + String get displayQuantity { + // Display either quantity or serial number! + + if (serialNumber.isNotEmpty) { + return "SN: $serialNumber"; + } else { + String q = simpleNumberString(quantity); + + if (units.isNotEmpty) { q += " ${units}"; } return q; } + } - double get allocated => getDouble("allocated"); + @override + InvenTreeModel createFromJson(Map json) => + InvenTreeStockItem.fromJson(json); - double get available => quantity - allocated; - - int get locationId => getInt("location"); - - bool isSerialized() => serialNumber.isNotEmpty && quantity.toInt() == 1; - - String serialOrQuantityDisplay() { - if (isSerialized()) { - return "SN ${serialNumber}"; - } else if (allocated > 0) { - return "${available} / ${quantity}"; - } else { - return simpleNumberString(quantity); - } - } - - String get locationName { - - if (locationId == -1 || !jsondata.containsKey("location_detail")) return "Unknown Location"; - - String loc = getString("name", subKey: "location_detail"); - - // Old-style name - if (loc.isEmpty) { - loc = getString("location__name"); - } - - return loc; - } - - String get locationPathString { - - if (locationId == -1 || !jsondata.containsKey("location_detail")) return L10().locationNotSet; - - String _loc = getString("pathstring", subKey: "location_detail"); - if (_loc.isNotEmpty) { - return _loc; - } else { - return locationName; - } - } - - String get displayQuantity { - // Display either quantity or serial number! - - if (serialNumber.isNotEmpty) { - return "SN: $serialNumber"; - } else { - String q = simpleNumberString(quantity); - - if (units.isNotEmpty) { - q += " ${units}"; - } - - return q; - } - } - - @override - InvenTreeModel createFromJson(Map json) => InvenTreeStockItem.fromJson(json); - - /* + /* * Perform stocktake action: * * - Add * - Remove * - Count */ - Future adjustStock(String endpoint, double q, {String? notes, int? location}) async { - - // Serialized stock cannot be adjusted (unless it is a "transfer") - if (isSerialized() && location == null) { - return false; - } - - // Cannot handle negative stock - if (q < 0) { - return false; - } - - Map data = {}; - - data = { - "items": [ - { - "pk": "${pk}", - "quantity": "${quantity}", - } - ], - "notes": notes ?? "", - }; - - if (location != null) { - data["location"] = location; - } - - var response = await api.post( - endpoint, - body: data, - ); - - return response.isValid() && (response.statusCode == 200 || response.statusCode == 201); + Future adjustStock( + String endpoint, + double q, { + String? notes, + int? location, + }) async { + // Serialized stock cannot be adjusted (unless it is a "transfer") + if (isSerialized() && location == null) { + return false; } - Future countStock(double q, {String? notes}) async { - - final bool result = await adjustStock("/stock/count/", q, notes: notes); - - return result; + // Cannot handle negative stock + if (q < 0) { + return false; } - Future addStock(double q, {String? notes}) async { + Map data = {}; - final bool result = await adjustStock("/stock/add/", q, notes: notes); + data = { + "items": [ + {"pk": "${pk}", "quantity": "${quantity}"}, + ], + "notes": notes ?? "", + }; - return result; + if (location != null) { + data["location"] = location; } - Future removeStock(double q, {String? notes}) async { + var response = await api.post(endpoint, body: data); - final bool result = await adjustStock("/stock/remove/", q, notes: notes); - - return result; - } - - Future transferStock(int location, {double? quantity, String? notes}) async { - - double q = this.quantity; - - if (quantity != null) { - q = quantity; - } - - final bool result = await adjustStock( - "/stock/transfer/", - q, - notes: notes, - location: location, - ); - - return result; - } + return response.isValid() && + (response.statusCode == 200 || response.statusCode == 201); } + Future countStock(double q, {String? notes}) async { + final bool result = await adjustStock("/stock/count/", q, notes: notes); + + return result; + } + + Future addStock(double q, {String? notes}) async { + final bool result = await adjustStock("/stock/add/", q, notes: notes); + + return result; + } + + Future removeStock(double q, {String? notes}) async { + final bool result = await adjustStock("/stock/remove/", q, notes: notes); + + return result; + } + + Future transferStock( + int location, { + double? quantity, + String? notes, + }) async { + double q = this.quantity; + + if (quantity != null) { + q = quantity; + } + + final bool result = await adjustStock( + "/stock/transfer/", + q, + notes: notes, + location: location, + ); + + return result; + } +} /* * Class representing an attachment file against a StockItem object */ class InvenTreeStockItemAttachment extends InvenTreeAttachment { - InvenTreeStockItemAttachment() : super(); - InvenTreeStockItemAttachment.fromJson(Map json) : super.fromJson(json); + InvenTreeStockItemAttachment.fromJson(Map json) + : super.fromJson(json); @override String get REFERENCE_FIELD => "stock_item"; @@ -638,18 +591,20 @@ class InvenTreeStockItemAttachment extends InvenTreeAttachment { String get REF_MODEL_TYPE => "stockitem"; @override - String get URL => InvenTreeAPI().supportsModernAttachments ? "attachment/" : "stock/attachment/"; + String get URL => InvenTreeAPI().supportsModernAttachments + ? "attachment/" + : "stock/attachment/"; @override - InvenTreeModel createFromJson(Map json) => InvenTreeStockItemAttachment.fromJson(json); + InvenTreeModel createFromJson(Map json) => + InvenTreeStockItemAttachment.fromJson(json); } - class InvenTreeStockLocation extends InvenTreeModel { - InvenTreeStockLocation() : super(); - InvenTreeStockLocation.fromJson(Map json) : super.fromJson(json); + InvenTreeStockLocation.fromJson(Map json) + : super.fromJson(json); @override String get URL => "stock/location/"; @@ -665,9 +620,7 @@ class InvenTreeStockLocation extends InvenTreeModel { Future goToDetailPage(BuildContext context) async { return Navigator.push( context, - MaterialPageRoute( - builder: (context) => LocationDisplayWidget(this) - ) + MaterialPageRoute(builder: (context) => LocationDisplayWidget(this)), ); } @@ -684,7 +637,6 @@ class InvenTreeStockLocation extends InvenTreeModel { } String get parentPathString { - List psplit = pathstring.split("/"); if (psplit.isNotEmpty) { @@ -703,6 +655,6 @@ class InvenTreeStockLocation extends InvenTreeModel { int get itemcount => (jsondata["items"] ?? 0) as int; @override - InvenTreeModel createFromJson(Map json) => InvenTreeStockLocation.fromJson(json); - + InvenTreeModel createFromJson(Map json) => + InvenTreeStockLocation.fromJson(json); } diff --git a/lib/l10.dart b/lib/l10.dart index 76a7558c..4b58a93d 100644 --- a/lib/l10.dart +++ b/lib/l10.dart @@ -7,8 +7,7 @@ import "package:flutter/material.dart"; import "package:inventree/helpers.dart"; // Shortcut function to reduce boilerplate! -I18N L10() -{ +I18N L10() { // Testing mode - ignore context if (!hasContext()) { return I18NEn(); @@ -26,4 +25,4 @@ I18N L10() // Fallback for "null" context return I18NEn(); -} \ No newline at end of file +} diff --git a/lib/l10n/collect_translations.py b/lib/l10n/collect_translations.py index a768632b..d47bd65a 100644 --- a/lib/l10n/collect_translations.py +++ b/lib/l10n/collect_translations.py @@ -90,6 +90,7 @@ def generate_locale_list(locales): output.write( "// This file is auto-generated by the 'collect_translations.py' script - do not edit it directly!\n\n" ) + output.write("// dart format off\n\n") output.write('import "package:flutter/material.dart";\n\n') output.write("const List supported_locales = [\n") diff --git a/lib/labels.dart b/lib/labels.dart index ca145e91..10e2c205 100644 --- a/lib/labels.dart +++ b/lib/labels.dart @@ -18,7 +18,6 @@ Future selectAndPrintLabel( String labelType, String labelQuery, ) async { - if (!InvenTreeAPI().isConnected()) { return; } @@ -44,10 +43,7 @@ Future selectAndPrintLabel( int pk = (label["pk"] ?? -1) as int; if (name.isNotEmpty && pk > 0) { - label_options.add({ - "display_name": name, - "value": pk, - }); + label_options.add({"display_name": name, "value": pk}); } } @@ -57,13 +53,12 @@ Future selectAndPrintLabel( // Construct list of available plugins for (var plugin in plugins) { - plugin_options.add({ - "display_name": plugin.humanName, - "value": plugin.key, - }); + plugin_options.add({"display_name": plugin.humanName, "value": plugin.key}); } - String selectedPlugin = await InvenTreeAPI().getUserSetting("LABEL_DEFAULT_PRINTER"); + String selectedPlugin = await InvenTreeAPI().getUserSetting( + "LABEL_DEFAULT_PRINTER", + ); if (selectedPlugin.isNotEmpty) { initial_plugin = selectedPlugin; @@ -85,7 +80,7 @@ Future selectAndPrintLabel( "value": initial_plugin, "choices": plugin_options, "required": true, - } + }, }; launchApiForm( @@ -99,18 +94,12 @@ Future selectAndPrintLabel( final plugin = data["plugin"]; if (template == null) { - showSnackIcon( - L10().labelSelectTemplate, - success: false, - ); + showSnackIcon(L10().labelSelectTemplate, success: false); return false; } if (plugin == null) { - showSnackIcon( - L10().labelSelectPrinter, - success: false, - ); + showSnackIcon(L10().labelSelectPrinter, success: false); return false; } @@ -123,42 +112,42 @@ Future selectAndPrintLabel( bool result = false; if (labelId != -1 && pluginKey != null) { - showLoadingOverlay(); if (InvenTreeAPI().supportsModernLabelPrinting) { - // Modern label printing API uses a POST request to a single API endpoint. - await InvenTreeAPI().post( - "/label/print/", - body: { - "plugin": pluginKey, - "template": labelId, - "items": [instanceId] - } - ).then((APIResponse response) { + await InvenTreeAPI() + .post( + "/label/print/", + body: { + "plugin": pluginKey, + "template": labelId, + "items": [instanceId], + }, + ) + .then((APIResponse response) { + if (response.isValid() && + response.statusCode >= 200 && + response.statusCode <= 201) { + var data = response.asMap(); - if (response.isValid() && response.statusCode >= 200 && - response.statusCode <= 201) { - var data = response.asMap(); + if (data.containsKey("output")) { + String? label_file = (data["output"]) as String?; - if (data.containsKey("output")) { - String? label_file = (data["output"]) as String?; + if (label_file != null && label_file.isNotEmpty) { + // Attempt to open generated file + InvenTreeAPI().downloadFile(label_file); + } - if (label_file != null && label_file.isNotEmpty) { - // Attempt to open generated file - InvenTreeAPI().downloadFile(label_file); + result = true; + } } - - result = true; - } - } - }); - + }); } else { // Legacy label printing API // Uses a GET request to a specially formed URL which depends on the parameters - String url = "/label/${labelType}/${labelId}/print/?${labelQuery}&plugin=${pluginKey}"; + String url = + "/label/${labelType}/${labelId}/print/?${labelQuery}&plugin=${pluginKey}"; await InvenTreeAPI().get(url).then((APIResponse response) { if (response.isValid() && response.statusCode == 200) { var data = response.asMap(); @@ -171,26 +160,20 @@ Future selectAndPrintLabel( } } }); - } + } - hideLoadingOverlay(); + hideLoadingOverlay(); - if (result) { - showSnackIcon( - L10().printLabelSuccess, - success: true - ); - } else { - showSnackIcon( - L10().printLabelFailure, - success: false, - ); + if (result) { + showSnackIcon(L10().printLabelSuccess, success: true); + } else { + showSnackIcon(L10().printLabelFailure, success: false); + } } - } - }); + }, + ); } - /* * Discover which label templates are available for a given item */ @@ -198,8 +181,8 @@ Future>> getLabelTemplates( String labelType, Map data, ) async { - - if (!InvenTreeAPI().isConnected() || !InvenTreeAPI().supportsMixin("labels")) { + if (!InvenTreeAPI().isConnected() || + !InvenTreeAPI().supportsMixin("labels")) { return []; } @@ -217,10 +200,7 @@ Future>> getLabelTemplates( List> labels = []; - await InvenTreeAPI().get( - url, - params: data, - ).then((APIResponse response) { + await InvenTreeAPI().get(url, params: data).then((APIResponse response) { if (response.isValid() && response.statusCode == 200) { for (var label in response.resultsList()) { if (label is Map) { @@ -231,4 +211,4 @@ Future>> getLabelTemplates( }); return labels; -} \ No newline at end of file +} diff --git a/lib/main.dart b/lib/main.dart index 033ef21a..22dd25bb 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -18,72 +18,73 @@ import "package:inventree/l10n/collected/app_localizations.dart"; import "package:inventree/settings/release.dart"; import "package:inventree/widget/home.dart"; - Future main() async { - WidgetsFlutterBinding.ensureInitialized(); final savedThemeMode = await AdaptiveTheme.getThemeMode(); - await runZonedGuarded>(() async { + await runZonedGuarded>( + () async { + PackageInfo info = await PackageInfo.fromPlatform(); + String pkg = info.packageName; + String version = info.version; + String build = info.buildNumber; - PackageInfo info = await PackageInfo.fromPlatform(); - String pkg = info.packageName; - String version = info.version; - String build = info.buildNumber; + String release = "${pkg}@${version}:${build}"; - String release = "${pkg}@${version}:${build}"; + if (SENTRY_DSN_KEY.isNotEmpty) { + await Sentry.init((options) { + options.dsn = SENTRY_DSN_KEY; + options.release = release; + options.environment = isInDebugMode() ? "debug" : "release"; + options.diagnosticLevel = SentryLevel.debug; + options.attachStacktrace = true; + }); + } - if (SENTRY_DSN_KEY.isNotEmpty) { - await Sentry.init((options) { - options.dsn = SENTRY_DSN_KEY; - options.release = release; - options.environment = isInDebugMode() ? "debug" : "release"; - options.diagnosticLevel = SentryLevel.debug; - options.attachStacktrace = true; + // Pass any flutter errors off to the Sentry reporting context! + FlutterError.onError = (FlutterErrorDetails details) async { + // Ensure that the error gets reported to sentry! + await sentryReportError( + "FlutterError.onError", + details.exception, + details.stack, + context: { + "context": details.context.toString(), + "summary": details.summary.toString(), + "library": details.library ?? "null", + }, + ); + }; + + final int orientation = + await InvenTreeSettingsManager().getValue( + INV_SCREEN_ORIENTATION, + SCREEN_ORIENTATION_SYSTEM, + ) + as int; + + List orientations = []; + + switch (orientation) { + case SCREEN_ORIENTATION_PORTRAIT: + orientations.add(DeviceOrientation.portraitUp); + case SCREEN_ORIENTATION_LANDSCAPE: + orientations.add(DeviceOrientation.landscapeLeft); + default: + orientations.add(DeviceOrientation.portraitUp); + orientations.add(DeviceOrientation.landscapeLeft); + orientations.add(DeviceOrientation.landscapeRight); + } + + SystemChrome.setPreferredOrientations(orientations).then((_) { + runApp(InvenTreeApp(savedThemeMode)); }); - } - - // Pass any flutter errors off to the Sentry reporting context! - FlutterError.onError = (FlutterErrorDetails details) async { - - // Ensure that the error gets reported to sentry! - await sentryReportError( - "FlutterError.onError", - details.exception, details.stack, - context: { - "context": details.context.toString(), - "summary": details.summary.toString(), - "library": details.library ?? "null", - } - ); - }; - - final int orientation = await InvenTreeSettingsManager().getValue(INV_SCREEN_ORIENTATION, SCREEN_ORIENTATION_SYSTEM) as int; - - List orientations = []; - - switch (orientation) { - case SCREEN_ORIENTATION_PORTRAIT: - orientations.add(DeviceOrientation.portraitUp); - case SCREEN_ORIENTATION_LANDSCAPE: - orientations.add(DeviceOrientation.landscapeLeft); - default: - orientations.add(DeviceOrientation.portraitUp); - orientations.add(DeviceOrientation.landscapeLeft); - orientations.add(DeviceOrientation.landscapeRight); - } - - SystemChrome.setPreferredOrientations(orientations).then((_) { - runApp( - InvenTreeApp(savedThemeMode) - ); - }); - - }, (Object error, StackTrace stackTrace) async { - sentryReportError("main.runZonedGuarded", error, stackTrace); - }); - + }, + (Object error, StackTrace stackTrace) async { + sentryReportError("main.runZonedGuarded", error, stackTrace); + }, + ); } class InvenTreeApp extends StatefulWidget { @@ -96,13 +97,11 @@ class InvenTreeApp extends StatefulWidget { @override InvenTreeAppState createState() => InvenTreeAppState(savedThemeMode); - static InvenTreeAppState? of(BuildContext context) => context.findAncestorStateOfType(); - + static InvenTreeAppState? of(BuildContext context) => + context.findAncestorStateOfType(); } - class InvenTreeAppState extends State { - InvenTreeAppState(this.savedThemeMode) : super(); // Custom _locale (default = null; use system default) @@ -120,16 +119,17 @@ class InvenTreeAppState extends State { // Run app init routines in the background Future runInitTasks() async { - // Set the app locale (language) Locale? locale = await InvenTreeSettingsManager().getSelectedLocale(); setLocale(locale); // Display release notes if this is a new version - final String version = await InvenTreeSettingsManager().getValue("recentVersion", "") as String; + final String version = + await InvenTreeSettingsManager().getValue("recentVersion", "") + as String; final PackageInfo info = await PackageInfo.fromPlatform(); - + if (version != info.version) { // Save latest version to the settings database await InvenTreeSettingsManager().setValue("recentVersion", info.version); @@ -139,7 +139,7 @@ class InvenTreeAppState extends State { // Show the release notes OneContext().push( - MaterialPageRoute(builder: (context) => ReleaseNotesWidget(notes)) + MaterialPageRoute(builder: (context) => ReleaseNotesWidget(notes)), ); } } @@ -155,7 +155,6 @@ class InvenTreeAppState extends State { @override Widget build(BuildContext context) { - return AdaptiveTheme( light: ThemeData( brightness: Brightness.light, @@ -168,7 +167,7 @@ class InvenTreeAppState extends State { useMaterial3: true, ), initial: savedThemeMode ?? AdaptiveThemeMode.light, - builder: (light, dark) => MaterialApp( + builder: (light, dark) => MaterialApp( theme: light, darkTheme: dark, debugShowCheckedModeBanner: false, @@ -185,7 +184,7 @@ class InvenTreeAppState extends State { ], supportedLocales: supported_locales, locale: _locale, - ) + ), ); } } diff --git a/lib/preferences.dart b/lib/preferences.dart index 0959ff5b..e10c3d93 100644 --- a/lib/preferences.dart +++ b/lib/preferences.dart @@ -6,7 +6,6 @@ import "package:path_provider/path_provider.dart"; import "package:sembast/sembast_io.dart"; import "package:path/path.dart"; - // Settings key values const String INV_HOME_SHOW_SUBSCRIBED = "homeShowSubscribed"; const String INV_HOME_SHOW_PO = "homeShowPo"; @@ -62,7 +61,6 @@ const int BARCODE_CONTROLLER_WEDGE = 1; * Class for storing InvenTree preferences in a NoSql DB */ class InvenTreePreferencesDB { - InvenTreePreferencesDB._(); static final InvenTreePreferencesDB _singleton = InvenTreePreferencesDB._(); @@ -74,7 +72,6 @@ class InvenTreePreferencesDB { bool isOpen = false; Future get database async { - if (!isOpen) { // Calling _openDatabase will also complete the completer with database instance _openDatabase(); @@ -101,13 +98,11 @@ class InvenTreePreferencesDB { } } - /* * InvenTree setings manager class. * Provides functions for loading and saving settings, with provision for default values */ class InvenTreeSettingsManager { - factory InvenTreeSettingsManager() { return _manager; } @@ -144,7 +139,6 @@ class InvenTreeSettingsManager { } Future getValue(String key, dynamic backup) async { - dynamic value = await store.record(key).get(await _db); // Retrieve value @@ -174,7 +168,6 @@ class InvenTreeSettingsManager { // Store a key:value pair in the database Future setValue(String key, dynamic value) async { - // Encode null values as strings value ??= "__null__"; @@ -182,5 +175,6 @@ class InvenTreeSettingsManager { } // Ensure we only ever create a single instance of this class - static final InvenTreeSettingsManager _manager = InvenTreeSettingsManager._internal(); + static final InvenTreeSettingsManager _manager = + InvenTreeSettingsManager._internal(); } diff --git a/lib/settings/about.dart b/lib/settings/about.dart index 27a5cd10..ce5ef695 100644 --- a/lib/settings/about.dart +++ b/lib/settings/about.dart @@ -13,34 +13,30 @@ import "package:url_launcher/url_launcher.dart"; const String DOCS_URL = "https://docs.inventree.org/app"; class InvenTreeAboutWidget extends StatelessWidget { - const InvenTreeAboutWidget(this.info) : super(); final PackageInfo info; - Future _releaseNotes(BuildContext context) async { - + Future _releaseNotes(BuildContext context) async { // Load release notes from external file String notes = await rootBundle.loadString("assets/release_notes.md"); Navigator.push( - context, - MaterialPageRoute(builder: (context) => ReleaseNotesWidget(notes)) + context, + MaterialPageRoute(builder: (context) => ReleaseNotesWidget(notes)), ); } - Future _credits(BuildContext context) async { - + Future _credits(BuildContext context) async { String notes = await rootBundle.loadString("assets/credits.md"); Navigator.push( context, - MaterialPageRoute(builder: (context) => CreditsWidget(notes)) + MaterialPageRoute(builder: (context) => CreditsWidget(notes)), ); } - Future _openDocs() async { - + Future _openDocs() async { var docsUrl = Uri.parse(DOCS_URL); if (await canLaunchUrl(docsUrl)) { @@ -48,23 +44,24 @@ class InvenTreeAboutWidget extends StatelessWidget { } } - Future _reportBug(BuildContext context) async { - + Future _reportBug(BuildContext context) async { var url = Uri( - scheme: "https", - host: "github.com", - path: "inventree/inventree-app/issues/new?title=Enter+bug+description"); + scheme: "https", + host: "github.com", + path: "inventree/inventree-app/issues/new?title=Enter+bug+description", + ); if (await canLaunchUrl(url)) { await launchUrl(url); } } - Future _translate() async { + Future _translate() async { var url = Uri( - scheme: "https", - host: "crowdin.com", - path: "/project/inventree"); + scheme: "https", + host: "crowdin.com", + path: "/project/inventree", + ); if (await canLaunchUrl(url)) { await launchUrl(url); @@ -73,7 +70,6 @@ class InvenTreeAboutWidget extends StatelessWidget { @override Widget build(BuildContext context) { - List tiles = []; tiles.add( @@ -82,41 +78,57 @@ class InvenTreeAboutWidget extends StatelessWidget { L10().serverDetails, style: TextStyle(fontWeight: FontWeight.bold), ), - ) + ), ); if (InvenTreeAPI().isConnected()) { tiles.add( - ListTile( - title: Text(L10().address), - subtitle: Text(InvenTreeAPI().baseUrl.isNotEmpty ? InvenTreeAPI().baseUrl : L10().notConnected), - leading: Icon(TablerIcons.globe), - trailing: InvenTreeAPI().isConnected() ? Icon(TablerIcons.circle_check, color: COLOR_SUCCESS) : Icon(TablerIcons.circle_x, color: COLOR_DANGER), - ) + ListTile( + title: Text(L10().address), + subtitle: Text( + InvenTreeAPI().baseUrl.isNotEmpty + ? InvenTreeAPI().baseUrl + : L10().notConnected, + ), + leading: Icon(TablerIcons.globe), + trailing: InvenTreeAPI().isConnected() + ? Icon(TablerIcons.circle_check, color: COLOR_SUCCESS) + : Icon(TablerIcons.circle_x, color: COLOR_DANGER), + ), ); tiles.add( ListTile( title: Text(L10().username), subtitle: Text(InvenTreeAPI().username), - leading: InvenTreeAPI().username.isNotEmpty ? Icon(TablerIcons.user) : Icon(TablerIcons.user_cancel, color: COLOR_DANGER), - ) + leading: InvenTreeAPI().username.isNotEmpty + ? Icon(TablerIcons.user) + : Icon(TablerIcons.user_cancel, color: COLOR_DANGER), + ), ); tiles.add( ListTile( title: Text(L10().version), - subtitle: Text(InvenTreeAPI().serverVersion.isNotEmpty ? InvenTreeAPI().serverVersion : L10().notConnected), + subtitle: Text( + InvenTreeAPI().serverVersion.isNotEmpty + ? InvenTreeAPI().serverVersion + : L10().notConnected, + ), leading: Icon(TablerIcons.info_circle), - ) + ), ); tiles.add( ListTile( title: Text(L10().serverInstance), - subtitle: Text(InvenTreeAPI().serverInstance.isNotEmpty ? InvenTreeAPI().serverInstance : L10().notConnected), + subtitle: Text( + InvenTreeAPI().serverInstance.isNotEmpty + ? InvenTreeAPI().serverInstance + : L10().notConnected, + ), leading: Icon(TablerIcons.server), - ) + ), ); // Display extra tile if the server supports plugins @@ -125,9 +137,8 @@ class InvenTreeAboutWidget extends StatelessWidget { title: Text(L10().pluginSupport), subtitle: Text(L10().pluginSupportDetail), leading: Icon(TablerIcons.plug), - ) + ), ); - } else { tiles.add( ListTile( @@ -136,8 +147,8 @@ class InvenTreeAboutWidget extends StatelessWidget { L10().serverNotConnected, style: TextStyle(fontStyle: FontStyle.italic), ), - leading: Icon(TablerIcons.exclamation_circle) - ) + leading: Icon(TablerIcons.exclamation_circle), + ), ); } @@ -147,23 +158,23 @@ class InvenTreeAboutWidget extends StatelessWidget { L10().appDetails, style: TextStyle(fontWeight: FontWeight.bold), ), - ) + ), ); tiles.add( ListTile( title: Text(L10().packageName), subtitle: Text("${info.packageName}"), - leading: Icon(TablerIcons.box) - ) + leading: Icon(TablerIcons.box), + ), ); tiles.add( ListTile( title: Text(L10().version), subtitle: Text("${info.version} - Build ${info.buildNumber}"), - leading: Icon(TablerIcons.info_circle) - ) + leading: Icon(TablerIcons.info_circle), + ), ); tiles.add( @@ -174,7 +185,7 @@ class InvenTreeAboutWidget extends StatelessWidget { onTap: () { _releaseNotes(context); }, - ) + ), ); tiles.add( @@ -184,8 +195,8 @@ class InvenTreeAboutWidget extends StatelessWidget { leading: Icon(TablerIcons.balloon, color: COLOR_ACTION), onTap: () { _credits(context); - } - ) + }, + ), ); tiles.add( @@ -196,7 +207,7 @@ class InvenTreeAboutWidget extends StatelessWidget { onTap: () { _openDocs(); }, - ) + ), ); tiles.add( @@ -206,8 +217,8 @@ class InvenTreeAboutWidget extends StatelessWidget { leading: Icon(TablerIcons.language, color: COLOR_ACTION), onTap: () { _translate(); - } - ) + }, + ), ); tiles.add( @@ -216,9 +227,9 @@ class InvenTreeAboutWidget extends StatelessWidget { subtitle: Text(L10().reportBugDescription), leading: Icon(TablerIcons.bug, color: COLOR_ACTION), onTap: () { - _reportBug(context); + _reportBug(context); }, - ) + ), ); return Scaffold( @@ -227,11 +238,8 @@ class InvenTreeAboutWidget extends StatelessWidget { backgroundColor: COLOR_APP_BAR, ), body: ListView( - children: ListTile.divideTiles( - context: context, - tiles: tiles, - ).toList(), - ) + children: ListTile.divideTiles(context: context, tiles: tiles).toList(), + ), ); } -} \ No newline at end of file +} diff --git a/lib/settings/app_settings.dart b/lib/settings/app_settings.dart index 2443e812..ba2d7cb4 100644 --- a/lib/settings/app_settings.dart +++ b/lib/settings/app_settings.dart @@ -16,17 +16,16 @@ import "package:inventree/preferences.dart"; import "package:inventree/widget/dialogs.dart"; import "package:inventree/widget/progress.dart"; - class InvenTreeAppSettingsWidget extends StatefulWidget { @override _InvenTreeAppSettingsState createState() => _InvenTreeAppSettingsState(); } class _InvenTreeAppSettingsState extends State { - _InvenTreeAppSettingsState(); - final GlobalKey<_InvenTreeAppSettingsState> _settingsKey = GlobalKey<_InvenTreeAppSettingsState>(); + final GlobalKey<_InvenTreeAppSettingsState> _settingsKey = + GlobalKey<_InvenTreeAppSettingsState>(); // Sound settings bool barcodeSounds = true; @@ -48,16 +47,33 @@ class _InvenTreeAppSettingsState extends State { loadSettings(OneContext().context!); } - Future loadSettings(BuildContext context) async { - + Future loadSettings(BuildContext context) async { showLoadingOverlay(); - barcodeSounds = await InvenTreeSettingsManager().getValue(INV_SOUNDS_BARCODE, true) as bool; - serverSounds = await InvenTreeSettingsManager().getValue(INV_SOUNDS_SERVER, true) as bool; - reportErrors = await InvenTreeSettingsManager().getValue(INV_REPORT_ERRORS, true) as bool; - strictHttps = await InvenTreeSettingsManager().getValue(INV_STRICT_HTTPS, false) as bool; - screenOrientation = await InvenTreeSettingsManager().getValue(INV_SCREEN_ORIENTATION, SCREEN_ORIENTATION_SYSTEM) as int; - enableLabelPrinting = await InvenTreeSettingsManager().getValue(INV_ENABLE_LABEL_PRINTING, true) as bool; + barcodeSounds = + await InvenTreeSettingsManager().getValue(INV_SOUNDS_BARCODE, true) + as bool; + serverSounds = + await InvenTreeSettingsManager().getValue(INV_SOUNDS_SERVER, true) + as bool; + reportErrors = + await InvenTreeSettingsManager().getValue(INV_REPORT_ERRORS, true) + as bool; + strictHttps = + await InvenTreeSettingsManager().getValue(INV_STRICT_HTTPS, false) + as bool; + screenOrientation = + await InvenTreeSettingsManager().getValue( + INV_SCREEN_ORIENTATION, + SCREEN_ORIENTATION_SYSTEM, + ) + as int; + enableLabelPrinting = + await InvenTreeSettingsManager().getValue( + INV_ENABLE_LABEL_PRINTING, + true, + ) + as bool; darkMode = AdaptiveTheme.of(context).mode.isDark; @@ -71,19 +87,15 @@ class _InvenTreeAppSettingsState extends State { } Future _selectLocale(BuildContext context) async { - List> options = [ - { - "display_name": L10().languageDefault, - "value": null, - } + {"display_name": L10().languageDefault, "value": null}, ]; // Construct a list of available locales for (var locale in supported_locales) { options.add({ "display_name": LocaleNames.of(context)!.nameOf(locale.toString()), - "value": locale.toString() + "value": locale.toString(), }); } @@ -93,7 +105,7 @@ class _InvenTreeAppSettingsState extends State { "type": "choice", "choices": options, "value": locale?.toString(), - } + }, }; launchApiForm( @@ -103,7 +115,6 @@ class _InvenTreeAppSettingsState extends State { fields, icon: TablerIcons.circle_check, onSuccess: (Map data) async { - String locale_name = (data["locale"] ?? "") as String; Locale? selected_locale; @@ -124,18 +135,18 @@ class _InvenTreeAppSettingsState extends State { // Clear the cached status label information InvenTreeAPI().clearStatusCodeData(); - } + }, ); } - @override Widget build(BuildContext context) { - String languageName = L10().languageDefault; if (locale != null) { - languageName = LocaleNames.of(context)!.nameOf(locale.toString()) ?? L10().languageDefault; + languageName = + LocaleNames.of(context)!.nameOf(locale.toString()) ?? + L10().languageDefault; } IconData orientationIcon = Icons.screen_rotation; @@ -154,7 +165,7 @@ class _InvenTreeAppSettingsState extends State { key: _settingsKey, appBar: AppBar( title: Text(L10().appSettings), - backgroundColor: COLOR_APP_BAR + backgroundColor: COLOR_APP_BAR, ), body: Container( child: ListView( @@ -183,8 +194,8 @@ class _InvenTreeAppSettingsState extends State { setState(() { darkMode = value; }); - } - ) + }, + ), ), GestureDetector( child: ListTile( @@ -198,26 +209,43 @@ class _InvenTreeAppSettingsState extends State { L10().orientation, [ ListTile( - leading: Icon(Icons.screen_rotation, color: screenOrientation == SCREEN_ORIENTATION_SYSTEM ? COLOR_ACTION : null), + leading: Icon( + Icons.screen_rotation, + color: screenOrientation == SCREEN_ORIENTATION_SYSTEM + ? COLOR_ACTION + : null, + ), title: Text(L10().orientationSystem), ), ListTile( - leading: Icon(Icons.screen_lock_portrait, color: screenOrientation == SCREEN_ORIENTATION_PORTRAIT ? COLOR_ACTION : null), + leading: Icon( + Icons.screen_lock_portrait, + color: screenOrientation == SCREEN_ORIENTATION_PORTRAIT + ? COLOR_ACTION + : null, + ), title: Text(L10().orientationPortrait), ), ListTile( - leading: Icon(Icons.screen_lock_landscape, color: screenOrientation == SCREEN_ORIENTATION_LANDSCAPE ? COLOR_ACTION : null), + leading: Icon( + Icons.screen_lock_landscape, + color: screenOrientation == SCREEN_ORIENTATION_LANDSCAPE + ? COLOR_ACTION + : null, + ), title: Text(L10().orientationLandscape), - ) + ), ], onSelected: (idx) async { screenOrientation = idx as int; - InvenTreeSettingsManager().setValue(INV_SCREEN_ORIENTATION, screenOrientation); + InvenTreeSettingsManager().setValue( + INV_SCREEN_ORIENTATION, + screenOrientation, + ); - setState(() { - }); - } + setState(() {}); + }, ); }, ), @@ -228,11 +256,14 @@ class _InvenTreeAppSettingsState extends State { trailing: Switch( value: enableLabelPrinting, onChanged: (bool value) { - InvenTreeSettingsManager().setValue(INV_ENABLE_LABEL_PRINTING, value); + InvenTreeSettingsManager().setValue( + INV_ENABLE_LABEL_PRINTING, + value, + ); setState(() { enableLabelPrinting = value; }); - } + }, ), ), ListTile( @@ -271,7 +302,7 @@ class _InvenTreeAppSettingsState extends State { }, ), ), - ListTile( + ListTile( title: Text( L10().sounds, style: TextStyle(fontWeight: FontWeight.bold), @@ -300,7 +331,10 @@ class _InvenTreeAppSettingsState extends State { trailing: Switch( value: barcodeSounds, onChanged: (bool value) { - InvenTreeSettingsManager().setValue(INV_SOUNDS_BARCODE, value); + InvenTreeSettingsManager().setValue( + INV_SOUNDS_BARCODE, + value, + ); setState(() { barcodeSounds = value; }); @@ -308,9 +342,9 @@ class _InvenTreeAppSettingsState extends State { ), ), Divider(height: 1), - ] - ) - ) + ], + ), + ), ); } -} \ No newline at end of file +} diff --git a/lib/settings/barcode_settings.dart b/lib/settings/barcode_settings.dart index f3776cd2..0d709703 100644 --- a/lib/settings/barcode_settings.dart +++ b/lib/settings/barcode_settings.dart @@ -7,44 +7,52 @@ import "package:inventree/app_colors.dart"; import "package:inventree/widget/dialogs.dart"; - class InvenTreeBarcodeSettingsWidget extends StatefulWidget { @override - _InvenTreeBarcodeSettingsState createState() => _InvenTreeBarcodeSettingsState(); + _InvenTreeBarcodeSettingsState createState() => + _InvenTreeBarcodeSettingsState(); } +class _InvenTreeBarcodeSettingsState + extends State { + _InvenTreeBarcodeSettingsState(); -class _InvenTreeBarcodeSettingsState extends State { + int barcodeScanDelay = 500; + int barcodeScanType = BARCODE_CONTROLLER_CAMERA; + bool barcodeScanSingle = false; - _InvenTreeBarcodeSettingsState(); + final TextEditingController _barcodeScanDelayController = + TextEditingController(); - int barcodeScanDelay = 500; - int barcodeScanType = BARCODE_CONTROLLER_CAMERA; - bool barcodeScanSingle = false; - - final TextEditingController _barcodeScanDelayController = TextEditingController(); - - @override - void initState() { - super.initState(); - loadSettings(); - } + @override + void initState() { + super.initState(); + loadSettings(); + } Future loadSettings() async { - barcodeScanDelay = await InvenTreeSettingsManager().getValue(INV_BARCODE_SCAN_DELAY, 500) as int; - barcodeScanType = await InvenTreeSettingsManager().getValue(INV_BARCODE_SCAN_TYPE, BARCODE_CONTROLLER_CAMERA) as int; - barcodeScanSingle = await InvenTreeSettingsManager().getBool(INV_BARCODE_SCAN_SINGLE, false); + barcodeScanDelay = + await InvenTreeSettingsManager().getValue(INV_BARCODE_SCAN_DELAY, 500) + as int; + barcodeScanType = + await InvenTreeSettingsManager().getValue( + INV_BARCODE_SCAN_TYPE, + BARCODE_CONTROLLER_CAMERA, + ) + as int; + barcodeScanSingle = await InvenTreeSettingsManager().getBool( + INV_BARCODE_SCAN_SINGLE, + false, + ); if (mounted) { - setState(() { - }); + setState(() {}); } } // Callback function to edit the barcode scan delay value // TODO: Next time any new settings are added, refactor this into a generic function Future _editBarcodeScanDelay(BuildContext context) async { - _barcodeScanDelayController.text = barcodeScanDelay.toString(); return showDialog( @@ -56,9 +64,7 @@ class _InvenTreeBarcodeSettingsState extends State[ MaterialButton( @@ -76,13 +82,18 @@ class _InvenTreeBarcodeSettingsState extends State 2500) delay = 2500; - InvenTreeSettingsManager().setValue(INV_BARCODE_SCAN_DELAY, delay); + InvenTreeSettingsManager().setValue( + INV_BARCODE_SCAN_DELAY, + delay, + ); setState(() { barcodeScanDelay = delay; Navigator.pop(context); @@ -91,13 +102,12 @@ class _InvenTreeBarcodeSettingsState extends State { - _HomeScreenSettingsState(); - final GlobalKey<_HomeScreenSettingsState> _settingsKey = GlobalKey<_HomeScreenSettingsState>(); + final GlobalKey<_HomeScreenSettingsState> _settingsKey = + GlobalKey<_HomeScreenSettingsState>(); // Home screen settings bool homeShowSubscribed = true; @@ -32,92 +31,113 @@ class _HomeScreenSettingsState extends State { loadSettings(); } - Future loadSettings() async { - + Future loadSettings() async { // Load initial settings - homeShowSubscribed = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_SUBSCRIBED, true) as bool; - homeShowPo = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_PO, true) as bool; - homeShowSo = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_SO, true) as bool; - homeShowManufacturers = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_MANUFACTURERS, true) as bool; - homeShowCustomers = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_CUSTOMERS, true) as bool; - homeShowSuppliers = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_SUPPLIERS, true) as bool; + homeShowSubscribed = + await InvenTreeSettingsManager().getValue( + INV_HOME_SHOW_SUBSCRIBED, + true, + ) + as bool; + homeShowPo = + await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_PO, true) + as bool; + homeShowSo = + await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_SO, true) + as bool; + homeShowManufacturers = + await InvenTreeSettingsManager().getValue( + INV_HOME_SHOW_MANUFACTURERS, + true, + ) + as bool; + homeShowCustomers = + await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_CUSTOMERS, true) + as bool; + homeShowSuppliers = + await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_SUPPLIERS, true) + as bool; - setState(() { - }); + setState(() {}); } @override Widget build(BuildContext context) { - return Scaffold( - key: _settingsKey, - appBar: AppBar( - title: Text(L10().homeScreen), - backgroundColor: COLOR_APP_BAR, - ), - body: Container( - child: ListView( - children: [ - ListTile( - title: Text(L10().homeShowSubscribed), - subtitle: Text(L10().homeShowSubscribedDescription), - leading: Icon(TablerIcons.bell), - trailing: Switch( - value: homeShowSubscribed, - onChanged: (bool value) { - InvenTreeSettingsManager().setValue(INV_HOME_SHOW_SUBSCRIBED, value); - setState(() { - homeShowSubscribed = value; - }); - }, - ) - ), - ListTile( - title: Text(L10().homeShowPo), - subtitle: Text(L10().homeShowPoDescription), - leading: Icon(TablerIcons.shopping_cart), - trailing: Switch( - value: homeShowPo, - onChanged: (bool value) { - InvenTreeSettingsManager().setValue(INV_HOME_SHOW_PO, value); - setState(() { - homeShowPo = value; - }); - }, - ), - ), - ListTile( - title: Text(L10().homeShowSo), - subtitle: Text(L10().homeShowSoDescription), - leading: Icon(TablerIcons.truck), - trailing: Switch( - value: homeShowSo, - onChanged: (bool value) { - InvenTreeSettingsManager().setValue(INV_HOME_SHOW_SO, value); - setState(() { - homeShowSo = value; - }); - }, - ), - ), - ListTile( - title: Text(L10().homeShowSuppliers), - subtitle: Text(L10().homeShowSuppliersDescription), - leading: Icon(TablerIcons.building), - trailing: Switch( - value: homeShowSuppliers, - onChanged: (bool value) { - InvenTreeSettingsManager().setValue(INV_HOME_SHOW_SUPPLIERS, value); - setState(() { - homeShowSuppliers = value; - }); - }, - ), - ), - // TODO: When these features are improved, add them back in! - // Currently, the company display does not provide any value - /* + key: _settingsKey, + appBar: AppBar( + title: Text(L10().homeScreen), + backgroundColor: COLOR_APP_BAR, + ), + body: Container( + child: ListView( + children: [ + ListTile( + title: Text(L10().homeShowSubscribed), + subtitle: Text(L10().homeShowSubscribedDescription), + leading: Icon(TablerIcons.bell), + trailing: Switch( + value: homeShowSubscribed, + onChanged: (bool value) { + InvenTreeSettingsManager().setValue( + INV_HOME_SHOW_SUBSCRIBED, + value, + ); + setState(() { + homeShowSubscribed = value; + }); + }, + ), + ), + ListTile( + title: Text(L10().homeShowPo), + subtitle: Text(L10().homeShowPoDescription), + leading: Icon(TablerIcons.shopping_cart), + trailing: Switch( + value: homeShowPo, + onChanged: (bool value) { + InvenTreeSettingsManager().setValue(INV_HOME_SHOW_PO, value); + setState(() { + homeShowPo = value; + }); + }, + ), + ), + ListTile( + title: Text(L10().homeShowSo), + subtitle: Text(L10().homeShowSoDescription), + leading: Icon(TablerIcons.truck), + trailing: Switch( + value: homeShowSo, + onChanged: (bool value) { + InvenTreeSettingsManager().setValue(INV_HOME_SHOW_SO, value); + setState(() { + homeShowSo = value; + }); + }, + ), + ), + ListTile( + title: Text(L10().homeShowSuppliers), + subtitle: Text(L10().homeShowSuppliersDescription), + leading: Icon(TablerIcons.building), + trailing: Switch( + value: homeShowSuppliers, + onChanged: (bool value) { + InvenTreeSettingsManager().setValue( + INV_HOME_SHOW_SUPPLIERS, + value, + ); + setState(() { + homeShowSuppliers = value; + }); + }, + ), + ), + // TODO: When these features are improved, add them back in! + // Currently, the company display does not provide any value + /* ListTile( title: Text(L10().homeShowManufacturers), subtitle: Text(L10().homeShowManufacturersDescription), @@ -133,23 +153,26 @@ class _HomeScreenSettingsState extends State { ), ), */ - ListTile( - title: Text(L10().homeShowCustomers), - subtitle: Text(L10().homeShowCustomersDescription), - leading: Icon(TablerIcons.user), - trailing: Switch( - value: homeShowCustomers, - onChanged: (bool value) { - InvenTreeSettingsManager().setValue(INV_HOME_SHOW_CUSTOMERS, value); - setState(() { - homeShowCustomers = value; - }); - }, - ), - ), - ] - ) - ) + ListTile( + title: Text(L10().homeShowCustomers), + subtitle: Text(L10().homeShowCustomersDescription), + leading: Icon(TablerIcons.user), + trailing: Switch( + value: homeShowCustomers, + onChanged: (bool value) { + InvenTreeSettingsManager().setValue( + INV_HOME_SHOW_CUSTOMERS, + value, + ); + setState(() { + homeShowCustomers = value; + }); + }, + ), + ), + ], + ), + ), ); } -} \ No newline at end of file +} diff --git a/lib/settings/login.dart b/lib/settings/login.dart index 908d09a5..2e04d498 100644 --- a/lib/settings/login.dart +++ b/lib/settings/login.dart @@ -1,4 +1,3 @@ - import "package:flutter/material.dart"; import "package:flutter_tabler_icons/flutter_tabler_icons.dart"; @@ -9,21 +8,16 @@ import "package:inventree/api.dart"; import "package:inventree/widget/dialogs.dart"; import "package:inventree/widget/progress.dart"; - class InvenTreeLoginWidget extends StatefulWidget { - const InvenTreeLoginWidget(this.profile) : super(); final UserProfile profile; @override _InvenTreeLoginState createState() => _InvenTreeLoginState(); - } - class _InvenTreeLoginState extends State { - final formKey = GlobalKey(); String username = ""; @@ -35,14 +29,12 @@ class _InvenTreeLoginState extends State { // Attempt login Future _doLogin(BuildContext context) async { - // Save form formKey.currentState?.save(); bool valid = formKey.currentState?.validate() ?? false; if (valid) { - // Dismiss the keyboard FocusScopeNode currentFocus = FocusScope.of(context); @@ -53,7 +45,11 @@ class _InvenTreeLoginState extends State { showLoadingOverlay(); // Attempt login - final response = await InvenTreeAPI().fetchToken(widget.profile, username, password); + final response = await InvenTreeAPI().fetchToken( + widget.profile, + username, + password, + ); hideLoadingOverlay(); @@ -75,12 +71,10 @@ class _InvenTreeLoginState extends State { }); } } - } @override Widget build(BuildContext context) { - List before = [ ListTile( title: Text(L10().loginEnter), @@ -99,11 +93,13 @@ class _InvenTreeLoginState extends State { if (error.isNotEmpty) { after.add(Divider()); - after.add(ListTile( - leading: Icon(TablerIcons.exclamation_circle, color: COLOR_DANGER), - title: Text(L10().error, style: TextStyle(color: COLOR_DANGER)), - subtitle: Text(error, style: TextStyle(color: COLOR_DANGER)), - )); + after.add( + ListTile( + leading: Icon(TablerIcons.exclamation_circle, color: COLOR_DANGER), + title: Text(L10().error, style: TextStyle(color: COLOR_DANGER)), + subtitle: Text(error, style: TextStyle(color: COLOR_DANGER)), + ), + ); } return Scaffold( appBar: AppBar( @@ -115,8 +111,8 @@ class _InvenTreeLoginState extends State { onPressed: () async { _doLogin(context); }, - ) - ] + ), + ], ), body: Form( key: formKey, @@ -129,9 +125,9 @@ class _InvenTreeLoginState extends State { ...before, TextFormField( decoration: InputDecoration( - labelText: L10().username, - labelStyle: TextStyle(fontWeight: FontWeight.bold), - hintText: L10().enterUsername + labelText: L10().username, + labelStyle: TextStyle(fontWeight: FontWeight.bold), + hintText: L10().enterUsername, ), initialValue: "", keyboardType: TextInputType.text, @@ -147,41 +143,41 @@ class _InvenTreeLoginState extends State { }, ), TextFormField( - decoration: InputDecoration( - labelText: L10().password, - labelStyle: TextStyle(fontWeight: FontWeight.bold), - hintText: L10().enterPassword, - suffixIcon: IconButton( - icon: _obscured ? Icon(TablerIcons.eye) : Icon(TablerIcons.eye_off), - onPressed: () { - setState(() { - _obscured = !_obscured; - }); - }, - ), + decoration: InputDecoration( + labelText: L10().password, + labelStyle: TextStyle(fontWeight: FontWeight.bold), + hintText: L10().enterPassword, + suffixIcon: IconButton( + icon: _obscured + ? Icon(TablerIcons.eye) + : Icon(TablerIcons.eye_off), + onPressed: () { + setState(() { + _obscured = !_obscured; + }); + }, ), - initialValue: "", - keyboardType: TextInputType.visiblePassword, - obscureText: _obscured, - onSaved: (value) { - password = value?.trim() ?? ""; - }, - validator: (value) { - if (value == null || value.trim().isEmpty) { - return L10().passwordEmpty; - } - - return null; + ), + initialValue: "", + keyboardType: TextInputType.visiblePassword, + obscureText: _obscured, + onSaved: (value) { + password = value?.trim() ?? ""; + }, + validator: (value) { + if (value == null || value.trim().isEmpty) { + return L10().passwordEmpty; } + + return null; + }, ), ...after, ], ), padding: EdgeInsets.all(16), - ) - ) + ), + ), ); - } - -} \ No newline at end of file +} diff --git a/lib/settings/part_settings.dart b/lib/settings/part_settings.dart index 5a8ccd23..b0919316 100644 --- a/lib/settings/part_settings.dart +++ b/lib/settings/part_settings.dart @@ -1,4 +1,3 @@ - import "package:flutter/material.dart"; import "package:flutter_tabler_icons/flutter_tabler_icons.dart"; @@ -6,15 +5,12 @@ import "package:inventree/l10.dart"; import "package:inventree/app_colors.dart"; import "package:inventree/preferences.dart"; - class InvenTreePartSettingsWidget extends StatefulWidget { @override _InvenTreePartSettingsState createState() => _InvenTreePartSettingsState(); } - class _InvenTreePartSettingsState extends State { - _InvenTreePartSettingsState(); bool partShowParameters = true; @@ -32,16 +28,33 @@ class _InvenTreePartSettingsState extends State { } Future loadSettings() async { - partShowParameters = await InvenTreeSettingsManager().getBool(INV_PART_SHOW_PARAMETERS, true); - partShowBom = await InvenTreeSettingsManager().getBool(INV_PART_SHOW_BOM, true); - partShowPricing = await InvenTreeSettingsManager().getBool(INV_PART_SHOW_PRICING, true); - stockShowHistory = await InvenTreeSettingsManager().getBool(INV_STOCK_SHOW_HISTORY, false); - stockShowTests = await InvenTreeSettingsManager().getBool(INV_STOCK_SHOW_TESTS, true); - stockConfirmScan = await InvenTreeSettingsManager().getBool(INV_STOCK_CONFIRM_SCAN, false); + partShowParameters = await InvenTreeSettingsManager().getBool( + INV_PART_SHOW_PARAMETERS, + true, + ); + partShowBom = await InvenTreeSettingsManager().getBool( + INV_PART_SHOW_BOM, + true, + ); + partShowPricing = await InvenTreeSettingsManager().getBool( + INV_PART_SHOW_PRICING, + true, + ); + stockShowHistory = await InvenTreeSettingsManager().getBool( + INV_STOCK_SHOW_HISTORY, + false, + ); + stockShowTests = await InvenTreeSettingsManager().getBool( + INV_STOCK_SHOW_TESTS, + true, + ); + stockConfirmScan = await InvenTreeSettingsManager().getBool( + INV_STOCK_CONFIRM_SCAN, + false, + ); if (mounted) { - setState(() { - }); + setState(() {}); } } @@ -50,7 +63,7 @@ class _InvenTreePartSettingsState extends State { return Scaffold( appBar: AppBar( title: Text(L10().partSettings), - backgroundColor: COLOR_APP_BAR + backgroundColor: COLOR_APP_BAR, ), body: Container( child: ListView( @@ -62,7 +75,10 @@ class _InvenTreePartSettingsState extends State { trailing: Switch( value: partShowParameters, onChanged: (bool value) { - InvenTreeSettingsManager().setValue(INV_PART_SHOW_PARAMETERS, value); + InvenTreeSettingsManager().setValue( + INV_PART_SHOW_PARAMETERS, + value, + ); setState(() { partShowParameters = value; }); @@ -90,7 +106,10 @@ class _InvenTreePartSettingsState extends State { trailing: Switch( value: partShowPricing, onChanged: (bool value) { - InvenTreeSettingsManager().setValue(INV_PART_SHOW_PRICING, value); + InvenTreeSettingsManager().setValue( + INV_PART_SHOW_PRICING, + value, + ); setState(() { partShowPricing = value; }); @@ -105,7 +124,10 @@ class _InvenTreePartSettingsState extends State { trailing: Switch( value: stockShowHistory, onChanged: (bool value) { - InvenTreeSettingsManager().setValue(INV_STOCK_SHOW_HISTORY, value); + InvenTreeSettingsManager().setValue( + INV_STOCK_SHOW_HISTORY, + value, + ); setState(() { stockShowHistory = value; }); @@ -115,11 +137,14 @@ class _InvenTreePartSettingsState extends State { ListTile( title: Text(L10().testResults), subtitle: Text(L10().testResultsDetail), - leading: Icon(TablerIcons.test_pipe), + leading: Icon(TablerIcons.test_pipe), trailing: Switch( value: stockShowTests, onChanged: (bool value) { - InvenTreeSettingsManager().setValue(INV_STOCK_SHOW_TESTS, value); + InvenTreeSettingsManager().setValue( + INV_STOCK_SHOW_TESTS, + value, + ); setState(() { stockShowTests = value; }); @@ -133,16 +158,19 @@ class _InvenTreePartSettingsState extends State { trailing: Switch( value: stockConfirmScan, onChanged: (bool value) { - InvenTreeSettingsManager().setValue(INV_STOCK_CONFIRM_SCAN, value); + InvenTreeSettingsManager().setValue( + INV_STOCK_CONFIRM_SCAN, + value, + ); setState(() { stockConfirmScan = value; }); - } + }, ), - ) - ] - ) - ) + ), + ], + ), + ), ); } -} \ No newline at end of file +} diff --git a/lib/settings/purchase_order_settings.dart b/lib/settings/purchase_order_settings.dart index 888b89b1..8b6452c4 100644 --- a/lib/settings/purchase_order_settings.dart +++ b/lib/settings/purchase_order_settings.dart @@ -1,4 +1,3 @@ - import "package:flutter/material.dart"; import "package:flutter_tabler_icons/flutter_tabler_icons.dart"; import "package:inventree/app_colors.dart"; @@ -6,15 +5,14 @@ import "package:inventree/app_colors.dart"; import "package:inventree/l10.dart"; import "package:inventree/preferences.dart"; - class InvenTreePurchaseOrderSettingsWidget extends StatefulWidget { @override - _InvenTreePurchaseOrderSettingsState createState() => _InvenTreePurchaseOrderSettingsState(); + _InvenTreePurchaseOrderSettingsState createState() => + _InvenTreePurchaseOrderSettingsState(); } - -class _InvenTreePurchaseOrderSettingsState extends State { - +class _InvenTreePurchaseOrderSettingsState + extends State { _InvenTreePurchaseOrderSettingsState(); bool poEnable = true; @@ -30,70 +28,81 @@ class _InvenTreePurchaseOrderSettingsState extends State loadSettings() async { poEnable = await InvenTreeSettingsManager().getBool(INV_PO_ENABLE, true); - poShowCamera = await InvenTreeSettingsManager().getBool(INV_PO_SHOW_CAMERA, true); - poConfirmScan = await InvenTreeSettingsManager().getBool(INV_PO_CONFIRM_SCAN, true); + poShowCamera = await InvenTreeSettingsManager().getBool( + INV_PO_SHOW_CAMERA, + true, + ); + poConfirmScan = await InvenTreeSettingsManager().getBool( + INV_PO_CONFIRM_SCAN, + true, + ); if (mounted) { - setState(() { - }); + setState(() {}); } } @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Text(L10().purchaseOrderSettings), - backgroundColor: COLOR_APP_BAR, + appBar: AppBar( + title: Text(L10().purchaseOrderSettings), + backgroundColor: COLOR_APP_BAR, + ), + body: Container( + child: ListView( + children: [ + ListTile( + title: Text(L10().purchaseOrderEnable), + subtitle: Text(L10().purchaseOrderEnableDetail), + leading: Icon(TablerIcons.shopping_cart), + trailing: Switch( + value: poEnable, + onChanged: (bool value) { + InvenTreeSettingsManager().setValue(INV_PO_ENABLE, value); + setState(() { + poEnable = value; + }); + }, + ), + ), + ListTile( + title: Text(L10().purchaseOrderShowCamera), + subtitle: Text(L10().purchaseOrderShowCameraDetail), + leading: Icon(TablerIcons.camera), + trailing: Switch( + value: poShowCamera, + onChanged: (bool value) { + InvenTreeSettingsManager().setValue( + INV_PO_SHOW_CAMERA, + value, + ); + setState(() { + poShowCamera = value; + }); + }, + ), + ), + ListTile( + title: Text(L10().purchaseOrderConfirmScan), + subtitle: Text(L10().purchaseOrderConfirmScanDetail), + leading: Icon(TablerIcons.barcode), + trailing: Switch( + value: poConfirmScan, + onChanged: (bool value) { + InvenTreeSettingsManager().setValue( + INV_PO_CONFIRM_SCAN, + value, + ); + setState(() { + poConfirmScan = value; + }); + }, + ), + ), + ], ), - body: Container( - child: ListView( - children: [ - ListTile( - title: Text(L10().purchaseOrderEnable), - subtitle: Text(L10().purchaseOrderEnableDetail), - leading: Icon(TablerIcons.shopping_cart), - trailing: Switch( - value: poEnable, - onChanged: (bool value) { - InvenTreeSettingsManager().setValue(INV_PO_ENABLE, value); - setState(() { - poEnable = value; - }); - }, - ), - ), - ListTile( - title: Text(L10().purchaseOrderShowCamera), - subtitle: Text(L10().purchaseOrderShowCameraDetail), - leading: Icon(TablerIcons.camera), - trailing: Switch( - value: poShowCamera, - onChanged: (bool value) { - InvenTreeSettingsManager().setValue(INV_PO_SHOW_CAMERA, value); - setState(() { - poShowCamera = value; - }); - }, - ), - ), - ListTile( - title: Text(L10().purchaseOrderConfirmScan), - subtitle: Text(L10().purchaseOrderConfirmScanDetail), - leading: Icon(TablerIcons.barcode), - trailing: Switch ( - value: poConfirmScan, - onChanged: (bool value) { - InvenTreeSettingsManager().setValue(INV_PO_CONFIRM_SCAN, value); - setState(() { - poConfirmScan = value; - }); - }, - ), - ) - ] - ) - ) + ), ); } -} \ No newline at end of file +} diff --git a/lib/settings/release.dart b/lib/settings/release.dart index 20e10b2f..7f8e11d7 100644 --- a/lib/settings/release.dart +++ b/lib/settings/release.dart @@ -6,15 +6,13 @@ import "package:url_launcher/url_launcher.dart"; import "package:inventree/l10.dart"; import "package:inventree/helpers.dart"; - class ReleaseNotesWidget extends StatelessWidget { - const ReleaseNotesWidget(this.releaseNotes); final String releaseNotes; @override - Widget build (BuildContext context) { + Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(L10().releaseNotes), @@ -29,21 +27,18 @@ class ReleaseNotesWidget extends StatelessWidget { openLink(link); } }, - ) + ), ); } } - class CreditsWidget extends StatelessWidget { - const CreditsWidget(this.credits); final String credits; // Callback function when a link is clicked in the markdown Future openLink(String url) async { - final link = Uri.parse(url); if (await canLaunchUrl(link)) { @@ -52,7 +47,7 @@ class CreditsWidget extends StatelessWidget { } @override - Widget build (BuildContext context) { + Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(L10().credits), @@ -67,7 +62,7 @@ class CreditsWidget extends StatelessWidget { openLink(link); } }, - ) + ), ); } -} \ No newline at end of file +} diff --git a/lib/settings/sales_order_settings.dart b/lib/settings/sales_order_settings.dart index cbeacb66..e009f86b 100644 --- a/lib/settings/sales_order_settings.dart +++ b/lib/settings/sales_order_settings.dart @@ -1,4 +1,3 @@ - import "package:flutter/material.dart"; import "package:flutter_tabler_icons/flutter_tabler_icons.dart"; @@ -6,15 +5,14 @@ import "package:inventree/l10.dart"; import "package:inventree/app_colors.dart"; import "package:inventree/preferences.dart"; - class InvenTreeSalesOrderSettingsWidget extends StatefulWidget { @override - _InvenTreeSalesOrderSettingsState createState() => _InvenTreeSalesOrderSettingsState(); + _InvenTreeSalesOrderSettingsState createState() => + _InvenTreeSalesOrderSettingsState(); } - -class _InvenTreeSalesOrderSettingsState extends State { - +class _InvenTreeSalesOrderSettingsState + extends State { _InvenTreeSalesOrderSettingsState(); bool soEnable = true; @@ -29,55 +27,60 @@ class _InvenTreeSalesOrderSettingsState extends State loadSettings() async { soEnable = await InvenTreeSettingsManager().getBool(INV_SO_ENABLE, true); - soShowCamera = await InvenTreeSettingsManager().getBool(INV_SO_SHOW_CAMERA, true); + soShowCamera = await InvenTreeSettingsManager().getBool( + INV_SO_SHOW_CAMERA, + true, + ); if (mounted) { - setState(() { - }); + setState(() {}); } } @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Text(L10().salesOrderSettings), - backgroundColor: COLOR_APP_BAR, + appBar: AppBar( + title: Text(L10().salesOrderSettings), + backgroundColor: COLOR_APP_BAR, + ), + body: Container( + child: ListView( + children: [ + ListTile( + title: Text(L10().salesOrderEnable), + subtitle: Text(L10().salesOrderEnableDetail), + leading: Icon(TablerIcons.shopping_cart), + trailing: Switch( + value: soEnable, + onChanged: (bool value) { + InvenTreeSettingsManager().setValue(INV_SO_ENABLE, value); + setState(() { + soEnable = value; + }); + }, + ), + ), + ListTile( + title: Text(L10().salesOrderShowCamera), + subtitle: Text(L10().salesOrderShowCameraDetail), + leading: Icon(TablerIcons.camera), + trailing: Switch( + value: soShowCamera, + onChanged: (bool value) { + InvenTreeSettingsManager().setValue( + INV_SO_SHOW_CAMERA, + value, + ); + setState(() { + soShowCamera = value; + }); + }, + ), + ), + ], ), - body: Container( - child: ListView( - children: [ - ListTile( - title: Text(L10().salesOrderEnable), - subtitle: Text(L10().salesOrderEnableDetail), - leading: Icon(TablerIcons.shopping_cart), - trailing: Switch( - value: soEnable, - onChanged: (bool value) { - InvenTreeSettingsManager().setValue(INV_SO_ENABLE, value); - setState(() { - soEnable = value; - }); - }, - ), - ), - ListTile( - title: Text(L10().salesOrderShowCamera), - subtitle: Text(L10().salesOrderShowCameraDetail), - leading: Icon(TablerIcons.camera), - trailing: Switch( - value: soShowCamera, - onChanged: (bool value) { - InvenTreeSettingsManager().setValue(INV_SO_SHOW_CAMERA, value); - setState(() { - soShowCamera = value; - }); - }, - ), - ), - ] - ) - ) + ), ); } -} \ No newline at end of file +} diff --git a/lib/settings/select_server.dart b/lib/settings/select_server.dart index 2244cad8..bfeae981 100644 --- a/lib/settings/select_server.dart +++ b/lib/settings/select_server.dart @@ -12,39 +12,37 @@ import "package:inventree/api.dart"; import "package:inventree/user_profile.dart"; class InvenTreeSelectServerWidget extends StatefulWidget { - @override _InvenTreeSelectServerState createState() => _InvenTreeSelectServerState(); } - class _InvenTreeSelectServerState extends State { - _InvenTreeSelectServerState() { _reload(); } - final GlobalKey<_InvenTreeSelectServerState> _loginKey = GlobalKey<_InvenTreeSelectServerState>(); + final GlobalKey<_InvenTreeSelectServerState> _loginKey = + GlobalKey<_InvenTreeSelectServerState>(); List profiles = []; - Future _reload() async { - + Future _reload() async { profiles = await UserProfileDBManager().getAllProfiles(); if (!mounted) { return; } - setState(() { - }); + setState(() {}); } /* * Logout the selected profile (delete the stored token) */ - Future _logoutProfile(BuildContext context, {UserProfile? userProfile}) async { - + Future _logoutProfile( + BuildContext context, { + UserProfile? userProfile, + }) async { if (userProfile != null) { userProfile.token = ""; await UserProfileDBManager().updateProfile(userProfile); @@ -54,26 +52,25 @@ class _InvenTreeSelectServerState extends State { InvenTreeAPI().disconnectFromServer(); _reload(); - } /* * Edit the selected profile */ - void _editProfile(BuildContext context, {UserProfile? userProfile, bool createNew = false}) { - + void _editProfile( + BuildContext context, { + UserProfile? userProfile, + bool createNew = false, + }) { Navigator.push( context, - MaterialPageRoute( - builder: (context) => ProfileEditWidget(userProfile) - ) + MaterialPageRoute(builder: (context) => ProfileEditWidget(userProfile)), ).then((context) { _reload(); }); } - Future _selectProfile(BuildContext context, UserProfile profile) async { - + Future _selectProfile(BuildContext context, UserProfile profile) async { // Disconnect InvenTree InvenTreeAPI().disconnectFromServer(); @@ -94,8 +91,9 @@ class _InvenTreeSelectServerState extends State { // First check if the profile has an associate token if (!prf.hasToken) { // Redirect user to login screen - Navigator.push(context, - MaterialPageRoute(builder: (context) => InvenTreeLoginWidget(profile)) + Navigator.push( + context, + MaterialPageRoute(builder: (context) => InvenTreeLoginWidget(profile)), ).then((value) async { _reload(); // Reload profile @@ -125,8 +123,7 @@ class _InvenTreeSelectServerState extends State { _reload(); } - Future _deleteProfile(UserProfile profile) async { - + Future _deleteProfile(UserProfile profile) async { await UserProfileDBManager().deleteProfile(profile); if (!mounted) { @@ -135,13 +132,13 @@ class _InvenTreeSelectServerState extends State { _reload(); - if (InvenTreeAPI().isConnected() && profile.key == (InvenTreeAPI().profile?.key ?? "")) { + if (InvenTreeAPI().isConnected() && + profile.key == (InvenTreeAPI().profile?.key ?? "")) { InvenTreeAPI().disconnectFromServer(); } } Widget? _getProfileIcon(UserProfile profile) { - // Not selected? No icon for you! if (!profile.selected) return null; @@ -152,45 +149,38 @@ class _InvenTreeSelectServerState extends State { // Reflect the connection status of the server if (InvenTreeAPI().isConnected()) { - return Icon( - TablerIcons.circle_check, - color: COLOR_SUCCESS - ); + return Icon(TablerIcons.circle_check, color: COLOR_SUCCESS); } else if (InvenTreeAPI().isConnecting()) { - return Spinner( - icon: TablerIcons.loader_2, - color: COLOR_PROGRESS, - ); + return Spinner(icon: TablerIcons.loader_2, color: COLOR_PROGRESS); } else { - return Icon( - TablerIcons.circle_x, - color: COLOR_DANGER, - ); + return Icon(TablerIcons.circle_x, color: COLOR_DANGER); } } @override Widget build(BuildContext context) { - List children = []; if (profiles.isNotEmpty) { for (int idx = 0; idx < profiles.length; idx++) { UserProfile profile = profiles[idx]; - children.add(ListTile( - title: Text( - profile.name, - ), - tileColor: profile.selected ? Theme.of(context).secondaryHeaderColor : null, - subtitle: Text("${profile.server}"), - leading: profile.hasToken ? Icon(TablerIcons.user_check, color: COLOR_SUCCESS) : Icon(TablerIcons.user_cancel, color: COLOR_WARNING), - trailing: _getProfileIcon(profile), - onTap: () { - _selectProfile(context, profile); - }, - onLongPress: () { - OneContext().showDialog( + children.add( + ListTile( + title: Text(profile.name), + tileColor: profile.selected + ? Theme.of(context).secondaryHeaderColor + : null, + subtitle: Text("${profile.server}"), + leading: profile.hasToken + ? Icon(TablerIcons.user_check, color: COLOR_SUCCESS) + : Icon(TablerIcons.user_cancel, color: COLOR_WARNING), + trailing: _getProfileIcon(profile), + onTap: () { + _selectProfile(context, profile); + }, + onLongPress: () { + OneContext().showDialog( builder: (BuildContext context) { return SimpleDialog( title: Text(profile.name), @@ -204,7 +194,7 @@ class _InvenTreeSelectServerState extends State { child: ListTile( title: Text(L10().profileConnect), leading: Icon(TablerIcons.server), - ) + ), ), SimpleDialogOption( onPressed: () { @@ -213,8 +203,8 @@ class _InvenTreeSelectServerState extends State { }, child: ListTile( title: Text(L10().profileEdit), - leading: Icon(TablerIcons.edit) - ) + leading: Icon(TablerIcons.edit), + ), ), SimpleDialogOption( onPressed: () { @@ -224,7 +214,7 @@ class _InvenTreeSelectServerState extends State { child: ListTile( title: Text(L10().profileLogout), leading: Icon(TablerIcons.logout), - ) + ), ), Divider(), SimpleDialogOption( @@ -238,28 +228,28 @@ class _InvenTreeSelectServerState extends State { icon: TablerIcons.trash, onAccept: () { _deleteProfile(profile); - } + }, ); }, child: ListTile( - title: Text(L10().profileDelete, style: TextStyle(color: Colors.red)), + title: Text( + L10().profileDelete, + style: TextStyle(color: Colors.red), + ), leading: Icon(TablerIcons.trash, color: Colors.red), - ) - ) + ), + ), ], ); - } - ); - }, - )); + }, + ); + }, + ), + ); } } else { // No profile available! - children.add( - ListTile( - title: Text(L10().profileNone), - ) - ); + children.add(ListTile(title: Text(L10().profileNone))); } return Scaffold( @@ -273,27 +263,25 @@ class _InvenTreeSelectServerState extends State { onPressed: () { _editProfile(context, createNew: true); }, - ) + ), ], ), body: Container( child: ListView( children: ListTile.divideTiles( context: context, - tiles: children + tiles: children, ).toList(), - ) + ), ), ); } } - /* * Widget for editing server details */ class ProfileEditWidget extends StatefulWidget { - const ProfileEditWidget(this.profile) : super(); final UserProfile? profile; @@ -303,7 +291,6 @@ class ProfileEditWidget extends StatefulWidget { } class _ProfileEditState extends State { - _ProfileEditState() : super(); final formKey = GlobalKey(); @@ -316,7 +303,9 @@ class _ProfileEditState extends State { return Scaffold( appBar: AppBar( backgroundColor: COLOR_APP_BAR, - title: Text(widget.profile == null ? L10().profileAdd : L10().profileEdit), + title: Text( + widget.profile == null ? L10().profileAdd : L10().profileEdit, + ), actions: [ IconButton( icon: Icon(TablerIcons.circle_check), @@ -327,14 +316,10 @@ class _ProfileEditState extends State { UserProfile? prf = widget.profile; if (prf == null) { - UserProfile profile = UserProfile( - name: name, - server: server, - ); + UserProfile profile = UserProfile(name: name, server: server); await UserProfileDBManager().addProfile(profile); } else { - prf.name = name; prf.server = server; @@ -345,8 +330,8 @@ class _ProfileEditState extends State { Navigator.of(context).pop(); } }, - ) - ] + ), + ], ), body: Form( key: formKey, @@ -373,7 +358,7 @@ class _ProfileEditState extends State { } return null; - } + }, ), TextFormField( decoration: InputDecoration( @@ -398,7 +383,8 @@ class _ProfileEditState extends State { return L10().invalidHost; } - if (!value.startsWith("http:") && !value.startsWith("https:")) { + if (!value.startsWith("http:") && + !value.startsWith("https:")) { // return L10().serverStart; } @@ -410,7 +396,10 @@ class _ProfileEditState extends State { Uri uri = Uri.parse(value); if (uri.hasScheme) { - if (!["http", "https"].contains(uri.scheme.toLowerCase())) { + if (![ + "http", + "https", + ].contains(uri.scheme.toLowerCase())) { return L10().serverStart; } } else { @@ -422,12 +411,11 @@ class _ProfileEditState extends State { return null; }, ), - ] + ], ), padding: EdgeInsets.all(16), ), - ) + ), ); } - -} \ No newline at end of file +} diff --git a/lib/settings/settings.dart b/lib/settings/settings.dart index 30f4f2b4..12b51aff 100644 --- a/lib/settings/settings.dart +++ b/lib/settings/settings.dart @@ -16,15 +16,11 @@ import "package:inventree/settings/sales_order_settings.dart"; // InvenTree settings view class InvenTreeSettingsWidget extends StatefulWidget { - @override _InvenTreeSettingsState createState() => _InvenTreeSettingsState(); - } - class _InvenTreeSettingsState extends State { - final _scaffoldKey = GlobalKey(); /* @@ -32,8 +28,10 @@ class _InvenTreeSettingsState extends State { */ Future _about() async { PackageInfo.fromPlatform().then((PackageInfo info) { - Navigator.push(context, - MaterialPageRoute(builder: (context) => InvenTreeAboutWidget(info))); + Navigator.push( + context, + MaterialPageRoute(builder: (context) => InvenTreeAboutWidget(info)), + ); }); } @@ -48,72 +46,108 @@ class _InvenTreeSettingsState extends State { body: Center( child: ListView( children: [ - ListTile( - title: Text(L10().server), - subtitle: Text(L10().configureServer), - leading: Icon(TablerIcons.server, color: COLOR_ACTION), - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) => InvenTreeSelectServerWidget())); - }, - ), - Divider(), - ListTile( - title: Text(L10().appSettings), - subtitle: Text(L10().appSettingsDetails), - leading: Icon(TablerIcons.settings, color: COLOR_ACTION), - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) => InvenTreeAppSettingsWidget())); - } - ), - ListTile( - title: Text(L10().homeScreen), - subtitle: Text(L10().homeScreenSettings), - leading: Icon(TablerIcons.home, color: COLOR_ACTION), - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) => HomeScreenSettingsWidget())); - } - ), - ListTile( - title: Text(L10().barcodes), - subtitle: Text(L10().barcodeSettings), - leading: Icon(TablerIcons.barcode, color: COLOR_ACTION), - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) => InvenTreeBarcodeSettingsWidget())); - } - ), - ListTile( - title: Text(L10().part), - subtitle: Text(L10().partSettings), - leading: Icon(TablerIcons.box, color: COLOR_ACTION), - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) => InvenTreePartSettingsWidget())); - } - ), - ListTile( - title: Text(L10().purchaseOrder), - subtitle: Text(L10().purchaseOrderSettings), - leading: Icon(TablerIcons.shopping_cart, color: COLOR_ACTION), - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) => InvenTreePurchaseOrderSettingsWidget())); - }, - ), - ListTile( - title: Text(L10().salesOrder), - subtitle: Text(L10().salesOrderSettings), - leading: Icon(TablerIcons.truck, color: COLOR_ACTION), - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) => InvenTreeSalesOrderSettingsWidget())); - }, - ), - Divider(), - ListTile( - title: Text(L10().about), - leading: Icon(TablerIcons.info_circle, color: COLOR_ACTION), - onTap: _about, - ) - ] - ) - ) + ListTile( + title: Text(L10().server), + subtitle: Text(L10().configureServer), + leading: Icon(TablerIcons.server, color: COLOR_ACTION), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => InvenTreeSelectServerWidget(), + ), + ); + }, + ), + Divider(), + ListTile( + title: Text(L10().appSettings), + subtitle: Text(L10().appSettingsDetails), + leading: Icon(TablerIcons.settings, color: COLOR_ACTION), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => InvenTreeAppSettingsWidget(), + ), + ); + }, + ), + ListTile( + title: Text(L10().homeScreen), + subtitle: Text(L10().homeScreenSettings), + leading: Icon(TablerIcons.home, color: COLOR_ACTION), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => HomeScreenSettingsWidget(), + ), + ); + }, + ), + ListTile( + title: Text(L10().barcodes), + subtitle: Text(L10().barcodeSettings), + leading: Icon(TablerIcons.barcode, color: COLOR_ACTION), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => InvenTreeBarcodeSettingsWidget(), + ), + ); + }, + ), + ListTile( + title: Text(L10().part), + subtitle: Text(L10().partSettings), + leading: Icon(TablerIcons.box, color: COLOR_ACTION), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => InvenTreePartSettingsWidget(), + ), + ); + }, + ), + ListTile( + title: Text(L10().purchaseOrder), + subtitle: Text(L10().purchaseOrderSettings), + leading: Icon(TablerIcons.shopping_cart, color: COLOR_ACTION), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + InvenTreePurchaseOrderSettingsWidget(), + ), + ); + }, + ), + ListTile( + title: Text(L10().salesOrder), + subtitle: Text(L10().salesOrderSettings), + leading: Icon(TablerIcons.truck, color: COLOR_ACTION), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => InvenTreeSalesOrderSettingsWidget(), + ), + ); + }, + ), + Divider(), + ListTile( + title: Text(L10().about), + leading: Icon(TablerIcons.info_circle, color: COLOR_ACTION), + onTap: _about, + ), + ], + ), + ), ); } -} \ No newline at end of file +} diff --git a/lib/user_profile.dart b/lib/user_profile.dart index 9d17b065..f53b412a 100644 --- a/lib/user_profile.dart +++ b/lib/user_profile.dart @@ -1,11 +1,9 @@ - import "package:sembast/sembast.dart"; import "package:inventree/helpers.dart"; import "package:inventree/preferences.dart"; class UserProfile { - UserProfile({ this.key, this.name = "", @@ -14,7 +12,11 @@ class UserProfile { this.selected = false, }); - factory UserProfile.fromJson(int key, Map json, bool isSelected) => UserProfile( + factory UserProfile.fromJson( + int key, + Map json, + bool isSelected, + ) => UserProfile( key: key, name: (json["name"] ?? "") as String, server: (json["server"] ?? "") as String, @@ -58,7 +60,6 @@ class UserProfile { * Class for storing and managing user (server) profiles */ class UserProfileDBManager { - final store = StoreRef("profiles"); Future get _db async => InvenTreePreferencesDB.instance.database; @@ -67,7 +68,6 @@ class UserProfileDBManager { * Check if a profile with the specified name exists in the database */ Future profileNameExists(String name) async { - final profiles = await getAllProfiles(); for (var prf in profiles) { @@ -84,9 +84,10 @@ class UserProfileDBManager { * Add a new UserProfile to the profiles database. */ Future addProfile(UserProfile profile) async { - if (profile.name.isEmpty) { - debug("addProfile() : Profile missing required values - not adding to database"); + debug( + "addProfile() : Profile missing required values - not adding to database", + ); return false; } @@ -113,7 +114,6 @@ class UserProfileDBManager { * The unique integer is used to determine if the profile already exists. */ Future updateProfile(UserProfile profile) async { - // Prevent invalid profile data from being updated if (profile.name.isEmpty) { debug("updateProfile() : Profile missing required values - not updating"); @@ -144,15 +144,15 @@ class UserProfileDBManager { * The key of the UserProfile should match the "selected" property */ Future getSelectedProfile() async { - final selected = await store.record("selected").get(await _db); final profiles = await store.find(await _db); - debug("getSelectedProfile() : ${profiles.length} profiles available - selected = ${selected}"); + debug( + "getSelectedProfile() : ${profiles.length} profiles available - selected = ${selected}", + ); for (int idx = 0; idx < profiles.length; idx++) { - if (profiles[idx].key is int && profiles[idx].key == selected) { return UserProfile.fromJson( profiles[idx].key! as int, @@ -169,7 +169,6 @@ class UserProfileDBManager { * Return all user profile objects */ Future> getAllProfiles() async { - final selected = await store.record("selected").get(await _db); final profiles = await store.find(await _db); @@ -177,25 +176,26 @@ class UserProfileDBManager { List profileList = []; for (int idx = 0; idx < profiles.length; idx++) { - if (profiles[idx].key is int) { profileList.add( UserProfile.fromJson( profiles[idx].key! as int, profiles[idx].value! as Map, profiles[idx].key == selected, - ) + ), ); } } // If there are no available profiles, create a demo profile if (profileList.isEmpty) { - bool added = await InvenTreeSettingsManager().getBool("demo_profile_added", false); + bool added = await InvenTreeSettingsManager().getBool( + "demo_profile_added", + false, + ); // Don't add a new profile if we have added it previously if (!added) { - await InvenTreeSettingsManager().setValue("demo_profile_added", true); UserProfile demoProfile = UserProfile( @@ -212,7 +212,6 @@ class UserProfileDBManager { return profileList; } - /* * Retrieve a profile by key (or null if no match exists) */ @@ -231,7 +230,6 @@ class UserProfileDBManager { return prf; } - /* * Retrieve a profile by name (or null if no match exists) */ diff --git a/lib/widget/attachment_widget.dart b/lib/widget/attachment_widget.dart index f92fbb69..b4c9746a 100644 --- a/lib/widget/attachment_widget.dart +++ b/lib/widget/attachment_widget.dart @@ -1,4 +1,3 @@ - import "dart:io"; import "package:flutter/material.dart"; @@ -17,7 +16,6 @@ import "package:inventree/widget/progress.dart"; import "package:inventree/widget/snacks.dart"; import "package:inventree/widget/refreshable_state.dart"; - /* * A generic widget for displaying a list of attachments. * @@ -25,8 +23,12 @@ import "package:inventree/widget/refreshable_state.dart"; * we pass a subclassed instance of the InvenTreeAttachment model. */ class AttachmentWidget extends StatefulWidget { - - const AttachmentWidget(this.attachmentClass, this.modelId, this.imagePrefix, this.hasUploadPermission) : super(); + const AttachmentWidget( + this.attachmentClass, + this.modelId, + this.imagePrefix, + this.hasUploadPermission, + ) : super(); final InvenTreeAttachment attachmentClass; final int modelId; @@ -37,9 +39,7 @@ class AttachmentWidget extends StatefulWidget { _AttachmentWidgetState createState() => _AttachmentWidgetState(); } - class _AttachmentWidgetState extends RefreshableState { - _AttachmentWidgetState(); List attachments = []; @@ -64,7 +64,7 @@ class _AttachmentWidgetState extends RefreshableState { refresh(context); }); }); - } + }, ), IconButton( icon: Icon(TablerIcons.file_upload), @@ -74,20 +74,19 @@ class _AttachmentWidgetState extends RefreshableState { refresh(context); }); }); - } - ) + }, + ), ]; } Future upload(BuildContext context, File? file) async { - if (file == null) return; showLoadingOverlay(); final bool result = await widget.attachmentClass.uploadAttachment( - file, - widget.modelId + file, + widget.modelId, ); hideLoadingOverlay(); @@ -101,35 +100,39 @@ class _AttachmentWidgetState extends RefreshableState { refresh(context); } - - Future editAttachment(BuildContext context, InvenTreeAttachment attachment) async - { - attachment.editForm(context, L10().editAttachment).then((result) => { - refresh(context) - }); + Future editAttachment( + BuildContext context, + InvenTreeAttachment attachment, + ) async { + attachment + .editForm(context, L10().editAttachment) + .then((result) => {refresh(context)}); } /* * Delete the specified attachment */ - Future deleteAttachment(BuildContext context, InvenTreeAttachment attachment) async { - + Future deleteAttachment( + BuildContext context, + InvenTreeAttachment attachment, + ) async { final bool result = await attachment.delete(); showSnackIcon( result ? L10().deleteSuccess : L10().deleteFailed, - success: result + success: result, ); refresh(context); - } /* * Display an option context menu for the selected attachment */ - Future showOptionsMenu(BuildContext context, InvenTreeAttachment attachment) async { - + Future showOptionsMenu( + BuildContext context, + InvenTreeAttachment attachment, + ) async { OneContext().showDialog( builder: (BuildContext ctx) { return SimpleDialog( @@ -144,7 +147,7 @@ class _AttachmentWidgetState extends RefreshableState { child: ListTile( title: Text(L10().edit), leading: Icon(TablerIcons.edit), - ) + ), ), SimpleDialogOption( onPressed: () async { @@ -154,29 +157,27 @@ class _AttachmentWidgetState extends RefreshableState { child: ListTile( title: Text(L10().delete), leading: Icon(TablerIcons.trash, color: COLOR_DANGER), - ) - ) - ] + ), + ), + ], ); - } + }, ); } @override Future request(BuildContext context) async { - Map filters = {}; if (InvenTreeAPI().supportsModernAttachments) { filters["model_type"] = widget.attachmentClass.REF_MODEL_TYPE; filters["model_id"] = widget.modelId.toString(); } else { - filters[widget.attachmentClass.REFERENCE_FIELD] = widget.modelId.toString(); + filters[widget.attachmentClass.REFERENCE_FIELD] = widget.modelId + .toString(); } - await widget.attachmentClass.list( - filters: filters - ).then((var results) { + await widget.attachmentClass.list(filters: filters).then((var results) { attachments.clear(); for (var result in results) { @@ -186,57 +187,58 @@ class _AttachmentWidgetState extends RefreshableState { } }); - setState(() { - }); + setState(() {}); } @override List getTiles(BuildContext context) { - List tiles = []; // An "attachment" can either be a file, or a URL for (var attachment in attachments) { - if (attachment.filename.isNotEmpty) { - tiles.add(ListTile( - title: Text(attachment.filename), - subtitle: Text(attachment.comment), - leading: Icon(attachment.icon, color: COLOR_ACTION), - onTap: () async { - showLoadingOverlay(); - await attachment.downloadAttachment(); - hideLoadingOverlay(); - }, - onLongPress: () { - showOptionsMenu(context, attachment); - }, - )); - } - - else if (attachment.link.isNotEmpty) { - tiles.add(ListTile( - title: Text(attachment.link), - subtitle: Text(attachment.comment), - leading: Icon(TablerIcons.link, color: COLOR_ACTION), - onTap: () async { - var uri = Uri.tryParse(attachment.link.trimLeft()); - if (uri != null && await canLaunchUrl(uri)) { - await launchUrl(uri); - } - }, - onLongPress: () { - showOptionsMenu(context, attachment); - }, - )); + tiles.add( + ListTile( + title: Text(attachment.filename), + subtitle: Text(attachment.comment), + leading: Icon(attachment.icon, color: COLOR_ACTION), + onTap: () async { + showLoadingOverlay(); + await attachment.downloadAttachment(); + hideLoadingOverlay(); + }, + onLongPress: () { + showOptionsMenu(context, attachment); + }, + ), + ); + } else if (attachment.link.isNotEmpty) { + tiles.add( + ListTile( + title: Text(attachment.link), + subtitle: Text(attachment.comment), + leading: Icon(TablerIcons.link, color: COLOR_ACTION), + onTap: () async { + var uri = Uri.tryParse(attachment.link.trimLeft()); + if (uri != null && await canLaunchUrl(uri)) { + await launchUrl(uri); + } + }, + onLongPress: () { + showOptionsMenu(context, attachment); + }, + ), + ); } } if (tiles.isEmpty) { - tiles.add(ListTile( - leading: Icon(TablerIcons.file_x, color: COLOR_WARNING), - title: Text(L10().attachmentNone), - )); + tiles.add( + ListTile( + leading: Icon(TablerIcons.file_x, color: COLOR_WARNING), + title: Text(L10().attachmentNone), + ), + ); } return tiles; diff --git a/lib/widget/back.dart b/lib/widget/back.dart index 27a9c23a..f5d342ae 100644 --- a/lib/widget/back.dart +++ b/lib/widget/back.dart @@ -6,7 +6,6 @@ import "package:flutter/material.dart"; * Long-pressing on this will return the user to the home screen */ Widget backButton(BuildContext context, GlobalKey key) { - return GestureDetector( onLongPress: () { // Display the menu @@ -21,4 +20,4 @@ Widget backButton(BuildContext context, GlobalKey key) { }, ), ); -} \ No newline at end of file +} diff --git a/lib/widget/company/company_detail.dart b/lib/widget/company/company_detail.dart index 10073869..076a1e66 100644 --- a/lib/widget/company/company_detail.dart +++ b/lib/widget/company/company_detail.dart @@ -16,24 +16,19 @@ import "package:inventree/widget/refreshable_state.dart"; import "package:inventree/widget/snacks.dart"; import "package:inventree/widget/company/supplier_part_list.dart"; - /* * Widget for displaying detail view of a single Company instance */ class CompanyDetailWidget extends StatefulWidget { - const CompanyDetailWidget(this.company, {Key? key}) : super(key: key); final InvenTreeCompany company; @override _CompanyDetailState createState() => _CompanyDetailState(); - } - class _CompanyDetailState extends RefreshableState { - _CompanyDetailState(); int supplierPartCount = 0; @@ -61,15 +56,15 @@ class _CompanyDetailState extends RefreshableState { if (InvenTreeCompany().canEdit) { actions.add( IconButton( - icon: Icon(TablerIcons.edit), - tooltip: L10().companyEdit, - onPressed: () { - editCompany(context); - } - ) + icon: Icon(TablerIcons.edit), + tooltip: L10().companyEdit, + onPressed: () { + editCompany(context); + }, + ), ); } - + return actions; } @@ -78,23 +73,27 @@ class _CompanyDetailState extends RefreshableState { List actions = []; if (widget.company.isCustomer && InvenTreeSalesOrder().canCreate) { - actions.add(SpeedDialChild( - child: Icon(TablerIcons.truck), - label: L10().salesOrderCreate, - onTap: () async { - _createSalesOrder(context); - } - )); + actions.add( + SpeedDialChild( + child: Icon(TablerIcons.truck), + label: L10().salesOrderCreate, + onTap: () async { + _createSalesOrder(context); + }, + ), + ); } if (widget.company.isSupplier && InvenTreePurchaseOrder().canCreate) { - actions.add(SpeedDialChild( - child: Icon(TablerIcons.shopping_cart), - label: L10().purchaseOrderCreate, - onTap: () async { - _createPurchaseOrder(context); - } - )); + actions.add( + SpeedDialChild( + child: Icon(TablerIcons.shopping_cart), + label: L10().purchaseOrderCreate, + onTap: () async { + _createPurchaseOrder(context); + }, + ), + ); } return actions; @@ -109,17 +108,17 @@ class _CompanyDetailState extends RefreshableState { fields["customer"]?["value"] = widget.company.pk; InvenTreeSalesOrder().createForm( - context, - L10().salesOrderCreate, - fields: fields, - onSuccess: (result) async { - Map data = result as Map; + context, + L10().salesOrderCreate, + fields: fields, + onSuccess: (result) async { + Map data = result as Map; - if (data.containsKey("pk")) { - var order = InvenTreeSalesOrder.fromJson(data); - order.goToDetailPage(context); - } + if (data.containsKey("pk")) { + var order = InvenTreeSalesOrder.fromJson(data); + order.goToDetailPage(context); } + }, ); } @@ -132,17 +131,17 @@ class _CompanyDetailState extends RefreshableState { fields["supplier"]?["value"] = widget.company.pk; InvenTreePurchaseOrder().createForm( - context, - L10().purchaseOrderCreate, - fields: fields, - onSuccess: (result) async { - Map data = result as Map; + context, + L10().purchaseOrderCreate, + fields: fields, + onSuccess: (result) async { + Map data = result as Map; - if (data.containsKey("pk")) { - var order = InvenTreePurchaseOrder.fromJson(data); - order.goToDetailPage(context); - } + if (data.containsKey("pk")) { + var order = InvenTreePurchaseOrder.fromJson(data); + order.goToDetailPage(context); } + }, ); } @@ -156,32 +155,37 @@ class _CompanyDetailState extends RefreshableState { return; } - outstandingPurchaseOrders = widget.company.isSupplier ? - await InvenTreePurchaseOrder().count(filters: { - "supplier": widget.company.pk.toString(), - "outstanding": "true" - }) : 0; + outstandingPurchaseOrders = widget.company.isSupplier + ? await InvenTreePurchaseOrder().count( + filters: { + "supplier": widget.company.pk.toString(), + "outstanding": "true", + }, + ) + : 0; - outstandingSalesOrders = widget.company.isCustomer ? - await InvenTreeSalesOrder().count(filters: { - "customer": widget.company.pk.toString(), - "outstanding": "true" - }) : 0; - - InvenTreeSupplierPart().count( - filters: { - "supplier": widget.company.pk.toString() - } - ).then((value) { - if (mounted) { - setState(() { - supplierPartCount = value; + outstandingSalesOrders = widget.company.isCustomer + ? await InvenTreeSalesOrder().count( + filters: { + "customer": widget.company.pk.toString(), + "outstanding": "true", + }, + ) + : 0; + + InvenTreeSupplierPart() + .count(filters: {"supplier": widget.company.pk.toString()}) + .then((value) { + if (mounted) { + setState(() { + supplierPartCount = value; + }); + } }); - } - }); - InvenTreeCompanyAttachment().countAttachments(widget.company.pk) - .then((value) { + InvenTreeCompanyAttachment().countAttachments(widget.company.pk).then(( + value, + ) { if (mounted) { setState(() { attachmentCount = value; @@ -190,15 +194,14 @@ class _CompanyDetailState extends RefreshableState { }); } - Future editCompany(BuildContext context) async { - + Future editCompany(BuildContext context) async { widget.company.editForm( context, L10().companyEdit, onSuccess: (data) async { refresh(context); showSnackIcon(L10().companyUpdated, success: true); - } + }, ); } @@ -207,87 +210,86 @@ class _CompanyDetailState extends RefreshableState { */ @override List getTiles(BuildContext context) { - List tiles = []; bool sep = false; - tiles.add(Card( - child: ListTile( - title: Text("${widget.company.name}"), - subtitle: Text("${widget.company.description}"), - leading: InvenTreeAPI().getThumbnail(widget.company.image), + tiles.add( + Card( + child: ListTile( + title: Text("${widget.company.name}"), + subtitle: Text("${widget.company.description}"), + leading: InvenTreeAPI().getThumbnail(widget.company.image), + ), ), - )); + ); if (!widget.company.active) { tiles.add( - ListTile( - title: Text( - L10().inactive, - style: TextStyle( - color: COLOR_DANGER - ) - ), - subtitle: Text( - L10().inactiveCompany, - style: TextStyle( - color: COLOR_DANGER - ) - ), - leading: Icon( - TablerIcons.exclamation_circle, - color: COLOR_DANGER - ), - ) + ListTile( + title: Text(L10().inactive, style: TextStyle(color: COLOR_DANGER)), + subtitle: Text( + L10().inactiveCompany, + style: TextStyle(color: COLOR_DANGER), + ), + leading: Icon(TablerIcons.exclamation_circle, color: COLOR_DANGER), + ), ); } - if (widget.company.website.isNotEmpty) { - tiles.add(ListTile( - title: Text("${widget.company.website}"), - leading: Icon(TablerIcons.globe, color: COLOR_ACTION), - onTap: () async { - openLink(widget.company.website); - }, - )); + if (widget.company.website.isNotEmpty) { + tiles.add( + ListTile( + title: Text("${widget.company.website}"), + leading: Icon(TablerIcons.globe, color: COLOR_ACTION), + onTap: () async { + openLink(widget.company.website); + }, + ), + ); - sep = true; - } + sep = true; + } - if (widget.company.email.isNotEmpty) { - tiles.add(ListTile( - title: Text("${widget.company.email}"), - leading: Icon(TablerIcons.at, color: COLOR_ACTION), - onTap: () async { - openLink("mailto:${widget.company.email}"); - }, - )); + if (widget.company.email.isNotEmpty) { + tiles.add( + ListTile( + title: Text("${widget.company.email}"), + leading: Icon(TablerIcons.at, color: COLOR_ACTION), + onTap: () async { + openLink("mailto:${widget.company.email}"); + }, + ), + ); - sep = true; - } + sep = true; + } - if (widget.company.phone.isNotEmpty) { - tiles.add(ListTile( - title: Text("${widget.company.phone}"), - leading: Icon(TablerIcons.phone, color: COLOR_ACTION), - onTap: () { - openLink("tel:${widget.company.phone}"); - }, - )); + if (widget.company.phone.isNotEmpty) { + tiles.add( + ListTile( + title: Text("${widget.company.phone}"), + leading: Icon(TablerIcons.phone, color: COLOR_ACTION), + onTap: () { + openLink("tel:${widget.company.phone}"); + }, + ), + ); - sep = true; - } + sep = true; + } // External link if (widget.company.link.isNotEmpty) { - tiles.add(ListTile( - title: Text("${widget.company.link}"), - leading: Icon(TablerIcons.link, color: COLOR_ACTION), - onTap: () { - widget.company.openLink(); - }, - )); + tiles.add( + ListTile( + title: Text("${widget.company.link}"), + leading: Icon(TablerIcons.link, color: COLOR_ACTION), + onTap: () { + widget.company.openLink(); + }, + ), + ); sep = true; } @@ -297,7 +299,6 @@ class _CompanyDetailState extends RefreshableState { } if (widget.company.isSupplier) { - if (supplierPartCount > 0) { tiles.add( ListTile( @@ -309,12 +310,12 @@ class _CompanyDetailState extends RefreshableState { context, MaterialPageRoute( builder: (context) => SupplierPartList({ - "supplier": widget.company.pk.toString() - }) - ) + "supplier": widget.company.pk.toString(), + }), + ), ); - } - ) + }, + ), ); } @@ -328,14 +329,12 @@ class _CompanyDetailState extends RefreshableState { context, MaterialPageRoute( builder: (context) => PurchaseOrderListWidget( - filters: { - "supplier": "${widget.company.pk}" - } - ) - ) + filters: {"supplier": "${widget.company.pk}"}, + ), + ), ); - } - ) + }, + ), ); // TODO: Display "supplied parts" count (click through to list of supplier parts) @@ -365,46 +364,46 @@ class _CompanyDetailState extends RefreshableState { context, MaterialPageRoute( builder: (context) => SalesOrderListWidget( - filters: { - "customer": widget.company.pk.toString() - } - ) - ) + filters: {"customer": widget.company.pk.toString()}, + ), + ), ); - } - ) + }, + ), ); } if (widget.company.notes.isNotEmpty) { - tiles.add(ListTile( - title: Text(L10().notes), - leading: Icon(TablerIcons.note), - onTap: null, - )); + tiles.add( + ListTile( + title: Text(L10().notes), + leading: Icon(TablerIcons.note), + onTap: null, + ), + ); } - - tiles.add(ListTile( - title: Text(L10().attachments), - leading: Icon(TablerIcons.file, color: COLOR_ACTION), - trailing: attachmentCount > 0 ? Text(attachmentCount.toString()) : null, - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => AttachmentWidget( - InvenTreeCompanyAttachment(), - widget.company.pk, - widget.company.name, - InvenTreeCompany().canEdit - ) - ) - ); - } - )); + tiles.add( + ListTile( + title: Text(L10().attachments), + leading: Icon(TablerIcons.file, color: COLOR_ACTION), + trailing: attachmentCount > 0 ? Text(attachmentCount.toString()) : null, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AttachmentWidget( + InvenTreeCompanyAttachment(), + widget.company.pk, + widget.company.name, + InvenTreeCompany().canEdit, + ), + ), + ); + }, + ), + ); return tiles; } - -} \ No newline at end of file +} diff --git a/lib/widget/company/company_list.dart b/lib/widget/company/company_list.dart index 8b0f03ca..69b851d3 100644 --- a/lib/widget/company/company_list.dart +++ b/lib/widget/company/company_list.dart @@ -1,4 +1,3 @@ - import "package:flutter/material.dart"; import "package:flutter_speed_dial/flutter_speed_dial.dart"; import "package:flutter_tabler_icons/flutter_tabler_icons.dart"; @@ -12,13 +11,12 @@ import "package:inventree/inventree/model.dart"; import "package:inventree/widget/paginator.dart"; import "package:inventree/widget/refreshable_state.dart"; - /* * Widget for displaying a filterable list of Company instances */ class CompanyListWidget extends StatefulWidget { - - const CompanyListWidget(this.title, this.filters, {Key? key}) : super(key: key); + const CompanyListWidget(this.title, this.filters, {Key? key}) + : super(key: key); final String title; @@ -28,16 +26,13 @@ class CompanyListWidget extends StatefulWidget { _CompanyListWidgetState createState() => _CompanyListWidgetState(); } - class _CompanyListWidgetState extends RefreshableState { - _CompanyListWidgetState(); @override String getAppBarTitle() => widget.title; Future _addCompany(BuildContext context) async { - InvenTreeCompany().createForm( context, L10().companyAdd, @@ -49,7 +44,7 @@ class _CompanyListWidgetState extends RefreshableState { var company = InvenTreeCompany.fromJson(data); company.goToDetailPage(context); } - } + }, ); } @@ -59,13 +54,13 @@ class _CompanyListWidgetState extends RefreshableState { if (InvenTreeAPI().checkPermission("company", "add")) { actions.add( - SpeedDialChild( - child: Icon(TablerIcons.circle_plus, color: Colors.green), - label: L10().companyAdd, - onTap: () { - _addCompany(context); - } - ) + SpeedDialChild( + child: Icon(TablerIcons.circle_plus, color: Colors.green), + label: L10().companyAdd, + onTap: () { + _addCompany(context); + }, + ), ); } @@ -76,12 +71,11 @@ class _CompanyListWidgetState extends RefreshableState { Widget getBody(BuildContext context) { return PaginatedCompanyList(widget.title, widget.filters); } - } class PaginatedCompanyList extends PaginatedSearchWidget { - - const PaginatedCompanyList(this.companyTitle, Map filters) : super(filters: filters); + const PaginatedCompanyList(this.companyTitle, Map filters) + : super(filters: filters); final String companyTitle; @@ -93,12 +87,10 @@ class PaginatedCompanyList extends PaginatedSearchWidget { } class _CompanyListState extends PaginatedSearchState { - _CompanyListState() : super(); @override Map> get filterOptions { - Map> filters = {}; if (InvenTreeAPI().supportsCompanyActiveStatus) { @@ -113,16 +105,22 @@ class _CompanyListState extends PaginatedSearchState { } @override - Future requestPage(int limit, int offset, Map params) async { - - final page = await InvenTreeCompany().listPaginated(limit, offset, filters: params); + Future requestPage( + int limit, + int offset, + Map params, + ) async { + final page = await InvenTreeCompany().listPaginated( + limit, + offset, + filters: params, + ); return page; } @override Widget buildItem(BuildContext context, InvenTreeModel model) { - InvenTreeCompany company = model as InvenTreeCompany; return ListTile( diff --git a/lib/widget/company/manufacturer_part_detail.dart b/lib/widget/company/manufacturer_part_detail.dart index 6d8fd095..5267a31b 100644 --- a/lib/widget/company/manufacturer_part_detail.dart +++ b/lib/widget/company/manufacturer_part_detail.dart @@ -1,4 +1,3 @@ - import "package:flutter/material.dart"; import "package:flutter_speed_dial/flutter_speed_dial.dart"; import "package:flutter_tabler_icons/flutter_tabler_icons.dart"; @@ -19,19 +18,18 @@ import "package:url_launcher/url_launcher.dart"; * Detail widget for viewing a single ManufacturerPart instance */ class ManufacturerPartDetailWidget extends StatefulWidget { - const ManufacturerPartDetailWidget(this.manufacturerPart, {Key? key}) - : super(key: key); + : super(key: key); final InvenTreeManufacturerPart manufacturerPart; @override - _ManufacturerPartDisplayState createState() => _ManufacturerPartDisplayState(); + _ManufacturerPartDisplayState createState() => + _ManufacturerPartDisplayState(); } - -class _ManufacturerPartDisplayState extends RefreshableState { - +class _ManufacturerPartDisplayState + extends RefreshableState { _ManufacturerPartDisplayState(); @override @@ -39,7 +37,8 @@ class _ManufacturerPartDisplayState extends RefreshableState request(BuildContext context) async { - final bool result = widget.manufacturerPart.pk > 0 && + final bool result = + widget.manufacturerPart.pk > 0 && await widget.manufacturerPart.reload(); if (!result) { @@ -49,12 +48,12 @@ class _ManufacturerPartDisplayState extends RefreshableState editManufacturerPart(BuildContext context) async { widget.manufacturerPart.editForm( - context, - L10().manufacturerPartEdit, - onSuccess: (data) async { - refresh(context); - showSnackIcon(L10().itemUpdated, success: true); - } + context, + L10().manufacturerPartEdit, + onSuccess: (data) async { + refresh(context); + showSnackIcon(L10().itemUpdated, success: true); + }, ); } @@ -73,13 +72,13 @@ class _ManufacturerPartDisplayState extends RefreshableState _SupplierPartDisplayState(); } - -class _SupplierPartDisplayState extends RefreshableState { - +class _SupplierPartDisplayState + extends RefreshableState { _SupplierPartDisplayState(); @override @@ -44,12 +42,12 @@ class _SupplierPartDisplayState extends RefreshableState editSupplierPart(BuildContext context) async { widget.supplierPart.editForm( - context, - L10().supplierPartEdit, - onSuccess: (data) async { - refresh(context); - showSnackIcon(L10().supplierPartUpdated, success: true); - } + context, + L10().supplierPartEdit, + onSuccess: (data) async { + refresh(context); + showSnackIcon(L10().supplierPartUpdated, success: true); + }, ); } @@ -60,11 +58,12 @@ class _SupplierPartDisplayState extends RefreshableState request(BuildContext context) async { - final bool result = widget.supplierPart.pk > 0 && await widget.supplierPart.reload(); + final bool result = + widget.supplierPart.pk > 0 && await widget.supplierPart.reload(); if (!result) { Navigator.of(context).pop(); @@ -113,43 +113,33 @@ class _SupplierPartDisplayState extends RefreshableState ManufacturerPartDetailWidget(manufacturerPart) - )); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + ManufacturerPartDetailWidget(manufacturerPart), + ), + ); } }, - ) + ), ); } // Packaging - if (widget.supplierPart.packaging.isNotEmpty || widget.supplierPart.pack_quantity.isNotEmpty) { + if (widget.supplierPart.packaging.isNotEmpty || + widget.supplierPart.pack_quantity.isNotEmpty) { tiles.add( ListTile( title: Text(L10().packaging), - subtitle: widget.supplierPart.packaging.isNotEmpty ? Text(widget.supplierPart.packaging) : null, + subtitle: widget.supplierPart.packaging.isNotEmpty + ? Text(widget.supplierPart.packaging) + : null, leading: Icon(TablerIcons.package), - trailing: widget.supplierPart.pack_quantity.isNotEmpty ? Text(widget.supplierPart.pack_quantity) : null, - ) + trailing: widget.supplierPart.pack_quantity.isNotEmpty + ? Text(widget.supplierPart.pack_quantity) + : null, + ), ); } @@ -244,7 +253,7 @@ class _SupplierPartDisplayState extends RefreshableState filters; @@ -24,9 +22,7 @@ class SupplierPartList extends StatefulWidget { _SupplierPartListState createState() => _SupplierPartListState(); } - class _SupplierPartListState extends RefreshableState { - @override String getAppBarTitle() => L10().supplierParts; @@ -34,25 +30,22 @@ class _SupplierPartListState extends RefreshableState { Widget getBody(BuildContext context) { return PaginatedSupplierPartList(widget.filters); } - } - class PaginatedSupplierPartList extends PaginatedSearchWidget { - - const PaginatedSupplierPartList(Map filters) : super(filters: filters); + const PaginatedSupplierPartList(Map filters) + : super(filters: filters); @override String get searchTitle => L10().supplierParts; @override - _PaginatedSupplierPartListState createState() => _PaginatedSupplierPartListState(); - + _PaginatedSupplierPartListState createState() => + _PaginatedSupplierPartListState(); } - -class _PaginatedSupplierPartListState extends PaginatedSearchState { - +class _PaginatedSupplierPartListState + extends PaginatedSearchState { _PaginatedSupplierPartListState() : super(); @override @@ -63,7 +56,6 @@ class _PaginatedSupplierPartListState extends PaginatedSearchState> get filterOptions { - Map> filters = {}; if (InvenTreeAPI().supportsCompanyActiveStatus) { @@ -78,8 +70,16 @@ class _PaginatedSupplierPartListState extends PaginatedSearchState requestPage(int limit, int offset, Map params) async { - final page = await InvenTreeSupplierPart().listPaginated(limit, offset, filters: params); + Future requestPage( + int limit, + int offset, + Map params, + ) async { + final page = await InvenTreeSupplierPart().listPaginated( + limit, + offset, + filters: params, + ); return page; } @@ -96,10 +96,10 @@ class _PaginatedSupplierPartListState extends PaginatedSearchState SupplierPartDetailWidget(supplierPart) - ) + builder: (context) => SupplierPartDetailWidget(supplierPart), + ), ); }, ); } -} \ No newline at end of file +} diff --git a/lib/widget/dialogs.dart b/lib/widget/dialogs.dart index f92bac3c..feb267ea 100644 --- a/lib/widget/dialogs.dart +++ b/lib/widget/dialogs.dart @@ -9,12 +9,14 @@ import "package:inventree/l10.dart"; import "package:inventree/preferences.dart"; import "package:inventree/widget/snacks.dart"; - /* * Launch a dialog allowing the user to select from a list of options */ -Future choiceDialog(String title, List items, {Function? onSelected}) async { - +Future choiceDialog( + String title, + List items, { + Function? onSelected, +}) async { List choices = []; for (int idx = 0; idx < items.length; idx++) { @@ -27,7 +29,7 @@ Future choiceDialog(String title, List items, {Function? onSelecte onSelected(idx); } }, - ) + ), ); } @@ -39,31 +41,33 @@ Future choiceDialog(String title, List items, {Function? onSelecte builder: (BuildContext context) { return AlertDialog( title: Text(title), - content: SingleChildScrollView( - child: Column( - children: choices, - ) - ), + content: SingleChildScrollView(child: Column(children: choices)), actions: [ TextButton( child: Text(L10().cancel), onPressed: () { Navigator.pop(context); }, - ) + ), ], ); - } + }, ); - } - /* * Display a "confirmation" dialog allowing the user to accept or reject an action */ -Future confirmationDialog(String title, String text, {Color? color, IconData icon = TablerIcons.help_circle, String? acceptText, String? rejectText, Function? onAccept, Function? onReject}) async { - +Future confirmationDialog( + String title, + String text, { + Color? color, + IconData icon = TablerIcons.help_circle, + String? acceptText, + String? rejectText, + Function? onAccept, + Function? onReject, +}) async { String _accept = acceptText ?? L10().ok; String _reject = rejectText ?? L10().cancel; @@ -90,7 +94,7 @@ Future confirmationDialog(String title, String text, {Color? color, IconDa if (onReject != null) { onReject(); } - } + }, ), TextButton( child: Text(_accept), @@ -102,14 +106,13 @@ Future confirmationDialog(String title, String text, {Color? color, IconDa onAccept(); } }, - ) - ] + ), + ], ); - } + }, ); } - /* * Construct an error dialog showing information to the user * @@ -117,24 +120,23 @@ Future confirmationDialog(String title, String text, {Color? color, IconDa * @description = Simple string description of error * @data = Error response (e.g from server) */ -Future showErrorDialog(String title, {String description = "", APIResponse? response, IconData icon = TablerIcons.exclamation_circle, Function? onDismissed}) async { - +Future showErrorDialog( + String title, { + String description = "", + APIResponse? response, + IconData icon = TablerIcons.exclamation_circle, + Function? onDismissed, +}) async { List children = []; if (description.isNotEmpty) { - children.add( - ListTile( - title: Text(description), - ) - ); + children.add(ListTile(title: Text(description))); } else if (response != null) { // Look for extra error information in the provided APIResponse object switch (response.statusCode) { - case 400: // Bad request (typically bad input) + case 400: // Bad request (typically bad input) if (response.data is Map) { - for (String field in response.asMap().keys) { - dynamic error = response.data[field]; if (error is List) { @@ -143,15 +145,15 @@ Future showErrorDialog(String title, {String description = "", APIResponse ListTile( title: Text(field), subtitle: Text(error[ii].toString()), - ) + ), ); } } else { children.add( - ListTile( - title: Text(field), - subtitle: Text(response.data[field].toString()), - ) + ListTile( + title: Text(field), + subtitle: Text(response.data[field].toString()), + ), ); } } @@ -159,8 +161,8 @@ Future showErrorDialog(String title, {String description = "", APIResponse children.add( ListTile( title: Text(L10().responseInvalid), - subtitle: Text(response.data.toString()) - ) + subtitle: Text(response.data.toString()), + ), ); } default: @@ -169,16 +171,15 @@ Future showErrorDialog(String title, {String description = "", APIResponse ListTile( title: Text(L10().statusCode), subtitle: Text(response.statusCode.toString()), - ) + ), ); children.add( ListTile( title: Text(L10().responseData), subtitle: Text(response.data.toString()), - ) + ), ); - } } @@ -186,26 +187,28 @@ Future showErrorDialog(String title, {String description = "", APIResponse return; } - OneContext().showDialog( - builder: (context) => SimpleDialog( - title: ListTile( - title: Text(title), - leading: Icon(icon), - ), - children: children - ) - ).then((value) { - if (onDismissed != null) { - onDismissed(); - } - }); + OneContext() + .showDialog( + builder: (context) => SimpleDialog( + title: ListTile(title: Text(title), leading: Icon(icon)), + children: children, + ), + ) + .then((value) { + if (onDismissed != null) { + onDismissed(); + } + }); } /* * Display a message indicating the nature of a server / API error */ -Future showServerError(String url, String title, String description) async { - +Future showServerError( + String url, + String title, + String description, +) async { if (!hasContext()) { return; } @@ -220,7 +223,9 @@ Future showServerError(String url, String title, String description) async } // Play a sound - final bool tones = await InvenTreeSettingsManager().getValue(INV_SOUNDS_SERVER, true) as bool; + final bool tones = + await InvenTreeSettingsManager().getValue(INV_SOUNDS_SERVER, true) + as bool; if (tones) { playAudioFile("sounds/server_error.mp3"); @@ -234,19 +239,22 @@ Future showServerError(String url, String title, String description) async actionText: L10().details, onAction: () { showErrorDialog( - title, - description: description, - icon: TablerIcons.server + title, + description: description, + icon: TablerIcons.server, ); - } + }, ); } /* * Displays an error indicating that the server returned an unexpected status code */ -Future showStatusCodeError(String url, int status, {String details=""}) async { - +Future showStatusCodeError( + String url, + int status, { + String details = "", +}) async { String msg = statusCodeToString(status); String extra = url + "\n" + "${L10().statusCode}: ${status}"; @@ -255,14 +263,9 @@ Future showStatusCodeError(String url, int status, {String details=""}) as extra += details; } - showServerError( - url, - msg, - extra, - ); + showServerError(url, msg, extra); } - /* * Provide a human-readable descriptor for a particular error code */ @@ -297,7 +300,6 @@ String statusCodeToString(int status) { } } - /* * Displays a message indicating that the server timed out on a certain request */ diff --git a/lib/widget/drawer.dart b/lib/widget/drawer.dart index 2288e340..90bae321 100644 --- a/lib/widget/drawer.dart +++ b/lib/widget/drawer.dart @@ -16,12 +16,10 @@ import "package:inventree/widget/notifications.dart"; import "package:inventree/widget/order/purchase_order_list.dart"; import "package:inventree/widget/stock/location_display.dart"; - /* * Custom "drawer" widget for the InvenTree app. */ class InvenTreeDrawer extends StatelessWidget { - const InvenTreeDrawer(this.context); final BuildContext context; @@ -53,8 +51,8 @@ class InvenTreeDrawer extends StatelessWidget { if (_checkConnection()) { Navigator.push( - context, - MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null)) + context, + MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null)), ); } } @@ -65,8 +63,8 @@ class InvenTreeDrawer extends StatelessWidget { if (_checkConnection()) { Navigator.push( - context, - MaterialPageRoute(builder: (context) => LocationDisplayWidget(null)) + context, + MaterialPageRoute(builder: (context) => LocationDisplayWidget(null)), ); } } @@ -77,24 +75,24 @@ class InvenTreeDrawer extends StatelessWidget { if (_checkConnection()) { Navigator.push( - context, - MaterialPageRoute( - builder: (context) => SalesOrderListWidget(filters: {}) - ) + context, + MaterialPageRoute( + builder: (context) => SalesOrderListWidget(filters: {}), + ), ); } } - + // Load "purchase orders" page void _purchaseOrders() { _closeDrawer(); if (_checkConnection()) { Navigator.push( - context, - MaterialPageRoute( - builder: (context) => PurchaseOrderListWidget(filters: {}) - ) + context, + MaterialPageRoute( + builder: (context) => PurchaseOrderListWidget(filters: {}), + ), ); } } @@ -104,15 +102,20 @@ class InvenTreeDrawer extends StatelessWidget { _closeDrawer(); if (_checkConnection()) { - Navigator.push(context, - MaterialPageRoute(builder: (context) => NotificationWidget())); + Navigator.push( + context, + MaterialPageRoute(builder: (context) => NotificationWidget()), + ); } } // Load settings widget void _settings() { _closeDrawer(); - Navigator.push(context, MaterialPageRoute(builder: (context) => InvenTreeSettingsWidget())); + Navigator.push( + context, + MaterialPageRoute(builder: (context) => InvenTreeSettingsWidget()), + ); } // Construct list of tiles to display in the "drawer" menu @@ -120,14 +123,16 @@ class InvenTreeDrawer extends StatelessWidget { List tiles = []; // "Home" access - tiles.add(ListTile( - leading: Icon(TablerIcons.home, color: COLOR_ACTION), - title: Text( - L10().appTitle, - style: TextStyle(fontWeight: FontWeight.bold), + tiles.add( + ListTile( + leading: Icon(TablerIcons.home, color: COLOR_ACTION), + title: Text( + L10().appTitle, + style: TextStyle(fontWeight: FontWeight.bold), + ), + onTap: _home, ), - onTap: _home, - )); + ); tiles.add(Divider()); @@ -137,7 +142,7 @@ class InvenTreeDrawer extends StatelessWidget { title: Text(L10().parts), leading: Icon(TablerIcons.box, color: COLOR_ACTION), onTap: _parts, - ) + ), ); } @@ -147,7 +152,7 @@ class InvenTreeDrawer extends StatelessWidget { title: Text(L10().stock), leading: Icon(TablerIcons.package, color: COLOR_ACTION), onTap: _stock, - ) + ), ); } @@ -157,7 +162,7 @@ class InvenTreeDrawer extends StatelessWidget { title: Text(L10().purchaseOrders), leading: Icon(TablerIcons.shopping_cart, color: COLOR_ACTION), onTap: _purchaseOrders, - ) + ), ); } @@ -167,7 +172,7 @@ class InvenTreeDrawer extends StatelessWidget { title: Text(L10().salesOrders), leading: Icon(TablerIcons.truck_delivery, color: COLOR_ACTION), onTap: _salesOrders, - ) + ), ); } @@ -180,10 +185,12 @@ class InvenTreeDrawer extends StatelessWidget { tiles.add( ListTile( leading: Icon(TablerIcons.bell, color: COLOR_ACTION), - trailing: notification_count > 0 ? Text(notification_count.toString()) : null, + trailing: notification_count > 0 + ? Text(notification_count.toString()) + : null, title: Text(L10().notifications), onTap: _notifications, - ) + ), ); tiles.add(Divider()); @@ -198,14 +205,9 @@ class InvenTreeDrawer extends StatelessWidget { }, title: Text(L10().colorScheme), subtitle: Text(L10().colorSchemeDetail), - leading: Icon( - TablerIcons.sun_moon, - color: COLOR_ACTION - ), - trailing: Icon( - darkMode ? TablerIcons.moon : TablerIcons.sun, - ) - ) + leading: Icon(TablerIcons.sun_moon, color: COLOR_ACTION), + trailing: Icon(darkMode ? TablerIcons.moon : TablerIcons.sun), + ), ); tiles.add( @@ -213,7 +215,7 @@ class InvenTreeDrawer extends StatelessWidget { title: Text(L10().settings), leading: Icon(Icons.settings, color: COLOR_ACTION), onTap: _settings, - ) + ), ); return tiles; @@ -221,11 +223,6 @@ class InvenTreeDrawer extends StatelessWidget { @override Widget build(BuildContext context) { - - return Drawer( - child: ListView( - children: drawerTiles(context), - ) - ); + return Drawer(child: ListView(children: drawerTiles(context))); } } diff --git a/lib/widget/fields.dart b/lib/widget/fields.dart index 8a7d6007..406c0fee 100644 --- a/lib/widget/fields.dart +++ b/lib/widget/fields.dart @@ -9,11 +9,8 @@ import "package:one_context/one_context.dart"; import "package:inventree/l10.dart"; - class FilePickerDialog { - static Future pickImageFromCamera() async { - final picker = ImagePicker(); final pickedImage = await picker.pickImage(source: ImageSource.camera); @@ -26,7 +23,6 @@ class FilePickerDialog { } static Future pickImageFromGallery() async { - final picker = ImagePicker(); final pickedImage = await picker.pickImage(source: ImageSource.gallery); @@ -39,7 +35,6 @@ class FilePickerDialog { } static Future pickFileFromDevice() async { - final FilePickerResult? result = await FilePicker.platform.pickFiles(); if (result != null) { @@ -54,8 +49,12 @@ class FilePickerDialog { } // Present a dialog to pick a file, either from local file system or from camera - static Future pickFile({String message = "", bool allowImages = true, bool allowFiles = true, Function(File)? onPicked}) async { - + static Future pickFile({ + String message = "", + bool allowImages = true, + bool allowFiles = true, + Function(File)? onPicked, + }) async { String title = ""; if (allowImages && !allowFiles) { @@ -65,16 +64,10 @@ class FilePickerDialog { } // Construct actions - List actions = [ - - ]; + List actions = []; if (message.isNotEmpty) { - actions.add( - ListTile( - title: Text(message) - ) - ); + actions.add(ListTile(title: Text(message))); } actions.add( @@ -84,7 +77,6 @@ class FilePickerDialog { title: Text(allowFiles ? L10().selectFile : L10().selectImage), ), onPressed: () async { - // Close the dialog OneContext().popDialog(); @@ -101,7 +93,7 @@ class FilePickerDialog { } } }, - ) + ), ); if (allowImages) { @@ -122,100 +114,104 @@ class FilePickerDialog { onPicked(file); } } - } - ) + }, + ), ); } OneContext().showDialog( - builder: (context) { - return SimpleDialog( - title: Text(title), - children: actions, - ); - } + builder: (context) { + return SimpleDialog(title: Text(title), children: actions); + }, ); } - } - class CheckBoxField extends FormField { CheckBoxField({ - String? label, - bool? initial = false, - bool tristate = false, - Function(bool?)? onSaved, - TextStyle? labelStyle, - String? helperText, - TextStyle? helperStyle, - }) : - super( - onSaved: onSaved, - initialValue: initial, - builder: (FormFieldState state) { - - return CheckboxListTile( - title: label != null ? Text(label, style: labelStyle) : null, - value: state.value, - tristate: tristate, - onChanged: state.didChange, - subtitle: helperText != null ? Text(helperText, style: helperStyle) : null, - contentPadding: EdgeInsets.zero, - ); - } - ); + String? label, + bool? initial = false, + bool tristate = false, + Function(bool?)? onSaved, + TextStyle? labelStyle, + String? helperText, + TextStyle? helperStyle, + }) : super( + onSaved: onSaved, + initialValue: initial, + builder: (FormFieldState state) { + return CheckboxListTile( + title: label != null ? Text(label, style: labelStyle) : null, + value: state.value, + tristate: tristate, + onChanged: state.didChange, + subtitle: helperText != null + ? Text(helperText, style: helperStyle) + : null, + contentPadding: EdgeInsets.zero, + ); + }, + ); } class StringField extends TextFormField { + StringField({ + String label = "", + String? hint, + String? initial, + Function(String?)? onSaved, + Function(String?)? validator, + bool allowEmpty = false, + bool isEnabled = true, + }) : super( + decoration: InputDecoration( + labelText: allowEmpty ? label : label + "*", + hintText: hint, + ), + initialValue: initial, + onSaved: onSaved, + enabled: isEnabled, + validator: (value) { + if (!allowEmpty && value != null && value.isEmpty) { + return L10().valueCannotBeEmpty; + } - StringField({String label = "", String? hint, String? initial, Function(String?)? onSaved, Function(String?)? validator, bool allowEmpty = false, bool isEnabled = true}) : - super( - decoration: InputDecoration( - labelText: allowEmpty ? label : label + "*", - hintText: hint - ), - initialValue: initial, - onSaved: onSaved, - enabled: isEnabled, - validator: (value) { - if (!allowEmpty && value != null && value.isEmpty) { - return L10().valueCannotBeEmpty; - } + if (validator != null) { + return validator(value) as String?; + } - if (validator != null) { - return validator(value) as String?; - } - - return null; - } - ); + return null; + }, + ); } - /* * Helper class for quantity values */ class QuantityField extends TextFormField { + QuantityField({ + String label = "", + String hint = "", + double? max, + TextEditingController? controller, + }) : super( + decoration: InputDecoration(labelText: label, hintText: hint), + controller: controller, + keyboardType: TextInputType.numberWithOptions( + signed: false, + decimal: true, + ), + validator: (value) { + if (value != null && value.isEmpty) return L10().quantityEmpty; - QuantityField({String label = "", String hint = "", double? max, TextEditingController? controller}) : - super( - decoration: InputDecoration( - labelText: label, - hintText: hint, - ), - controller: controller, - keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true), - validator: (value) { + double quantity = double.tryParse(value.toString()) ?? 0; - if (value != null && value.isEmpty) return L10().quantityEmpty; + if (quantity <= 0) return L10().quantityPositive; + if ((max != null) && (quantity > max)) { + return "Quantity must not exceed ${max}"; + } - double quantity = double.tryParse(value.toString()) ?? 0; - - if (quantity <= 0) return L10().quantityPositive; - if ((max != null) && (quantity > max)) return "Quantity must not exceed ${max}"; - - return null; - }, - ); -} \ No newline at end of file + return null; + }, + ); +} diff --git a/lib/widget/home.dart b/lib/widget/home.dart index f7a84045..2a2b615d 100644 --- a/lib/widget/home.dart +++ b/lib/widget/home.dart @@ -26,18 +26,15 @@ import "package:inventree/widget/snacks.dart"; import "package:inventree/widget/spinner.dart"; import "package:inventree/widget/company/company_list.dart"; - class InvenTreeHomePage extends StatefulWidget { - const InvenTreeHomePage({Key? key}) : super(key: key); @override _InvenTreeHomePageState createState() => _InvenTreeHomePageState(); } - -class _InvenTreeHomePageState extends State with BaseWidgetProperties { - +class _InvenTreeHomePageState extends State + with BaseWidgetProperties { _InvenTreeHomePageState() : super() { // Load display settings _loadSettings(); @@ -46,7 +43,6 @@ class _InvenTreeHomePageState extends State with BaseWidgetPr _loadProfile(); InvenTreeAPI().registerCallback(() { - if (mounted) { setState(() { // Reload the widget @@ -70,7 +66,10 @@ class _InvenTreeHomePageState extends State with BaseWidgetPr void _showParts(BuildContext context) { if (!InvenTreeAPI().checkConnection()) return; - Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null))); + Navigator.push( + context, + MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null)), + ); } void _showStarredParts(BuildContext context) { @@ -78,18 +77,17 @@ class _InvenTreeHomePageState extends State with BaseWidgetPr Navigator.push( context, - MaterialPageRoute( - builder: (context) => PartList({ - "starred": "true" - }) - ) + MaterialPageRoute(builder: (context) => PartList({"starred": "true"})), ); } void _showStock(BuildContext context) { if (!InvenTreeAPI().checkConnection()) return; - Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(null))); + Navigator.push( + context, + MaterialPageRoute(builder: (context) => LocationDisplayWidget(null)), + ); } void _showPurchaseOrders(BuildContext context) { @@ -98,8 +96,8 @@ class _InvenTreeHomePageState extends State with BaseWidgetPr Navigator.push( context, MaterialPageRoute( - builder: (context) => PurchaseOrderListWidget(filters: {}) - ) + builder: (context) => PurchaseOrderListWidget(filters: {}), + ), ); } @@ -107,17 +105,23 @@ class _InvenTreeHomePageState extends State with BaseWidgetPr if (!InvenTreeAPI().checkConnection()) return; Navigator.push( - context, - MaterialPageRoute( - builder: (context) => SalesOrderListWidget(filters: {}) - ) + context, + MaterialPageRoute( + builder: (context) => SalesOrderListWidget(filters: {}), + ), ); } void _showSuppliers(BuildContext context) { if (!InvenTreeAPI().checkConnection()) return; - Navigator.push(context, MaterialPageRoute(builder: (context) => CompanyListWidget(L10().suppliers, {"is_supplier": "true"}))); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + CompanyListWidget(L10().suppliers, {"is_supplier": "true"}), + ), + ); } /* @@ -131,39 +135,60 @@ class _InvenTreeHomePageState extends State with BaseWidgetPr void _showCustomers(BuildContext context) { if (!InvenTreeAPI().checkConnection()) return; - Navigator.push(context, MaterialPageRoute(builder: (context) => CompanyListWidget(L10().customers, {"is_customer": "true"}))); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + CompanyListWidget(L10().customers, {"is_customer": "true"}), + ), + ); } void _selectProfile() { Navigator.push( - context, MaterialPageRoute(builder: (context) => InvenTreeSelectServerWidget()) + context, + MaterialPageRoute(builder: (context) => InvenTreeSelectServerWidget()), ).then((context) { // Once we return _loadProfile(); }); } - Future _loadSettings() async { + Future _loadSettings() async { + homeShowSubscribed = + await InvenTreeSettingsManager().getValue( + INV_HOME_SHOW_SUBSCRIBED, + true, + ) + as bool; + homeShowPo = + await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_PO, true) + as bool; + homeShowSo = + await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_SO, true) + as bool; + homeShowManufacturers = + await InvenTreeSettingsManager().getValue( + INV_HOME_SHOW_MANUFACTURERS, + true, + ) + as bool; + homeShowCustomers = + await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_CUSTOMERS, true) + as bool; + homeShowSuppliers = + await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_SUPPLIERS, true) + as bool; - homeShowSubscribed = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_SUBSCRIBED, true) as bool; - homeShowPo = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_PO, true) as bool; - homeShowSo = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_SO, true) as bool; - homeShowManufacturers = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_MANUFACTURERS, true) as bool; - homeShowCustomers = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_CUSTOMERS, true) as bool; - homeShowSuppliers = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_SUPPLIERS, true) as bool; - - setState(() { - }); + setState(() {}); } - Future _loadProfile() async { - + Future _loadProfile() async { _profile = await UserProfileDBManager().getSelectedProfile(); // A valid profile was loaded! if (_profile != null) { if (!InvenTreeAPI().isConnected() && !InvenTreeAPI().isConnecting()) { - // Attempt server connection InvenTreeAPI().connectToServer(_profile!).then((result) { if (mounted) { @@ -176,8 +201,15 @@ class _InvenTreeHomePageState extends State with BaseWidgetPr setState(() {}); } - Widget _listTile(BuildContext context, String label, IconData icon, {Function()? callback, String role = "", String permission = "", Widget? trailing}) { - + Widget _listTile( + BuildContext context, + String label, + IconData icon, { + Function()? callback, + String role = "", + String permission = "", + Widget? trailing, + }) { bool connected = InvenTreeAPI().isConnected(); bool allowed = true; @@ -192,20 +224,15 @@ class _InvenTreeHomePageState extends State with BaseWidgetPr child: Align( child: ListTile( leading: Icon( - icon, - size: 32, - color: connected && allowed ? COLOR_ACTION : Colors.grey - ), - title: Text( - label, - style: TextStyle( - fontSize: 20 - ), + icon, + size: 32, + color: connected && allowed ? COLOR_ACTION : Colors.grey, ), + title: Text(label, style: TextStyle(fontSize: 20)), trailing: trailing, ), alignment: Alignment.center, - ) + ), ), onTap: () { if (!allowed) { @@ -228,78 +255,89 @@ class _InvenTreeHomePageState extends State with BaseWidgetPr * Constructs a list of tiles for the main screen */ List getListTiles(BuildContext context) { - List tiles = []; // Parts if (InvenTreePart().canView) { - tiles.add(_listTile( - context, - L10().parts, - TablerIcons.box, - callback: () { - _showParts(context); - }, - )); + tiles.add( + _listTile( + context, + L10().parts, + TablerIcons.box, + callback: () { + _showParts(context); + }, + ), + ); } // Starred parts if (homeShowSubscribed && InvenTreePart().canView) { - tiles.add(_listTile( - context, - L10().partsStarred, - TablerIcons.bell, - callback: () { - _showStarredParts(context); - } - )); + tiles.add( + _listTile( + context, + L10().partsStarred, + TablerIcons.bell, + callback: () { + _showStarredParts(context); + }, + ), + ); } // Stock button if (InvenTreeStockItem().canView) { - tiles.add(_listTile( + tiles.add( + _listTile( context, L10().stock, TablerIcons.package, callback: () { _showStock(context); - } - )); + }, + ), + ); } // Purchase orders if (homeShowPo && InvenTreePurchaseOrder().canView) { - tiles.add(_listTile( + tiles.add( + _listTile( context, L10().purchaseOrders, TablerIcons.shopping_cart, callback: () { _showPurchaseOrders(context); - } - )); + }, + ), + ); } if (homeShowSo && InvenTreeSalesOrder().canView) { - tiles.add(_listTile( - context, - L10().salesOrders, - TablerIcons.truck_delivery, - callback: () { - _showSalesOrders(context); - } - )); + tiles.add( + _listTile( + context, + L10().salesOrders, + TablerIcons.truck_delivery, + callback: () { + _showSalesOrders(context); + }, + ), + ); } // Suppliers if (homeShowSuppliers && InvenTreePurchaseOrder().canView) { - tiles.add(_listTile( + tiles.add( + _listTile( context, L10().suppliers, TablerIcons.building, callback: () { _showSuppliers(context); - } - )); + }, + ), + ); } // TODO: Add these tiles back in once the features are fleshed out @@ -320,14 +358,16 @@ class _InvenTreeHomePageState extends State with BaseWidgetPr */ // Customers if (homeShowCustomers) { - tiles.add(_listTile( + tiles.add( + _listTile( context, L10().customers, TablerIcons.building_store, callback: () { _showCustomers(context); - } - )); + }, + ), + ); } return tiles; @@ -338,10 +378,10 @@ class _InvenTreeHomePageState extends State with BaseWidgetPr * display a connection status widget */ Widget _connectionStatusWidget(BuildContext context) { - String? serverAddress = InvenTreeAPI().serverAddress; bool validAddress = serverAddress != null; - bool connecting = !InvenTreeAPI().isConnected() && InvenTreeAPI().isConnecting(); + bool connecting = + !InvenTreeAPI().isConnected() && InvenTreeAPI().isConnecting(); Widget leading = Icon(TablerIcons.exclamation_circle, color: COLOR_DANGER); Widget trailing = Icon(TablerIcons.server, color: COLOR_ACTION); @@ -373,8 +413,8 @@ class _InvenTreeHomePageState extends State with BaseWidgetPr trailing: trailing, leading: leading, onTap: _selectProfile, - ) - ] + ), + ], ), ); } @@ -384,7 +424,6 @@ class _InvenTreeHomePageState extends State with BaseWidgetPr */ @override Widget getBody(BuildContext context) { - if (!InvenTreeAPI().isConnected()) { return _connectionStatusWidget(context); } @@ -398,7 +437,7 @@ class _InvenTreeHomePageState extends State with BaseWidgetPr int hTiles = smallScreen ? 1 : 2; double aspect = smallScreen ? 5 : 3; double padding = smallScreen ? 2 : 10; - + return GridView.count( crossAxisCount: w > h ? vTiles : hTiles, children: getListTiles(context), @@ -408,12 +447,10 @@ class _InvenTreeHomePageState extends State with BaseWidgetPr mainAxisSpacing: padding, padding: EdgeInsets.all(padding), ); - } @override Widget build(BuildContext context) { - var connected = InvenTreeAPI().isConnected(); var connecting = !connected && InvenTreeAPI().isConnecting(); @@ -426,15 +463,19 @@ class _InvenTreeHomePageState extends State with BaseWidgetPr IconButton( icon: Icon( TablerIcons.server, - color: connected ? COLOR_SUCCESS : (connecting ? COLOR_PROGRESS: COLOR_DANGER), + color: connected + ? COLOR_SUCCESS + : (connecting ? COLOR_PROGRESS : COLOR_DANGER), ), onPressed: _selectProfile, - ) + ), ], ), drawer: InvenTreeDrawer(context), body: getBody(context), - bottomNavigationBar: InvenTreeAPI().isConnected() ? buildBottomAppBar(context, homeKey) : null, + bottomNavigationBar: InvenTreeAPI().isConnected() + ? buildBottomAppBar(context, homeKey) + : null, ); } } diff --git a/lib/widget/notes_widget.dart b/lib/widget/notes_widget.dart index d871dd00..196eb918 100644 --- a/lib/widget/notes_widget.dart +++ b/lib/widget/notes_widget.dart @@ -5,7 +5,6 @@ import "package:inventree/widget/refreshable_state.dart"; import "package:flutter_markdown/flutter_markdown.dart"; import "package:inventree/l10.dart"; - /* * A widget for displaying the notes associated with a given model. * We need to pass in the following parameters: @@ -14,7 +13,6 @@ import "package:inventree/l10.dart"; * - Title for the app bar */ class NotesWidget extends StatefulWidget { - const NotesWidget(this.model, {Key? key}) : super(key: key); final InvenTreeModel model; @@ -23,12 +21,10 @@ class NotesWidget extends StatefulWidget { _NotesState createState() => _NotesState(); } - /* * Class representing the state of the NotesWidget */ class _NotesState extends RefreshableState { - _NotesState(); @override @@ -41,7 +37,6 @@ class _NotesState extends RefreshableState { @override List appBarActions(BuildContext context) { - List actions = []; if (widget.model.canEdit) { @@ -54,16 +49,14 @@ class _NotesState extends RefreshableState { context, L10().editNotes, fields: { - "notes": { - "multiline": true, - } + "notes": {"multiline": true}, }, onSuccess: (data) async { refresh(context); - } + }, ); - } - ) + }, + ), ); } @@ -72,10 +65,6 @@ class _NotesState extends RefreshableState { @override Widget getBody(BuildContext context) { - return Markdown( - selectable: false, - data: widget.model.notes, - ); + return Markdown(selectable: false, data: widget.model.notes); } - -} \ No newline at end of file +} diff --git a/lib/widget/notifications.dart b/lib/widget/notifications.dart index 9b5dabe3..d29b8c9d 100644 --- a/lib/widget/notifications.dart +++ b/lib/widget/notifications.dart @@ -1,4 +1,3 @@ - import "package:flutter/material.dart"; import "package:flutter_tabler_icons/flutter_tabler_icons.dart"; @@ -8,17 +7,12 @@ import "package:inventree/inventree/model.dart"; import "package:inventree/inventree/notification.dart"; import "package:inventree/widget/refreshable_state.dart"; - class NotificationWidget extends StatefulWidget { - @override _NotificationState createState() => _NotificationState(); - } - class _NotificationState extends RefreshableState { - _NotificationState() : super(); List notifications = []; @@ -29,8 +23,7 @@ class _NotificationState extends RefreshableState { String getAppBarTitle() => L10().notifications; @override - Future request (BuildContext context) async { - + Future request(BuildContext context) async { final results = await InvenTreeNotification().list(); notifications.clear(); @@ -45,8 +38,10 @@ class _NotificationState extends RefreshableState { /* * Dismiss an individual notification entry (mark it as "read") */ - Future dismissNotification(BuildContext context, InvenTreeNotification notification) async { - + Future dismissNotification( + BuildContext context, + InvenTreeNotification notification, + ) async { if (mounted) { setState(() { isDismissing = true; @@ -71,18 +66,17 @@ class _NotificationState extends RefreshableState { */ @override List getTiles(BuildContext context) { - List tiles = []; tiles.add( ListTile( - title: Text( - L10().notifications, - ), + title: Text(L10().notifications), subtitle: notifications.isEmpty ? Text(L10().notificationsEmpty) : null, - leading: notifications.isEmpty ? Icon(TablerIcons.bell_exclamation) : Icon(TablerIcons.bell), + leading: notifications.isEmpty + ? Icon(TablerIcons.bell_exclamation) + : Icon(TablerIcons.bell), trailing: Text("${notifications.length}"), - ) + ), ); for (var notification in notifications) { @@ -92,15 +86,16 @@ class _NotificationState extends RefreshableState { subtitle: Text(notification.message), trailing: IconButton( icon: Icon(TablerIcons.bookmark), - onPressed: isDismissing ? null : () async { - dismissNotification(context, notification); - }, + onPressed: isDismissing + ? null + : () async { + dismissNotification(context, notification); + }, ), - ) + ), ); } return tiles; - } } diff --git a/lib/widget/order/extra_line_detail.dart b/lib/widget/order/extra_line_detail.dart index bdf7dbce..055a58ec 100644 --- a/lib/widget/order/extra_line_detail.dart +++ b/lib/widget/order/extra_line_detail.dart @@ -8,7 +8,6 @@ import "package:inventree/widget/snacks.dart"; import "package:inventree/inventree/orders.dart"; - class ExtraLineDetailWidget extends StatefulWidget { const ExtraLineDetailWidget(this.item, {Key? key}) : super(key: key); @@ -18,8 +17,8 @@ class ExtraLineDetailWidget extends StatefulWidget { _ExtraLineDetailWidgetState createState() => _ExtraLineDetailWidgetState(); } -class _ExtraLineDetailWidgetState extends RefreshableState { - +class _ExtraLineDetailWidgetState + extends RefreshableState { _ExtraLineDetailWidgetState(); @override @@ -35,8 +34,8 @@ class _ExtraLineDetailWidgetState extends RefreshableState tiles = []; tiles.add( - ListTile( - title: Text(L10().reference), - trailing: Text(widget.item.reference), - ) + ListTile( + title: Text(L10().reference), + trailing: Text(widget.item.reference), + ), ); tiles.add( - ListTile( - title: Text(L10().description), - trailing: Text(widget.item.description), - ) + ListTile( + title: Text(L10().description), + trailing: Text(widget.item.description), + ), ); tiles.add( ListTile( title: Text(L10().quantity), trailing: Text(widget.item.quantity.toString()), - ) + ), ); tiles.add( ListTile( title: Text(L10().unitPrice), trailing: Text( - renderCurrency(widget.item.price, widget.item.priceCurrency) - ) - ) + renderCurrency(widget.item.price, widget.item.priceCurrency), + ), + ), ); if (widget.item.notes.isNotEmpty) { tiles.add( - ListTile( - title: Text(L10().notes), - subtitle: Text(widget.item.notes), - ) + ListTile(title: Text(L10().notes), subtitle: Text(widget.item.notes)), ); } return tiles; } -} \ No newline at end of file +} diff --git a/lib/widget/order/po_extra_line_list.dart b/lib/widget/order/po_extra_line_list.dart index 4c382976..3ede5082 100644 --- a/lib/widget/order/po_extra_line_list.dart +++ b/lib/widget/order/po_extra_line_list.dart @@ -9,28 +9,27 @@ import "package:inventree/widget/paginator.dart"; import "package:inventree/widget/refreshable_state.dart"; import "package:inventree/widget/snacks.dart"; - class POExtraLineListWidget extends StatefulWidget { - - const POExtraLineListWidget(this.order, {this.filters = const {}, Key? key}) : super(key: key); + const POExtraLineListWidget(this.order, {this.filters = const {}, Key? key}) + : super(key: key); final InvenTreePurchaseOrder order; final Map filters; @override - _PurchaseOrderExtraLineListWidgetState createState() => _PurchaseOrderExtraLineListWidgetState(); + _PurchaseOrderExtraLineListWidgetState createState() => + _PurchaseOrderExtraLineListWidgetState(); } -class _PurchaseOrderExtraLineListWidgetState extends RefreshableState { - +class _PurchaseOrderExtraLineListWidgetState + extends RefreshableState { _PurchaseOrderExtraLineListWidgetState(); @override String getAppBarTitle() => L10().extraLineItems; Future _addLineItem(BuildContext context) async { - var fields = InvenTreePOExtraLineItem().formFields(); fields["order"]?["value"] = widget.order.pk; @@ -42,7 +41,7 @@ class _PurchaseOrderExtraLineListWidgetState extends RefreshableState filters) : super(filters: filters); + const PaginatedPOExtraLineList(Map filters) + : super(filters: filters); @override String get searchTitle => L10().extraLineItems; @override - _PaginatedPOExtraLineListState createState() => _PaginatedPOExtraLineListState(); - + _PaginatedPOExtraLineListState createState() => + _PaginatedPOExtraLineListState(); } -class _PaginatedPOExtraLineListState extends PaginatedSearchState { - +class _PaginatedPOExtraLineListState + extends PaginatedSearchState { _PaginatedPOExtraLineListState() : super(); @override String get prefix => "po_extra_line_"; @override - Future requestPage(int limit, int offset, Map params) async { - final page = await InvenTreePOExtraLineItem().listPaginated(limit, offset, filters: params); + Future requestPage( + int limit, + int offset, + Map params, + ) async { + final page = await InvenTreePOExtraLineItem().listPaginated( + limit, + offset, + filters: params, + ); return page; } @override Widget buildItem(BuildContext context, InvenTreeModel model) { - InvenTreePOExtraLineItem line = model as InvenTreePOExtraLineItem; return ListTile( @@ -113,4 +118,4 @@ class _PaginatedPOExtraLineListState extends PaginatedSearchState _POLineDetailWidgetState(); - } - /* * State for the POLineDetailWidget */ class _POLineDetailWidgetState extends RefreshableState { - _POLineDetailWidgetState(); InvenTreeStockLocation? destination; @@ -55,7 +51,7 @@ class _POLineDetailWidgetState extends RefreshableState { onPressed: () { _editLineItem(context); }, - ) + ), ); } @@ -75,8 +71,8 @@ class _POLineDetailWidgetState extends RefreshableState { label: L10().receiveItem, onTap: () async { receiveLineItem(context); - } - ) + }, + ), ); } } @@ -89,7 +85,9 @@ class _POLineDetailWidgetState extends RefreshableState { await widget.item.reload(); if (widget.item.destinationId > 0) { - InvenTreeStockLocation().get(widget.item.destinationId).then((InvenTreeModel? loc) { + InvenTreeStockLocation().get(widget.item.destinationId).then(( + InvenTreeModel? loc, + ) { if (mounted) { if (loc != null && loc is InvenTreeStockLocation) { setState(() { @@ -109,7 +107,6 @@ class _POLineDetailWidgetState extends RefreshableState { }); } } - } // Callback to edit this line item @@ -123,21 +120,21 @@ class _POLineDetailWidgetState extends RefreshableState { onSuccess: (data) async { refresh(context); showSnackIcon(L10().lineItemUpdated, success: true); - } + }, ); } - // Launch a form to 'receive' this line item + // Launch a form to 'receive' this line item Future receiveLineItem(BuildContext context) async { widget.item.receive( context, onSuccess: () => { showSnackIcon(L10().receivedItem, success: true), - refresh(context) - } + refresh(context), + }, ); } - + @override List getTiles(BuildContext context) { List tiles = []; @@ -158,7 +155,7 @@ class _POLineDetailWidgetState extends RefreshableState { part.goToDetailPage(context); } }, - ) + ), ); // Reference to the supplier part @@ -169,26 +166,33 @@ class _POLineDetailWidgetState extends RefreshableState { leading: Icon(TablerIcons.building, color: COLOR_ACTION), onTap: () async { showLoadingOverlay(); - var part = await InvenTreeSupplierPart().get(widget.item.supplierPartId); + var part = await InvenTreeSupplierPart().get( + widget.item.supplierPartId, + ); hideLoadingOverlay(); if (part is InvenTreeSupplierPart) { - Navigator.push(context, MaterialPageRoute(builder: (context) => SupplierPartDetailWidget(part))); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => SupplierPartDetailWidget(part), + ), + ); } }, - ) + ), ); // Destination if (destination != null) { - tiles.add(ListTile( + tiles.add( + ListTile( title: Text(L10().destination), subtitle: Text(destination!.name), leading: Icon(TablerIcons.map_pin, color: COLOR_ACTION), - onTap: () => { - destination!.goToDetailPage(context) - } - )); + onTap: () => {destination!.goToDetailPage(context)}, + ), + ); } // Received quantity @@ -197,23 +201,23 @@ class _POLineDetailWidgetState extends RefreshableState { title: Text(L10().received), subtitle: ProgressBar(widget.item.progressRatio), trailing: Text( - widget.item.progressString, - style: TextStyle( - color: widget.item.isComplete ? COLOR_SUCCESS: COLOR_WARNING - ) + widget.item.progressString, + style: TextStyle( + color: widget.item.isComplete ? COLOR_SUCCESS : COLOR_WARNING, + ), ), leading: Icon(TablerIcons.progress), - ) + ), ); // Reference if (widget.item.reference.isNotEmpty) { tiles.add( - ListTile( - title: Text(L10().reference), - subtitle: Text(widget.item.reference), - leading: Icon(TablerIcons.hash), - ) + ListTile( + title: Text(L10().reference), + subtitle: Text(widget.item.reference), + leading: Icon(TablerIcons.hash), + ), ); } @@ -223,9 +227,12 @@ class _POLineDetailWidgetState extends RefreshableState { title: Text(L10().unitPrice), leading: Icon(TablerIcons.currency_dollar), trailing: Text( - renderCurrency(widget.item.purchasePrice, widget.item.purchasePriceCurrency) + renderCurrency( + widget.item.purchasePrice, + widget.item.purchasePriceCurrency, + ), ), - ) + ), ); // Note @@ -235,7 +242,7 @@ class _POLineDetailWidgetState extends RefreshableState { title: Text(L10().notes), subtitle: Text(widget.item.notes), leading: Icon(TablerIcons.note), - ) + ), ); } @@ -249,11 +256,10 @@ class _POLineDetailWidgetState extends RefreshableState { onTap: () async { await openLink(widget.item.link); }, - ) + ), ); } return tiles; } - -} \ No newline at end of file +} diff --git a/lib/widget/order/po_line_list.dart b/lib/widget/order/po_line_list.dart index 5050477e..45a27024 100644 --- a/lib/widget/order/po_line_list.dart +++ b/lib/widget/order/po_line_list.dart @@ -16,22 +16,21 @@ import "package:inventree/widget/progress.dart"; * Paginated widget class for displaying a list of purchase order line items */ class PaginatedPOLineList extends PaginatedSearchWidget { - - const PaginatedPOLineList(Map filters) : super(filters: filters); + const PaginatedPOLineList(Map filters) + : super(filters: filters); @override String get searchTitle => L10().lineItems; @override _PaginatedPOLineListState createState() => _PaginatedPOLineListState(); - } /* * State class for PaginatedPOLineList */ -class _PaginatedPOLineListState extends PaginatedSearchState { - +class _PaginatedPOLineListState + extends PaginatedSearchState { _PaginatedPOLineListState() : super(); @override @@ -55,13 +54,20 @@ class _PaginatedPOLineListState extends PaginatedSearchState requestPage(int limit, int offset, Map params) async { - - final page = await InvenTreePOLineItem().listPaginated(limit, offset, filters: params); + Future requestPage( + int limit, + int offset, + Map params, + ) async { + final page = await InvenTreePOLineItem().listPaginated( + limit, + offset, + filters: params, + ); return page; } @@ -71,24 +77,34 @@ class _PaginatedPOLineListState extends PaginatedSearchState POLineDetailWidget(item))); + Navigator.push( + context, + MaterialPageRoute(builder: (context) => POLineDetailWidget(item)), + ); }, ); } else { // Return an error tile return ListTile( title: Text(L10().error), - subtitle: Text("supplier part not defined", style: TextStyle(color: COLOR_DANGER)), + subtitle: Text( + "supplier part not defined", + style: TextStyle(color: COLOR_DANGER), + ), ); } } diff --git a/lib/widget/order/purchase_order_detail.dart b/lib/widget/order/purchase_order_detail.dart index 12332ae1..f515cf86 100644 --- a/lib/widget/order/purchase_order_detail.dart +++ b/lib/widget/order/purchase_order_detail.dart @@ -18,7 +18,6 @@ import "package:inventree/widget/order/po_extra_line_list.dart"; import "package:inventree/widget/stock/location_display.dart"; import "package:inventree/widget/order/po_line_list.dart"; - import "package:inventree/widget/attachment_widget.dart"; import "package:inventree/widget/notes_widget.dart"; import "package:inventree/widget/progress.dart"; @@ -27,13 +26,11 @@ import "package:inventree/widget/snacks.dart"; import "package:inventree/widget/stock/stock_list.dart"; import "package:inventree/preferences.dart"; - /* * Widget for viewing a single PurchaseOrder instance */ class PurchaseOrderDetailWidget extends StatefulWidget { - - const PurchaseOrderDetailWidget(this.order, {Key? key}): super(key: key); + const PurchaseOrderDetailWidget(this.order, {Key? key}) : super(key: key); final InvenTreePurchaseOrder order; @@ -41,11 +38,10 @@ class PurchaseOrderDetailWidget extends StatefulWidget { _PurchaseOrderDetailState createState() => _PurchaseOrderDetailState(); } - -class _PurchaseOrderDetailState extends RefreshableState { - +class _PurchaseOrderDetailState + extends RefreshableState { _PurchaseOrderDetailState(); - + List lines = []; int extraLineCount = 0; @@ -79,8 +75,8 @@ class _PurchaseOrderDetailState extends RefreshableState _addLineItem(BuildContext context) async { - var fields = InvenTreePOLineItem().formFields(); // Update part field definition fields["part"]?["hidden"] = false; - fields["part"]?["filters"] = { - "supplier": widget.order.supplierId - }; + fields["part"]?["filters"] = {"supplier": widget.order.supplierId}; fields["order"]?["value"] = widget.order.pk; @@ -163,24 +155,22 @@ class _PurchaseOrderDetailState extends RefreshableState _uploadImage(BuildContext context) async { - - InvenTreePurchaseOrderAttachment().uploadImage( - widget.order.pk, - prefix: widget.order.reference, - ).then((result) => refresh(context)); + InvenTreePurchaseOrderAttachment() + .uploadImage(widget.order.pk, prefix: widget.order.reference) + .then((result) => refresh(context)); } /// Issue this order Future _issueOrder(BuildContext context) async { - confirmationDialog( - L10().issueOrder, "", + L10().issueOrder, + "", icon: TablerIcons.send, color: Colors.blue, acceptText: L10().issue, @@ -188,15 +178,15 @@ class _PurchaseOrderDetailState extends RefreshableState _cancelOrder(BuildContext context) async { - confirmationDialog( - L10().cancelOrder, "", + L10().cancelOrder, + "", icon: TablerIcons.circle_x, color: Colors.red, acceptText: L10().cancel, @@ -204,7 +194,7 @@ class _PurchaseOrderDetailState extends RefreshableState request(BuildContext context) async { await widget.order.reload(); @@ -256,8 +245,16 @@ class _PurchaseOrderDetailState extends RefreshableState 0) { - InvenTreeStockLocation().get(widget.order.destinationId).then((InvenTreeModel? loc) { + if (api.supportsPurchaseOrderDestination && + widget.order.destinationId > 0) { + InvenTreeStockLocation().get(widget.order.destinationId).then(( + InvenTreeModel? loc, + ) { if (mounted) { if (loc != null && loc is InvenTreeStockLocation) { setState(() { @@ -298,17 +300,19 @@ class _PurchaseOrderDetailState extends RefreshableState editOrder(BuildContext context) async { + Future editOrder(BuildContext context) async { var fields = widget.order.formFields(); // Cannot edit supplier field from here @@ -331,32 +335,29 @@ class _PurchaseOrderDetailState extends RefreshableState orderTiles(BuildContext context) { - List tiles = []; InvenTreeCompany? supplier = widget.order.supplier; @@ -364,165 +365,204 @@ class _PurchaseOrderDetailState extends RefreshableState { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => LocationDisplayWidget(destination), + ), + ), + }, + ), + ); + } + + Color lineColor = completedLines < widget.order.lineItemCount + ? COLOR_WARNING + : COLOR_SUCCESS; + + tiles.add( + ListTile( + title: Text(L10().lineItems), + subtitle: ProgressBar( + completedLines.toDouble(), + maximum: widget.order.lineItemCount.toDouble(), + ), + leading: Icon(TablerIcons.clipboard_check), + trailing: Text( + "${completedLines} / ${widget.order.lineItemCount}", + style: TextStyle(color: lineColor), + ), + ), + ); + + // Extra line items + tiles.add( + ListTile( + title: Text(L10().extraLineItems), + leading: Icon(TablerIcons.clipboard_list, color: COLOR_ACTION), + trailing: Text(extraLineCount.toString()), onTap: () => { Navigator.push( context, MaterialPageRoute( - builder: (context) => LocationDisplayWidget(destination) - ) - ) - } - )); - } - - Color lineColor = completedLines < widget.order.lineItemCount ? COLOR_WARNING : COLOR_SUCCESS; - - tiles.add(ListTile( - title: Text(L10().lineItems), - subtitle: ProgressBar( - completedLines.toDouble(), - maximum: widget.order.lineItemCount.toDouble(), + builder: (context) => POExtraLineListWidget( + widget.order, + filters: {"order": widget.order.pk.toString()}, + ), + ), + ), + }, ), - leading: Icon(TablerIcons.clipboard_check), - trailing: Text("${completedLines} / ${widget.order.lineItemCount}", style: TextStyle(color: lineColor)), - )); + ); - // Extra line items - tiles.add(ListTile( - title: Text(L10().extraLineItems), - leading: Icon(TablerIcons.clipboard_list, color: COLOR_ACTION), - trailing: Text(extraLineCount.toString()), - onTap: () => { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => POExtraLineListWidget(widget.order, filters: {"order": widget.order.pk.toString()}) - ) - ) - }, - )); - - tiles.add(ListTile( - title: Text(L10().totalPrice), - leading: Icon(TablerIcons.currency_dollar), - trailing: Text( - renderCurrency(widget.order.totalPrice, widget.order.totalPriceCurrency) + tiles.add( + ListTile( + title: Text(L10().totalPrice), + leading: Icon(TablerIcons.currency_dollar), + trailing: Text( + renderCurrency( + widget.order.totalPrice, + widget.order.totalPriceCurrency, + ), + ), ), - )); + ); if (widget.order.issueDate.isNotEmpty) { - tiles.add(ListTile( - title: Text(L10().issueDate), - trailing: Text(widget.order.issueDate), - leading: Icon(TablerIcons.calendar), - )); + tiles.add( + ListTile( + title: Text(L10().issueDate), + trailing: Text(widget.order.issueDate), + leading: Icon(TablerIcons.calendar), + ), + ); } if (widget.order.startDate.isNotEmpty) { - tiles.add(ListTile( - title: Text(L10().startDate), - trailing: Text(widget.order.startDate), - leading: Icon(TablerIcons.calendar), - )); + tiles.add( + ListTile( + title: Text(L10().startDate), + trailing: Text(widget.order.startDate), + leading: Icon(TablerIcons.calendar), + ), + ); } if (widget.order.targetDate.isNotEmpty) { - tiles.add(ListTile( - title: Text(L10().targetDate), - trailing: Text(widget.order.targetDate), - leading: Icon(TablerIcons.calendar), - )); + tiles.add( + ListTile( + title: Text(L10().targetDate), + trailing: Text(widget.order.targetDate), + leading: Icon(TablerIcons.calendar), + ), + ); } if (widget.order.completionDate.isNotEmpty) { - tiles.add(ListTile( - title: Text(L10().completionDate), - trailing: Text(widget.order.completionDate), - leading: Icon(TablerIcons.calendar), - )); + tiles.add( + ListTile( + title: Text(L10().completionDate), + trailing: Text(widget.order.completionDate), + leading: Icon(TablerIcons.calendar), + ), + ); } // Responsible "owner" - if (widget.order.responsibleName.isNotEmpty && widget.order.responsibleLabel.isNotEmpty) { - tiles.add(ListTile( - title: Text(L10().responsible), - leading: Icon(widget.order.responsibleLabel == "group" ? TablerIcons.users : TablerIcons.user), - trailing: Text(widget.order.responsibleName) - )); + if (widget.order.responsibleName.isNotEmpty && + widget.order.responsibleLabel.isNotEmpty) { + tiles.add( + ListTile( + title: Text(L10().responsible), + leading: Icon( + widget.order.responsibleLabel == "group" + ? TablerIcons.users + : TablerIcons.user, + ), + trailing: Text(widget.order.responsibleName), + ), + ); } // Notes tile tiles.add( - ListTile( - title: Text(L10().notes), - leading: Icon(TablerIcons.note, color: COLOR_ACTION), - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => NotesWidget(widget.order) - ) - ); - }, - ) + ListTile( + title: Text(L10().notes), + leading: Icon(TablerIcons.note, color: COLOR_ACTION), + onTap: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => NotesWidget(widget.order)), + ); + }, + ), ); // Attachments tiles.add( - ListTile( - title: Text(L10().attachments), - leading: Icon(TablerIcons.file, color: COLOR_ACTION), - trailing: attachmentCount > 0 ? Text(attachmentCount.toString()) : null, - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => AttachmentWidget( - InvenTreePurchaseOrderAttachment(), - widget.order.pk, - widget.order.reference, - widget.order.canEdit - ) - ) - ); - }, - ) + ListTile( + title: Text(L10().attachments), + leading: Icon(TablerIcons.file, color: COLOR_ACTION), + trailing: attachmentCount > 0 ? Text(attachmentCount.toString()) : null, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AttachmentWidget( + InvenTreePurchaseOrderAttachment(), + widget.order.pk, + widget.order.reference, + widget.order.canEdit, + ), + ), + ); + }, + ), ); return tiles; - } @override @@ -530,10 +570,10 @@ class _PurchaseOrderDetailState extends RefreshableState getTabs(BuildContext context) { return [ diff --git a/lib/widget/order/purchase_order_list.dart b/lib/widget/order/purchase_order_list.dart index 8351ac46..033e3066 100644 --- a/lib/widget/order/purchase_order_list.dart +++ b/lib/widget/order/purchase_order_list.dart @@ -16,18 +16,18 @@ import "package:inventree/inventree/purchase_order.dart"; * Widget class for displaying a list of Purchase Orders */ class PurchaseOrderListWidget extends StatefulWidget { - - const PurchaseOrderListWidget({this.filters = const {}, Key? key}) : super(key: key); + const PurchaseOrderListWidget({this.filters = const {}, Key? key}) + : super(key: key); final Map filters; @override - _PurchaseOrderListWidgetState createState() => _PurchaseOrderListWidgetState(); + _PurchaseOrderListWidgetState createState() => + _PurchaseOrderListWidgetState(); } - -class _PurchaseOrderListWidgetState extends RefreshableState { - +class _PurchaseOrderListWidgetState + extends RefreshableState { _PurchaseOrderListWidgetState(); @override @@ -44,8 +44,8 @@ class _PurchaseOrderListWidgetState extends RefreshableState filters) : super(filters: filters); + const PaginatedPurchaseOrderList(Map filters) + : super(filters: filters); @override String get searchTitle => L10().purchaseOrders; @override - _PaginatedPurchaseOrderListState createState() => _PaginatedPurchaseOrderListState(); - + _PaginatedPurchaseOrderListState createState() => + _PaginatedPurchaseOrderListState(); } - -class _PaginatedPurchaseOrderListState extends PaginatedSearchState { - +class _PaginatedPurchaseOrderListState + extends PaginatedSearchState { _PaginatedPurchaseOrderListState() : super(); @override @@ -147,29 +142,37 @@ class _PaginatedPurchaseOrderListState extends PaginatedSearchState requestPage(int limit, int offset, Map params) async { - + Future requestPage( + int limit, + int offset, + Map params, + ) async { await InvenTreeAPI().PurchaseOrderStatus.load(); - final page = await InvenTreePurchaseOrder().listPaginated(limit, offset, filters: params); + final page = await InvenTreePurchaseOrder().listPaginated( + limit, + offset, + filters: params, + ); return page; } @override Widget buildItem(BuildContext context, InvenTreeModel model) { - InvenTreePurchaseOrder order = model as InvenTreePurchaseOrder; InvenTreeCompany? supplier = order.supplier; - + return ListTile( title: Text(order.reference), subtitle: Text(order.description), - leading: supplier == null ? null : InvenTreeAPI().getThumbnail(supplier.thumbnail), + leading: supplier == null + ? null + : InvenTreeAPI().getThumbnail(supplier.thumbnail), trailing: Text( InvenTreeAPI().PurchaseOrderStatus.label(order.status), style: TextStyle( @@ -181,4 +184,4 @@ class _PaginatedPurchaseOrderListState extends PaginatedSearchState _SalesOrderDetailState(); } - class _SalesOrderDetailState extends RefreshableState { - _SalesOrderDetailState(); List lines = []; @@ -68,7 +64,7 @@ class _SalesOrderDetailState extends RefreshableState { onPressed: () { editOrder(context); }, - ) + ), ); } @@ -77,7 +73,6 @@ class _SalesOrderDetailState extends RefreshableState { // Add a new shipment against this sales order Future _addShipment(BuildContext context) async { - var fields = InvenTreeSalesOrderShipment().formFields(); fields["order"]?["value"] = widget.order.pk; @@ -89,9 +84,8 @@ class _SalesOrderDetailState extends RefreshableState { fields: fields, onSuccess: (result) async { refresh(context); - } + }, ); - } // Add a new line item to this sales order @@ -102,52 +96,51 @@ class _SalesOrderDetailState extends RefreshableState { fields["order"]?["hidden"] = true; InvenTreeSOLineItem().createForm( - context, - L10().lineItemAdd, - fields: fields, - onSuccess: (result) async { - refresh(context); - } + context, + L10().lineItemAdd, + fields: fields, + onSuccess: (result) async { + refresh(context); + }, ); } /// Upload an image for this order Future _uploadImage(BuildContext context) async { - InvenTreeSalesOrderAttachment().uploadImage( - widget.order.pk, - prefix: widget.order.reference, - ).then((result) => refresh(context)); + InvenTreeSalesOrderAttachment() + .uploadImage(widget.order.pk, prefix: widget.order.reference) + .then((result) => refresh(context)); } /// Issue this order Future _issueOrder(BuildContext context) async { - confirmationDialog( - L10().issueOrder, "", - icon: TablerIcons.send, - color: Colors.blue, - acceptText: L10().issue, - onAccept: () async { - widget.order.issueOrder().then((dynamic) { - refresh(context); - }); - } + L10().issueOrder, + "", + icon: TablerIcons.send, + color: Colors.blue, + acceptText: L10().issue, + onAccept: () async { + widget.order.issueOrder().then((dynamic) { + refresh(context); + }); + }, ); } /// Cancel this order Future _cancelOrder(BuildContext context) async { - confirmationDialog( - L10().cancelOrder, "", - icon: TablerIcons.circle_x, - color: Colors.red, - acceptText: L10().cancel, - onAccept: () async { - await widget.order.cancelOrder().then((dynamic) { - refresh(context); - }); - } + L10().cancelOrder, + "", + icon: TablerIcons.circle_x, + color: Colors.red, + acceptText: L10().cancel, + onAccept: () async { + await widget.order.cancelOrder().then((dynamic) { + refresh(context); + }); + }, ); } @@ -157,50 +150,51 @@ class _SalesOrderDetailState extends RefreshableState { if (showCameraShortcut && widget.order.canEdit) { actions.add( - SpeedDialChild( - child: Icon(TablerIcons.camera, color: Colors.blue), - label: L10().takePicture, - onTap: () async { - _uploadImage(context); - } - ) + SpeedDialChild( + child: Icon(TablerIcons.camera, color: Colors.blue), + label: L10().takePicture, + onTap: () async { + _uploadImage(context); + }, + ), ); } if (widget.order.isPending) { actions.add( - SpeedDialChild( - child: Icon(TablerIcons.send, color: Colors.blue), - label: L10().issueOrder, - onTap: () async { - _issueOrder(context); - } - ) + SpeedDialChild( + child: Icon(TablerIcons.send, color: Colors.blue), + label: L10().issueOrder, + onTap: () async { + _issueOrder(context); + }, + ), ); } if (widget.order.isOpen) { actions.add( - SpeedDialChild( - child: Icon(TablerIcons.circle_x, color: Colors.red), - label: L10().cancelOrder, - onTap: () async { - _cancelOrder(context); - } - ) + SpeedDialChild( + child: Icon(TablerIcons.circle_x, color: Colors.red), + label: L10().cancelOrder, + onTap: () async { + _cancelOrder(context); + }, + ), ); } // Add line item - if ((widget.order.isPending || widget.order.isInProgress) && InvenTreeSOLineItem().canCreate) { + if ((widget.order.isPending || widget.order.isInProgress) && + InvenTreeSOLineItem().canCreate) { actions.add( SpeedDialChild( child: Icon(TablerIcons.circle_plus, color: Colors.green), label: L10().lineItemAdd, onTap: () async { _addLineItem(context); - } - ) + }, + ), ); actions.add( @@ -209,8 +203,8 @@ class _SalesOrderDetailState extends RefreshableState { label: L10().shipmentAdd, onTap: () async { _addShipment(context); - } - ) + }, + ), ); } @@ -221,7 +215,8 @@ class _SalesOrderDetailState extends RefreshableState { List barcodeButtons(BuildContext context) { List actions = []; - if ((widget.order.isInProgress || widget.order.isPending) && InvenTreeSOLineItem().canCreate) { + if ((widget.order.isInProgress || widget.order.isPending) && + InvenTreeSOLineItem().canCreate) { actions.add( SpeedDialChild( child: Icon(Icons.barcode_reader), @@ -231,8 +226,8 @@ class _SalesOrderDetailState extends RefreshableState { context, handler: SOAddItemBarcodeHandler(salesOrder: widget.order), ); - } - ) + }, + ), ); if (api.supportsBarcodeSOAllocateEndpoint) { @@ -243,12 +238,10 @@ class _SalesOrderDetailState extends RefreshableState { onTap: () async { scanBarcode( context, - handler: SOAllocateStockHandler( - salesOrder: widget.order, - ) + handler: SOAllocateStockHandler(salesOrder: widget.order), ); - } - ) + }, + ), ); } } @@ -261,10 +254,20 @@ class _SalesOrderDetailState extends RefreshableState { await widget.order.reload(); await api.SalesOrderStatus.load(); - supportsProjectCodes = api.supportsProjectCodes && await api.getGlobalBooleanSetting("PROJECT_CODES_ENABLED", backup: true); - showCameraShortcut = await InvenTreeSettingsManager().getBool(INV_SO_SHOW_CAMERA, true); + supportsProjectCodes = + api.supportsProjectCodes && + await api.getGlobalBooleanSetting( + "PROJECT_CODES_ENABLED", + backup: true, + ); + showCameraShortcut = await InvenTreeSettingsManager().getBool( + INV_SO_SHOW_CAMERA, + true, + ); - InvenTreeSalesOrderAttachment().countAttachments(widget.order.pk).then((int value) { + InvenTreeSalesOrderAttachment().countAttachments(widget.order.pk).then(( + int value, + ) { if (mounted) { setState(() { attachmentCount = value; @@ -273,13 +276,15 @@ class _SalesOrderDetailState extends RefreshableState { }); // Count number of "extra line items" against this order - InvenTreeSOExtraLineItem().count(filters: {"order": widget.order.pk.toString() }).then((int value) { - if (mounted) { - setState(() { - extraLineCount = value; + InvenTreeSOExtraLineItem() + .count(filters: {"order": widget.order.pk.toString()}) + .then((int value) { + if (mounted) { + setState(() { + extraLineCount = value; + }); + } }); - } - }); } // Edit the current SalesOrder instance @@ -305,7 +310,7 @@ class _SalesOrderDetailState extends RefreshableState { onSuccess: (data) async { refresh(context); showSnackIcon(L10().salesOrderUpdated, success: true); - } + }, ); } @@ -321,121 +326,154 @@ class _SalesOrderDetailState extends RefreshableState { trailing: Text( api.SalesOrderStatus.label(widget.order.status), style: TextStyle( - color: api.SalesOrderStatus.color(widget.order.status) + color: api.SalesOrderStatus.color(widget.order.status), ), ), - ) + ), ); } List orderTiles(BuildContext context) { - - List tiles = [ - headerTile(context) - ]; + List tiles = [headerTile(context)]; InvenTreeCompany? customer = widget.order.customer; if (supportsProjectCodes && widget.order.hasProjectCode) { - tiles.add(ListTile( - title: Text(L10().projectCode), - subtitle: Text("${widget.order.projectCode} - ${widget.order.projectCodeDescription}"), - leading: Icon(TablerIcons.list), - )); + tiles.add( + ListTile( + title: Text(L10().projectCode), + subtitle: Text( + "${widget.order.projectCode} - ${widget.order.projectCodeDescription}", + ), + leading: Icon(TablerIcons.list), + ), + ); } if (customer != null) { - tiles.add(ListTile( - title: Text(L10().customer), - subtitle: Text(customer.name), - leading: Icon(TablerIcons.user, color: COLOR_ACTION), - onTap: () { - customer.goToDetailPage(context); - } - )); + tiles.add( + ListTile( + title: Text(L10().customer), + subtitle: Text(customer.name), + leading: Icon(TablerIcons.user, color: COLOR_ACTION), + onTap: () { + customer.goToDetailPage(context); + }, + ), + ); } if (widget.order.customerReference.isNotEmpty) { - tiles.add(ListTile( - title: Text(L10().customerReference), - trailing: Text(widget.order.customerReference), - leading: Icon(TablerIcons.hash), - )); + tiles.add( + ListTile( + title: Text(L10().customerReference), + trailing: Text(widget.order.customerReference), + leading: Icon(TablerIcons.hash), + ), + ); } Color lineColor = widget.order.complete ? COLOR_SUCCESS : COLOR_WARNING; - tiles.add(ListTile( - title: Text(L10().lineItems), - subtitle: ProgressBar( - widget.order.completedLineItemCount.toDouble(), - maximum: widget.order.lineItemCount.toDouble() + tiles.add( + ListTile( + title: Text(L10().lineItems), + subtitle: ProgressBar( + widget.order.completedLineItemCount.toDouble(), + maximum: widget.order.lineItemCount.toDouble(), + ), + leading: Icon(TablerIcons.clipboard_check), + trailing: Text( + "${widget.order.completedLineItemCount} / ${widget.order.lineItemCount}", + style: TextStyle(color: lineColor), + ), ), - leading: Icon(TablerIcons.clipboard_check), - trailing: Text("${widget.order.completedLineItemCount} / ${widget.order.lineItemCount}", style: TextStyle(color: lineColor)), - )); + ); // Extra line items - tiles.add(ListTile( - title: Text(L10().extraLineItems), - leading: Icon(TablerIcons.clipboard_list, color: COLOR_ACTION), - trailing: Text(extraLineCount.toString()), - onTap: () => { - Navigator.push( + tiles.add( + ListTile( + title: Text(L10().extraLineItems), + leading: Icon(TablerIcons.clipboard_list, color: COLOR_ACTION), + trailing: Text(extraLineCount.toString()), + onTap: () => { + Navigator.push( context, MaterialPageRoute( - builder: (context) => SOExtraLineListWidget(widget.order, filters: {"order": widget.order.pk.toString()}) - ) - ) - }, - )); + builder: (context) => SOExtraLineListWidget( + widget.order, + filters: {"order": widget.order.pk.toString()}, + ), + ), + ), + }, + ), + ); // Shipment progress if (widget.order.shipmentCount > 0) { - tiles.add(ListTile( - title: Text(L10().shipments), - subtitle: ProgressBar( - widget.order.completedShipmentCount.toDouble(), - maximum: widget.order.shipmentCount.toDouble() + tiles.add( + ListTile( + title: Text(L10().shipments), + subtitle: ProgressBar( + widget.order.completedShipmentCount.toDouble(), + maximum: widget.order.shipmentCount.toDouble(), + ), + leading: Icon(TablerIcons.truck_delivery), + trailing: Text( + "${widget.order.completedShipmentCount} / ${widget.order.shipmentCount}", + style: TextStyle(color: lineColor), + ), ), - leading: Icon(TablerIcons.truck_delivery), - trailing: Text("${widget.order.completedShipmentCount} / ${widget.order.shipmentCount}", style: TextStyle(color: lineColor)), - )); + ); } // TODO: total price if (widget.order.startDate.isNotEmpty) { - tiles.add(ListTile( - title: Text(L10().startDate), - trailing: Text(widget.order.startDate), - leading: Icon(TablerIcons.calendar), - )); + tiles.add( + ListTile( + title: Text(L10().startDate), + trailing: Text(widget.order.startDate), + leading: Icon(TablerIcons.calendar), + ), + ); } if (widget.order.targetDate.isNotEmpty) { - tiles.add(ListTile( - title: Text(L10().targetDate), - trailing: Text(widget.order.targetDate), - leading: Icon(TablerIcons.calendar), - )); + tiles.add( + ListTile( + title: Text(L10().targetDate), + trailing: Text(widget.order.targetDate), + leading: Icon(TablerIcons.calendar), + ), + ); } if (widget.order.shipmentDate.isNotEmpty) { - tiles.add(ListTile( - title: Text(L10().completionDate), - trailing: Text(widget.order.shipmentDate), - leading: Icon(TablerIcons.calendar), - )); + tiles.add( + ListTile( + title: Text(L10().completionDate), + trailing: Text(widget.order.shipmentDate), + leading: Icon(TablerIcons.calendar), + ), + ); } // Responsible "owner" - if (widget.order.responsibleName.isNotEmpty && widget.order.responsibleLabel.isNotEmpty) { - tiles.add(ListTile( + if (widget.order.responsibleName.isNotEmpty && + widget.order.responsibleLabel.isNotEmpty) { + tiles.add( + ListTile( title: Text(L10().responsible), - leading: Icon(widget.order.responsibleLabel == "group" ? TablerIcons.users : TablerIcons.user), - trailing: Text(widget.order.responsibleName) - )); + leading: Icon( + widget.order.responsibleLabel == "group" + ? TablerIcons.users + : TablerIcons.user, + ), + trailing: Text(widget.order.responsibleName), + ), + ); } // Notes tile @@ -446,12 +484,10 @@ class _SalesOrderDetailState extends RefreshableState { onTap: () { Navigator.push( context, - MaterialPageRoute( - builder: (context) => NotesWidget(widget.order) - ) + MaterialPageRoute(builder: (context) => NotesWidget(widget.order)), ); }, - ) + ), ); // Attachments @@ -462,18 +498,18 @@ class _SalesOrderDetailState extends RefreshableState { trailing: attachmentCount > 0 ? Text(attachmentCount.toString()) : null, onTap: () { Navigator.push( - context, - MaterialPageRoute( - builder: (context) => AttachmentWidget( - InvenTreeSalesOrderAttachment(), - widget.order.pk, - widget.order.reference, - widget.order.canEdit - ) - ) - ); + context, + MaterialPageRoute( + builder: (context) => AttachmentWidget( + InvenTreeSalesOrderAttachment(), + widget.order.pk, + widget.order.reference, + widget.order.canEdit, + ), + ), + ); }, - ) + ), ); return tiles; @@ -496,5 +532,4 @@ class _SalesOrderDetailState extends RefreshableState { PaginatedSOShipmentList({"order": widget.order.pk.toString()}), ]; } - } diff --git a/lib/widget/order/sales_order_list.dart b/lib/widget/order/sales_order_list.dart index 46264c47..089363ea 100644 --- a/lib/widget/order/sales_order_list.dart +++ b/lib/widget/order/sales_order_list.dart @@ -1,4 +1,3 @@ - import "package:flutter/material.dart"; import "package:flutter_speed_dial/flutter_speed_dial.dart"; import "package:flutter_tabler_icons/flutter_tabler_icons.dart"; @@ -12,20 +11,18 @@ import "package:inventree/api.dart"; import "package:inventree/inventree/company.dart"; import "package:inventree/inventree/model.dart"; - class SalesOrderListWidget extends StatefulWidget { - - const SalesOrderListWidget({this.filters = const {}, Key? key}) : super(key: key); + const SalesOrderListWidget({this.filters = const {}, Key? key}) + : super(key: key); final Map filters; @override _SalesOrderListWidgetState createState() => _SalesOrderListWidgetState(); - } -class _SalesOrderListWidgetState extends RefreshableState { - +class _SalesOrderListWidgetState + extends RefreshableState { _SalesOrderListWidgetState(); @override @@ -37,13 +34,13 @@ class _SalesOrderListWidgetState extends RefreshableState if (InvenTreeSalesOrder().canCreate) { actions.add( - SpeedDialChild( - child: Icon(TablerIcons.circle_plus), - label: L10().salesOrderCreate, - onTap: () { - _createSalesOrder(context); - } - ) + SpeedDialChild( + child: Icon(TablerIcons.circle_plus), + label: L10().salesOrderCreate, + onTap: () { + _createSalesOrder(context); + }, + ), ); } @@ -58,17 +55,17 @@ class _SalesOrderListWidgetState extends RefreshableState fields.remove("contact"); InvenTreeSalesOrder().createForm( - context, - L10().salesOrderCreate, - fields: fields, - onSuccess: (result) async { - Map data = result as Map; + context, + L10().salesOrderCreate, + fields: fields, + onSuccess: (result) async { + Map data = result as Map; - if (data.containsKey("pk")) { - var order = InvenTreeSalesOrder.fromJson(data); - order.goToDetailPage(context); - } + if (data.containsKey("pk")) { + var order = InvenTreeSalesOrder.fromJson(data); + order.goToDetailPage(context); } + }, ); } @@ -82,25 +79,22 @@ class _SalesOrderListWidgetState extends RefreshableState Widget getBody(BuildContext context) { return PaginatedSalesOrderList(widget.filters); } - } - class PaginatedSalesOrderList extends PaginatedSearchWidget { - - const PaginatedSalesOrderList(Map filters) : super(filters: filters); + const PaginatedSalesOrderList(Map filters) + : super(filters: filters); @override String get searchTitle => L10().salesOrders; @override - _PaginatedSalesOrderListState createState() => _PaginatedSalesOrderListState(); - + _PaginatedSalesOrderListState createState() => + _PaginatedSalesOrderListState(); } - -class _PaginatedSalesOrderListState extends PaginatedSearchState { - +class _PaginatedSalesOrderListState + extends PaginatedSearchState { _PaginatedSalesOrderListState() : super(); @override @@ -130,21 +124,27 @@ class _PaginatedSalesOrderListState extends PaginatedSearchState requestPage(int limit, int offset, Map params) async { - + Future requestPage( + int limit, + int offset, + Map params, + ) async { await InvenTreeAPI().SalesOrderStatus.load(); - final page = await InvenTreeSalesOrder().listPaginated(limit, offset, filters: params); + final page = await InvenTreeSalesOrder().listPaginated( + limit, + offset, + filters: params, + ); return page; } @override Widget buildItem(BuildContext context, InvenTreeModel model) { - InvenTreeSalesOrder order = model as InvenTreeSalesOrder; InvenTreeCompany? customer = order.customer; @@ -152,18 +152,18 @@ class _PaginatedSalesOrderListState extends PaginatedSearchState filters; @override - _SalesOrderExtraLineListWidgetState createState() => _SalesOrderExtraLineListWidgetState(); + _SalesOrderExtraLineListWidgetState createState() => + _SalesOrderExtraLineListWidgetState(); } -class _SalesOrderExtraLineListWidgetState extends RefreshableState { - +class _SalesOrderExtraLineListWidgetState + extends RefreshableState { _SalesOrderExtraLineListWidgetState(); @override String getAppBarTitle() => L10().extraLineItems; Future _addLineItem(BuildContext context) async { - var fields = InvenTreeSOExtraLineItem().formFields(); fields["order"]?["value"] = widget.order.pk; InvenTreeSOExtraLineItem().createForm( - context, - L10().lineItemAdd, - fields: fields, - onSuccess: (data) async { - refresh(context); - showSnackIcon(L10().lineItemUpdated, success: true); - } + context, + L10().lineItemAdd, + fields: fields, + onSuccess: (data) async { + refresh(context); + showSnackIcon(L10().lineItemUpdated, success: true); + }, ); } @@ -54,13 +53,13 @@ class _SalesOrderExtraLineListWidgetState extends RefreshableState filters) : super(filters: filters); + const PaginatedSOExtraLineList(Map filters) + : super(filters: filters); @override String get searchTitle => L10().extraLineItems; @override - _PaginatedSOExtraLineListState createState() => _PaginatedSOExtraLineListState(); - + _PaginatedSOExtraLineListState createState() => + _PaginatedSOExtraLineListState(); } -class _PaginatedSOExtraLineListState extends PaginatedSearchState { - +class _PaginatedSOExtraLineListState + extends PaginatedSearchState { _PaginatedSOExtraLineListState() : super(); @override String get prefix => "so_extra_line_"; @override - Future requestPage(int limit, int offset, Map params) async { - final page = await InvenTreeSOExtraLineItem().listPaginated(limit, offset, filters: params); + Future requestPage( + int limit, + int offset, + Map params, + ) async { + final page = await InvenTreeSOExtraLineItem().listPaginated( + limit, + offset, + filters: params, + ); return page; } @override Widget buildItem(BuildContext context, InvenTreeModel model) { - InvenTreeSOExtraLineItem line = model as InvenTreeSOExtraLineItem; return ListTile( @@ -115,4 +120,4 @@ class _PaginatedSOExtraLineListState extends PaginatedSearchState _SOLineDetailWidgetState(); - } - class _SOLineDetailWidgetState extends RefreshableState { - _SOLineDetailWidgetState(); InvenTreeSalesOrder? order; @@ -51,10 +44,11 @@ class _SOLineDetailWidgetState extends RefreshableState { if (widget.item.canEdit) { actions.add( IconButton( - icon: Icon(TablerIcons.edit), - onPressed: () { - _editLineItem(context); - }), + icon: Icon(TablerIcons.edit), + onPressed: () { + _editLineItem(context); + }, + ), ); } @@ -62,7 +56,6 @@ class _SOLineDetailWidgetState extends RefreshableState { } Future _allocateStock(BuildContext context) async { - if (order == null) { return; } @@ -73,12 +66,10 @@ class _SOLineDetailWidgetState extends RefreshableState { fields["stock_item"]?["filters"] = { "in_stock": true, "available": true, - "part": widget.item.partId.toString() + "part": widget.item.partId.toString(), }; fields["quantity"]?["value"] = widget.item.unallocatedQuantity.toString(); - fields["shipment"]?["filters"] = { - "order": order!.pk.toString() - }; + fields["shipment"]?["filters"] = {"order": order!.pk.toString()}; launchApiForm( context, @@ -89,9 +80,8 @@ class _SOLineDetailWidgetState extends RefreshableState { icon: TablerIcons.transition_right, onSuccess: (data) async { refresh(context); - } + }, ); - } Future _editLineItem(BuildContext context) async { @@ -109,13 +99,12 @@ class _SOLineDetailWidgetState extends RefreshableState { onSuccess: (data) async { refresh(context); showSnackIcon(L10().lineItemUpdated, success: true); - } + }, ); } @override List actionButtons(BuildContext context) { - List buttons = []; if (order != null && order!.isOpen) { @@ -125,8 +114,8 @@ class _SOLineDetailWidgetState extends RefreshableState { label: L10().allocateStock, onTap: () async { _allocateStock(context); - } - ) + }, + ), ); } @@ -138,22 +127,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 - ) - ); - } - ) + SpeedDialChild( + child: Icon(TablerIcons.transition_right), + label: L10().allocateStock, + onTap: () async { + scanBarcode( + context, + handler: SOAllocateStockHandler( + salesOrder: order, + lineItem: widget.item, + ), + ); + }, + ), ); } } @@ -193,8 +181,8 @@ class _SOLineDetailWidgetState extends RefreshableState { if (part is InvenTreePart) { part.goToDetailPage(context); } - } - ) + }, + ), ); // Available quantity @@ -202,8 +190,8 @@ class _SOLineDetailWidgetState extends RefreshableState { ListTile( title: Text(L10().availableStock), leading: Icon(TablerIcons.packages), - trailing: Text(simpleNumberString(widget.item.availableStock)) - ) + trailing: Text(simpleNumberString(widget.item.availableStock)), + ), ); // Allocated quantity @@ -215,10 +203,10 @@ class _SOLineDetailWidgetState extends RefreshableState { trailing: Text( widget.item.allocatedString, style: TextStyle( - color: widget.item.isAllocated ? COLOR_SUCCESS : COLOR_WARNING - ) - ) - ) + color: widget.item.isAllocated ? COLOR_SUCCESS : COLOR_WARNING, + ), + ), + ), ); // Shipped quantity @@ -229,11 +217,11 @@ class _SOLineDetailWidgetState extends RefreshableState { trailing: Text( widget.item.progressString, style: TextStyle( - color: widget.item.isComplete ? COLOR_SUCCESS : COLOR_WARNING + color: widget.item.isComplete ? COLOR_SUCCESS : COLOR_WARNING, ), ), - leading: Icon(TablerIcons.truck) - ) + leading: Icon(TablerIcons.truck), + ), ); // Reference @@ -242,36 +230,36 @@ class _SOLineDetailWidgetState extends RefreshableState { ListTile( title: Text(L10().reference), subtitle: Text(widget.item.reference), - leading: Icon(TablerIcons.hash) - ) + leading: Icon(TablerIcons.hash), + ), ); } // Note if (widget.item.notes.isNotEmpty) { tiles.add( - ListTile( - title: Text(L10().notes), - subtitle: Text(widget.item.notes), - leading: Icon(TablerIcons.note), - ) + ListTile( + title: Text(L10().notes), + subtitle: Text(widget.item.notes), + leading: Icon(TablerIcons.note), + ), ); } // External link if (widget.item.link.isNotEmpty) { tiles.add( - ListTile( - title: Text(L10().link), - subtitle: Text(widget.item.link), - leading: Icon(TablerIcons.link, color: COLOR_ACTION), - onTap: () async { - await openLink(widget.item.link); - }, - ) + ListTile( + title: Text(L10().link), + subtitle: Text(widget.item.link), + leading: Icon(TablerIcons.link, color: COLOR_ACTION), + onTap: () async { + await openLink(widget.item.link); + }, + ), ); } return tiles; } -} \ No newline at end of file +} diff --git a/lib/widget/order/so_line_list.dart b/lib/widget/order/so_line_list.dart index a4c74548..2530d3e1 100644 --- a/lib/widget/order/so_line_list.dart +++ b/lib/widget/order/so_line_list.dart @@ -9,28 +9,26 @@ import "package:inventree/api.dart"; import "package:inventree/app_colors.dart"; import "package:inventree/widget/progress.dart"; - /* * Paginated widget class for displaying a list of sales order line items */ class PaginatedSOLineList extends PaginatedSearchWidget { - const PaginatedSOLineList(Map filters) : super(filters: filters); + const PaginatedSOLineList(Map filters) + : super(filters: filters); @override String get searchTitle => L10().lineItems; @override _PaginatedSOLineListState createState() => _PaginatedSOLineListState(); - } - /* * State class for PaginatedSOLineList */ -class _PaginatedSOLineListState extends PaginatedSearchState { - +class _PaginatedSOLineListState + extends PaginatedSearchState { _PaginatedSOLineListState() : super(); @override @@ -43,13 +41,19 @@ class _PaginatedSOLineListState extends PaginatedSearchState> get filterOptions => { - - }; + Map> get filterOptions => {}; @override - Future requestPage(int limit, int offset, Map params) async { - final page = await InvenTreeSOLineItem().listPaginated(limit, offset, filters: params); + Future requestPage( + int limit, + int offset, + Map params, + ) async { + final page = await InvenTreeSOLineItem().listPaginated( + limit, + offset, + filters: params, + ); return page; } @@ -63,24 +67,30 @@ class _PaginatedSOLineListState extends PaginatedSearchState SoLineDetailWidget(item)) + MaterialPageRoute(builder: (context) => SoLineDetailWidget(item)), ); - } + }, ); } else { return ListTile( title: Text(L10().error), - subtitle: Text("Missing part detail", style: TextStyle(color: COLOR_DANGER)), + subtitle: Text( + "Missing part detail", + style: TextStyle(color: COLOR_DANGER), + ), ); } } - } diff --git a/lib/widget/order/so_shipment_list.dart b/lib/widget/order/so_shipment_list.dart index a1e0dc74..a6c14723 100644 --- a/lib/widget/order/so_shipment_list.dart +++ b/lib/widget/order/so_shipment_list.dart @@ -1,4 +1,3 @@ - import "package:flutter/material.dart"; import "package:flutter_tabler_icons/flutter_tabler_icons.dart"; import "package:inventree/app_colors.dart"; @@ -9,19 +8,19 @@ import "package:inventree/inventree/model.dart"; import "package:inventree/l10.dart"; class PaginatedSOShipmentList extends PaginatedSearchWidget { - - const PaginatedSOShipmentList(Map filters) : super(filters: filters); + const PaginatedSOShipmentList(Map filters) + : super(filters: filters); @override String get searchTitle => L10().shipments; @override - _PaginatedSOShipmentListState createState() => _PaginatedSOShipmentListState(); + _PaginatedSOShipmentListState createState() => + _PaginatedSOShipmentListState(); } - -class _PaginatedSOShipmentListState extends PaginatedSearchState { - +class _PaginatedSOShipmentListState + extends PaginatedSearchState { _PaginatedSOShipmentListState() : super(); @override @@ -34,22 +33,30 @@ class _PaginatedSOShipmentListState extends PaginatedSearchState> get filterOptions => {}; @override - Future requestPage(int limit, int offset, Map params) async { - final page = await InvenTreeSalesOrderShipment().listPaginated(limit, offset, filters: params); + Future requestPage( + int limit, + int offset, + Map params, + ) async { + final page = await InvenTreeSalesOrderShipment().listPaginated( + limit, + offset, + filters: params, + ); return page; } @override Widget buildItem(BuildContext context, InvenTreeModel model) { - InvenTreeSalesOrderShipment shipment = model as InvenTreeSalesOrderShipment; return ListTile( title: Text(shipment.reference), subtitle: Text(shipment.tracking_number), - leading: shipment.shipped ? Icon(TablerIcons.calendar_check, color: COLOR_SUCCESS) : Icon(TablerIcons.calendar_cancel, color: COLOR_WARNING), - trailing: shipment.shipped ? Text(shipment.shipment_date ?? "") : null + leading: shipment.shipped + ? Icon(TablerIcons.calendar_check, color: COLOR_SUCCESS) + : Icon(TablerIcons.calendar_cancel, color: COLOR_WARNING), + trailing: shipment.shipped ? Text(shipment.shipment_date ?? "") : null, ); - } -} \ No newline at end of file +} diff --git a/lib/widget/paginator.dart b/lib/widget/paginator.dart index 01507270..c525de56 100644 --- a/lib/widget/paginator.dart +++ b/lib/widget/paginator.dart @@ -15,12 +15,10 @@ import "package:inventree/preferences.dart"; import "package:inventree/widget/refreshable_state.dart"; - /* * Abstract base widget class for rendering a PaginatedSearchState */ abstract class PaginatedSearchWidget extends StatefulWidget { - const PaginatedSearchWidget({this.filters = const {}, this.title = ""}); final String title; @@ -30,12 +28,12 @@ abstract class PaginatedSearchWidget extends StatefulWidget { final Map filters; } - /* * Generic stateful widget for displaying paginated data retrieved via the API */ -abstract class PaginatedSearchState extends State with BaseWidgetProperties { - +abstract class PaginatedSearchState + extends State + with BaseWidgetProperties { static const _pageSize = 25; bool showSearchWidget = false; @@ -73,7 +71,6 @@ abstract class PaginatedSearchState extends Sta // Construct the boolean filter options for this list Future> constructFilters() async { - Map f = {}; for (String k in filterOptions.keys) { @@ -95,7 +92,10 @@ abstract class PaginatedSearchState extends Sta // Return the selected ordering "field" for this list widget Future orderingField() async { - dynamic field = await InvenTreeSettingsManager().getValue("${prefix}ordering_field", null); + dynamic field = await InvenTreeSettingsManager().getValue( + "${prefix}ordering_field", + null, + ); if (field != null && orderingOptions.containsKey(field.toString())) { // A valid ordering field has been found @@ -110,7 +110,10 @@ abstract class PaginatedSearchState extends Sta // Return the selected ordering "order" ("+" or "-") for this list widget Future orderingOrder() async { - dynamic order = await InvenTreeSettingsManager().getValue("${prefix}ordering_order", "+"); + dynamic order = await InvenTreeSettingsManager().getValue( + "${prefix}ordering_order", + "+", + ); return order == "+" ? "+" : "-"; } @@ -137,10 +140,10 @@ abstract class PaginatedSearchState extends Sta // Construct the 'ordering' options List> _opts = []; - orderingOptions.forEach((k, v) => _opts.add({ - "value": k.toString(), - "display_name": v.toString() - })); + orderingOptions.forEach( + (k, v) => + _opts.add({"value": k.toString(), "display_name": v.toString()}), + ); if (_field == null && _opts.isNotEmpty) { _field = _opts.first["value"]; @@ -160,16 +163,10 @@ abstract class PaginatedSearchState extends Sta "required": true, "value": _order, "choices": [ - { - "value": "+", - "display_name": "Ascending", - }, - { - "value": "-", - "display_name": "Descending", - } - ] - } + {"value": "+", "display_name": "Ascending"}, + {"value": "-", "display_name": "Descending"}, + ], + }, }; // Add in selected filter options @@ -219,7 +216,6 @@ abstract class PaginatedSearchState extends Sta fields, icon: TablerIcons.circle_check, onSuccess: (Map data) async { - // Extract data from the processed form String f = (data["ordering_field"] ?? _field) as String; String o = (data["ordering_order"] ?? _order) as String; @@ -235,7 +231,7 @@ abstract class PaginatedSearchState extends Sta // Refresh data from the server _pagingController.refresh(); - } + }, ); } @@ -245,7 +241,6 @@ abstract class PaginatedSearchState extends Sta int resultCount = 0; String resultsString() { - if (resultCount <= 0) { return noResultsText; } else { @@ -260,7 +255,8 @@ abstract class PaginatedSearchState extends Sta Timer? _debounceTimer; // Pagination controller - final PagingController _pagingController = PagingController(firstPageKey: 0); + final PagingController _pagingController = + PagingController(firstPageKey: 0); void refresh() { _pagingController.refresh(); @@ -286,8 +282,11 @@ abstract class PaginatedSearchState extends Sta * Each implementing class must override this function, * and return an InvenTreePageResponse object with the correct data format */ - Future requestPage(int limit, int offset, Map params) async { - + Future requestPage( + int limit, + int offset, + Map params, + ) async { // Default implementation returns null - must be overridden return null; } @@ -301,7 +300,6 @@ abstract class PaginatedSearchState extends Sta // Include user search term if (searchTerm.isNotEmpty) { - String _search = searchTerm; // Include original search in search test @@ -329,11 +327,7 @@ abstract class PaginatedSearchState extends Sta params.addAll(f); } - final page = await requestPage( - _pageSize, - pageKey, - params - ); + final page = await requestPage(_pageSize, pageKey, params); // We may have disposed of the widget while the request was in progress // If this is the case, abort @@ -350,7 +344,7 @@ abstract class PaginatedSearchState extends Sta if (page != null) { for (var result in page.results) { - items.add(result); + items.add(result); } } @@ -367,16 +361,12 @@ abstract class PaginatedSearchState extends Sta } catch (error, stackTrace) { _pagingController.error = error; - sentryReportError( - "paginator.fetchPage", - error, stackTrace, - ); + sentryReportError("paginator.fetchPage", error, stackTrace); } } // Callback function when the search term is updated void updateSearchTerm() { - if (searchTerm == searchController.text) { // No change return; @@ -410,7 +400,6 @@ abstract class PaginatedSearchState extends Sta // Function to construct a single paginated item // Must be overridden in an implementing subclass Widget buildItem(BuildContext context, InvenTreeModel item) { - // This method must be overridden by the child class return ListTile( title: Text("*** UNIMPLEMENTED ***"), @@ -423,12 +412,8 @@ abstract class PaginatedSearchState extends Sta String get noResultsText => L10().noResults; @override - Widget build (BuildContext context) { - - List children = [ - buildTitleWidget(context), - Divider(), - ]; + Widget build(BuildContext context) { + List children = [buildTitleWidget(context), Divider()]; if (showSearchWidget) { children.add(buildSearchInput(context)); @@ -436,26 +421,26 @@ abstract class PaginatedSearchState extends Sta children.add( Expanded( - child: CustomScrollView( - shrinkWrap: true, - physics: AlwaysScrollableScrollPhysics(), - scrollDirection: Axis.vertical, - slivers: [ - PagedSliverList.separated( - pagingController: _pagingController, - builderDelegate: PagedChildBuilderDelegate( - itemBuilder: (ctx, item, index) { - return buildItem(ctx, item); - }, - noItemsFoundIndicatorBuilder: (context) { - return NoResultsWidget(noResultsText); - } - ), - separatorBuilder: (context, item) => const Divider(height: 1), - ) - ] - ) - ) + child: CustomScrollView( + shrinkWrap: true, + physics: AlwaysScrollableScrollPhysics(), + scrollDirection: Axis.vertical, + slivers: [ + PagedSliverList.separated( + pagingController: _pagingController, + builderDelegate: PagedChildBuilderDelegate( + itemBuilder: (ctx, item, index) { + return buildItem(ctx, item); + }, + noItemsFoundIndicatorBuilder: (context) { + return NoResultsWidget(noResultsText); + }, + ), + separatorBuilder: (context, item) => const Divider(height: 1), + ), + ], + ), + ), ); return RefreshIndicator( @@ -473,28 +458,34 @@ abstract class PaginatedSearchState extends Sta * Build the title widget for this list */ Widget buildTitleWidget(BuildContext context) { - const double icon_size = 32; List _icons = []; if (filterOptions.isNotEmpty || orderingOptions.isNotEmpty) { - _icons.add(IconButton( - onPressed: () async { - _setOrderingOptions(context); - }, - icon: Icon(Icons.filter_alt, size: icon_size) - )); + _icons.add( + IconButton( + onPressed: () async { + _setOrderingOptions(context); + }, + icon: Icon(Icons.filter_alt, size: icon_size), + ), + ); } - _icons.add(IconButton( + _icons.add( + IconButton( onPressed: () { setState(() { showSearchWidget = !showSearchWidget; }); }, - icon: Icon(showSearchWidget ? Icons.zoom_out : Icons.search, size: icon_size) - )); + icon: Icon( + showSearchWidget ? Icons.zoom_out : Icons.search, + size: icon_size, + ), + ), + ); // _icons.add(IconButton( // onPressed: () async { @@ -506,20 +497,13 @@ abstract class PaginatedSearchState extends Sta return ListTile( title: Text( widget.searchTitle, - style: TextStyle( - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontWeight: FontWeight.bold), ), subtitle: Text( "${L10().results}: ${resultCount}", - style: TextStyle( - fontStyle: FontStyle.italic - ), - ), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: _icons, + style: TextStyle(fontStyle: FontStyle.italic), ), + trailing: Row(mainAxisSize: MainAxisSize.min, children: _icons), ); } @@ -530,7 +514,9 @@ abstract class PaginatedSearchState extends Sta return ListTile( trailing: GestureDetector( child: Icon( - searchController.text.isEmpty ? TablerIcons.search : TablerIcons.backspace, + searchController.text.isEmpty + ? TablerIcons.search + : TablerIcons.backspace, color: searchController.text.isNotEmpty ? COLOR_DANGER : COLOR_ACTION, ), onTap: () { @@ -545,31 +531,22 @@ abstract class PaginatedSearchState extends Sta onChanged: (value) { updateSearchTerm(); }, - decoration: InputDecoration( - hintText: L10().search, - ), - ) + decoration: InputDecoration(hintText: L10().search), + ), ); } } - class NoResultsWidget extends StatelessWidget { - const NoResultsWidget(this.description); final String description; @override Widget build(BuildContext context) { - return ListTile( - title: Text( - description, - style: TextStyle(fontStyle: FontStyle.italic), - ), + title: Text(description, style: TextStyle(fontStyle: FontStyle.italic)), leading: Icon(TablerIcons.exclamation_circle, color: COLOR_WARNING), ); } - } diff --git a/lib/widget/part/bom_list.dart b/lib/widget/part/bom_list.dart index 77a169be..5e17e530 100644 --- a/lib/widget/part/bom_list.dart +++ b/lib/widget/part/bom_list.dart @@ -1,4 +1,3 @@ - import "package:flutter/material.dart"; import "package:flutter_tabler_icons/flutter_tabler_icons.dart"; @@ -14,13 +13,15 @@ import "package:inventree/widget/paginator.dart"; import "package:inventree/widget/progress.dart"; import "package:inventree/widget/refreshable_state.dart"; - /* * Widget for displaying a Bill of Materials for a specified Part instance */ class BillOfMaterialsWidget extends StatefulWidget { - - const BillOfMaterialsWidget(this.part, {this.isParentComponent = true, Key? key}) : super(key: key); + const BillOfMaterialsWidget( + this.part, { + this.isParentComponent = true, + Key? key, + }) : super(key: key); final InvenTreePart part; @@ -53,12 +54,11 @@ class _BillOfMaterialsState extends RefreshableState { showFilterOptions = !showFilterOptions; }); }, - ) + ), ]; @override Widget getBody(BuildContext context) { - Map filters = {}; if (widget.isParentComponent) { @@ -72,7 +72,11 @@ class _BillOfMaterialsState extends RefreshableState { ListTile( leading: InvenTreeAPI().getThumbnail(widget.part.thumbnail), title: Text(widget.part.fullname), - subtitle: Text(widget.isParentComponent ? L10().billOfMaterials : L10().usedInDetails), + subtitle: Text( + widget.isParentComponent + ? L10().billOfMaterials + : L10().usedInDetails, + ), trailing: Text(L10().quantity), ), Divider(thickness: 1.25), @@ -87,13 +91,14 @@ class _BillOfMaterialsState extends RefreshableState { } } - /* * Create a paginated widget displaying a list of BomItem objects */ class PaginatedBomList extends PaginatedSearchWidget { - - const PaginatedBomList(Map filters, {this.isParentPart = true}) : super(filters: filters); + const PaginatedBomList( + Map filters, { + this.isParentPart = true, + }) : super(filters: filters); final bool isParentPart; @@ -104,9 +109,7 @@ class PaginatedBomList extends PaginatedSearchWidget { _PaginatedBomListState createState() => _PaginatedBomListState(); } - class _PaginatedBomListState extends PaginatedSearchState { - _PaginatedBomListState() : super(); @override @@ -123,23 +126,31 @@ class _PaginatedBomListState extends PaginatedSearchState { "sub_part_assembly": { "label": L10().filterAssembly, "help_text": L10().filterAssemblyDetail, - } + }, }; @override - Future requestPage(int limit, int offset, Map params) async { - - final page = await InvenTreeBomItem().listPaginated(limit, offset, filters: params); + Future requestPage( + int limit, + int offset, + Map params, + ) async { + final page = await InvenTreeBomItem().listPaginated( + limit, + offset, + filters: params, + ); return page; } @override Widget buildItem(BuildContext context, InvenTreeModel model) { - InvenTreeBomItem bomItem = model as InvenTreeBomItem; - InvenTreePart? subPart = widget.isParentPart ? bomItem.subPart : bomItem.part; + InvenTreePart? subPart = widget.isParentPart + ? bomItem.subPart + : bomItem.part; String title = subPart?.fullname ?? "error - no name"; @@ -151,16 +162,17 @@ class _PaginatedBomListState extends PaginatedSearchState { style: TextStyle(fontWeight: FontWeight.bold), ), leading: InvenTreeAPI().getThumbnail(subPart?.thumbnail ?? ""), - onTap: subPart == null ? null : () async { + onTap: subPart == null + ? null + : () async { + showLoadingOverlay(); + var part = await InvenTreePart().get(subPart.pk); + hideLoadingOverlay(); - showLoadingOverlay(); - var part = await InvenTreePart().get(subPart.pk); - hideLoadingOverlay(); - - if (part is InvenTreePart) { - part.goToDetailPage(context); - } - }, + if (part is InvenTreePart) { + part.goToDetailPage(context); + } + }, ); } -} \ No newline at end of file +} diff --git a/lib/widget/part/category_display.dart b/lib/widget/part/category_display.dart index 7ca833ee..19138c95 100644 --- a/lib/widget/part/category_display.dart +++ b/lib/widget/part/category_display.dart @@ -13,9 +13,7 @@ import "package:inventree/widget/progress.dart"; import "package:inventree/widget/snacks.dart"; import "package:inventree/widget/refreshable_state.dart"; - class CategoryDisplayWidget extends StatefulWidget { - const CategoryDisplayWidget(this.category, {Key? key}) : super(key: key); final InvenTreePartCategory? category; @@ -24,9 +22,7 @@ class CategoryDisplayWidget extends StatefulWidget { _CategoryDisplayState createState() => _CategoryDisplayState(); } - class _CategoryDisplayState extends RefreshableState { - _CategoryDisplayState(); @override @@ -40,12 +36,12 @@ class _CategoryDisplayState extends RefreshableState { if (InvenTreePartCategory().canEdit) { actions.add( IconButton( - icon: Icon(TablerIcons.edit), + icon: Icon(TablerIcons.edit), tooltip: L10().editCategory, onPressed: () { _editCategoryDialog(context); }, - ) + ), ); } } @@ -58,13 +54,13 @@ class _CategoryDisplayState extends RefreshableState { List actions = []; if (InvenTreePart().canCreate) { - actions.add( - SpeedDialChild( - child: Icon(TablerIcons.box), - label: L10().partCreateDetail, - onTap: _newPart, - ) - ); + actions.add( + SpeedDialChild( + child: Icon(TablerIcons.box), + label: L10().partCreateDetail, + onTap: _newPart, + ), + ); } if (InvenTreePartCategory().canCreate) { @@ -74,8 +70,8 @@ class _CategoryDisplayState extends RefreshableState { label: L10().categoryCreateDetail, onTap: () { _newCategory(context); - } - ) + }, + ), ); } @@ -91,12 +87,12 @@ class _CategoryDisplayState extends RefreshableState { } _cat.editForm( - context, - L10().editCategory, - onSuccess: (data) async { - refresh(context); - showSnackIcon(L10().categoryUpdated, success: true); - } + context, + L10().editCategory, + onSuccess: (data) async { + refresh(context); + showSnackIcon(L10().categoryUpdated, success: true); + }, ); } @@ -107,7 +103,6 @@ class _CategoryDisplayState extends RefreshableState { @override Future request(BuildContext context) async { - // Update the category if (widget.category != null) { final bool result = await widget.category?.reload() ?? false; @@ -126,79 +121,69 @@ class _CategoryDisplayState extends RefreshableState { title: Text( L10().partCategoryTopLevel, style: TextStyle(fontStyle: FontStyle.italic), - ) - ) + ), + ), ); } else { - List children = [ ListTile( - title: Text("${widget.category?.name}", - style: TextStyle(fontWeight: FontWeight.bold) + title: Text( + "${widget.category?.name}", + style: TextStyle(fontWeight: FontWeight.bold), ), subtitle: Text("${widget.category?.description}"), - leading: widget.category!.customIcon != null ? Icon(widget.category!.customIcon) : Icon(TablerIcons.sitemap) + leading: widget.category!.customIcon != null + ? Icon(widget.category!.customIcon) + : Icon(TablerIcons.sitemap), ), ]; if (extra) { children.add( - ListTile( - title: Text(L10().parentCategory), - subtitle: Text("${widget.category?.parentPathString}"), - leading: Icon( - TablerIcons.arrow_move_up, - color: COLOR_ACTION, - ), - onTap: () async { + ListTile( + title: Text(L10().parentCategory), + subtitle: Text("${widget.category?.parentPathString}"), + leading: Icon(TablerIcons.arrow_move_up, color: COLOR_ACTION), + onTap: () async { + int parentId = widget.category?.parentId ?? -1; - int parentId = widget.category?.parentId ?? -1; + if (parentId < 0) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => CategoryDisplayWidget(null), + ), + ); + } else { + showLoadingOverlay(); + var cat = await InvenTreePartCategory().get(parentId); + hideLoadingOverlay(); - if (parentId < 0) { - Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null))); - } else { - - showLoadingOverlay(); - var cat = await InvenTreePartCategory().get(parentId); - hideLoadingOverlay(); - - if (cat is InvenTreePartCategory) { - cat.goToDetailPage(context); - } + if (cat is InvenTreePartCategory) { + cat.goToDetailPage(context); } - }, - ) + } + }, + ), ); } - return Card( - child: Column( - children: children - ), - ); + return Card(child: Column(children: children)); } } @override List getTabIcons(BuildContext context) { - - return [ - Tab(text: L10().details), - Tab(text: L10().parts), - ]; + return [Tab(text: L10().details), Tab(text: L10().parts)]; } @override List getTabs(BuildContext context) { - return [ - Column(children: detailTiles()), - Column(children: partsTiles()), - ]; + return [Column(children: detailTiles()), Column(children: partsTiles())]; } // Construct the "details" panel List detailTiles() { - Map filters = {}; int? parent = widget.category?.pk; @@ -212,12 +197,9 @@ class _CategoryDisplayState extends RefreshableState { List tiles = [ getCategoryDescriptionCard(), Expanded( - child: PaginatedPartCategoryList( - filters, - title: L10().subcategories, - ), + child: PaginatedPartCategoryList(filters, title: L10().subcategories), flex: 10, - ) + ), ]; return tiles; @@ -225,31 +207,21 @@ class _CategoryDisplayState extends RefreshableState { // Construct the "parts" panel List partsTiles() { - Map filters = { "category": widget.category?.pk.toString() ?? "null", }; - return [ - Expanded( - child: PaginatedPartList(filters), - flex: 10, - ) - ]; + return [Expanded(child: PaginatedPartList(filters), flex: 10)]; } Future _newCategory(BuildContext context) async { - int pk = widget.category?.pk ?? -1; InvenTreePartCategory().createForm( context, L10().categoryCreate, - data: { - "parent": (pk > 0) ? pk : null, - }, + data: {"parent": (pk > 0) ? pk : null}, onSuccess: (result) async { - Map data = result as Map; if (data.containsKey("pk")) { @@ -260,29 +232,25 @@ class _CategoryDisplayState extends RefreshableState { } else { refresh(context); } - } + }, ); } Future _newPart() async { - int pk = widget.category?.pk ?? -1; InvenTreePart().createForm( context, L10().partCreate, - data: { - "category": (pk > 0) ? pk : null - }, + data: {"category": (pk > 0) ? pk : null}, onSuccess: (result) async { - Map data = result as Map; if (data.containsKey("pk")) { var part = InvenTreePart.fromJson(data); part.goToDetailPage(context); } - } + }, ); } } diff --git a/lib/widget/part/category_list.dart b/lib/widget/part/category_list.dart index c0aaa865..41442d97 100644 --- a/lib/widget/part/category_list.dart +++ b/lib/widget/part/category_list.dart @@ -9,19 +9,15 @@ import "package:inventree/api.dart"; import "package:inventree/l10.dart"; class PartCategoryList extends StatefulWidget { - const PartCategoryList(this.filters); final Map filters; @override _PartCategoryListState createState() => _PartCategoryListState(); - } - class _PartCategoryListState extends RefreshableState { - _PartCategoryListState(); @override @@ -34,19 +30,21 @@ class _PartCategoryListState extends RefreshableState { } class PaginatedPartCategoryList extends PaginatedSearchWidget { - - const PaginatedPartCategoryList(Map filters, {String title = ""}) : super(filters: filters, title: title); + const PaginatedPartCategoryList( + Map filters, { + String title = "", + }) : super(filters: filters, title: title); @override String get searchTitle => title.isNotEmpty ? title : L10().partCategories; @override - _PaginatedPartCategoryListState createState() => _PaginatedPartCategoryListState(); + _PaginatedPartCategoryListState createState() => + _PaginatedPartCategoryListState(); } - -class _PaginatedPartCategoryListState extends PaginatedSearchState { - +class _PaginatedPartCategoryListState + extends PaginatedSearchState { // _PaginatedPartCategoryListState(Map filters, bool searchEnabled) : super(filters, searchEnabled); @override @@ -59,16 +57,12 @@ class _PaginatedPartCategoryListState extends PaginatedSearchState get orderingOptions { - - Map options = { - "name": L10().name, - "level": L10().level, - }; + Map options = {"name": L10().name, "level": L10().level}; // Note: API v69 changed 'parts' to 'part_count' if (InvenTreeAPI().apiVersion >= 69) { @@ -81,16 +75,22 @@ class _PaginatedPartCategoryListState extends PaginatedSearchState requestPage(int limit, int offset, Map params) async { - - final page = await InvenTreePartCategory().listPaginated(limit, offset, filters: params); + Future requestPage( + int limit, + int offset, + Map params, + ) async { + final page = await InvenTreePartCategory().listPaginated( + limit, + offset, + filters: params, + ); return page; } @override Widget buildItem(BuildContext context, InvenTreeModel model) { - InvenTreePartCategory category = model as InvenTreePartCategory; return ListTile( @@ -103,4 +103,4 @@ class _PaginatedPartCategoryListState extends PaginatedSearchState _PartDisplayState(part); - } - class _PartDisplayState extends RefreshableState { - _PartDisplayState(this.part); InvenTreePart part; @@ -76,13 +71,13 @@ class _PartDisplayState extends RefreshableState { if (InvenTreePart().canEdit) { actions.add( - IconButton( - icon: Icon(TablerIcons.edit), - tooltip: L10().editPart, - onPressed: () { - _editPartDialog(context); - } - ) + IconButton( + icon: Icon(TablerIcons.edit), + tooltip: L10().editPart, + onPressed: () { + _editPartDialog(context); + }, + ), ); } return actions; @@ -94,11 +89,13 @@ class _PartDisplayState extends RefreshableState { if (InvenTreePart().canEdit) { actions.add( - customBarcodeAction( - context, this, - widget.part.customBarcode, "part", - widget.part.pk - ) + customBarcodeAction( + context, + this, + widget.part.customBarcode, + "part", + widget.part.pk, + ), ); } @@ -111,13 +108,13 @@ class _PartDisplayState extends RefreshableState { if (InvenTreeStockItem().canCreate) { actions.add( - SpeedDialChild( - child: Icon(TablerIcons.packages), - label: L10().stockItemCreate, - onTap: () { - _newStockItem(context); - } - ) + SpeedDialChild( + child: Icon(TablerIcons.packages), + label: L10().stockItemCreate, + onTap: () { + _newStockItem(context); + }, + ), ); } @@ -132,10 +129,10 @@ class _PartDisplayState extends RefreshableState { labels, widget.part.pk, "part", - "part=${widget.part.pk}" + "part=${widget.part.pk}", ); - } - ) + }, + ), ); } @@ -153,14 +150,22 @@ class _PartDisplayState extends RefreshableState { @override Future request(BuildContext context) async { - final bool result = await part.reload(); // Load page settings from local storage - showPricing = await InvenTreeSettingsManager().getBool(INV_PART_SHOW_PRICING, true); - showParameters = await InvenTreeSettingsManager().getBool(INV_PART_SHOW_PARAMETERS, true); + showPricing = await InvenTreeSettingsManager().getBool( + INV_PART_SHOW_PRICING, + true, + ); + showParameters = await InvenTreeSettingsManager().getBool( + INV_PART_SHOW_PARAMETERS, + true, + ); showBom = await InvenTreeSettingsManager().getBool(INV_PART_SHOW_BOM, true); - allowLabelPrinting = await InvenTreeSettingsManager().getBool(INV_ENABLE_LABEL_PRINTING, true); + allowLabelPrinting = await InvenTreeSettingsManager().getBool( + INV_ENABLE_LABEL_PRINTING, + true, + ); if (!result || part.pk == -1) { // Part could not be loaded, for some reason @@ -211,11 +216,9 @@ class _PartDisplayState extends RefreshableState { } // Request the number of BOM items - InvenTreePart().count( - filters: { - "in_bom_for": part.pk.toString(), - } - ).then((int value) { + InvenTreePart().count(filters: {"in_bom_for": part.pk.toString()}).then(( + int value, + ) { if (mounted) { setState(() { bomCount = value; @@ -224,11 +227,9 @@ class _PartDisplayState extends RefreshableState { }); // Request number of "used in" parts - InvenTreeBomItem().count( - filters: { - "uses": part.pk.toString(), - } - ).then((int value) { + InvenTreeBomItem().count(filters: {"uses": part.pk.toString()}).then(( + int value, + ) { if (mounted) { setState(() { usedInCount = value; @@ -237,11 +238,9 @@ class _PartDisplayState extends RefreshableState { }); // Request the number of variant items - InvenTreePart().count( - filters: { - "variant_of": part.pk.toString(), - } - ).then((int value) { + InvenTreePart().count(filters: {"variant_of": part.pk.toString()}).then(( + int value, + ) { if (mounted) { setState(() { variantCount = value; @@ -253,16 +252,14 @@ class _PartDisplayState extends RefreshableState { allowLabelPrinting &= api.supportsMixin("labels"); if (allowLabelPrinting) { - - String model_type = api.supportsModernLabelPrinting ? InvenTreePart.MODEL_TYPE : "part"; + String model_type = api.supportsModernLabelPrinting + ? InvenTreePart.MODEL_TYPE + : "part"; String item_key = api.supportsModernLabelPrinting ? "items" : "part"; - _labels = await getLabelTemplates( - model_type, - { - item_key: widget.part.pk.toString() - } - ); + _labels = await getLabelTemplates(model_type, { + item_key: widget.part.pk.toString(), + }); } if (mounted) { @@ -273,41 +270,34 @@ class _PartDisplayState extends RefreshableState { } void _editPartDialog(BuildContext context) { - part.editForm( context, L10().editPart, onSuccess: (data) async { refresh(context); showSnackIcon(L10().partEdited, success: true); - } + }, ); } Widget headerTile() { return Card( - child: ListTile( - title: Text(part.fullname), - subtitle: Text(part.description), - trailing: Text( - part.stockString(), - style: TextStyle( - fontSize: 20, - ) - ), - leading: GestureDetector( - child: api.getImage(part.thumbnail), - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => PartImageWidget(part) - ) - ).then((value) { - refresh(context); - }); - }), + child: ListTile( + title: Text(part.fullname), + subtitle: Text(part.description), + trailing: Text(part.stockString(), style: TextStyle(fontSize: 20)), + leading: GestureDetector( + child: api.getImage(part.thumbnail), + onTap: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => PartImageWidget(part)), + ).then((value) { + refresh(context); + }); + }, ), + ), ); } @@ -315,13 +305,10 @@ class _PartDisplayState extends RefreshableState { * Build a list of tiles to display under the part description */ List partTiles() { - List tiles = []; // Image / name / description - tiles.add( - headerTile() - ); + tiles.add(headerTile()); if (loading) { tiles.add(progressIndicator()); @@ -331,23 +318,13 @@ class _PartDisplayState extends RefreshableState { if (!part.isActive) { tiles.add( ListTile( - title: Text( - L10().inactive, - style: TextStyle( - color: COLOR_DANGER - ) - ), + title: Text(L10().inactive, style: TextStyle(color: COLOR_DANGER)), subtitle: Text( L10().inactiveDetail, - style: TextStyle( - color: COLOR_DANGER - ) + style: TextStyle(color: COLOR_DANGER), ), - leading: Icon( - TablerIcons.exclamation_circle, - color: COLOR_DANGER - ), - ) + leading: Icon(TablerIcons.exclamation_circle, color: COLOR_DANGER), + ), ); } @@ -356,15 +333,11 @@ class _PartDisplayState extends RefreshableState { ListTile( title: Text(L10().templatePart), subtitle: Text(parentPart!.fullname), - leading: api.getImage( - parentPart!.thumbnail, - width: 32, - height: 32, - ), + leading: api.getImage(parentPart!.thumbnail, width: 32, height: 32), onTap: () { parentPart?.goToDetailPage(context); - } - ) + }, + ), ); } @@ -372,58 +345,58 @@ class _PartDisplayState extends RefreshableState { if (part.categoryName.isNotEmpty) { tiles.add( ListTile( - title: Text(L10().partCategory), - subtitle: Text("${part.categoryName}"), - leading: Icon(TablerIcons.sitemap, color: COLOR_ACTION), - onTap: () async { - if (part.categoryId > 0) { + title: Text(L10().partCategory), + subtitle: Text("${part.categoryName}"), + leading: Icon(TablerIcons.sitemap, color: COLOR_ACTION), + onTap: () async { + if (part.categoryId > 0) { + showLoadingOverlay(); + var cat = await InvenTreePartCategory().get(part.categoryId); + hideLoadingOverlay(); - showLoadingOverlay(); - var cat = await InvenTreePartCategory().get(part.categoryId); - hideLoadingOverlay(); - - if (cat is InvenTreePartCategory) { - cat.goToDetailPage(context); - } + if (cat is InvenTreePartCategory) { + cat.goToDetailPage(context); } - }, - ) + } + }, + ), ); } else { tiles.add( - ListTile( - title: Text(L10().partCategory), - subtitle: Text(L10().partCategoryTopLevel), - leading: Icon(TablerIcons.sitemap, color: COLOR_ACTION), - onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) => CategoryDisplayWidget(null))); - }, - ) + ListTile( + title: Text(L10().partCategory), + subtitle: Text(L10().partCategoryTopLevel), + leading: Icon(TablerIcons.sitemap, color: COLOR_ACTION), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => CategoryDisplayWidget(null), + ), + ); + }, + ), ); } // Display number of "variant" parts if any exist if (variantCount > 0) { tiles.add( - ListTile( - title: Text(L10().variants), - leading: Icon(TablerIcons.versions, color: COLOR_ACTION), - trailing: Text(variantCount.toString()), - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => PartList( - { - "variant_of": part.pk.toString(), - }, - title: L10().variants - ) - ) - ); - }, - ) + ListTile( + title: Text(L10().variants), + leading: Icon(TablerIcons.versions, color: COLOR_ACTION), + trailing: Text(variantCount.toString()), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => PartList({ + "variant_of": part.pk.toString(), + }, title: L10().variants), + ), + ); + }, + ), ); } @@ -434,19 +407,16 @@ class _PartDisplayState extends RefreshableState { leading: Icon(TablerIcons.packages), trailing: Text( part.stockString(), - style: TextStyle( - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontWeight: FontWeight.bold), ), ), ); if (showPricing && partPricing != null) { - String pricing = formatPriceRange( partPricing?.overallMin, partPricing?.overallMax, - currency: partPricing?.currency + currency: partPricing?.currency, ); tiles.add( @@ -455,15 +425,14 @@ class _PartDisplayState extends RefreshableState { leading: Icon(TablerIcons.currency_dollar, color: COLOR_ACTION), trailing: Text( pricing.isNotEmpty ? pricing : L10().noPricingAvailable, - style: TextStyle( - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontWeight: FontWeight.bold), ), onTap: () { Navigator.push( context, MaterialPageRoute( - builder: (context) => PartPricingWidget(part: part, partPricing: partPricing), + builder: (context) => + PartPricingWidget(part: part, partPricing: partPricing), ), ); }, @@ -473,7 +442,6 @@ class _PartDisplayState extends RefreshableState { // Tiles for "purchaseable" parts if (part.isPurchaseable) { - // On order tiles.add( ListTile( @@ -484,39 +452,41 @@ class _PartDisplayState extends RefreshableState { onTap: () { // TODO - Order views }, - ) + ), ); - } // Tiles for an "assembly" part if (part.isAssembly) { - if (showBom && bomCount > 0) { tiles.add( - ListTile( - title: Text(L10().billOfMaterials), - leading: Icon(TablerIcons.list_tree, color: COLOR_ACTION), - trailing: Text(bomCount.toString()), - onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) => BillOfMaterialsWidget(part, isParentComponent: true) - )); - }, - ) + ListTile( + title: Text(L10().billOfMaterials), + leading: Icon(TablerIcons.list_tree, color: COLOR_ACTION), + trailing: Text(bomCount.toString()), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + BillOfMaterialsWidget(part, isParentComponent: true), + ), + ); + }, + ), ); } if (part.building > 0) { tiles.add( - ListTile( - title: Text(L10().building), - leading: Icon(TablerIcons.tools), - trailing: Text("${simpleNumberString(part.building)}"), - onTap: () { - // TODO - }, - ) + ListTile( + title: Text(L10().building), + leading: Icon(TablerIcons.tools), + trailing: Text("${simpleNumberString(part.building)}"), + onTap: () { + // TODO + }, + ), ); } } @@ -529,15 +499,16 @@ class _PartDisplayState extends RefreshableState { subtitle: Text(L10().usedInDetails), leading: Icon(TablerIcons.stack_2, color: COLOR_ACTION), trailing: Text(usedInCount.toString()), - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => BillOfMaterialsWidget(part, isParentComponent: false) - ) - ); - } - ) + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + BillOfMaterialsWidget(part, isParentComponent: false), + ), + ); + }, + ), ); } } @@ -545,29 +516,28 @@ class _PartDisplayState extends RefreshableState { // Keywords? if (part.keywords.isNotEmpty) { tiles.add( - ListTile( - title: Text("${part.keywords}"), - leading: Icon(TablerIcons.tags), - ) + ListTile( + title: Text("${part.keywords}"), + leading: Icon(TablerIcons.tags), + ), ); } // External link? if (part.link.isNotEmpty) { tiles.add( - ListTile( - title: Text("${part.link}"), - leading: Icon(TablerIcons.link, color: COLOR_ACTION), - onTap: () { - part.openLink(); - }, - ) + ListTile( + title: Text("${part.link}"), + leading: Icon(TablerIcons.link, color: COLOR_ACTION), + onTap: () { + part.openLink(); + }, + ), ); } // Tiles for "component" part if (part.isComponent && part.usedInCount > 0) { - tiles.add( ListTile( title: Text(L10().usedIn), @@ -577,44 +547,44 @@ class _PartDisplayState extends RefreshableState { onTap: () { // TODO }, - ) + ), ); } if (part.isPurchaseable) { - if (part.supplierCount > 0) { tiles.add( - ListTile( - title: Text(L10().suppliers), - leading: Icon(TablerIcons.building_factory, color: COLOR_ACTION), - trailing: Text("${part.supplierCount}"), - onTap: () { - Navigator.push( - context, - MaterialPageRoute(builder: (context) => SupplierPartList({ - "part": part.pk.toString() - })) - ); - }, - ) + ListTile( + title: Text(L10().suppliers), + leading: Icon(TablerIcons.building_factory, color: COLOR_ACTION), + trailing: Text("${part.supplierCount}"), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + SupplierPartList({"part": part.pk.toString()}), + ), + ); + }, + ), ); } } // Notes field tiles.add( - ListTile( - title: Text(L10().notes), - leading: Icon(TablerIcons.note, color: COLOR_ACTION), - trailing: Text(""), - onTap: () { - Navigator.push( - context, - MaterialPageRoute(builder: (context) => NotesWidget(part)) - ); - }, - ) + ListTile( + title: Text(L10().notes), + leading: Icon(TablerIcons.note, color: COLOR_ACTION), + trailing: Text(""), + onTap: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => NotesWidget(part)), + ); + }, + ), ); tiles.add( @@ -627,19 +597,18 @@ class _PartDisplayState extends RefreshableState { context, MaterialPageRoute( builder: (context) => AttachmentWidget( - InvenTreePartAttachment(), - part.pk, - L10().part, - part.canEdit - ) - ) + InvenTreePartAttachment(), + part.pk, + L10().part, + part.canEdit, + ), + ), ); }, - ) + ), ); return tiles; - } // Return tiles for each stock item @@ -654,9 +623,13 @@ class _PartDisplayState extends RefreshableState { L10().stockItems, style: TextStyle(fontWeight: FontWeight.bold), ), - subtitle: part.stockItems.isEmpty ? Text(L10().stockItemsNotAvailable) : null, - trailing: part.stockItems.isNotEmpty ? Text("${part.stockItems.length}") : null, - ) + subtitle: part.stockItems.isEmpty + ? Text(L10().stockItemsNotAvailable) + : null, + trailing: part.stockItems.isNotEmpty + ? Text("${part.stockItems.length}") + : null, + ), ); return tiles; @@ -666,7 +639,6 @@ class _PartDisplayState extends RefreshableState { * Launch a form to create a new StockItem for this part */ Future _newStockItem(BuildContext context) async { - var fields = InvenTreeStockItem().formFields(); // Serial number cannot be directly edited here @@ -677,9 +649,7 @@ class _PartDisplayState extends RefreshableState { int? default_location = part.defaultLocation; - Map data = { - "part": part.pk.toString() - }; + Map data = {"part": part.pk.toString()}; if (default_location != null) { data["location"] = default_location; @@ -688,15 +658,22 @@ class _PartDisplayState extends RefreshableState { if (part.isTrackable) { // read the next available serial number showLoadingOverlay(); - var response = await api.get("/api/part/${part.pk}/serial-numbers/", expectedStatusCode: null); + var response = await api.get( + "/api/part/${part.pk}/serial-numbers/", + expectedStatusCode: null, + ); hideLoadingOverlay(); if (response.isValid() && response.statusCode == 200) { - data["serial_numbers"] = response.data["next"] ?? response.data["latest"]; + data["serial_numbers"] = + response.data["next"] ?? response.data["latest"]; } - print("response: " + response.statusCode.toString() + response.data.toString()); - + print( + "response: " + + response.statusCode.toString() + + response.data.toString(), + ); } else { // Cannot set serial numbers for non-trackable parts fields.remove("serial_numbers"); @@ -705,28 +682,24 @@ class _PartDisplayState extends RefreshableState { print("data: ${data.toString()}"); InvenTreeStockItem().createForm( - context, - L10().stockItemCreate, - fields: fields, - data: data, - onSuccess: (result) async { + context, + L10().stockItemCreate, + fields: fields, + data: data, + onSuccess: (result) async { + Map data = result as Map; - Map data = result as Map; - - if (data.containsKey("pk")) { - var item = InvenTreeStockItem.fromJson(data); - item.goToDetailPage(context); - } + if (data.containsKey("pk")) { + var item = InvenTreeStockItem.fromJson(data); + item.goToDetailPage(context); } + }, ); } @override List getTabIcons(BuildContext context) { - List icons = [ - Tab(text: L10().details), - Tab(text: L10().stock) - ]; + List icons = [Tab(text: L10().details), Tab(text: L10().stock)]; if (showParameters) { icons.add(Tab(text: L10().parameters)); @@ -740,11 +713,9 @@ class _PartDisplayState extends RefreshableState { List tabs = [ SingleChildScrollView( physics: AlwaysScrollableScrollPhysics(), - child: Column( - children: partTiles(), - ) + child: Column(children: partTiles()), ), - PaginatedStockItemList({"part": part.pk.toString()}) + PaginatedStockItemList({"part": part.pk.toString()}), ]; if (showParameters) { @@ -753,5 +724,4 @@ class _PartDisplayState extends RefreshableState { return tabs; } - } diff --git a/lib/widget/part/part_image_widget.dart b/lib/widget/part/part_image_widget.dart index 41da3714..885f7bf0 100644 --- a/lib/widget/part/part_image_widget.dart +++ b/lib/widget/part/part_image_widget.dart @@ -11,19 +11,15 @@ import "package:inventree/widget/snacks.dart"; import "package:inventree/l10.dart"; class PartImageWidget extends StatefulWidget { - const PartImageWidget(this.part, {Key? key}) : super(key: key); final InvenTreePart part; @override _PartImageState createState() => _PartImageState(part); - } - class _PartImageState extends RefreshableState { - _PartImageState(this.part); final InvenTreePart part; @@ -38,17 +34,14 @@ class _PartImageState extends RefreshableState { @override List appBarActions(BuildContext context) { - List actions = []; if (part.canEdit) { - // File upload actions.add( IconButton( icon: Icon(TablerIcons.file_upload), onPressed: () async { - FilePickerDialog.pickFile( onPicked: (File file) async { final result = await part.uploadImage(file); @@ -58,11 +51,10 @@ class _PartImageState extends RefreshableState { } refresh(context); - } + }, ); - }, - ) + ), ); } @@ -73,5 +65,4 @@ class _PartImageState extends RefreshableState { Widget getBody(BuildContext context) { return InvenTreeAPI().getImage(part.image); } - -} \ No newline at end of file +} diff --git a/lib/widget/part/part_list.dart b/lib/widget/part/part_list.dart index 8433e0b7..e4545229 100644 --- a/lib/widget/part/part_list.dart +++ b/lib/widget/part/part_list.dart @@ -9,9 +9,7 @@ import "package:inventree/inventree/part.dart"; import "package:inventree/widget/paginator.dart"; import "package:inventree/widget/refreshable_state.dart"; - class PartList extends StatefulWidget { - const PartList(this.filters, {this.title = ""}); final String title; @@ -22,9 +20,7 @@ class PartList extends StatefulWidget { _PartListState createState() => _PartListState(filters, title); } - class _PartListState extends RefreshableState { - _PartListState(this.filters, this.title); final String title; @@ -40,13 +36,11 @@ class _PartListState extends RefreshableState { Widget getBody(BuildContext context) { return PaginatedPartList(filters); } - } - class PaginatedPartList extends PaginatedSearchWidget { - - const PaginatedPartList(Map filters) : super(filters: filters); + const PaginatedPartList(Map filters) + : super(filters: filters); @override String get searchTitle => L10().parts; @@ -55,9 +49,7 @@ class PaginatedPartList extends PaginatedSearchWidget { _PaginatedPartListState createState() => _PaginatedPartListState(); } - class _PaginatedPartListState extends PaginatedSearchState { - _PaginatedPartListState() : super(); @override @@ -84,7 +76,7 @@ class _PaginatedPartListState extends PaginatedSearchState { }, "assembly": { "label": L10().filterAssembly, - "help_text": L10().filterAssemblyDetail + "help_text": L10().filterAssemblyDetail, }, "component": { "label": L10().filterComponent, @@ -92,7 +84,7 @@ class _PaginatedPartListState extends PaginatedSearchState { }, "is_template": { "label": L10().filterTemplate, - "help_text": L10().filterTemplateDetail + "help_text": L10().filterTemplateDetail, }, "trackable": { "label": L10().filterTrackable, @@ -105,18 +97,25 @@ class _PaginatedPartListState extends PaginatedSearchState { "has_stock": { "label": L10().filterInStock, "help_text": L10().filterInStockDetail, - } + }, }; @override - Future requestPage(int limit, int offset, Map params) async { - final page = await InvenTreePart().listPaginated(limit, offset, filters: params); + Future requestPage( + int limit, + int offset, + Map params, + ) async { + final page = await InvenTreePart().listPaginated( + limit, + offset, + filters: params, + ); return page; } @override Widget buildItem(BuildContext context, InvenTreeModel model) { - InvenTreePart part = model as InvenTreePart; return ListTile( @@ -124,10 +123,7 @@ class _PaginatedPartListState extends PaginatedSearchState { subtitle: Text(part.description), trailing: Text( part.stockString(), - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold - ) + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), leading: InvenTreeAPI().getThumbnail(part.thumbnail), onTap: () { @@ -135,4 +131,4 @@ class _PaginatedPartListState extends PaginatedSearchState { }, ); } -} \ No newline at end of file +} diff --git a/lib/widget/part/part_parameter_widget.dart b/lib/widget/part/part_parameter_widget.dart index 53e772da..c4df1890 100644 --- a/lib/widget/part/part_parameter_widget.dart +++ b/lib/widget/part/part_parameter_widget.dart @@ -11,7 +11,6 @@ import "package:inventree/widget/refreshable_state.dart"; * Widget for displaying a list of parameters associated with a given Part instance */ class PartParameterWidget extends StatefulWidget { - const PartParameterWidget(this.part); final InvenTreePart part; @@ -20,7 +19,6 @@ class PartParameterWidget extends StatefulWidget { _ParameterWidgetState createState() => _ParameterWidgetState(); } - class _ParameterWidgetState extends RefreshableState { _ParameterWidgetState(); @@ -36,28 +34,18 @@ class _ParameterWidgetState extends RefreshableState { @override Widget getBody(BuildContext context) { + Map filters = {"part": widget.part.pk.toString()}; - Map filters = { - "part": widget.part.pk.toString() - }; - - return Column( - children: [ - Expanded( - child: PaginatedParameterList(filters) - ) - ], - ); + return Column(children: [Expanded(child: PaginatedParameterList(filters))]); } } - /* * Widget for displaying a paginated list of Part parameters */ class PaginatedParameterList extends PaginatedSearchWidget { - - const PaginatedParameterList(Map filters) : super(filters: filters); + const PaginatedParameterList(Map filters) + : super(filters: filters); @override String get searchTitle => L10().parameters; @@ -66,18 +54,15 @@ class PaginatedParameterList extends PaginatedSearchWidget { _PaginatedParameterState createState() => _PaginatedParameterState(); } - -class _PaginatedParameterState extends PaginatedSearchState { - +class _PaginatedParameterState + extends PaginatedSearchState { _PaginatedParameterState() : super(); @override String get prefix => "parameters_"; @override - Map get orderingOptions => { - - }; + Map get orderingOptions => {}; @override Map> get filterOptions => { @@ -85,32 +70,37 @@ class _PaginatedParameterState extends PaginatedSearchState requestPage(int limit, int offset, Map params) async { - - final page = await InvenTreePartParameter().listPaginated(limit, offset, filters: params); + Future requestPage( + int limit, + int offset, + Map params, + ) async { + final page = await InvenTreePartParameter().listPaginated( + limit, + offset, + filters: params, + ); return page; } Future editParameter(InvenTreePartParameter parameter) async { - // Checkbox values are handled separately if (parameter.is_checkbox) { return; } else { parameter.editForm( - context, - L10().editParameter, - onSuccess: (data) async { - updateSearchTerm(); - } + context, + L10().editParameter, + onSuccess: (data) async { + updateSearchTerm(); + }, ); } } @override Widget buildItem(BuildContext context, InvenTreeModel model) { - InvenTreePartParameter parameter = model as InvenTreePartParameter; String title = parameter.name; @@ -123,27 +113,28 @@ class _PaginatedParameterState extends PaginatedSearchState { - @override String getAppBarTitle() { return L10().partPricing; @@ -25,14 +27,13 @@ class _PartPricingWidgetState extends RefreshableState { @override List getTiles(BuildContext context) { - List tiles = [ Card( child: ListTile( title: Text(widget.part.fullname), subtitle: Text(widget.part.description), - leading: api.getThumbnail(widget.part.thumbnail) - ) + leading: api.getThumbnail(widget.part.thumbnail), + ), ), ]; @@ -41,7 +42,7 @@ class _PartPricingWidgetState extends RefreshableState { ListTile( title: Text(L10().noPricingAvailable), subtitle: Text(L10().noPricingDataFound), - ) + ), ); return tiles; @@ -50,10 +51,7 @@ class _PartPricingWidgetState extends RefreshableState { final pricing = widget.partPricing!; tiles.add( - ListTile( - title: Text(L10().currency), - trailing: Text(pricing.currency), - ) + ListTile(title: Text(L10().currency), trailing: Text(pricing.currency)), ); tiles.add( @@ -63,10 +61,10 @@ class _PartPricingWidgetState extends RefreshableState { formatPriceRange( pricing.overallMin, pricing.overallMax, - currency: pricing.currency - ) + currency: pricing.currency, + ), ), - ) + ), ); if (pricing.overallMin != null) { @@ -74,9 +72,9 @@ class _PartPricingWidgetState extends RefreshableState { ListTile( title: Text(L10().priceOverrideMin), trailing: Text( - renderCurrency(pricing.overallMin, pricing.overrideMinCurrency) - ) - ) + renderCurrency(pricing.overallMin, pricing.overrideMinCurrency), + ), + ), ); } @@ -85,9 +83,9 @@ class _PartPricingWidgetState extends RefreshableState { ListTile( title: Text(L10().priceOverrideMax), trailing: Text( - renderCurrency(pricing.overallMax, pricing.overrideMaxCurrency) - ) - ) + renderCurrency(pricing.overallMax, pricing.overrideMaxCurrency), + ), + ), ); } @@ -98,10 +96,10 @@ class _PartPricingWidgetState extends RefreshableState { formatPriceRange( pricing.internalCostMin, pricing.internalCostMax, - currency: pricing.currency - ) + currency: pricing.currency, + ), ), - ) + ), ); if (widget.part.isTemplate) { @@ -112,10 +110,10 @@ class _PartPricingWidgetState extends RefreshableState { formatPriceRange( pricing.variantCostMin, pricing.variantCostMax, - currency: pricing.currency - ) + currency: pricing.currency, + ), ), - ) + ), ); } @@ -124,13 +122,13 @@ class _PartPricingWidgetState extends RefreshableState { ListTile( title: Text(L10().bomCost), trailing: Text( - formatPriceRange( + formatPriceRange( pricing.bomCostMin, pricing.bomCostMax, - currency: pricing.currency - ) - ) - ) + currency: pricing.currency, + ), + ), + ), ); } @@ -142,10 +140,10 @@ class _PartPricingWidgetState extends RefreshableState { formatPriceRange( pricing.purchaseCostMin, pricing.purchaseCostMax, - currency: pricing.currency - ) + currency: pricing.currency, + ), ), - ) + ), ); tiles.add( @@ -155,10 +153,10 @@ class _PartPricingWidgetState extends RefreshableState { formatPriceRange( pricing.supplierPriceMin, pricing.supplierPriceMax, - currency: pricing.currency - ) + currency: pricing.currency, + ), ), - ) + ), ); } @@ -172,10 +170,10 @@ class _PartPricingWidgetState extends RefreshableState { formatPriceRange( pricing.salePriceMin, pricing.salePriceMax, - currency: pricing.currency - ) + currency: pricing.currency, + ), ), - ) + ), ); tiles.add( @@ -185,14 +183,13 @@ class _PartPricingWidgetState extends RefreshableState { formatPriceRange( pricing.saleHistoryMin, pricing.saleHistoryMax, - currency: pricing.currency - ) + currency: pricing.currency, + ), ), - ) + ), ); } return tiles; } - } diff --git a/lib/widget/part/part_suppliers.dart b/lib/widget/part/part_suppliers.dart index 204ac6e7..0fa79aef 100644 --- a/lib/widget/part/part_suppliers.dart +++ b/lib/widget/part/part_suppliers.dart @@ -10,19 +10,15 @@ import "package:inventree/inventree/company.dart"; import "package:inventree/widget/refreshable_state.dart"; class PartSupplierWidget extends StatefulWidget { - const PartSupplierWidget(this.part, {Key? key}) : super(key: key); final InvenTreePart part; @override _PartSupplierState createState() => _PartSupplierState(part); - } - class _PartSupplierState extends RefreshableState { - _PartSupplierState(this.part); final InvenTreePart part; @@ -46,7 +42,6 @@ class _PartSupplierState extends RefreshableState { } Widget _supplierPartTile(BuildContext context, int index) { - InvenTreeSupplierPart _part = _supplierParts[index]; return ListTile( @@ -73,5 +68,4 @@ class _PartSupplierState extends RefreshableState { itemBuilder: _supplierPartTile, ); } - -} \ No newline at end of file +} diff --git a/lib/widget/progress.dart b/lib/widget/progress.dart index 3deb9387..a95ace97 100644 --- a/lib/widget/progress.dart +++ b/lib/widget/progress.dart @@ -1,5 +1,3 @@ - - import "dart:io"; import "package:flutter/material.dart"; @@ -7,17 +5,11 @@ import "package:flutter_overlay_loader/flutter_overlay_loader.dart"; import "package:inventree/app_colors.dart"; import "package:one_context/one_context.dart"; - /* * A simplified linear progress bar widget, * with standardized color depiction */ -Widget ProgressBar( - double value, -{ - double maximum = 1.0 -}) { - +Widget ProgressBar(double value, {double maximum = 1.0}) { double v = 0; if (value <= 0 || maximum <= 0) { @@ -33,20 +25,14 @@ Widget ProgressBar( ); } - /* * Construct a circular progress indicator */ Widget progressIndicator() { - - return Center( - child: CircularProgressIndicator() - ); + return Center(child: CircularProgressIndicator()); } - void showLoadingOverlay() { - // Do not show overlay if running unit tests if (Platform.environment.containsKey("FLUTTER_TEST")) { return; @@ -60,11 +46,12 @@ void showLoadingOverlay() { Loader.show( context, - themeData: Theme.of(context).copyWith(colorScheme: ColorScheme.fromSwatch()) + themeData: Theme.of( + context, + ).copyWith(colorScheme: ColorScheme.fromSwatch()), ); } - void hideLoadingOverlay() { if (Loader.isShown) { Loader.hide(); diff --git a/lib/widget/refreshable_state.dart b/lib/widget/refreshable_state.dart index 59e70fab..9b09c975 100644 --- a/lib/widget/refreshable_state.dart +++ b/lib/widget/refreshable_state.dart @@ -10,12 +10,10 @@ import "package:inventree/widget/back.dart"; import "package:inventree/widget/drawer.dart"; import "package:inventree/widget/search.dart"; - /* * Simple mixin class which defines simple methods for defining widget properties */ mixin BaseWidgetProperties { - /* * Return a list of appBar actions * By default, no appBar actions are available @@ -23,7 +21,9 @@ mixin BaseWidgetProperties { List appBarActions(BuildContext context) => []; // Return a title for the appBar (placeholder) - String getAppBarTitle() { return "--- app bar ---"; } + String getAppBarTitle() { + return "--- app bar ---"; + } // Function to construct a drawer (override if needed) Widget getDrawer(BuildContext context) { @@ -38,7 +38,6 @@ mixin BaseWidgetProperties { // Function to construct a body Widget getBody(BuildContext context) { - // Default implementation is to return a ListView // Override getTiles to replace the internal context return ListView( @@ -51,7 +50,6 @@ mixin BaseWidgetProperties { * Construct the top AppBar for this view */ AppBar? buildAppBar(BuildContext context, GlobalKey key) { - List tabs = getTabIcons(context); return AppBar( @@ -70,8 +68,10 @@ mixin BaseWidgetProperties { * - Button to access global search * - Button to access barcode scan */ - BottomAppBar? buildBottomAppBar(BuildContext context, GlobalKey key) { - + BottomAppBar? buildBottomAppBar( + BuildContext context, + GlobalKey key, + ) { const double iconSize = 40; List icons = [ @@ -90,10 +90,8 @@ mixin BaseWidgetProperties { onPressed: () { if (InvenTreeAPI().checkConnection()) { Navigator.push( - context, - MaterialPageRoute( - builder: (context) => SearchWidget(true) - ) + context, + MaterialPageRoute(builder: (context) => SearchWidget(true)), ); } }, @@ -106,27 +104,27 @@ mixin BaseWidgetProperties { scanBarcode(context); } }, - ) + ), ]; return BottomAppBar( - shape: AutomaticNotchedShape( - RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20)), - ), - RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(40)), - ), + shape: AutomaticNotchedShape( + RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(20)), ), - notchMargin: 10, - child: IconTheme( - data: IconThemeData(color: Theme.of(context).colorScheme.onPrimary), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - children: icons, - ) - ) + RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(40)), + ), + ), + notchMargin: 10, + child: IconTheme( + data: IconThemeData(color: Theme.of(context).colorScheme.onPrimary), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: icons, + ), + ), ); } @@ -146,7 +144,6 @@ mixin BaseWidgetProperties { * Build out action buttons for a given widget */ Widget? buildSpeedDial(BuildContext context) { - final actions = actionButtons(context); final barcodeActions = barcodeButtons(context); @@ -165,44 +162,38 @@ mixin BaseWidgetProperties { spacing: 14, childPadding: const EdgeInsets.all(5), spaceBetweenChildren: 15, - ) + ), ); } if (actions.isNotEmpty) { children.add( - SpeedDial( - icon: Icons.more_horiz, - activeIcon: Icons.close, - children: actions, - spacing: 14, - childPadding: const EdgeInsets.all(5), - spaceBetweenChildren: 15, - ) + SpeedDial( + icon: Icons.more_horiz, + activeIcon: Icons.close, + children: actions, + spacing: 14, + childPadding: const EdgeInsets.all(5), + spaceBetweenChildren: 15, + ), ); } - return Wrap( - direction: Axis.horizontal, - children: children, - spacing: 15, - ); + return Wrap(direction: Axis.horizontal, children: children, spacing: 15); } // Return list of "tabs" for this widget List getTabIcons(BuildContext context) => []; - } - /* * Abstract base class which provides generic "refresh" functionality. * * - Drag down and release to 'refresh' the widget * - Define some method which runs to 'refresh' the widget state */ -abstract class RefreshableState extends State with BaseWidgetProperties { - +abstract class RefreshableState extends State + with BaseWidgetProperties { final scaffoldKey = GlobalKey(); final refreshKey = GlobalKey(); @@ -235,7 +226,6 @@ abstract class RefreshableState extends State with // Refresh the widget - handler for custom request() method Future refresh(BuildContext context) async { - // Escape if the widget is no longer loaded if (!mounted) { return; @@ -259,13 +249,14 @@ abstract class RefreshableState extends State with @override Widget build(BuildContext context) { - // Save the context for future use _context = context; List tabs = getTabIcons(context); - Widget body = tabs.isEmpty ? getBody(context) : TabBarView(children: getTabs(context)); + Widget body = tabs.isEmpty + ? getBody(context) + : TabBarView(children: getTabs(context)); Scaffold view = Scaffold( key: scaffoldKey, @@ -282,19 +273,16 @@ abstract class RefreshableState extends State with onRefresh: () async { refresh(context); }, - child: body + child: body, ), bottomNavigationBar: buildBottomAppBar(context, scaffoldKey), ); // Default implementation is *not* tabbed if (tabs.isNotEmpty) { - return DefaultTabController( - length: tabs.length, - child: view, - ); + return DefaultTabController(length: tabs.length, child: view); } else { return view; } } -} \ No newline at end of file +} diff --git a/lib/widget/search.dart b/lib/widget/search.dart index f9ab6e0c..40c5a333 100644 --- a/lib/widget/search.dart +++ b/lib/widget/search.dart @@ -23,21 +23,17 @@ import "package:inventree/widget/order/sales_order_list.dart"; import "package:inventree/widget/company/company_list.dart"; import "package:inventree/widget/company/supplier_part_list.dart"; - // Widget for performing database-wide search class SearchWidget extends StatefulWidget { - const SearchWidget(this.hasAppbar); final bool hasAppbar; @override _SearchDisplayState createState() => _SearchDisplayState(hasAppbar); - } class _SearchDisplayState extends RefreshableState { - _SearchDisplayState(this.hasAppBar) : super(); final _formKey = GlobalKey(); @@ -80,7 +76,6 @@ class _SearchDisplayState extends RefreshableState { * Determine if the search is still running */ bool isSearching() { - if (searchController.text.isEmpty) { return false; } @@ -128,7 +123,6 @@ class _SearchDisplayState extends RefreshableState { // Callback when the text is being edited // Incorporates a debounce timer to restrict search frequency void onSearchTextChanged(String text, {bool immediate = false}) { - if (debounceTimer?.isActive ?? false) { debounceTimer!.cancel(); } @@ -151,8 +145,7 @@ class _SearchDisplayState extends RefreshableState { * } * } */ - int getSearchResultCount(Map results, String key) { - + int getSearchResultCount(Map results, String key) { dynamic result = results[key]; if (result == null || result is! Map) { @@ -170,43 +163,70 @@ class _SearchDisplayState extends RefreshableState { // Actually perform the search query Future _perform_search(Map body) async { - InvenTreeAPI().post( - "search/", - body: body, - expectedStatusCode: 200).then((APIResponse response) { + InvenTreeAPI().post("search/", body: body, expectedStatusCode: 200).then(( + APIResponse response, + ) { + String searchTerm = (body["search"] ?? "").toString(); - String searchTerm = (body["search"] ?? "").toString(); + // Only update if the results correspond to the current search term + if (searchTerm == searchController.text && mounted) { + decrementPendingSearches(); - // Only update if the results correspond to the current search term - if (searchTerm == searchController.text && mounted) { + Map results = {}; - decrementPendingSearches(); + if (response.isValid() && response.data is Map) { + results = response.data as Map; - Map results = {}; + setState(() { + nPartResults = getSearchResultCount( + results, + InvenTreePart.MODEL_TYPE, + ); + nCategoryResults = getSearchResultCount( + results, + InvenTreePartCategory.MODEL_TYPE, + ); + nStockResults = getSearchResultCount( + results, + InvenTreeStockItem.MODEL_TYPE, + ); + nLocationResults = getSearchResultCount( + results, + InvenTreeStockLocation.MODEL_TYPE, + ); + nPurchaseOrderResults = getSearchResultCount( + results, + InvenTreePurchaseOrder.MODEL_TYPE, + ); + nSalesOrderResults = getSearchResultCount( + results, + InvenTreeSalesOrder.MODEL_TYPE, + ); + nSupplierPartResults = getSearchResultCount( + results, + InvenTreeSupplierPart.MODEL_TYPE, + ); + nManufacturerPartResults = getSearchResultCount( + results, + InvenTreeManufacturerPart.MODEL_TYPE, + ); + nCompanyResults = getSearchResultCount( + results, + InvenTreeCompany.MODEL_TYPE, + ); - if (response.isValid() && response.data is Map) { - results = response.data as Map; - - setState(() { - nPartResults = getSearchResultCount(results, InvenTreePart.MODEL_TYPE); - nCategoryResults = getSearchResultCount(results, InvenTreePartCategory.MODEL_TYPE); - nStockResults = getSearchResultCount(results, InvenTreeStockItem.MODEL_TYPE); - nLocationResults = getSearchResultCount(results, InvenTreeStockLocation.MODEL_TYPE); - nPurchaseOrderResults = getSearchResultCount(results, InvenTreePurchaseOrder.MODEL_TYPE); - nSalesOrderResults = getSearchResultCount(results, InvenTreeSalesOrder.MODEL_TYPE); - nSupplierPartResults = getSearchResultCount(results, InvenTreeSupplierPart.MODEL_TYPE); - nManufacturerPartResults = getSearchResultCount(results, InvenTreeManufacturerPart.MODEL_TYPE); - nCompanyResults = getSearchResultCount(results, InvenTreeCompany.MODEL_TYPE); - - // Special case for company search results - nCustomerResults = getSearchResultCount(results, "customer"); - nManufacturerResults = getSearchResultCount(results, "manufacturer"); - nSupplierResults = getSearchResultCount(results, "supplier"); - }); - } else { - resetSearchResults(); - } + // Special case for company search results + nCustomerResults = getSearchResultCount(results, "customer"); + nManufacturerResults = getSearchResultCount( + results, + "manufacturer", + ); + nSupplierResults = getSearchResultCount(results, "supplier"); + }); + } else { + resetSearchResults(); } + } }); } @@ -237,7 +257,6 @@ class _SearchDisplayState extends RefreshableState { // Consolidated search allows us to perform *all* searches in a single query if (api.supportsConsolidatedSearch) { - Map body = { "limit": 1, "search": term, @@ -262,17 +281,13 @@ class _SearchDisplayState extends RefreshableState { } if (body.isNotEmpty) { - if (mounted) { setState(() { nPendingSearches = 1; }); - _search_query = CancelableOperation.fromFuture( - _perform_search(body), - ); + _search_query = CancelableOperation.fromFuture(_perform_search(body)); } - } } else { legacySearch(term); @@ -283,7 +298,6 @@ class _SearchDisplayState extends RefreshableState { * Perform "legacy" search (without consolidated search API endpoint */ Future legacySearch(String term) async { - // Search parts if (InvenTreePart().canView) { nPendingSearches++; @@ -302,7 +316,7 @@ class _SearchDisplayState extends RefreshableState { // Search part categories if (InvenTreePartCategory().canView) { nPendingSearches++; - InvenTreePartCategory().count(searchQuery: term,).then((int n) { + InvenTreePartCategory().count(searchQuery: term).then((int n) { if (term == searchController.text) { if (mounted) { decrementPendingSearches(); @@ -346,37 +360,31 @@ class _SearchDisplayState extends RefreshableState { // 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; - }); - } - } - }); + nPendingSearches++; + InvenTreePurchaseOrder() + .count(searchQuery: term, filters: {"outstanding": "true"}) + .then((int n) { + if (term == searchController.text) { + if (mounted) { + decrementPendingSearches(); + setState(() { + nPurchaseOrderResults = n; + }); + } + } + }); } } @override List getTiles(BuildContext context) { - List tiles = []; // Search input tiles.add( ListTile( title: TextFormField( - decoration: InputDecoration( - hintText: L10().queryEmpty, - ), + decoration: InputDecoration(hintText: L10().queryEmpty), key: _formKey, readOnly: false, autofocus: true, @@ -385,12 +393,13 @@ class _SearchDisplayState extends RefreshableState { onChanged: (String text) { onSearchTextChanged(text); }, - onFieldSubmitted: (String text) { - }, + onFieldSubmitted: (String text) {}, ), trailing: GestureDetector( child: Icon( - searchController.text.isEmpty ? TablerIcons.search : TablerIcons.backspace, + searchController.text.isEmpty + ? TablerIcons.search + : TablerIcons.backspace, color: searchController.text.isEmpty ? COLOR_ACTION : COLOR_DANGER, ), onTap: () { @@ -398,8 +407,7 @@ class _SearchDisplayState extends RefreshableState { onSearchTextChanged("", immediate: true); }, ), - ) - + ), ); String query = searchController.text; @@ -415,17 +423,13 @@ class _SearchDisplayState extends RefreshableState { trailing: Text("${nPartResults}"), onTap: () { Navigator.push( - context, - MaterialPageRoute( - builder: (context) => PartList( - { - "original_search": query - } - ) - ) + context, + MaterialPageRoute( + builder: (context) => PartList({"original_search": query}), + ), ); - } - ) + }, + ), ); } @@ -440,15 +444,12 @@ class _SearchDisplayState extends RefreshableState { Navigator.push( context, MaterialPageRoute( - builder: (context) => PartCategoryList( - { - "original_search": query - } - ) - ) + builder: (context) => + PartCategoryList({"original_search": query}), + ), ); }, - ) + ), ); } @@ -463,15 +464,11 @@ class _SearchDisplayState extends RefreshableState { Navigator.push( context, MaterialPageRoute( - builder: (context) => StockItemList( - { - "original_search": query, - } - ) - ) + builder: (context) => StockItemList({"original_search": query}), + ), ); }, - ) + ), ); } @@ -486,15 +483,12 @@ class _SearchDisplayState extends RefreshableState { Navigator.push( context, MaterialPageRoute( - builder: (context) => StockLocationList( - { - "original_search": query - } - ) - ) + builder: (context) => + StockLocationList({"original_search": query}), + ), ); }, - ) + ), ); } @@ -510,14 +504,12 @@ class _SearchDisplayState extends RefreshableState { context, MaterialPageRoute( builder: (context) => PurchaseOrderListWidget( - filters: { - "original_search": query - } - ) - ) + filters: {"original_search": query}, + ), + ), ); }, - ) + ), ); } @@ -532,15 +524,12 @@ class _SearchDisplayState extends RefreshableState { Navigator.push( context, MaterialPageRoute( - builder: (context) => SalesOrderListWidget( - filters: { - "original_search": query - } - ) - ) + builder: (context) => + SalesOrderListWidget(filters: {"original_search": query}), + ), ); }, - ) + ), ); } @@ -555,16 +544,13 @@ class _SearchDisplayState extends RefreshableState { Navigator.push( context, MaterialPageRoute( - builder: (context) => CompanyListWidget( - L10().companies, - { - "original_search": query - } - ) - ) + builder: (context) => CompanyListWidget(L10().companies, { + "original_search": query, + }), + ), ); }, - ) + ), ); } @@ -579,17 +565,14 @@ class _SearchDisplayState extends RefreshableState { Navigator.push( context, MaterialPageRoute( - builder: (context) => CompanyListWidget( - L10().customers, - { - "original_search": query, - "is_customer": "true" - } - ) - ) + builder: (context) => CompanyListWidget(L10().customers, { + "original_search": query, + "is_customer": "true", + }), + ), ); }, - ) + ), ); } @@ -604,17 +587,14 @@ class _SearchDisplayState extends RefreshableState { Navigator.push( context, MaterialPageRoute( - builder: (context) => CompanyListWidget( - L10().manufacturers, - { - "original_search": query, - "is_manufacturer": "true" - } - ) - ) + builder: (context) => CompanyListWidget(L10().manufacturers, { + "original_search": query, + "is_manufacturer": "true", + }), + ), ); }, - ) + ), ); } @@ -629,17 +609,14 @@ class _SearchDisplayState extends RefreshableState { Navigator.push( context, MaterialPageRoute( - builder: (context) => CompanyListWidget( - L10().suppliers, - { - "original_search": query, - "is_supplier": "true" - } - ) - ) + builder: (context) => CompanyListWidget(L10().suppliers, { + "original_search": query, + "is_supplier": "true", + }), + ), ); }, - ) + ), ); } @@ -654,15 +631,12 @@ class _SearchDisplayState extends RefreshableState { Navigator.push( context, MaterialPageRoute( - builder: (context) => SupplierPartList( - { - "original_search": query - } - ) - ) + builder: (context) => + SupplierPartList({"original_search": query}), + ), ); }, - ) + ), ); } @@ -672,7 +646,7 @@ class _SearchDisplayState extends RefreshableState { title: Text(L10().searching), leading: Icon(TablerIcons.search), trailing: CircularProgressIndicator(), - ) + ), ); } @@ -684,7 +658,7 @@ class _SearchDisplayState extends RefreshableState { style: TextStyle(fontStyle: FontStyle.italic), ), leading: Icon(TablerIcons.zoom_cancel), - ) + ), ); } else { for (Widget result in results) { @@ -694,5 +668,4 @@ class _SearchDisplayState extends RefreshableState { return tiles; } - } diff --git a/lib/widget/snacks.dart b/lib/widget/snacks.dart index 398a4840..580955f2 100644 --- a/lib/widget/snacks.dart +++ b/lib/widget/snacks.dart @@ -8,8 +8,13 @@ import "package:inventree/l10.dart"; /* * Display a configurable 'snackbar' at the bottom of the screen */ -void showSnackIcon(String text, {IconData? icon, Function()? onAction, bool? success, String? actionText}) { - +void showSnackIcon( + String text, { + IconData? icon, + Function()? onAction, + bool? success, + String? actionText, +}) { debug("showSnackIcon: '${text}'"); // Escape quickly if we do not have context @@ -34,7 +39,6 @@ void showSnackIcon(String text, {IconData? icon, Function()? onAction, bool? suc if (icon == null && onAction == null) { icon = TablerIcons.circle_check; } - } else if (success != null && success == false) { backgroundColor = Colors.deepOrange; @@ -45,35 +49,32 @@ void showSnackIcon(String text, {IconData? icon, Function()? onAction, bool? suc String _action = actionText ?? L10().details; - List childs = [ - Text(text), - Spacer(), - ]; + List childs = [Text(text), Spacer()]; if (icon != null) { childs.add(Icon(icon)); } - OneContext().showSnackBar(builder: (context) => SnackBar( - content: GestureDetector( - child: Row( - children: childs + OneContext().showSnackBar( + builder: (context) => SnackBar( + content: GestureDetector( + child: Row(children: childs), + onTap: () { + ScaffoldMessenger.of(context!).hideCurrentSnackBar(); + }, ), - onTap: () { - ScaffoldMessenger.of(context!).hideCurrentSnackBar(); - }, + backgroundColor: backgroundColor, + action: onAction == null + ? null + : SnackBarAction( + label: _action, + onPressed: () { + // Immediately dismiss the notification + ScaffoldMessenger.of(context!).hideCurrentSnackBar(); + onAction(); + }, + ), + duration: Duration(seconds: onAction == null ? 5 : 10), ), - backgroundColor: backgroundColor, - action: onAction == null ? null : SnackBarAction( - label: _action, - onPressed: () { - // Immediately dismiss the notification - ScaffoldMessenger.of(context!).hideCurrentSnackBar(); - onAction(); - } - ), - duration: Duration(seconds: onAction == null ? 5 : 10), - ) ); - -} \ No newline at end of file +} diff --git a/lib/widget/spinner.dart b/lib/widget/spinner.dart index 3ac68c50..e06e25b3 100644 --- a/lib/widget/spinner.dart +++ b/lib/widget/spinner.dart @@ -2,7 +2,6 @@ import "package:flutter/material.dart"; import "package:inventree/app_colors.dart"; class Spinner extends StatefulWidget { - const Spinner({ this.color = COLOR_GRAY_LIGHT, Key? key, @@ -27,12 +26,8 @@ class _SpinnerState extends State with SingleTickerProviderStateMixin { _controller = AnimationController( vsync: this, duration: Duration(milliseconds: 2000), - ) - ..repeat(); - _child = Icon( - widget.icon, - color: widget.color - ); + )..repeat(); + _child = Icon(widget.icon, color: widget.color); super.initState(); } @@ -45,9 +40,6 @@ class _SpinnerState extends State with SingleTickerProviderStateMixin { @override Widget build(BuildContext context) { - return RotationTransition( - turns: _controller!, - child: _child, - ); + return RotationTransition(turns: _controller!, child: _child); } -} \ No newline at end of file +} diff --git a/lib/widget/stock/location_display.dart b/lib/widget/stock/location_display.dart index b12f834c..8c1a681e 100644 --- a/lib/widget/stock/location_display.dart +++ b/lib/widget/stock/location_display.dart @@ -18,12 +18,10 @@ import "package:inventree/widget/snacks.dart"; import "package:inventree/widget/stock/stock_list.dart"; import "package:inventree/labels.dart"; - /* * Widget for displaying detail view for a single StockLocation instance */ class LocationDisplayWidget extends StatefulWidget { - LocationDisplayWidget(this.location, {Key? key}) : super(key: key); final InvenTreeStockLocation? location; @@ -35,7 +33,6 @@ class LocationDisplayWidget extends StatefulWidget { } class _LocationDisplayState extends RefreshableState { - _LocationDisplayState(this.location); final InvenTreeStockLocation? location; @@ -54,30 +51,29 @@ class _LocationDisplayState extends RefreshableState { // Add "locate" button if (location != null && api.supportsMixin("locate")) { actions.add( - IconButton( - icon: Icon(Icons.travel_explore), - tooltip: L10().locateLocation, - onPressed: () async { - api.locateItemOrLocation(context, location: location!.pk); - } - ) + IconButton( + icon: Icon(Icons.travel_explore), + tooltip: L10().locateLocation, + onPressed: () async { + api.locateItemOrLocation(context, location: location!.pk); + }, + ), ); } // Add "edit" button if (location != null && InvenTreeStockLocation().canEdit) { actions.add( - IconButton( - icon: Icon(TablerIcons.edit), - tooltip: L10().editLocation, - onPressed: () { - _editLocationDialog(context); - } - ) + IconButton( + icon: Icon(TablerIcons.edit), + tooltip: L10().editLocation, + onPressed: () { + _editLocationDialog(context); + }, + ), ); } - return actions; } @@ -89,18 +85,18 @@ class _LocationDisplayState extends RefreshableState { // Scan items into this location if (InvenTreeStockItem().canEdit) { actions.add( - SpeedDialChild( - child: Icon(TablerIcons.qrcode), - label: L10().barcodeScanItem, - onTap: () { - scanBarcode( - context, - handler: StockLocationScanInItemsHandler(location!), - ).then((value) { - refresh(context); - }); - } - ) + SpeedDialChild( + child: Icon(TablerIcons.qrcode), + label: L10().barcodeScanItem, + onTap: () { + scanBarcode( + context, + handler: StockLocationScanInItemsHandler(location!), + ).then((value) { + refresh(context); + }); + }, + ), ); } @@ -109,41 +105,43 @@ class _LocationDisplayState extends RefreshableState { SpeedDialChild( child: Icon(Icons.barcode_reader), label: L10().scanReceivedParts, - onTap:() async { + onTap: () async { scanBarcode( context, handler: POReceiveBarcodeHandler(location: location), ); }, - ) + ), ); } // Scan this location into another one if (InvenTreeStockLocation().canEdit) { actions.add( - SpeedDialChild( - child: Icon(TablerIcons.qrcode), - label: L10().transferStockLocation, - onTap: () { - scanBarcode( - context, - handler: ScanParentLocationHandler(location!), - ).then((value) { - refresh(context); - }); - } - ) + SpeedDialChild( + child: Icon(TablerIcons.qrcode), + label: L10().transferStockLocation, + onTap: () { + scanBarcode( + context, + handler: ScanParentLocationHandler(location!), + ).then((value) { + refresh(context); + }); + }, + ), ); } // Assign or un-assign barcodes actions.add( - customBarcodeAction( - context, this, - location!.customBarcode, "stocklocation", - location!.pk - ) + customBarcodeAction( + context, + this, + location!.customBarcode, + "stocklocation", + location!.pk, + ), ); } @@ -157,44 +155,44 @@ class _LocationDisplayState extends RefreshableState { // Create new location if (InvenTreeStockLocation().canCreate) { actions.add( - SpeedDialChild( - child: Icon(TablerIcons.sitemap), - label: L10().locationCreate, - onTap: () async { - _newLocation(context); - } - ) + SpeedDialChild( + child: Icon(TablerIcons.sitemap), + label: L10().locationCreate, + onTap: () async { + _newLocation(context); + }, + ), ); } // Create new item if (InvenTreeStockItem().canCreate) { actions.add( - SpeedDialChild( - child: Icon(TablerIcons.packages), - label: L10().stockItemCreate, - onTap: () async { - _newStockItem(context); - } - ) + SpeedDialChild( + child: Icon(TablerIcons.packages), + label: L10().stockItemCreate, + onTap: () async { + _newStockItem(context); + }, + ), ); } if (widget.location != null && labels.isNotEmpty) { actions.add( - SpeedDialChild( - child: Icon(TablerIcons.printer), - label: L10().printLabel, - onTap: () async { - selectAndPrintLabel( - context, - labels, - widget.location!.pk, - "location", - "location=${widget.location!.pk}" - ); - } - ) + SpeedDialChild( + child: Icon(TablerIcons.printer), + label: L10().printLabel, + onTap: () async { + selectAndPrintLabel( + context, + labels, + widget.location!.pk, + "location", + "location=${widget.location!.pk}", + ); + }, + ), ); } @@ -212,12 +210,12 @@ class _LocationDisplayState extends RefreshableState { } _loc.editForm( - context, - L10().editLocation, - onSuccess: (data) async { - refresh(context); - showSnackIcon(L10().locationUpdated, success: true); - } + context, + L10().editLocation, + onSuccess: (data) async { + refresh(context); + showSnackIcon(L10().locationUpdated, success: true); + }, ); } @@ -238,22 +236,24 @@ class _LocationDisplayState extends RefreshableState { } List> _labels = []; - bool allowLabelPrinting = await InvenTreeSettingsManager().getBool(INV_ENABLE_LABEL_PRINTING, true); + bool allowLabelPrinting = await InvenTreeSettingsManager().getBool( + INV_ENABLE_LABEL_PRINTING, + true, + ); allowLabelPrinting &= api.supportsMixin("labels"); if (allowLabelPrinting) { - if (widget.location != null) { + String model_type = api.supportsModernLabelPrinting + ? InvenTreeStockLocation.MODEL_TYPE + : "location"; + String item_key = api.supportsModernLabelPrinting + ? "items" + : "location"; - String model_type = api.supportsModernLabelPrinting ? InvenTreeStockLocation.MODEL_TYPE : "location"; - String item_key = api.supportsModernLabelPrinting ? "items" : "location"; - - _labels = await getLabelTemplates( - model_type, - { - item_key: widget.location!.pk.toString() - } - ); + _labels = await getLabelTemplates(model_type, { + item_key: widget.location!.pk.toString(), + }); } } @@ -268,19 +268,17 @@ class _LocationDisplayState extends RefreshableState { int pk = location?.pk ?? -1; InvenTreeStockLocation().createForm( - context, - L10().locationCreate, - data: { - "parent": (pk > 0) ? pk : null, - }, - onSuccess: (result) async { - Map data = result as Map; + context, + L10().locationCreate, + data: {"parent": (pk > 0) ? pk : null}, + onSuccess: (result) async { + Map data = result as Map; - if (data.containsKey("pk")) { - var loc = InvenTreeStockLocation.fromJson(data); - loc.goToDetailPage(context); - } + if (data.containsKey("pk")) { + var loc = InvenTreeStockLocation.fromJson(data); + loc.goToDetailPage(context); } + }, ); } @@ -288,7 +286,6 @@ class _LocationDisplayState extends RefreshableState { * Launch a dialog form to create a new stock item */ Future _newStockItem(BuildContext context) async { - var fields = InvenTreeStockItem().formFields(); // Serial number field is not required here @@ -301,94 +298,89 @@ class _LocationDisplayState extends RefreshableState { } InvenTreeStockItem().createForm( - context, - L10().stockItemCreate, - data: data, - fields: fields, - onSuccess: (result) async { - Map data = result as Map; + context, + L10().stockItemCreate, + data: data, + fields: fields, + onSuccess: (result) async { + Map data = result as Map; - if (data.containsKey("pk")) { - var item = InvenTreeStockItem.fromJson(data); - item.goToDetailPage(context); - } + if (data.containsKey("pk")) { + var item = InvenTreeStockItem.fromJson(data); + item.goToDetailPage(context); } + }, ); } Widget locationDescriptionCard({bool includeActions = true}) { if (location == null) { return Card( - child: ListTile( - title: Text( - L10().stockTopLevel, - style: TextStyle(fontStyle: FontStyle.italic) - ), - leading: Icon(TablerIcons.packages), - ) + child: ListTile( + title: Text( + L10().stockTopLevel, + style: TextStyle(fontStyle: FontStyle.italic), + ), + leading: Icon(TablerIcons.packages), + ), ); } else { List children = [ ListTile( title: Text("${location!.name}"), subtitle: Text("${location!.description}"), - leading: location!.customIcon == null ? Icon(TablerIcons.packages) : Icon(location!.customIcon) + leading: location!.customIcon == null + ? Icon(TablerIcons.packages) + : Icon(location!.customIcon), ), ]; if (includeActions) { children.add( - ListTile( - title: Text(L10().parentLocation), - subtitle: Text("${location!.parentPathString}"), - leading: Icon(TablerIcons.arrow_move_up, color: COLOR_ACTION), - onTap: () async { - int parentId = location?.parentId ?? -1; + ListTile( + title: Text(L10().parentLocation), + subtitle: Text("${location!.parentPathString}"), + leading: Icon(TablerIcons.arrow_move_up, color: COLOR_ACTION), + onTap: () async { + int parentId = location?.parentId ?? -1; - if (parentId < 0) { - Navigator.push(context, MaterialPageRoute( - builder: (context) => LocationDisplayWidget(null))); - } else { - showLoadingOverlay(); - var loc = await InvenTreeStockLocation().get(parentId); - hideLoadingOverlay(); + if (parentId < 0) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => LocationDisplayWidget(null), + ), + ); + } else { + showLoadingOverlay(); + var loc = await InvenTreeStockLocation().get(parentId); + hideLoadingOverlay(); - if (loc is InvenTreeStockLocation) { - loc.goToDetailPage(context); - } + if (loc is InvenTreeStockLocation) { + loc.goToDetailPage(context); } - }, - ) + } + }, + ), ); } - return Card( - child: Column( - children: children, - ) - ); + return Card(child: Column(children: children)); } } @override List getTabIcons(BuildContext context) { - return [ - Tab(text: L10().details), - Tab(text: L10().stockItems), - ]; + return [Tab(text: L10().details), Tab(text: L10().stockItems)]; } @override List getTabs(BuildContext context) { - return [ - Column(children: detailTiles()), - Column(children: stockTiles()), - ]; + return [Column(children: detailTiles()), Column(children: stockTiles())]; } // Construct the "details" panel List detailTiles() { - Map filters = {}; int? parent = location?.pk; @@ -402,12 +394,9 @@ class _LocationDisplayState extends RefreshableState { List tiles = [ locationDescriptionCard(), Expanded( - child: PaginatedStockLocationList( - filters, - title: L10().sublocations, - ), + child: PaginatedStockLocationList(filters, title: L10().sublocations), flex: 10, - ) + ), ]; return tiles; @@ -419,11 +408,6 @@ class _LocationDisplayState extends RefreshableState { "location": location?.pk.toString() ?? "null", }; - return [ - Expanded( - child: PaginatedStockItemList(filters), - flex: 10, - ) - ]; + return [Expanded(child: PaginatedStockItemList(filters), flex: 10)]; } } diff --git a/lib/widget/stock/location_list.dart b/lib/widget/stock/location_list.dart index 05cb204a..1fc8032b 100644 --- a/lib/widget/stock/location_list.dart +++ b/lib/widget/stock/location_list.dart @@ -7,9 +7,7 @@ import "package:inventree/widget/paginator.dart"; import "package:inventree/widget/refreshable_state.dart"; import "package:inventree/l10.dart"; - class StockLocationList extends StatefulWidget { - const StockLocationList(this.filters); final Map filters; @@ -18,9 +16,7 @@ class StockLocationList extends StatefulWidget { _StockLocationListState createState() => _StockLocationListState(filters); } - class _StockLocationListState extends RefreshableState { - _StockLocationListState(this.filters); final Map filters; @@ -34,21 +30,22 @@ class _StockLocationListState extends RefreshableState { } } - class PaginatedStockLocationList extends PaginatedSearchWidget { - - const PaginatedStockLocationList(Map filters, {String title = ""}) : super(filters: filters, title: title); + const PaginatedStockLocationList( + Map filters, { + String title = "", + }) : super(filters: filters, title: title); @override String get searchTitle => title.isNotEmpty ? title : L10().stockLocations; @override - _PaginatedStockLocationListState createState() => _PaginatedStockLocationListState(); + _PaginatedStockLocationListState createState() => + _PaginatedStockLocationListState(); } - -class _PaginatedStockLocationListState extends PaginatedSearchState { - +class _PaginatedStockLocationListState + extends PaginatedSearchState { _PaginatedStockLocationListState() : super(); @override @@ -64,20 +61,26 @@ class _PaginatedStockLocationListState extends PaginatedSearchState requestPage(int limit, int offset, Map params) async { - - final page = await InvenTreeStockLocation().listPaginated(limit, offset, filters: params); + Future requestPage( + int limit, + int offset, + Map params, + ) async { + final page = await InvenTreeStockLocation().listPaginated( + limit, + offset, + filters: params, + ); return page; } @override Widget buildItem(BuildContext context, InvenTreeModel model) { - InvenTreeStockLocation location = model as InvenTreeStockLocation; return ListTile( @@ -90,4 +93,4 @@ class _PaginatedStockLocationListState extends PaginatedSearchState _StockItemDisplayState(); } - class _StockItemDisplayState extends RefreshableState { - _StockItemDisplayState(); @override @@ -62,25 +58,25 @@ class _StockItemDisplayState extends RefreshableState { if (api.supportsMixin("locate")) { actions.add( - IconButton( - icon: Icon(Icons.travel_explore), - tooltip: L10().locateItem, - onPressed: () async { - api.locateItemOrLocation(context, item: widget.item.pk); - } - ) + IconButton( + icon: Icon(Icons.travel_explore), + tooltip: L10().locateItem, + onPressed: () async { + api.locateItemOrLocation(context, item: widget.item.pk); + }, + ), ); } if (widget.item.canEdit) { actions.add( - IconButton( - icon: Icon(TablerIcons.edit), - tooltip: L10().editItem, - onPressed: () { - _editStockItem(context); - } - ) + IconButton( + icon: Icon(TablerIcons.edit), + tooltip: L10().editItem, + onPressed: () { + _editStockItem(context); + }, + ), ); } @@ -89,20 +85,17 @@ class _StockItemDisplayState extends RefreshableState { @override List actionButtons(BuildContext context) { - List actions = []; if (widget.item.canEdit) { - // Stock adjustment actions available if item is *not* serialized if (!widget.item.isSerialized()) { - actions.add( SpeedDialChild( child: Icon(TablerIcons.circle_check, color: Colors.blue), label: L10().countStock, onTap: _countStockDialog, - ) + ), ); actions.add( @@ -110,7 +103,7 @@ class _StockItemDisplayState extends RefreshableState { child: Icon(TablerIcons.circle_minus, color: Colors.red), label: L10().removeStock, onTap: _removeStockDialog, - ) + ), ); actions.add( @@ -118,7 +111,7 @@ class _StockItemDisplayState extends RefreshableState { child: Icon(TablerIcons.circle_plus, color: Colors.green), label: L10().addStock, onTap: _addStockDialog, - ) + ), ); } @@ -129,8 +122,8 @@ class _StockItemDisplayState extends RefreshableState { label: L10().transferStock, onTap: () { _transferStockDialog(context); - } - ) + }, + ), ); } @@ -141,26 +134,26 @@ class _StockItemDisplayState extends RefreshableState { label: L10().printLabel, onTap: () async { selectAndPrintLabel( - context, - labels, - widget.item.pk, - "stock", - "item=${widget.item.pk}" + context, + labels, + widget.item.pk, + "stock", + "item=${widget.item.pk}", ); - } - ) + }, + ), ); } if (widget.item.canDelete) { actions.add( - SpeedDialChild( - child: Icon(TablerIcons.trash, color: Colors.red), - label: L10().stockItemDelete, - onTap: () { - _deleteItem(context); - } - ) + SpeedDialChild( + child: Icon(TablerIcons.trash, color: Colors.red), + label: L10().stockItemDelete, + onTap: () { + _deleteItem(context); + }, + ), ); } @@ -174,26 +167,28 @@ class _StockItemDisplayState extends RefreshableState { if (widget.item.canEdit) { // Scan item into location actions.add( - SpeedDialChild( - child: Icon(Icons.qr_code_scanner), - label: L10().scanIntoLocation, - onTap: () { - scanBarcode( - context, - handler: StockItemScanIntoLocationHandler(widget.item) - ).then((ctx) { - refresh(context); - }); - } - ) + SpeedDialChild( + child: Icon(Icons.qr_code_scanner), + label: L10().scanIntoLocation, + onTap: () { + scanBarcode( + context, + handler: StockItemScanIntoLocationHandler(widget.item), + ).then((ctx) { + refresh(context); + }); + }, + ), ); actions.add( - customBarcodeAction( - context, this, - widget.item.customBarcode, - "stockitem", widget.item.pk - ) + customBarcodeAction( + context, + this, + widget.item.customBarcode, + "stockitem", + widget.item.pk, + ), ); } @@ -217,8 +212,12 @@ class _StockItemDisplayState extends RefreshableState { @override Future request(BuildContext context) async { await api.StockStatus.load(); - stockShowHistory = await InvenTreeSettingsManager().getValue(INV_STOCK_SHOW_HISTORY, false) as bool; - stockShowTests = await InvenTreeSettingsManager().getValue(INV_STOCK_SHOW_TESTS, true) as bool; + stockShowHistory = + await InvenTreeSettingsManager().getValue(INV_STOCK_SHOW_HISTORY, false) + as bool; + stockShowTests = + await InvenTreeSettingsManager().getValue(INV_STOCK_SHOW_TESTS, true) + as bool; final bool result = widget.item.pk > 0 && await widget.item.reload(); @@ -238,7 +237,6 @@ class _StockItemDisplayState extends RefreshableState { // Request test results (async) if (stockShowTests) { widget.item.getTestResults().then((value) { - if (mounted) { setState(() { // Update @@ -248,7 +246,9 @@ class _StockItemDisplayState extends RefreshableState { } // Request the number of attachments - InvenTreeStockItemAttachment().countAttachments(widget.item.pk).then((int value) { + InvenTreeStockItemAttachment().countAttachments(widget.item.pk).then(( + int value, + ) { if (mounted) { setState(() { attachmentCount = value; @@ -258,13 +258,18 @@ class _StockItemDisplayState extends RefreshableState { // Request SalesOrder information if (widget.item.hasSalesOrder) { - InvenTreeSalesOrder().get(widget.item.salesOrderId).then((instance) => { - if (mounted) { - setState(() { - salesOrder = instance as InvenTreeSalesOrder?; - }) - } - }); + InvenTreeSalesOrder() + .get(widget.item.salesOrderId) + .then( + (instance) => { + if (mounted) + { + setState(() { + salesOrder = instance as InvenTreeSalesOrder?; + }), + }, + }, + ); } else { if (mounted) { setState(() { @@ -275,13 +280,18 @@ class _StockItemDisplayState extends RefreshableState { // Request Customer information if (widget.item.hasCustomer) { - InvenTreeCompany().get(widget.item.customerId).then((instance) => { - if (mounted) { - setState(() { - customer = instance as InvenTreeCompany?; - }) - } - }); + InvenTreeCompany() + .get(widget.item.customerId) + .then( + (instance) => { + if (mounted) + { + setState(() { + customer = instance as InvenTreeCompany?; + }), + }, + }, + ); } else { if (mounted) { setState(() { @@ -291,22 +301,23 @@ class _StockItemDisplayState extends RefreshableState { } List> _labels = []; - bool allowLabelPrinting = await InvenTreeSettingsManager().getBool(INV_ENABLE_LABEL_PRINTING, true); + bool allowLabelPrinting = await InvenTreeSettingsManager().getBool( + INV_ENABLE_LABEL_PRINTING, + true, + ); allowLabelPrinting &= api.supportsMixin("labels"); // Request information on labels available for this stock item if (allowLabelPrinting) { - - String model_type = api.supportsModernLabelPrinting ? InvenTreeStockItem.MODEL_TYPE : "stock"; + String model_type = api.supportsModernLabelPrinting + ? InvenTreeStockItem.MODEL_TYPE + : "stock"; String item_key = api.supportsModernLabelPrinting ? "items" : "item"; // Clear the existing labels list - _labels = await getLabelTemplates( - model_type, - { - item_key: widget.item.pk.toString() - } - ); + _labels = await getLabelTemplates(model_type, { + item_key: widget.item.pk.toString(), + }); } if (mounted) { @@ -318,7 +329,6 @@ class _StockItemDisplayState extends RefreshableState { /// Delete the stock item from the database Future _deleteItem(BuildContext context) async { - confirmationDialog( L10().stockItemDelete, L10().stockItemDeleteConfirm, @@ -327,7 +337,7 @@ class _StockItemDisplayState extends RefreshableState { acceptText: L10().delete, onAccept: () async { final bool result = await widget.item.delete(); - + if (result) { Navigator.of(context).pop(); showSnackIcon(L10().stockItemDeleteSuccess, success: true); @@ -336,11 +346,9 @@ class _StockItemDisplayState extends RefreshableState { } }, ); - } - Future _editStockItem(BuildContext context) async { - + Future _editStockItem(BuildContext context) async { var fields = InvenTreeStockItem().formFields(); // Some fields we don't want to edit! @@ -360,16 +368,14 @@ class _StockItemDisplayState extends RefreshableState { onSuccess: (data) async { refresh(context); showSnackIcon(L10().stockItemUpdated, success: true); - } + }, ); - } /* * Launch a dialog to 'add' quantity to this StockItem */ - Future _addStockDialog() async { - + Future _addStockDialog() async { Map fields = { "pk": { "parent": "items", @@ -377,11 +383,7 @@ class _StockItemDisplayState extends RefreshableState { "hidden": true, "value": widget.item.pk, }, - "quantity": { - "parent": "items", - "nested": true, - "value": 0, - }, + "quantity": {"parent": "items", "nested": true, "value": 0}, "notes": {}, }; @@ -395,12 +397,11 @@ class _StockItemDisplayState extends RefreshableState { onSuccess: (data) async { _stockUpdateMessage(true); refresh(context); - } + }, ); } void _stockUpdateMessage(bool result) { - if (result) { showSnackIcon(L10().stockItemUpdated, success: true); } @@ -410,7 +411,6 @@ class _StockItemDisplayState extends RefreshableState { * Launch a dialog to 'remove' quantity from this StockItem */ void _removeStockDialog() { - Map fields = { "pk": { "parent": "items", @@ -418,30 +418,25 @@ class _StockItemDisplayState extends RefreshableState { "hidden": true, "value": widget.item.pk, }, - "quantity": { - "parent": "items", - "nested": true, - "value": 0, - }, + "quantity": {"parent": "items", "nested": true, "value": 0}, "notes": {}, }; launchApiForm( - context, - L10().removeStock, - InvenTreeStockItem.removeStockUrl(), - fields, - method: "POST", - icon: TablerIcons.circle_minus, - onSuccess: (data) async { - _stockUpdateMessage(true); - refresh(context); - } + context, + L10().removeStock, + InvenTreeStockItem.removeStockUrl(), + fields, + method: "POST", + icon: TablerIcons.circle_minus, + onSuccess: (data) async { + _stockUpdateMessage(true); + refresh(context); + }, ); } - Future _countStockDialog() async { - + Future _countStockDialog() async { Map fields = { "pk": { "parent": "items", @@ -458,58 +453,51 @@ class _StockItemDisplayState extends RefreshableState { }; launchApiForm( - context, - L10().countStock, - InvenTreeStockItem.countStockUrl(), - fields, - method: "POST", - icon: TablerIcons.clipboard_check, - onSuccess: (data) async { - _stockUpdateMessage(true); - refresh(context); - } + context, + L10().countStock, + InvenTreeStockItem.countStockUrl(), + fields, + method: "POST", + icon: TablerIcons.clipboard_check, + onSuccess: (data) async { + _stockUpdateMessage(true); + refresh(context); + }, ); } /* * Launches an API Form to transfer this stock item to a new location */ - Future _transferStockDialog(BuildContext context) async { - + Future _transferStockDialog(BuildContext context) async { Map fields = widget.item.transferFields(); launchApiForm( - context, - L10().transferStock, - InvenTreeStockItem.transferStockUrl(), - fields, - method: "POST", - icon: TablerIcons.transfer, - onSuccess: (data) async { - _stockUpdateMessage(true); - refresh(context); - } + context, + L10().transferStock, + InvenTreeStockItem.transferStockUrl(), + fields, + method: "POST", + icon: TablerIcons.transfer, + onSuccess: (data) async { + _stockUpdateMessage(true); + refresh(context); + }, ); } Widget headerTile() { - Widget? trailing; if (!widget.item.isInStock) { - trailing = Text( - L10().unavailable, - style: TextStyle( - color: COLOR_DANGER - ) - ); + trailing = Text(L10().unavailable, style: TextStyle(color: COLOR_DANGER)); } else if (!widget.item.isSerialized()) { trailing = Text( - widget.item.quantityString(), - style: TextStyle( - fontSize: 20, - color: api.StockStatus.color(widget.item.status), - ) + widget.item.quantityString(), + style: TextStyle( + fontSize: 20, + color: api.StockStatus.color(widget.item.status), + ), ); } @@ -521,7 +509,6 @@ class _StockItemDisplayState extends RefreshableState { trailing: trailing, onTap: () async { if (widget.item.partId > 0) { - showLoadingOverlay(); var part = await InvenTreePart().get(widget.item.partId); hideLoadingOverlay(); @@ -532,7 +519,7 @@ class _StockItemDisplayState extends RefreshableState { } }, //trailing: Text(item.serialOrQuantityDisplay()), - ) + ), ); } @@ -558,15 +545,13 @@ class _StockItemDisplayState extends RefreshableState { ListTile( title: Text(L10().stockLocation), subtitle: Text("${widget.item.locationPathString}"), - leading: Icon( - TablerIcons.location, - color: COLOR_ACTION, - ), + leading: Icon(TablerIcons.location, color: COLOR_ACTION), onTap: () async { if (widget.item.locationId > 0) { - showLoadingOverlay(); - var loc = await InvenTreeStockLocation().get(widget.item.locationId); + var loc = await InvenTreeStockLocation().get( + widget.item.locationId, + ); hideLoadingOverlay(); if (loc is InvenTreeStockLocation) { @@ -578,30 +563,32 @@ class _StockItemDisplayState extends RefreshableState { ); } else { tiles.add( - ListTile( - title: Text(L10().stockLocation), - leading: Icon(TablerIcons.location), - subtitle: Text(L10().locationNotSet), - ) + ListTile( + title: Text(L10().stockLocation), + leading: Icon(TablerIcons.location), + subtitle: Text(L10().locationNotSet), + ), ); } // Quantity information if (widget.item.isSerialized()) { tiles.add( - ListTile( - title: Text(L10().serialNumber), - leading: Icon(TablerIcons.hash), - subtitle: Text("${widget.item.serialNumber}"), - ) + ListTile( + title: Text(L10().serialNumber), + leading: Icon(TablerIcons.hash), + subtitle: Text("${widget.item.serialNumber}"), + ), ); } else if (widget.item.isInStock) { tiles.add( - ListTile( - title: widget.item.allocated > 0 ? Text(L10().quantityAvailable) : Text(L10().quantity), - leading: Icon(TablerIcons.packages), - trailing: Text("${widget.item.quantityString()}"), - ) + ListTile( + title: widget.item.allocated > 0 + ? Text(L10().quantityAvailable) + : Text(L10().quantity), + leading: Icon(TablerIcons.packages), + trailing: Text("${widget.item.quantityString()}"), + ), ); } @@ -611,18 +598,13 @@ class _StockItemDisplayState extends RefreshableState { leading: Icon(TablerIcons.box_off), title: Text( L10().unavailable, - style: TextStyle( - color: COLOR_DANGER, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: COLOR_DANGER, fontWeight: FontWeight.bold), ), subtitle: Text( L10().unavailableDetail, - style: TextStyle( - color: COLOR_DANGER - ) - ) - ) + style: TextStyle(color: COLOR_DANGER), + ), + ), ); } @@ -633,11 +615,9 @@ class _StockItemDisplayState extends RefreshableState { leading: Icon(TablerIcons.help_circle), trailing: Text( api.StockStatus.label(widget.item.status), - style: TextStyle( - color: api.StockStatus.color(widget.item.status), - ) - ) - ) + style: TextStyle(color: api.StockStatus.color(widget.item.status)), + ), + ), ); // Supplier part information (if available) @@ -647,20 +627,26 @@ class _StockItemDisplayState extends RefreshableState { title: Text(L10().supplierPart), subtitle: Text(widget.item.supplierSKU), leading: Icon(TablerIcons.building, color: COLOR_ACTION), - trailing: InvenTreeAPI().getThumbnail(widget.item.supplierImage, hideIfNull: true), + trailing: InvenTreeAPI().getThumbnail( + widget.item.supplierImage, + hideIfNull: true, + ), onTap: () async { showLoadingOverlay(); var sp = await InvenTreeSupplierPart().get( - widget.item.supplierPartId); + widget.item.supplierPartId, + ); hideLoadingOverlay(); if (sp is InvenTreeSupplierPart) { Navigator.push( - context, MaterialPageRoute( - builder: (context) => SupplierPartDetailWidget(sp)) + context, + MaterialPageRoute( + builder: (context) => SupplierPartDetailWidget(sp), + ), ); } - } - ) + }, + ), ); } @@ -673,7 +659,7 @@ class _StockItemDisplayState extends RefreshableState { onTap: () { // TODO: Click through to the "build order" }, - ) + ), ); } @@ -686,8 +672,8 @@ class _StockItemDisplayState extends RefreshableState { trailing: Text(salesOrder?.reference ?? ""), onTap: () { salesOrder?.goToDetailPage(context); - } - ) + }, + ), ); } @@ -701,7 +687,7 @@ class _StockItemDisplayState extends RefreshableState { onTap: () { customer?.goToDetailPage(context); }, - ) + ), ); } @@ -711,7 +697,7 @@ class _StockItemDisplayState extends RefreshableState { title: Text(L10().batchCode), subtitle: Text(widget.item.batch), leading: Icon(TablerIcons.clipboard_text), - ) + ), ); } @@ -721,18 +707,23 @@ class _StockItemDisplayState extends RefreshableState { title: Text(L10().packaging), subtitle: Text(widget.item.packaging), leading: Icon(TablerIcons.package), - ) + ), ); } if (expiryEnabled && widget.item.expiryDate != null) { - Widget? _expiryIcon; if (widget.item.expired) { - _expiryIcon = Text(L10().expiryExpired, style: TextStyle(color: COLOR_DANGER)); + _expiryIcon = Text( + L10().expiryExpired, + style: TextStyle(color: COLOR_DANGER), + ); } else if (widget.item.stale) { - _expiryIcon = Text(L10().expiryStale, style: TextStyle(color: COLOR_WARNING)); + _expiryIcon = Text( + L10().expiryStale, + style: TextStyle(color: COLOR_WARNING), + ); } tiles.add( @@ -741,19 +732,18 @@ class _StockItemDisplayState extends RefreshableState { subtitle: Text(widget.item.expiryDateString), leading: Icon(TablerIcons.calendar_x), trailing: _expiryIcon, - ) + ), ); } // Last update? if (widget.item.updatedDateString.isNotEmpty) { - tiles.add( ListTile( title: Text(L10().lastUpdated), subtitle: Text(widget.item.updatedDateString), - leading: Icon(TablerIcons.calendar) - ) + leading: Icon(TablerIcons.calendar), + ), ); } @@ -763,8 +753,8 @@ class _StockItemDisplayState extends RefreshableState { ListTile( title: Text(L10().lastStocktake), subtitle: Text(widget.item.stocktakeDateString), - leading: Icon(TablerIcons.calendar) - ) + leading: Icon(TablerIcons.calendar), + ), ); } @@ -776,26 +766,27 @@ class _StockItemDisplayState extends RefreshableState { onTap: () { widget.item.openLink(); }, - ) + ), ); } if (stockShowTests || (widget.item.testResultCount > 0)) { tiles.add( - ListTile( - title: Text(L10().testResults), - leading: Icon(TablerIcons.list_check, color: COLOR_ACTION), - trailing: Text("${widget.item.testResultCount}"), - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => StockItemTestResultsWidget(widget.item)) - ).then((ctx) { - refresh(context); - }); - } - ) + ListTile( + title: Text(L10().testResults), + leading: Icon(TablerIcons.list_check, color: COLOR_ACTION), + trailing: Text("${widget.item.testResultCount}"), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => StockItemTestResultsWidget(widget.item), + ), + ).then((ctx) { + refresh(context); + }); + }, + ), ); } @@ -805,9 +796,12 @@ class _StockItemDisplayState extends RefreshableState { title: Text(L10().purchasePrice), leading: Icon(TablerIcons.currency_dollar), trailing: Text( - renderCurrency(widget.item.purchasePrice, widget.item.purchasePriceCurrency) - ) - ) + renderCurrency( + widget.item.purchasePrice, + widget.item.purchasePriceCurrency, + ), + ), + ), ); } @@ -823,12 +817,13 @@ class _StockItemDisplayState extends RefreshableState { Navigator.push( context, MaterialPageRoute( - builder: (context) => StockItemHistoryWidget(widget.item)) - ).then((ctx) { - refresh(context); + builder: (context) => StockItemHistoryWidget(widget.item), + ), + ).then((ctx) { + refresh(context); }); }, - ) + ), ); } @@ -840,34 +835,33 @@ class _StockItemDisplayState extends RefreshableState { onTap: () { Navigator.push( context, - MaterialPageRoute(builder: (context) => NotesWidget(widget.item)) + MaterialPageRoute(builder: (context) => NotesWidget(widget.item)), ); - } - ) + }, + ), ); tiles.add( - ListTile( - title: Text(L10().attachments), - leading: Icon(TablerIcons.file, color: COLOR_ACTION), - trailing: attachmentCount > 0 ? Text(attachmentCount.toString()) : null, - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => AttachmentWidget( - InvenTreeStockItemAttachment(), - widget.item.pk, - L10().stockItem, - widget.item.canEdit, - ) - ) - ); - }, - ) + ListTile( + title: Text(L10().attachments), + leading: Icon(TablerIcons.file, color: COLOR_ACTION), + trailing: attachmentCount > 0 ? Text(attachmentCount.toString()) : null, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AttachmentWidget( + InvenTreeStockItemAttachment(), + widget.item.pk, + L10().stockItem, + widget.item.canEdit, + ), + ), + ); + }, + ), ); return tiles; } - -} \ No newline at end of file +} diff --git a/lib/widget/stock/stock_item_history.dart b/lib/widget/stock/stock_item_history.dart index b2829fd2..7324156b 100644 --- a/lib/widget/stock/stock_item_history.dart +++ b/lib/widget/stock/stock_item_history.dart @@ -14,10 +14,12 @@ class StockItemHistoryWidget extends StatefulWidget { final InvenTreeStockItem item; @override - _StockItemHistoryDisplayState createState() => _StockItemHistoryDisplayState(item); + _StockItemHistoryDisplayState createState() => + _StockItemHistoryDisplayState(item); } -class _StockItemHistoryDisplayState extends RefreshableState { +class _StockItemHistoryDisplayState + extends RefreshableState { _StockItemHistoryDisplayState(this.item); final InvenTreeStockItem item; @@ -30,20 +32,18 @@ class _StockItemHistoryDisplayState extends RefreshableState filters = { - "item": widget.item.pk.toString(), - }; + Map filters = {"item": widget.item.pk.toString()}; return PaginatedStockHistoryList(filters); } - } /* * Widget which displays a paginated stock history list */ class PaginatedStockHistoryList extends PaginatedSearchWidget { - const PaginatedStockHistoryList(Map filters) : super(filters: filters); + const PaginatedStockHistoryList(Map filters) + : super(filters: filters); @override String get searchTitle => L10().stockItemHistory; @@ -67,15 +67,22 @@ class _PaginatedStockHistoryState @override Map> get filterOptions => { - // TODO: Add filter options - }; + // TODO: Add filter options + }; @override Future requestPage( - int limit, int offset, Map params) async { + int limit, + int offset, + Map params, + ) async { await InvenTreeAPI().StockHistoryStatus.load(); - final page = await InvenTreeStockItemHistory().listPaginated(limit, offset, filters: params); + final page = await InvenTreeStockItemHistory().listPaginated( + limit, + offset, + filters: params, + ); return page; } diff --git a/lib/widget/stock/stock_item_test_results.dart b/lib/widget/stock/stock_item_test_results.dart index d1671e57..9fb7d926 100644 --- a/lib/widget/stock/stock_item_test_results.dart +++ b/lib/widget/stock/stock_item_test_results.dart @@ -13,20 +13,18 @@ import "package:inventree/inventree/model.dart"; import "package:inventree/widget/progress.dart"; import "package:inventree/widget/refreshable_state.dart"; - class StockItemTestResultsWidget extends StatefulWidget { - const StockItemTestResultsWidget(this.item, {Key? key}) : super(key: key); final InvenTreeStockItem item; @override - _StockItemTestResultDisplayState createState() => _StockItemTestResultDisplayState(item); + _StockItemTestResultDisplayState createState() => + _StockItemTestResultDisplayState(item); } - -class _StockItemTestResultDisplayState extends RefreshableState { - +class _StockItemTestResultDisplayState + extends RefreshableState { _StockItemTestResultDisplayState(this.item); @override @@ -46,8 +44,8 @@ class _StockItemTestResultDisplayState extends RefreshableState addTestResult(BuildContext context, {int templateId = 0, String name = "", bool nameIsEditable = true, bool result = false, String value = "", bool valueRequired = false, bool attachmentRequired = false}) async { - - Map> fields = InvenTreeStockItemTestResult().formFields(); + Future addTestResult( + BuildContext context, { + int templateId = 0, + String name = "", + bool nameIsEditable = true, + bool result = false, + String value = "", + bool valueRequired = false, + bool attachmentRequired = false, + }) async { + Map> fields = InvenTreeStockItemTestResult() + .formFields(); // Add additional filters fields["template"]?["filters"]?["part"] = "${item.partId}"; @@ -102,7 +109,6 @@ class _StockItemTestResultDisplayState extends RefreshableState filters; @@ -18,9 +16,7 @@ class StockItemList extends StatefulWidget { _StockListState createState() => _StockListState(filters); } - class _StockListState extends RefreshableState { - _StockListState(this.filters); final Map filters; @@ -35,20 +31,18 @@ class _StockListState extends RefreshableState { } class PaginatedStockItemList extends PaginatedSearchWidget { - - const PaginatedStockItemList(Map filters) : super(filters: filters); + const PaginatedStockItemList(Map filters) + : super(filters: filters); @override String get searchTitle => L10().stockItems; @override _PaginatedStockItemListState createState() => _PaginatedStockItemListState(); - } - -class _PaginatedStockItemListState extends PaginatedSearchState { - +class _PaginatedStockItemListState + extends PaginatedSearchState { _PaginatedStockItemListState() : super(); @override @@ -100,7 +94,7 @@ class _PaginatedStockItemListState extends PaginatedSearchState requestPage(int limit, int offset, Map params) async { - + Future requestPage( + int limit, + int offset, + Map params, + ) async { // Ensure StockStatus codes are loaded await InvenTreeAPI().StockStatus.load(); final page = await InvenTreeStockItem().listPaginated( limit, offset, - filters: params + filters: params, ); return page; @@ -127,7 +124,6 @@ class _PaginatedStockItemListState extends PaginatedSearchState fetchProfileToken({ UserProfile? profile, String username = testUsername, - String password = testPassword + String password = testPassword, }) async { - profile ??= await UserProfileDBManager().getProfileByName(testServerName); assert(profile != null); - final response = await InvenTreeAPI().fetchToken(profile!, username, password); + final response = await InvenTreeAPI().fetchToken( + profile!, + username, + password, + ); return response.successful(); } - /* * Setup a valid profile, and return it */ -Future setupServerProfile({bool select = true, bool fetchToken = false}) async { +Future setupServerProfile({ + bool select = true, + bool fetchToken = false, +}) async { // Setup a valid server profile - UserProfile? profile = await UserProfileDBManager().getProfileByName(testServerName); + UserProfile? profile = await UserProfileDBManager().getProfileByName( + testServerName, + ); if (profile == null) { // Profile does not already exist - create it! bool result = await UserProfileDBManager().addProfile( - UserProfile( - server: testServerAddress, - name: testServerName - ) + UserProfile(server: testServerAddress, name: testServerName), ); assert(result); @@ -84,15 +88,13 @@ Future setupServerProfile({bool select = true, bool fetchToken = fa return profile!; } - /* * Complete all steps necessary to login to the server */ Future connectToTestServer() async { - // Setup profile, and fetch user token as necessary final profile = await setupServerProfile(fetchToken: true); // Connect to the server assert(await InvenTreeAPI().connectToServer(profile)); -} \ No newline at end of file +} diff --git a/test/user_profile_test.dart b/test/user_profile_test.dart index 0921d761..e1b49cf8 100644 --- a/test/user_profile_test.dart +++ b/test/user_profile_test.dart @@ -26,11 +26,13 @@ void main() { expect(profiles.length, equals(0)); // Now, create one! - bool result = await UserProfileDBManager().addProfile(UserProfile( - name: testServerName, - server: testServerAddress, - selected: true, - )); + bool result = await UserProfileDBManager().addProfile( + UserProfile( + name: testServerName, + server: testServerAddress, + selected: true, + ), + ); expect(result, equals(true)); @@ -56,20 +58,15 @@ void main() { // Run a set of tests for user profile functionality group("Profile Tests:", () { - test("Add Invalid Profiles", () async { // Add a profile with missing data - bool result = await UserProfileDBManager().addProfile( - UserProfile() - ); + bool result = await UserProfileDBManager().addProfile(UserProfile()); expect(result, equals(false)); // Add a profile with a new name result = await UserProfileDBManager().addProfile( - UserProfile( - name: "Another Test Profile", - ) + UserProfile(name: "Another Test Profile"), ); expect(result, equals(true)); @@ -81,7 +78,9 @@ void main() { }); test("Profile Name Check", () async { - bool result = await UserProfileDBManager().profileNameExists("doesnotexist"); + bool result = await UserProfileDBManager().profileNameExists( + "doesnotexist", + ); expect(result, equals(false)); result = await UserProfileDBManager().profileNameExists("Test Server"); @@ -100,7 +99,10 @@ void main() { expect(p.name, equals(testServerName)); expect(p.server, equals(testServerAddress)); - expect(p.toString(), equals("<${p.key}> Test Server : http://localhost:8000/")); + expect( + p.toString(), + equals("<${p.key}> Test Server : http://localhost:8000/"), + ); // Test that we can update the profile p.name = "different name"; @@ -110,5 +112,4 @@ void main() { } }); }); - -} \ No newline at end of file +} diff --git a/test/wedge_scanner_test.dart b/test/wedge_scanner_test.dart index 8cdca593..bd98872f 100644 --- a/test/wedge_scanner_test.dart +++ b/test/wedge_scanner_test.dart @@ -1,5 +1,3 @@ - - import "package:flutter/material.dart"; import "package:flutter/services.dart"; import "package:flutter_test/flutter_test.dart"; @@ -7,14 +5,10 @@ import "package:inventree/barcode/barcode.dart"; import "package:inventree/barcode/wedge_controller.dart"; import "package:inventree/helpers.dart"; - void main() { testWidgets("Wedge Scanner Test", (tester) async { - await tester.pumpWidget( - MaterialApp( - home: WedgeBarcodeController(BarcodeScanHandler()) - ) + MaterialApp(home: WedgeBarcodeController(BarcodeScanHandler())), ); // Generate some keyboard data @@ -27,6 +21,5 @@ void main() { debugContains("scanned: abc"); debugContains("No match for barcode"); debugContains("Server Error"); - }); -} \ No newline at end of file +}