mirror of
				https://github.com/inventree/inventree-app.git
				synced 2025-10-30 21:05:42 +00:00 
			
		
		
		
	Create detail view for part stock
- Shows all in-stock stock items
This commit is contained in:
		| @@ -147,10 +147,38 @@ class InvenTreePart extends InvenTreeModel { | |||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   // Cached list of stock items | ||||||
|  |   List<InvenTreeStockItem> stockItems = List<InvenTreeStockItem>(); | ||||||
|  |  | ||||||
|  |   int get stockItemCount => stockItems.length; | ||||||
|  |  | ||||||
|  |   // Request stock items for this part | ||||||
|  |   Future<void> 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<InvenTreePartTestTemplate> testingTemplates = List<InvenTreePartTestTemplate>(); |   List<InvenTreePartTestTemplate> testingTemplates = List<InvenTreePartTestTemplate>(); | ||||||
|  |  | ||||||
|   int get testTemplateCount => testingTemplates.length; |   int get testTemplateCount => testingTemplates.length; | ||||||
|  |  | ||||||
|  |   // Request test templates from the serve | ||||||
|   Future<void> getTestTemplates(BuildContext context, {bool showDialog=false}) async { |   Future<void> getTestTemplates(BuildContext context, {bool showDialog=false}) async { | ||||||
|  |  | ||||||
|     InvenTreePartTestTemplate().list( |     InvenTreePartTestTemplate().list( | ||||||
|   | |||||||
| @@ -283,9 +283,9 @@ class InvenTreeStockItem extends InvenTreeModel { | |||||||
|   String get locationName { |   String get locationName { | ||||||
|     String loc = ''; |     String loc = ''; | ||||||
|  |  | ||||||
|     if (jsondata.containsKey('location_detail')) { |     if (locationId == -1 || !jsondata.containsKey('location_detail')) return 'Unknown Location'; | ||||||
|  |  | ||||||
|     loc = jsondata['location_detail']['name'] ?? ''; |     loc = jsondata['location_detail']['name'] ?? ''; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Old-style name |     // Old-style name | ||||||
|     if (loc.isEmpty) { |     if (loc.isEmpty) { | ||||||
| @@ -298,11 +298,9 @@ class InvenTreeStockItem extends InvenTreeModel { | |||||||
|   String get locationPathString { |   String get locationPathString { | ||||||
|     String path = ''; |     String path = ''; | ||||||
|  |  | ||||||
|     if (jsondata.containsKey('location_detail')) { |     if (locationId == -1 || !jsondata.containsKey('location_detail')) return 'No location specified'; | ||||||
|       path = jsondata['location_detail']['pathstring'] ?? ''; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return path; |     return jsondata['location_detail']['pathstring'] ?? ''; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   String get displayQuantity { |   String get displayQuantity { | ||||||
|   | |||||||
| @@ -220,7 +220,9 @@ class SublocationList extends StatelessWidget { | |||||||
|     return ListView.builder( |     return ListView.builder( | ||||||
|         shrinkWrap: true, |         shrinkWrap: true, | ||||||
|         physics: ClampingScrollPhysics(), |         physics: ClampingScrollPhysics(), | ||||||
|         itemBuilder: _build, itemCount: _locations.length); |         itemBuilder: _build, | ||||||
|  |         itemCount: _locations.length | ||||||
|  |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ import 'package:InvenTree/inventree/part.dart'; | |||||||
| import 'package:InvenTree/widget/category_display.dart'; | import 'package:InvenTree/widget/category_display.dart'; | ||||||
| import 'package:InvenTree/widget/dialogs.dart'; | import 'package:InvenTree/widget/dialogs.dart'; | ||||||
| import 'package:InvenTree/widget/fields.dart'; | import 'package:InvenTree/widget/fields.dart'; | ||||||
|  | import 'package:InvenTree/widget/part_stock_detail.dart'; | ||||||
| import 'package:flutter/cupertino.dart'; | import 'package:flutter/cupertino.dart'; | ||||||
| import 'package:flutter/foundation.dart'; | import 'package:flutter/foundation.dart'; | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| @@ -39,10 +40,23 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> { | |||||||
|  |  | ||||||
|   int _tabIndex = 0; |   int _tabIndex = 0; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Future<void> onBuild(BuildContext context) async { | ||||||
|  |     refresh(); | ||||||
|  |  | ||||||
|  |     setState(() { | ||||||
|  |  | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   Future<void> request(BuildContext context) async { |   Future<void> request(BuildContext context) async { | ||||||
|     await part.reload(context); |     await part.reload(context); | ||||||
|     await part.getTestTemplates(context); |     await part.getTestTemplates(context); | ||||||
|  |  | ||||||
|  |     setState(() { | ||||||
|  |  | ||||||
|  |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   void _savePart(Map<String, String> values) async { |   void _savePart(Map<String, String> values) async { | ||||||
| @@ -121,7 +135,7 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> { | |||||||
|         child: ListTile( |         child: ListTile( | ||||||
|           title: Text("${part.fullname}"), |           title: Text("${part.fullname}"), | ||||||
|           subtitle: Text("${part.description}"), |           subtitle: Text("${part.description}"), | ||||||
|           leading: InvenTreeAPI().getImage(part.image), |           leading: InvenTreeAPI().getImage(part.thumbnail), | ||||||
|           trailing: IconButton( |           trailing: IconButton( | ||||||
|             icon: FaIcon(FontAwesomeIcons.edit), |             icon: FaIcon(FontAwesomeIcons.edit), | ||||||
|             onPressed: _editPartDialog, |             onPressed: _editPartDialog, | ||||||
| @@ -178,7 +192,12 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> { | |||||||
|           title: Text("Stock"), |           title: Text("Stock"), | ||||||
|           leading: FaIcon(FontAwesomeIcons.boxes), |           leading: FaIcon(FontAwesomeIcons.boxes), | ||||||
|           trailing: Text("${part.inStock}"), |           trailing: Text("${part.inStock}"), | ||||||
|           onTap: null, |           onTap: () { | ||||||
|  |             Navigator.push( | ||||||
|  |               context, | ||||||
|  |               MaterialPageRoute(builder: (context) => PartStockDetailWidget(part)) | ||||||
|  |             ); | ||||||
|  |           }, | ||||||
|         ), |         ), | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										111
									
								
								lib/widget/part_stock_detail.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								lib/widget/part_stock_detail.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -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<PartStockDetailWidget> { | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String getAppBarTitle(BuildContext context) { return "Part Stock"; } | ||||||
|  |  | ||||||
|  |   _PartStockDisplayState(this.part) { | ||||||
|  |     // TODO | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   InvenTreePart part; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Future<void> onBuild(BuildContext context) async { | ||||||
|  |     refresh(); | ||||||
|  |     print("onBuild"); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Future<void> request(BuildContext context) async { | ||||||
|  |     await part.reload(context); | ||||||
|  |     await part.getStockItems(context); | ||||||
|  |  | ||||||
|  |     print("request"); | ||||||
|  |     setState(() { | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Widget getBody(BuildContext context) { | ||||||
|  |     return ListView( | ||||||
|  |       children: <Widget>[ | ||||||
|  |         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<InvenTreeStockItem> _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 | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user