diff --git a/lib/inventree/part.dart b/lib/inventree/part.dart index 255f9cb2..ac98857b 100644 --- a/lib/inventree/part.dart +++ b/lib/inventree/part.dart @@ -147,10 +147,38 @@ class InvenTreePart extends InvenTreeModel { }; } + // Cached list of stock items + List stockItems = List(); + + int get stockItemCount => stockItems.length; + + // Request stock items for this part + Future getStockItems(BuildContext context, {bool showDialog=false}) async { + + InvenTreeStockItem().list( + context, + filters: { + "part": "${pk}", + "in_stock": "true", + }, + dialog: showDialog, + ).then((var items) { + stockItems.clear(); + + for (var item in items) { + if (item is InvenTreeStockItem) { + stockItems.add(item); + } + } + }); + } + + // Cached list of test templates List testingTemplates = List(); int get testTemplateCount => testingTemplates.length; + // Request test templates from the serve Future getTestTemplates(BuildContext context, {bool showDialog=false}) async { InvenTreePartTestTemplate().list( diff --git a/lib/inventree/stock.dart b/lib/inventree/stock.dart index 0e4653f7..d60442af 100644 --- a/lib/inventree/stock.dart +++ b/lib/inventree/stock.dart @@ -283,9 +283,9 @@ class InvenTreeStockItem extends InvenTreeModel { String get locationName { String loc = ''; - if (jsondata.containsKey('location_detail')) { - loc = jsondata['location_detail']['name'] ?? ''; - } + if (locationId == -1 || !jsondata.containsKey('location_detail')) return 'Unknown Location'; + + loc = jsondata['location_detail']['name'] ?? ''; // Old-style name if (loc.isEmpty) { @@ -298,11 +298,9 @@ class InvenTreeStockItem extends InvenTreeModel { String get locationPathString { String path = ''; - if (jsondata.containsKey('location_detail')) { - path = jsondata['location_detail']['pathstring'] ?? ''; - } + if (locationId == -1 || !jsondata.containsKey('location_detail')) return 'No location specified'; - return path; + return jsondata['location_detail']['pathstring'] ?? ''; } String get displayQuantity { diff --git a/lib/widget/location_display.dart b/lib/widget/location_display.dart index 3610cf4e..359ee8f3 100644 --- a/lib/widget/location_display.dart +++ b/lib/widget/location_display.dart @@ -220,7 +220,9 @@ class SublocationList extends StatelessWidget { return ListView.builder( shrinkWrap: true, physics: ClampingScrollPhysics(), - itemBuilder: _build, itemCount: _locations.length); + itemBuilder: _build, + itemCount: _locations.length + ); } } diff --git a/lib/widget/part_detail.dart b/lib/widget/part_detail.dart index 227ea48f..063ec743 100644 --- a/lib/widget/part_detail.dart +++ b/lib/widget/part_detail.dart @@ -3,6 +3,7 @@ import 'package:InvenTree/inventree/part.dart'; import 'package:InvenTree/widget/category_display.dart'; import 'package:InvenTree/widget/dialogs.dart'; import 'package:InvenTree/widget/fields.dart'; +import 'package:InvenTree/widget/part_stock_detail.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -39,10 +40,23 @@ class _PartDisplayState extends RefreshableState { int _tabIndex = 0; + @override + Future onBuild(BuildContext context) async { + refresh(); + + setState(() { + + }); + } + @override Future request(BuildContext context) async { await part.reload(context); await part.getTestTemplates(context); + + setState(() { + + }); } void _savePart(Map values) async { @@ -121,7 +135,7 @@ class _PartDisplayState extends RefreshableState { child: ListTile( title: Text("${part.fullname}"), subtitle: Text("${part.description}"), - leading: InvenTreeAPI().getImage(part.image), + leading: InvenTreeAPI().getImage(part.thumbnail), trailing: IconButton( icon: FaIcon(FontAwesomeIcons.edit), onPressed: _editPartDialog, @@ -178,7 +192,12 @@ class _PartDisplayState extends RefreshableState { title: Text("Stock"), leading: FaIcon(FontAwesomeIcons.boxes), trailing: Text("${part.inStock}"), - onTap: null, + onTap: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => PartStockDetailWidget(part)) + ); + }, ), ); diff --git a/lib/widget/part_stock_detail.dart b/lib/widget/part_stock_detail.dart new file mode 100644 index 00000000..2946eb52 --- /dev/null +++ b/lib/widget/part_stock_detail.dart @@ -0,0 +1,111 @@ +// Flutter packages +import 'package:InvenTree/inventree/stock.dart'; +import 'package:InvenTree/widget/refreshable_state.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +// InvenTree packages +import 'package:InvenTree/api.dart'; +import 'package:InvenTree/inventree/part.dart'; +import 'package:InvenTree/widget/stock_detail.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; + +class PartStockDetailWidget extends StatefulWidget { + /* + * The PartStockDetail widget displays all "in stock" instances for a given Part + */ + + PartStockDetailWidget(this.part, {Key key}) : super(key: key); + + final InvenTreePart part; + + @override + _PartStockDisplayState createState() => _PartStockDisplayState(part); +} + + +class _PartStockDisplayState extends RefreshableState { + + @override + String getAppBarTitle(BuildContext context) { return "Part Stock"; } + + _PartStockDisplayState(this.part) { + // TODO + } + + InvenTreePart part; + + @override + Future onBuild(BuildContext context) async { + refresh(); + print("onBuild"); + } + + @override + Future request(BuildContext context) async { + await part.reload(context); + await part.getStockItems(context); + + print("request"); + setState(() { + }); + } + + @override + Widget getBody(BuildContext context) { + return ListView( + children: [ + Card( + child: ListTile( + title: Text(part.fullname), + subtitle: Text(part.description), + leading: InvenTreeAPI().getImage(part.thumbnail), + trailing: Text('${part.inStock}'), + ) + ), + PartStockList(part.stockItems), + ] + ); + } +} + + +class PartStockList extends StatelessWidget { + final List _items; + + PartStockList(this._items); + + void _openItem(BuildContext context, int pk) { + // Load detail view for stock item + InvenTreeStockItem().get(context, pk).then((var item) { + if (item is InvenTreeStockItem) { + Navigator.push(context, MaterialPageRoute(builder: (context) => StockDetailWidget(item))); + } + }); + } + + Widget _build(BuildContext context, int index) { + + InvenTreeStockItem item = _items[index]; + + return ListTile( + title: Text("${item.locationName}"), + subtitle: Text("${item.locationPathString}"), + trailing: Text("${item.quantity}"), + leading: FaIcon(FontAwesomeIcons.mapMarkerAlt), + onTap: () { + _openItem(context, item.pk); + }, + ); + } + + @override + Widget build(BuildContext context) { + return ListView.builder( + shrinkWrap: true, + physics: ClampingScrollPhysics(), + itemBuilder: _build, + itemCount: _items.length + ); + } +} \ No newline at end of file