mirror of
https://github.com/inventree/inventree-app.git
synced 2025-04-29 14:06:47 +00:00
Merge pull request #19 from SchrodingersGat/stock-item-page
Improve view for StockItem
This commit is contained in:
commit
c7fbe99f53
43
lib/api.dart
43
lib/api.dart
@ -72,8 +72,46 @@ class InvenTreeAPI {
|
|||||||
// Authentication token (initially empty, must be requested)
|
// Authentication token (initially empty, must be requested)
|
||||||
String _token = "";
|
String _token = "";
|
||||||
|
|
||||||
|
bool isConnected() {
|
||||||
|
return _token.isNotEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check server connection and display messages if not connected.
|
||||||
|
* Useful as a precursor check before performing operations.
|
||||||
|
*/
|
||||||
|
bool checkConnection(BuildContext context) {
|
||||||
|
|
||||||
|
// Firstly, is the server connected?
|
||||||
|
if (!isConnected()) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
child: new SimpleDialog(
|
||||||
|
title: new Text("Not Connected"),
|
||||||
|
children: <Widget> [
|
||||||
|
ListTile(
|
||||||
|
title: Text("Server not connected"),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is the server version too old?
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
// Finally
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server instance information
|
||||||
|
String instance = '';
|
||||||
|
|
||||||
// Server version information
|
// Server version information
|
||||||
String _version;
|
String _version = '';
|
||||||
|
|
||||||
// Getter for server version information
|
// Getter for server version information
|
||||||
String get version => _version;
|
String get version => _version;
|
||||||
@ -174,6 +212,9 @@ class InvenTreeAPI {
|
|||||||
|
|
||||||
_version = data["version"];
|
_version = data["version"];
|
||||||
|
|
||||||
|
// Record the instance name of the server
|
||||||
|
instance = data['instance'] ?? '';
|
||||||
|
|
||||||
// Request token from the server if we do not already have one
|
// Request token from the server if we do not already have one
|
||||||
if (_token.isNotEmpty) {
|
if (_token.isNotEmpty) {
|
||||||
print("Already have token - $_token");
|
print("Already have token - $_token");
|
||||||
|
@ -74,8 +74,13 @@ class InvenTreeModel {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
Map<String, String> defaultListFilters() { return Map<String, String>(); }
|
||||||
|
|
||||||
|
// A map of "default" headers to use when performing a GET request
|
||||||
|
Map<String, String> defaultGetFilters() { return Map<String, String>(); }
|
||||||
|
|
||||||
// Return the detail view for the associated pk
|
// Return the detail view for the associated pk
|
||||||
Future<InvenTreeModel> get(int pk) async {
|
Future<InvenTreeModel> get(int pk, {Map<String, String> filters}) async {
|
||||||
|
|
||||||
// TODO - Add "timeout"
|
// TODO - Add "timeout"
|
||||||
// TODO - Add error catching
|
// TODO - Add error catching
|
||||||
@ -86,7 +91,18 @@ class InvenTreeModel {
|
|||||||
addr += "/";
|
addr += "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = await InvenTreeAPI().get(addr);
|
var params = defaultGetFilters();
|
||||||
|
|
||||||
|
if (filters != null) {
|
||||||
|
// Override any default values
|
||||||
|
for (String key in filters.keys) {
|
||||||
|
params[key] = filters[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("GET: $addr ${params.toString()}");
|
||||||
|
|
||||||
|
var response = await InvenTreeAPI().get(addr, params: params);
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
print("Error retrieving data");
|
print("Error retrieving data");
|
||||||
@ -105,12 +121,20 @@ class InvenTreeModel {
|
|||||||
filters = {};
|
filters = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
print("Listing endpoint: $URL");
|
var params = defaultListFilters();
|
||||||
|
|
||||||
|
if (filters != null) {
|
||||||
|
for (String key in filters.keys) {
|
||||||
|
params[key] = filters[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("LIST: $URL ${params.toString()}");
|
||||||
|
|
||||||
// TODO - Add "timeout"
|
// TODO - Add "timeout"
|
||||||
// TODO - Add error catching
|
// TODO - Add error catching
|
||||||
|
|
||||||
var response = await InvenTreeAPI().get(URL, params:filters);
|
var response = await InvenTreeAPI().get(URL, params:params);
|
||||||
|
|
||||||
// A list of "InvenTreeModel" items
|
// A list of "InvenTreeModel" items
|
||||||
List<InvenTreeModel> results = new List<InvenTreeModel>();
|
List<InvenTreeModel> results = new List<InvenTreeModel>();
|
||||||
|
@ -12,6 +12,15 @@ class InvenTreePartCategory extends InvenTreeModel {
|
|||||||
@override
|
@override
|
||||||
String URL = "part/category/";
|
String URL = "part/category/";
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, String> defaultListFilters() {
|
||||||
|
var filters = new Map<String, String>();
|
||||||
|
|
||||||
|
filters["active"] = "true";
|
||||||
|
|
||||||
|
return filters;
|
||||||
|
}
|
||||||
|
|
||||||
String get pathstring => jsondata['pathstring'] ?? '';
|
String get pathstring => jsondata['pathstring'] ?? '';
|
||||||
|
|
||||||
String get parentpathstring {
|
String get parentpathstring {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'model.dart';
|
import 'model.dart';
|
||||||
|
|
||||||
import 'package:InvenTree/api.dart';
|
import 'package:InvenTree/api.dart';
|
||||||
@ -6,15 +8,72 @@ class InvenTreeStockItem extends InvenTreeModel {
|
|||||||
@override
|
@override
|
||||||
String URL = "stock/";
|
String URL = "stock/";
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, String> defaultGetFilters() {
|
||||||
|
|
||||||
|
var headers = new Map<String, String>();
|
||||||
|
|
||||||
|
headers["part_detail"] = "true";
|
||||||
|
headers["location_detail"] = "true";
|
||||||
|
headers["supplier_detail"] = "true";
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
InvenTreeStockItem() : super();
|
InvenTreeStockItem() : super();
|
||||||
|
|
||||||
InvenTreeStockItem.fromJson(Map<String, dynamic> json) : super.fromJson(json) {
|
InvenTreeStockItem.fromJson(Map<String, dynamic> json) : super.fromJson(json) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
String get partName => jsondata['part__name'] as String ?? '';
|
int get partId => jsondata['part'] ?? -1;
|
||||||
|
|
||||||
String get partDescription => jsondata['part__description'] as String ?? '';
|
int get trackingItemCount => jsondata['tracking_items'] as int ?? 0;
|
||||||
|
|
||||||
|
String get partName {
|
||||||
|
|
||||||
|
String nm = '';
|
||||||
|
|
||||||
|
// Use the detailed part information as priority
|
||||||
|
if (jsondata.containsKey('part_detail')) {
|
||||||
|
nm = jsondata['part_detail']['full_name'] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nm.isEmpty) {
|
||||||
|
nm = jsondata['part__name'] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return nm;
|
||||||
|
}
|
||||||
|
|
||||||
|
String get partDescription {
|
||||||
|
String desc = '';
|
||||||
|
|
||||||
|
// Use the detailed part description as priority
|
||||||
|
if (jsondata.containsKey('part_detail')) {
|
||||||
|
desc = jsondata['part_detail']['description'] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (desc.isEmpty) {
|
||||||
|
desc = jsondata['part__description'] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
String get partImage {
|
||||||
|
String img = '';
|
||||||
|
|
||||||
|
if (jsondata.containsKey('part_detail')) {
|
||||||
|
img = jsondata['part_detail']['thumbnail'] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (img.isEmpty) {
|
||||||
|
img = jsondata['part__thumbnail'] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
String get partThumbnail {
|
String get partThumbnail {
|
||||||
String thumb = jsondata['part__thumbnail'] as String ?? '';
|
String thumb = jsondata['part__thumbnail'] as String ?? '';
|
||||||
@ -24,12 +83,58 @@ class InvenTreeStockItem extends InvenTreeModel {
|
|||||||
return thumb;
|
return thumb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int get supplierPartId => jsondata['supplier_part'] as int ?? -1;
|
||||||
|
|
||||||
|
String get supplierImage {
|
||||||
|
String thumb = '';
|
||||||
|
|
||||||
|
if (jsondata.containsKey("supplier_detail")) {
|
||||||
|
thumb = jsondata['supplier_detail']['supplier_logo'] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return thumb;
|
||||||
|
}
|
||||||
|
|
||||||
|
String get supplierName {
|
||||||
|
String sname = '';
|
||||||
|
|
||||||
|
if (jsondata.containsKey("supplier_detail")) {
|
||||||
|
sname = jsondata["supplier_detail"]["supplier_name"] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return sname;
|
||||||
|
}
|
||||||
|
|
||||||
|
String get supplierSKU {
|
||||||
|
String sku = '';
|
||||||
|
|
||||||
|
if (jsondata.containsKey("supplier_detail")) {
|
||||||
|
sku = jsondata["supplier_detail"]["SKU"] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return sku;
|
||||||
|
}
|
||||||
|
|
||||||
int get serialNumber => jsondata['serial'] as int ?? null;
|
int get serialNumber => jsondata['serial'] as int ?? null;
|
||||||
|
|
||||||
double get quantity => jsondata['quantity'] as double ?? 0.0;
|
double get quantity => double.tryParse(jsondata['quantity'].toString() ?? '0');
|
||||||
|
|
||||||
int get locationId => jsondata['location'] as int ?? -1;
|
int get locationId => jsondata['location'] as int ?? -1;
|
||||||
|
|
||||||
|
String get locationName {
|
||||||
|
String loc = '';
|
||||||
|
|
||||||
|
if (jsondata.containsKey('location_detail')) {
|
||||||
|
loc = jsondata['location_detail']['name'] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loc.isEmpty) {
|
||||||
|
loc = jsondata['location__name'] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return loc;
|
||||||
|
}
|
||||||
|
|
||||||
String get displayQuantity {
|
String get displayQuantity {
|
||||||
// Display either quantity or serial number!
|
// Display either quantity or serial number!
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ import 'barcode.dart';
|
|||||||
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'settings.dart';
|
import 'settings/settings.dart';
|
||||||
import 'api.dart';
|
import 'api.dart';
|
||||||
import 'preferences.dart';
|
import 'preferences.dart';
|
||||||
|
|
||||||
@ -151,6 +151,13 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
|
|
||||||
_serverAddress = prefs.getString("server");
|
_serverAddress = prefs.getString("server");
|
||||||
|
|
||||||
|
// Reset the connection status variables
|
||||||
|
_serverStatus = "Connecting to server";
|
||||||
|
_serverMessage = "";
|
||||||
|
_serverConnection = false;
|
||||||
|
_serverIcon = new FaIcon(FontAwesomeIcons.spinner);
|
||||||
|
_serverStatusColor = Color.fromARGB(255, 50, 50, 250);
|
||||||
|
|
||||||
InvenTreeAPI().connect().then((bool result) {
|
InvenTreeAPI().connect().then((bool result) {
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
@ -176,21 +183,33 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
|
|
||||||
onConnectFailure(fault);
|
onConnectFailure(fault);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update widget state
|
||||||
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _search() {
|
void _search() {
|
||||||
|
if (!InvenTreeAPI().checkConnection(context)) return;
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
void _scan() {
|
void _scan() {
|
||||||
|
if (!InvenTreeAPI().checkConnection(context)) return;
|
||||||
|
|
||||||
scanQrCode(context);
|
scanQrCode(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _parts() {
|
void _parts() {
|
||||||
|
if (!InvenTreeAPI().checkConnection(context)) return;
|
||||||
|
|
||||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null)));
|
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _stock() {
|
void _stock() {
|
||||||
|
|
||||||
|
if (!InvenTreeAPI().checkConnection(context)) return;
|
||||||
|
|
||||||
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(null)));
|
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(null)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,6 +368,11 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
style: TextStyle(color: _serverStatusColor),
|
style: TextStyle(color: _serverStatusColor),
|
||||||
),
|
),
|
||||||
leading: _serverIcon,
|
leading: _serverIcon,
|
||||||
|
onTap: () {
|
||||||
|
if (!_serverConnection) {
|
||||||
|
_checkServerConnection();
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
64
lib/settings/about.dart
Normal file
64
lib/settings/about.dart
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import 'package:InvenTree/api.dart';
|
||||||
|
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:package_info/package_info.dart';
|
||||||
|
|
||||||
|
class InvenTreeAboutWidget extends StatelessWidget {
|
||||||
|
|
||||||
|
final PackageInfo info;
|
||||||
|
|
||||||
|
InvenTreeAboutWidget(this.info) : super();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text("About InvenTree"),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
children: <Widget>[
|
||||||
|
ListTile(
|
||||||
|
title: Text("Server Address"),
|
||||||
|
subtitle: Text(InvenTreeAPI().baseUrl.isNotEmpty ? InvenTreeAPI().baseUrl : "Not connected"),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text("Server Version"),
|
||||||
|
subtitle: Text(InvenTreeAPI().version.isNotEmpty ? InvenTreeAPI().version : "Not connected"),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text("Server Instance"),
|
||||||
|
subtitle: Text(InvenTreeAPI().instance.isNotEmpty ? InvenTreeAPI().instance : "Not connected"),
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
ListTile(
|
||||||
|
title: Text("App Name"),
|
||||||
|
subtitle: Text("${info.appName}"),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text("Package Name"),
|
||||||
|
subtitle: Text("${info.packageName}"),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text("App Version"),
|
||||||
|
subtitle: Text("${info.version}"),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text("Build Number"),
|
||||||
|
subtitle: Text("${info.buildNumber}")
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
ListTile(
|
||||||
|
title: Text("Submit Bug Report"),
|
||||||
|
subtitle: Text("https://github.com/inventree/inventree-app/issues/"),
|
||||||
|
onTap: () {
|
||||||
|
// TODO - Open the URL in an external webpage?
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
import 'api.dart';
|
import '../api.dart';
|
||||||
import 'preferences.dart';
|
import '../preferences.dart';
|
||||||
|
|
||||||
class InvenTreeLoginSettingsWidget extends StatefulWidget {
|
class InvenTreeLoginSettingsWidget extends StatefulWidget {
|
||||||
|
|
||||||
@ -81,7 +81,6 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
|
|||||||
initialValue: _server,
|
initialValue: _server,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: "127.0.0.1:8000",
|
hintText: "127.0.0.1:8000",
|
||||||
labelText: "Server:Port",
|
|
||||||
),
|
),
|
||||||
validator: _validateServer,
|
validator: _validateServer,
|
||||||
onSaved: (String value) {
|
onSaved: (String value) {
|
||||||
@ -89,7 +88,7 @@ class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
Divider(),
|
Divider(),
|
||||||
Text("Login Details"),
|
Text("Account Details"),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
initialValue: _username,
|
initialValue: _username,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
@ -1,8 +1,12 @@
|
|||||||
|
import 'package:InvenTree/settings/about.dart';
|
||||||
|
import 'package:InvenTree/settings/login.dart';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
import 'package:InvenTree/api.dart';
|
import 'package:InvenTree/api.dart';
|
||||||
import 'login_settings.dart';
|
import 'login.dart';
|
||||||
|
|
||||||
import 'package:package_info/package_info.dart';
|
import 'package:package_info/package_info.dart';
|
||||||
|
|
||||||
@ -28,12 +32,14 @@ class _InvenTreeSettingsState extends State<InvenTreeSettingsWidget> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
title: Text("Server Settings"),
|
title: Text("Server Settings"),
|
||||||
subtitle: Text("Configure server and login settings"),
|
subtitle: Text("Configure server and login settings"),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.server),
|
||||||
onTap: _editServerSettings,
|
onTap: _editServerSettings,
|
||||||
),
|
),
|
||||||
Divider(),
|
Divider(),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text("About"),
|
title: Text("About"),
|
||||||
subtitle: Text("App details"),
|
subtitle: Text("App details"),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.infoCircle),
|
||||||
onTap: _about,
|
onTap: _about,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -52,40 +58,8 @@ class _InvenTreeSettingsState extends State<InvenTreeSettingsWidget> {
|
|||||||
void _about() async {
|
void _about() async {
|
||||||
|
|
||||||
PackageInfo.fromPlatform().then((PackageInfo info) {
|
PackageInfo.fromPlatform().then((PackageInfo info) {
|
||||||
showDialog(
|
Navigator.push(context,
|
||||||
context: context,
|
MaterialPageRoute(builder: (context) => InvenTreeAboutWidget(info)));
|
||||||
child: new SimpleDialog(
|
|
||||||
title: new Text("About InvenTree"),
|
|
||||||
children: <Widget>[
|
|
||||||
ListTile(
|
|
||||||
title: Text("Server Version"),
|
|
||||||
subtitle: Text(InvenTreeAPI().version.isNotEmpty ? InvenTreeAPI().version : "Not connected"),
|
|
||||||
),
|
|
||||||
Divider(),
|
|
||||||
ListTile(
|
|
||||||
title: Text("App Name"),
|
|
||||||
subtitle: Text("${info.appName}"),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text("Package Name"),
|
|
||||||
subtitle: Text("${info.packageName}"),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text("App Version"),
|
|
||||||
subtitle: Text("${info.version}"),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text("Build Number"),
|
|
||||||
subtitle: Text("${info.buildNumber}")
|
|
||||||
),
|
|
||||||
Divider(),
|
|
||||||
ListTile(
|
|
||||||
title: Text("Submit Bug Report"),
|
|
||||||
subtitle: Text("Submit a bug report or feature request at:\n https://github.com/inventree/inventree-app/issues/"),
|
|
||||||
)
|
|
||||||
]
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -169,7 +169,7 @@ class _CategoryDisplayState extends State<CategoryDisplayWidget> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
body: SubcategoryList(_subcategories),
|
body: SubcategoryList(_subcategories),
|
||||||
isExpanded: _subcategoriesExpanded,
|
isExpanded: _subcategoriesExpanded && _subcategories.length > 0,
|
||||||
),
|
),
|
||||||
ExpansionPanel(
|
ExpansionPanel(
|
||||||
headerBuilder: (BuildContext context, bool isExpanded) {
|
headerBuilder: (BuildContext context, bool isExpanded) {
|
||||||
@ -185,7 +185,7 @@ class _CategoryDisplayState extends State<CategoryDisplayWidget> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
body: PartList(_parts),
|
body: PartList(_parts),
|
||||||
isExpanded: _partListExpanded,
|
isExpanded: _partListExpanded && _parts.length > 0,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
|
import 'package:InvenTree/api.dart';
|
||||||
import 'package:InvenTree/barcode.dart';
|
import 'package:InvenTree/barcode.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:InvenTree/api.dart';
|
||||||
|
|
||||||
import 'package:InvenTree/widget/category_display.dart';
|
import 'package:InvenTree/widget/category_display.dart';
|
||||||
import 'package:InvenTree/widget/location_display.dart';
|
import 'package:InvenTree/widget/location_display.dart';
|
||||||
|
|
||||||
import 'package:InvenTree/settings.dart';
|
import 'package:InvenTree/settings/settings.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
|
||||||
class InvenTreeDrawer extends StatelessWidget {
|
class InvenTreeDrawer extends StatelessWidget {
|
||||||
@ -33,6 +36,7 @@ class InvenTreeDrawer extends StatelessWidget {
|
|||||||
* Upon successful scan, data are passed off to be decoded.
|
* Upon successful scan, data are passed off to be decoded.
|
||||||
*/
|
*/
|
||||||
void _scan() async {
|
void _scan() async {
|
||||||
|
if (!InvenTreeAPI().checkConnection(context)) return;
|
||||||
|
|
||||||
_closeDrawer();
|
_closeDrawer();
|
||||||
scanQrCode(context);
|
scanQrCode(context);
|
||||||
@ -42,6 +46,7 @@ class InvenTreeDrawer extends StatelessWidget {
|
|||||||
* Display the top-level PartCategory list
|
* Display the top-level PartCategory list
|
||||||
*/
|
*/
|
||||||
void _showParts() {
|
void _showParts() {
|
||||||
|
if (!InvenTreeAPI().checkConnection(context)) return;
|
||||||
|
|
||||||
_closeDrawer();
|
_closeDrawer();
|
||||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null)));
|
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null)));
|
||||||
@ -51,6 +56,7 @@ class InvenTreeDrawer extends StatelessWidget {
|
|||||||
* Display the top-level StockLocation list
|
* Display the top-level StockLocation list
|
||||||
*/
|
*/
|
||||||
void _showStock() {
|
void _showStock() {
|
||||||
|
if (!InvenTreeAPI().checkConnection(context)) return;
|
||||||
_closeDrawer();
|
_closeDrawer();
|
||||||
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(null)));
|
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(null)));
|
||||||
}
|
}
|
||||||
@ -72,6 +78,7 @@ class InvenTreeDrawer extends StatelessWidget {
|
|||||||
leading: new Image.asset(
|
leading: new Image.asset(
|
||||||
"assets/image/icon.png",
|
"assets/image/icon.png",
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
|
width: 40,
|
||||||
),
|
),
|
||||||
title: new Text("InvenTree"),
|
title: new Text("InvenTree"),
|
||||||
onTap: _home,
|
onTap: _home,
|
||||||
|
@ -176,7 +176,7 @@ class _LocationDisplayState extends State<LocationDisplayWidget> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
body: SublocationList(_sublocations),
|
body: SublocationList(_sublocations),
|
||||||
isExpanded: _locationListExpanded,
|
isExpanded: _locationListExpanded && _sublocations.length > 0,
|
||||||
),
|
),
|
||||||
ExpansionPanel(
|
ExpansionPanel(
|
||||||
headerBuilder: (BuildContext context, bool isExpanded) {
|
headerBuilder: (BuildContext context, bool isExpanded) {
|
||||||
@ -192,7 +192,7 @@ class _LocationDisplayState extends State<LocationDisplayWidget> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
body: StockList(_items),
|
body: StockList(_items),
|
||||||
isExpanded: _stockListExpanded,
|
isExpanded: _stockListExpanded && _items.length > 0,
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
@ -30,14 +30,16 @@ class _PartDisplayState extends State<PartDisplayWidget> {
|
|||||||
InvenTreePart part;
|
InvenTreePart part;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Construct a list of detail elements about this part.
|
* Build a list of tiles to display under the part description
|
||||||
* Not all elements are set for each part, so only add the ones that are important.
|
|
||||||
*/
|
*/
|
||||||
List<Widget> partDetails() {
|
List<Widget> partTiles() {
|
||||||
List<Widget> widgets = [
|
|
||||||
|
List<Widget> tiles = [];
|
||||||
|
|
||||||
// Image / name / description
|
// Image / name / description
|
||||||
ListTile(
|
tiles.add(
|
||||||
|
Card(
|
||||||
|
child: ListTile(
|
||||||
title: Text("${part.fullname}"),
|
title: Text("${part.fullname}"),
|
||||||
subtitle: Text("${part.description}"),
|
subtitle: Text("${part.description}"),
|
||||||
leading: Image(
|
leading: Image(
|
||||||
@ -48,139 +50,105 @@ class _PartDisplayState extends State<PartDisplayWidget> {
|
|||||||
onPressed: null,
|
onPressed: null,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
];
|
|
||||||
|
|
||||||
return widgets;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Build a list of tiles to display under the part description
|
|
||||||
*/
|
|
||||||
List<Widget> partTiles() {
|
|
||||||
|
|
||||||
List<Widget> tiles = [
|
|
||||||
Card(
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: partDetails(),
|
|
||||||
)
|
)
|
||||||
),
|
);
|
||||||
];
|
|
||||||
|
|
||||||
// Category information
|
// Category information
|
||||||
if (part.categoryName.isNotEmpty) {
|
if (part.categoryName.isNotEmpty) {
|
||||||
tiles.add(
|
tiles.add(
|
||||||
Card(
|
ListTile(
|
||||||
child: ListTile(
|
|
||||||
title: Text("Part Category"),
|
title: Text("Part Category"),
|
||||||
subtitle: Text("${part.categoryName}"),
|
subtitle: Text("${part.categoryName}"),
|
||||||
leading: FaIcon(FontAwesomeIcons.stream),
|
leading: FaIcon(FontAwesomeIcons.stream),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
if (part.categoryId > 0) {
|
||||||
InvenTreePartCategory().get(part.categoryId).then((var cat) {
|
InvenTreePartCategory().get(part.categoryId).then((var cat) {
|
||||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(cat)));
|
Navigator.push(context, MaterialPageRoute(
|
||||||
|
builder: (context) => CategoryDisplayWidget(cat)));
|
||||||
});
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// External link?
|
// External link?
|
||||||
if (part.link.isNotEmpty) {
|
if (part.link.isNotEmpty) {
|
||||||
tiles.add(
|
tiles.add(
|
||||||
Card(
|
ListTile(
|
||||||
child: ListTile(
|
|
||||||
title: Text("${part.link}"),
|
title: Text("${part.link}"),
|
||||||
leading: FaIcon(FontAwesomeIcons.link),
|
leading: FaIcon(FontAwesomeIcons.link),
|
||||||
trailing: Text(""),
|
trailing: Text(""),
|
||||||
onTap: null,
|
onTap: null,
|
||||||
)
|
)
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stock information
|
// Stock information
|
||||||
tiles.add(
|
tiles.add(
|
||||||
Card(
|
ListTile(
|
||||||
child: ListTile(
|
|
||||||
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: null,
|
||||||
),
|
),
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Parts on order
|
// Parts on order
|
||||||
if (part.isPurchaseable) {
|
if (part.isPurchaseable) {
|
||||||
tiles.add(
|
tiles.add(
|
||||||
Card(
|
ListTile(
|
||||||
child: ListTile(
|
|
||||||
title: Text("On Order"),
|
title: Text("On Order"),
|
||||||
leading: FaIcon(FontAwesomeIcons.shoppingCart),
|
leading: FaIcon(FontAwesomeIcons.shoppingCart),
|
||||||
trailing: Text("${part.onOrder}"),
|
trailing: Text("${part.onOrder}"),
|
||||||
onTap: null,
|
onTap: null,
|
||||||
)
|
)
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parts being built
|
// Parts being built
|
||||||
if (part.isAssembly) {
|
if (part.isAssembly) {
|
||||||
|
|
||||||
tiles.add(
|
tiles.add(ListTile(
|
||||||
Card(
|
|
||||||
child: ListTile(
|
|
||||||
title: Text("Bill of Materials"),
|
title: Text("Bill of Materials"),
|
||||||
leading: FaIcon(FontAwesomeIcons.thList),
|
leading: FaIcon(FontAwesomeIcons.thList),
|
||||||
trailing: Text("${part.bomItemCount}"),
|
trailing: Text("${part.bomItemCount}"),
|
||||||
onTap: null,
|
onTap: null,
|
||||||
)
|
)
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
tiles.add(
|
tiles.add(
|
||||||
Card(
|
ListTile(
|
||||||
child: ListTile(
|
|
||||||
title: Text("Building"),
|
title: Text("Building"),
|
||||||
leading: FaIcon(FontAwesomeIcons.tools),
|
leading: FaIcon(FontAwesomeIcons.tools),
|
||||||
trailing: Text("${part.building}"),
|
trailing: Text("${part.building}"),
|
||||||
onTap: null,
|
onTap: null,
|
||||||
)
|
)
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (part.isComponent) {
|
if (part.isComponent) {
|
||||||
tiles.add(
|
tiles.add(ListTile(
|
||||||
Card(
|
|
||||||
child: ListTile(
|
|
||||||
title: Text("Used In"),
|
title: Text("Used In"),
|
||||||
leading: FaIcon(FontAwesomeIcons.sitemap),
|
leading: FaIcon(FontAwesomeIcons.sitemap),
|
||||||
trailing: Text("${part.usedInCount}"),
|
trailing: Text("${part.usedInCount}"),
|
||||||
onTap: null,
|
onTap: null,
|
||||||
)
|
)
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notes field?
|
// Notes field?
|
||||||
if (part.notes.isNotEmpty) {
|
if (part.notes.isNotEmpty) {
|
||||||
tiles.add(
|
tiles.add(
|
||||||
Card(
|
ListTile(
|
||||||
child: ListTile(
|
|
||||||
title: Text("Notes"),
|
title: Text("Notes"),
|
||||||
leading: FaIcon(FontAwesomeIcons.stickyNote),
|
leading: FaIcon(FontAwesomeIcons.stickyNote),
|
||||||
trailing: Text(""),
|
trailing: Text(""),
|
||||||
onTap: null,
|
onTap: null,
|
||||||
)
|
)
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
tiles.add(Spacer());
|
|
||||||
|
|
||||||
return tiles;
|
return tiles;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -193,9 +161,7 @@ class _PartDisplayState extends State<PartDisplayWidget> {
|
|||||||
),
|
),
|
||||||
drawer: new InvenTreeDrawer(context),
|
drawer: new InvenTreeDrawer(context),
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Column(
|
child: ListView(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
children: partTiles(),
|
children: partTiles(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
|
|
||||||
|
|
||||||
import 'package:InvenTree/inventree/stock.dart';
|
import 'package:InvenTree/inventree/stock.dart';
|
||||||
|
import 'package:InvenTree/inventree/part.dart';
|
||||||
|
import 'package:InvenTree/widget/location_display.dart';
|
||||||
|
import 'package:InvenTree/widget/part_display.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:InvenTree/api.dart';
|
||||||
|
|
||||||
import 'package:InvenTree/widget/drawer.dart';
|
import 'package:InvenTree/widget/drawer.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
|
||||||
class StockItemDisplayWidget extends StatefulWidget {
|
class StockItemDisplayWidget extends StatefulWidget {
|
||||||
|
|
||||||
@ -25,27 +31,137 @@ class _StockItemDisplayState extends State<StockItemDisplayWidget> {
|
|||||||
|
|
||||||
final InvenTreeStockItem item;
|
final InvenTreeStockItem item;
|
||||||
|
|
||||||
String get _title {
|
/*
|
||||||
if (item == null) {
|
* Construct a list of detail elements about this StockItem.
|
||||||
return "Stock Item";
|
* The number of elements may vary depending on the StockItem details
|
||||||
} else {
|
*/
|
||||||
return "Item: x ${item.partName}";
|
List<Widget> stockTiles() {
|
||||||
|
List<Widget> tiles = [];
|
||||||
|
|
||||||
|
// Image / name / description
|
||||||
|
tiles.add(
|
||||||
|
Card(
|
||||||
|
child: ListTile(
|
||||||
|
title: Text("${item.partName}"),
|
||||||
|
subtitle: Text("${item.partDescription}"),
|
||||||
|
leading: Image(
|
||||||
|
image: InvenTreeAPI().getImage(item.partImage),
|
||||||
|
),
|
||||||
|
trailing: IconButton(
|
||||||
|
icon: FaIcon(FontAwesomeIcons.edit),
|
||||||
|
onPressed: null,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text("Part"),
|
||||||
|
subtitle: Text("${item.partName}"),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.shapes),
|
||||||
|
onTap: () {
|
||||||
|
if (item.partId > 0) {
|
||||||
|
InvenTreePart().get(item.partId).then((var part) {
|
||||||
|
if (part is InvenTreePart) {
|
||||||
|
Navigator.push(context, MaterialPageRoute(builder: (context) => PartDisplayWidget(part)));
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Quantity information
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text("Quantity"),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.cubes),
|
||||||
|
trailing: Text("${item.quantity}"),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Location information
|
||||||
|
if (item.locationName.isNotEmpty) {
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text("Stock Location"),
|
||||||
|
subtitle: Text("${item.locationName}"),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.mapMarkerAlt),
|
||||||
|
onTap: () {
|
||||||
|
if (item.locationId > 0) {
|
||||||
|
InvenTreeStockLocation().get(item.locationId).then((var loc) {
|
||||||
|
Navigator.push(context, MaterialPageRoute(
|
||||||
|
builder: (context) => LocationDisplayWidget(loc)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supplier part?
|
||||||
|
if (item.supplierPartId > 0) {
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text("${item.supplierName}"),
|
||||||
|
subtitle: Text("${item.supplierSKU}"),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.industry),
|
||||||
|
trailing: Image(
|
||||||
|
image: InvenTreeAPI().getImage(item.supplierImage),
|
||||||
|
height: 32,
|
||||||
|
),
|
||||||
|
onTap: null,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.link.isNotEmpty) {
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text("${item.link}"),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.link),
|
||||||
|
trailing: Text(""),
|
||||||
|
onTap: null,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.trackingItemCount > 0) {
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text("History"),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.history),
|
||||||
|
trailing: Text("${item.trackingItemCount}"),
|
||||||
|
onTap: null,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.notes.isNotEmpty) {
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text("Notes"),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.stickyNote),
|
||||||
|
trailing: Text(""),
|
||||||
|
onTap: null,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(_title),
|
title: Text("Stock Item"),
|
||||||
),
|
),
|
||||||
drawer: new InvenTreeDrawer(context),
|
drawer: new InvenTreeDrawer(context),
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Column(
|
child: ListView(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
children: stockTiles(),
|
||||||
children: <Widget>[
|
|
||||||
Text("Stock Item: hello"),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user