diff --git a/lib/inventree/part.dart b/lib/inventree/part.dart index 7a625a35..a5b7ef39 100644 --- a/lib/inventree/part.dart +++ b/lib/inventree/part.dart @@ -1,6 +1,7 @@ import "dart:io"; import "package:inventree/api.dart"; +import "package:inventree/helpers.dart"; import "package:inventree/inventree/stock.dart"; import "package:inventree/inventree/company.dart"; import "package:flutter/material.dart"; @@ -250,11 +251,7 @@ class InvenTreePart extends InvenTreeModel { String get onOrderString { - if (onOrder == onOrder.toInt()) { - return onOrder.toInt().toString(); - } else { - return onOrder.toString(); - } + return simpleNumberString(onOrder); } // Get the stock count for this Part @@ -262,11 +259,7 @@ class InvenTreePart extends InvenTreeModel { String get inStockString { - String q = inStock.toString(); - - if (inStock == inStock.toInt()) { - q = inStock.toInt().toString(); - } + String q = simpleNumberString(inStock); if (units.isNotEmpty) { q += " ${units}"; @@ -275,6 +268,41 @@ class InvenTreePart extends InvenTreeModel { return q; } + // Get the 'available stock' for this Part + double get unallocatedStock { + + // Note that the 'available_stock' was not added until API v35 + if (jsondata.containsKey("unallocated_stock")) { + return double.tryParse(jsondata["unallocated_stock"].toString()) ?? 0; + } else { + return inStock; + } + } + + String get unallocatedStockString { + String q = simpleNumberString(unallocatedStock); + + if (units.isNotEmpty) { + q += " ${units}"; + } + + return q; + } + + String stockString({bool includeUnits = true}) { + String q = unallocatedStockString; + + if (unallocatedStock != inStock) { + q += " / ${inStockString}"; + } + + if (includeUnits && units.isNotEmpty) { + q += " ${units}"; + } + + return q; + } + String get units => (jsondata["units"] ?? "") as String; // Get the number of units being build for this Part diff --git a/lib/l10n b/lib/l10n index 581ce0a8..7cc9177c 160000 --- a/lib/l10n +++ b/lib/l10n @@ -1 +1 @@ -Subproject commit 581ce0a818532157ffa0a1b449bf1a240558a238 +Subproject commit 7cc9177c89a4dbd122892b96bbd25f9e9125dfbf diff --git a/lib/widget/part_detail.dart b/lib/widget/part_detail.dart index 2a6d43f3..07a37c44 100644 --- a/lib/widget/part_detail.dart +++ b/lib/widget/part_detail.dart @@ -5,6 +5,7 @@ import "package:font_awesome_flutter/font_awesome_flutter.dart"; import "package:inventree/app_colors.dart"; import "package:inventree/inventree/stock.dart"; import "package:inventree/l10.dart"; +import "package:inventree/helpers.dart"; import "package:inventree/widget/part_attachments_widget.dart"; import "package:inventree/widget/part_notes.dart"; import "package:inventree/widget/progress.dart"; @@ -203,24 +204,24 @@ class _PartDisplayState extends RefreshableState { ); } else { tiles.add( - ListTile( - title: Text(L10().partCategory), - subtitle: Text(L10().partCategoryTopLevel), - leading: FaIcon(FontAwesomeIcons.sitemap, color: COLOR_CLICK), - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null))); - }, - ) + ListTile( + title: Text(L10().partCategory), + subtitle: Text(L10().partCategoryTopLevel), + leading: FaIcon(FontAwesomeIcons.sitemap, color: COLOR_CLICK), + onTap: () { + Navigator.push(context, MaterialPageRoute( + builder: (context) => CategoryDisplayWidget(null))); + }, + ) ); } - // Stock information tiles.add( ListTile( - title: Text(L10().stock), + title: Text(L10().availableStock), subtitle: Text(L10().stockDetails), leading: FaIcon(FontAwesomeIcons.boxes, color: COLOR_CLICK), - trailing: Text("${part.inStockString}"), + trailing: Text(part.stockString()), onTap: () { setState(() { tabIndex = 1; @@ -229,48 +230,9 @@ class _PartDisplayState extends RefreshableState { ), ); - // Keywords? - if (part.keywords.isNotEmpty) { - tiles.add( - ListTile( - title: Text("${part.keywords}"), - leading: FaIcon(FontAwesomeIcons.key), - ) - ); - } - - // External link? - if (part.link.isNotEmpty) { - tiles.add( - ListTile( - title: Text("${part.link}"), - leading: FaIcon(FontAwesomeIcons.link, color: COLOR_CLICK), - onTap: () { - part.openLink(); - }, - ) - ); - } - // Tiles for "purchaseable" parts if (part.isPurchaseable) { - tiles.add( - ListTile( - title: Text(L10().suppliers), - leading: FaIcon(FontAwesomeIcons.industry), - trailing: Text("${part.supplierCount}"), - /* TODO: - onTap: () { - Navigator.push( - context, - MaterialPageRoute(builder: (context) => PartSupplierWidget(part)) - ); - }, - */ - ) - ); - // On order tiles.add( ListTile( @@ -307,7 +269,7 @@ class _PartDisplayState extends RefreshableState { ListTile( title: Text(L10().building), leading: FaIcon(FontAwesomeIcons.tools), - trailing: Text("${part.building}"), + trailing: Text("${simpleNumberString(part.building)}"), onTap: () { // TODO }, @@ -316,6 +278,29 @@ class _PartDisplayState extends RefreshableState { } } + // Keywords? + if (part.keywords.isNotEmpty) { + tiles.add( + ListTile( + title: Text("${part.keywords}"), + leading: FaIcon(FontAwesomeIcons.key), + ) + ); + } + + // External link? + if (part.link.isNotEmpty) { + tiles.add( + ListTile( + title: Text("${part.link}"), + leading: FaIcon(FontAwesomeIcons.link, color: COLOR_CLICK), + onTap: () { + part.openLink(); + }, + ) + ); + } + // Tiles for "component" part if (part.isComponent && part.usedInCount > 0) { @@ -332,6 +317,25 @@ class _PartDisplayState extends RefreshableState { ); } + if (part.isPurchaseable) { + tiles.add( + ListTile( + title: Text(L10().suppliers), + leading: FaIcon(FontAwesomeIcons.industry), + trailing: Text("${part.supplierCount}"), + /* TODO: + onTap: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => PartSupplierWidget(part)) + ); + }, + */ + ) + ); + } + + // TODO - Add request tests? /* if (part.isTrackable) { diff --git a/lib/widget/part_list.dart b/lib/widget/part_list.dart index 7c1233f6..ca4d158e 100644 --- a/lib/widget/part_list.dart +++ b/lib/widget/part_list.dart @@ -86,8 +86,8 @@ class _PaginatedPartListState extends PaginatedSearchState { return ListTile( title: Text(part.fullname), - subtitle: Text("${part.description}"), - trailing: Text("${part.inStockString}"), + subtitle: Text(part.description), + trailing: Text(part.stockString()), leading: InvenTreeAPI().getImage( part.thumbnail, width: 40,