From d926686a895ae06a28544a6d3f391abd73cb247a Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 18 Apr 2023 22:46:08 +1000 Subject: [PATCH] Stock history fix (#320) * Improves quantity parsing from * Add paginated history widget * Refactor stock history widget as a paginated widget * Allow paginated result list to handle results returned as list - Some API endpoints (older ones most likely) don't paginate results correctly * Fix code layout * Render user information in "history" widget (not quantity) * Hide filter button * Update release notes * remove unused import --- android/gradle.properties | 4 ++ assets/release_notes.md | 6 ++ lib/inventree/model.dart | 29 ++++++-- lib/inventree/stock.dart | 31 +++++--- lib/widget/stock_item_history.dart | 109 +++++++++++++++++------------ 5 files changed, 116 insertions(+), 63 deletions(-) diff --git a/android/gradle.properties b/android/gradle.properties index 615b166b..ce4fa015 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,3 +1,7 @@ +org.gradle.daemon=true +org.gradle.parallel=true +org.gradle.configureondemand=true +org.gradle.caching=true org.gradle.jvmargs=-Xmx1536M android.enableD8=true android.enableJetifier=true diff --git a/assets/release_notes.md b/assets/release_notes.md index 10b280bc..07a3df35 100644 --- a/assets/release_notes.md +++ b/assets/release_notes.md @@ -1,6 +1,12 @@ ## InvenTree App Release Notes --- +### 0.11.4 - April 2023 +--- + +- Bug fix for stock history widget +- Improved display of stock history widget + ### 0.11.3 - April 2023 --- diff --git a/lib/inventree/model.dart b/lib/inventree/model.dart index 8427b58c..7c75129a 100644 --- a/lib/inventree/model.dart +++ b/lib/inventree/model.dart @@ -575,10 +575,12 @@ class InvenTreeModel { // Construct the response InvenTreePageResponse page = InvenTreePageResponse(); - var data = response.asMap(); + var dataMap = response.asMap(); - if (data.containsKey("count") && data.containsKey("results")) { - page.count = (data["count"] ?? 0) as int; + // First attempt is to look for paginated data, returned as a map + + if (dataMap.isNotEmpty && dataMap.containsKey("count") && dataMap.containsKey("results")) { + page.count = (dataMap["count"] ?? 0) as int; page.results = []; @@ -587,15 +589,28 @@ class InvenTreeModel { } return page; - - } else { - return null; } + + // Second attempt is to look for a list of data (not paginated) + var dataList = response.asList(); + + if (dataList.isNotEmpty) { + page.count = dataList.length; + page.results = []; + + for (var result in dataList) { + page.addResult(createFromJson(result as Map)); + } + + return page; + } + + // Finally, no results available + return null; } // Return list of objects from the database, with optional filters Future> list({Map filters = const {}}) async { - var params = defaultListFilters(); for (String key in filters.keys) { diff --git a/lib/inventree/stock.dart b/lib/inventree/stock.dart index a52cc9bb..ab6cc146 100644 --- a/lib/inventree/stock.dart +++ b/lib/inventree/stock.dart @@ -23,9 +23,7 @@ class InvenTreeStockItemTestResult extends InvenTreeModel { @override Map formFields() { return { - "stock_item": { - "hidden": true - }, + "stock_item": {"hidden": true}, "test": {}, "result": {}, "value": {}, @@ -75,6 +73,7 @@ class InvenTreeStockItemHistory extends InvenTreeModel { // By default, order by decreasing date return { "ordering": "-date", + "user_detail": "true", }; } @@ -98,21 +97,31 @@ class InvenTreeStockItemHistory extends InvenTreeModel { String get label => (jsondata["label"] ?? "") as String; - String get quantityString { - Map deltas = (jsondata["deltas"] ?? {}) as Map; + // Return the "deltas" associated with this historical object + Map get deltas { + if (jsondata.containsKey("deltas")) { + return jsondata["deltas"] as Map; + } else { + return {}; + } + } - // Serial number takes priority here - if (deltas.containsKey("serial")) { - var serial = (deltas["serial"] ?? "").toString(); - return "# ${serial}"; - } else if (deltas.containsKey("quantity")) { - double q = (deltas["quantity"] ?? 0) as double; + // Return the quantity string for this historical object + String get quantityString { + var _deltas = deltas; + + if (_deltas.containsKey("quantity")) { + double q = double.tryParse(_deltas["quantity"].toString()) ?? 0; return simpleNumberString(q); } else { return ""; } } + + String get userString { + return (jsondata["user_detail"]?["username"] ?? "") as String; + } } diff --git a/lib/widget/stock_item_history.dart b/lib/widget/stock_item_history.dart index 15ecf83d..3c3771e4 100644 --- a/lib/widget/stock_item_history.dart +++ b/lib/widget/stock_item_history.dart @@ -1,13 +1,14 @@ import "package:flutter/material.dart"; -import "package:inventree/widget/refreshable_state.dart"; +import "package:inventree/api.dart"; import "package:inventree/l10.dart"; import "package:inventree/inventree/stock.dart"; import "package:inventree/inventree/model.dart"; +import "package:inventree/widget/paginator.dart"; +import "package:inventree/widget/refreshable_state.dart"; class StockItemHistoryWidget extends StatefulWidget { - const StockItemHistoryWidget(this.item, {Key? key}) : super(key: key); final InvenTreeStockItem item; @@ -16,60 +17,78 @@ class StockItemHistoryWidget extends StatefulWidget { _StockItemHistoryDisplayState createState() => _StockItemHistoryDisplayState(item); } - class _StockItemHistoryDisplayState extends RefreshableState { - _StockItemHistoryDisplayState(this.item); final InvenTreeStockItem item; + bool showFilterOptions = false; + @override String getAppBarTitle() => L10().stockItemHistory; - List history = []; - @override - Future request(BuildContext refresh) async { - - history.clear(); - - await InvenTreeStockItemHistory().list(filters: {"item": "${item.pk}"}).then((List results) { - for (var result in results) { - if (result is InvenTreeStockItemHistory) { - history.add(result); - } - } - - // Refresh - setState(() { - }); - }); - } + List appBarActions(BuildContext context) => []; @override Widget getBody(BuildContext context) { - return ListView( - children: ListTile.divideTiles( - context: context, - tiles: historyList(), - ).toList() + Map filters = { + "item": widget.item.pk.toString(), + }; + + return PaginatedStockHistoryList(filters, showFilterOptions); + } + +} + +/* + * Widget which displays a paginated stock history list + */ +class PaginatedStockHistoryList extends PaginatedSearchWidget { + const PaginatedStockHistoryList(Map filters, bool showSearch) + : super(filters: filters, showSearch: showSearch); + + @override + _PaginatedStockHistoryState createState() => _PaginatedStockHistoryState(); +} + +/* + * State class for the paginated stock history list + */ +class _PaginatedStockHistoryState + extends PaginatedSearchState { + _PaginatedStockHistoryState() : super(); + + @override + String get prefix => "stock_history"; + + @override + Map get orderingOptions => {}; + + @override + Map> get filterOptions => { + // TODO: Add filter options + }; + + @override + Future requestPage( + int limit, int offset, Map params) async { + await InvenTreeAPI().StockHistoryStatus.load(); + + final page = await InvenTreeStockItemHistory().listPaginated(limit, offset, filters: params); + + return page; + } + + @override + Widget buildItem(BuildContext context, InvenTreeModel model) { + InvenTreeStockItemHistory entry = model as InvenTreeStockItemHistory; + + return ListTile( + leading: Text(entry.dateString), + trailing: entry.userString.isNotEmpty ? Text(entry.userString) : null, + title: Text(entry.label), + subtitle: entry.notes.isNotEmpty ? Text(entry.notes) : null, ); } - - List historyList() { - List tiles = []; - - for (var entry in history) { - tiles.add( - ListTile( - leading: Text(entry.dateString), - trailing: entry.quantityString.isNotEmpty ? Text(entry.quantityString) : null, - title: Text(entry.label), - subtitle: entry.notes.isNotEmpty ? Text(entry.notes) : null, - ) - ); - } - - return tiles; - } -} \ No newline at end of file +}