mirror of
https://github.com/inventree/inventree-app.git
synced 2025-04-28 13:36:50 +00:00
Merge branch 'edit-item'
This commit is contained in:
commit
fc6e90ce0b
@ -380,7 +380,7 @@ class InvenTreeAPI {
|
|||||||
* Load image from the InvenTree server,
|
* Load image from the InvenTree server,
|
||||||
* or from local cache (if it has been cached!)
|
* or from local cache (if it has been cached!)
|
||||||
*/
|
*/
|
||||||
CachedNetworkImage getImage(String imageUrl) {
|
CachedNetworkImage getImage(String imageUrl, {double height, double width}) {
|
||||||
if (imageUrl.isEmpty) {
|
if (imageUrl.isEmpty) {
|
||||||
imageUrl = staticImage;
|
imageUrl = staticImage;
|
||||||
}
|
}
|
||||||
@ -392,6 +392,8 @@ class InvenTreeAPI {
|
|||||||
placeholder: (context, url) => CircularProgressIndicator(),
|
placeholder: (context, url) => CircularProgressIndicator(),
|
||||||
errorWidget: (context, url, error) => Icon(FontAwesomeIcons.exclamation),
|
errorWidget: (context, url, error) => Icon(FontAwesomeIcons.exclamation),
|
||||||
httpHeaders: defaultHeaders(),
|
httpHeaders: defaultHeaders(),
|
||||||
|
height: height,
|
||||||
|
width: width,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -47,13 +47,15 @@ class InvenTreeModel {
|
|||||||
|
|
||||||
String get description => jsondata['description'] ?? '';
|
String get description => jsondata['description'] ?? '';
|
||||||
|
|
||||||
String get notes => jsondata['notes'] ?? '';
|
String get notes => jsondata['notes'] as String ?? '';
|
||||||
|
|
||||||
int get parentId => jsondata['parent'] ?? -1;
|
int get parentId => jsondata['parent'] as int ?? -1;
|
||||||
|
|
||||||
// Legacy API provided external link as "URL", while newer API uses "link"
|
// Legacy API provided external link as "URL", while newer API uses "link"
|
||||||
String get link => jsondata['link'] ?? jsondata['URL'] ?? '';
|
String get link => jsondata['link'] ?? jsondata['URL'] ?? '';
|
||||||
|
|
||||||
|
String get keywords => jsondata['keywords'] as String ?? '';
|
||||||
|
|
||||||
// Create a new object from JSON data (not a constructor!)
|
// Create a new object from JSON data (not a constructor!)
|
||||||
InvenTreeModel createFromJson(Map<String, dynamic> json) {
|
InvenTreeModel createFromJson(Map<String, dynamic> json) {
|
||||||
|
|
||||||
|
@ -31,6 +31,17 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
|||||||
@override
|
@override
|
||||||
String getAppBarTitle(BuildContext context) { return "Part Category"; }
|
String getAppBarTitle(BuildContext context) { return "Part Category"; }
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Widget> getAppBarActions(BuildContext context) {
|
||||||
|
return <Widget>[
|
||||||
|
IconButton(
|
||||||
|
icon: FaIcon(FontAwesomeIcons.edit),
|
||||||
|
tooltip: 'Edit',
|
||||||
|
onPressed: null,
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
_CategoryDisplayState(this.category) {}
|
_CategoryDisplayState(this.category) {}
|
||||||
|
|
||||||
// The local InvenTreePartCategory object
|
// The local InvenTreePartCategory object
|
||||||
@ -94,10 +105,6 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
title: Text("${category.name}"),
|
title: Text("${category.name}"),
|
||||||
subtitle: Text("${category.description}"),
|
subtitle: Text("${category.description}"),
|
||||||
trailing: IconButton(
|
|
||||||
icon: FaIcon(FontAwesomeIcons.edit),
|
|
||||||
onPressed: null,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text("Parent Category"),
|
title: Text("Parent Category"),
|
||||||
@ -260,7 +267,11 @@ class PartList extends StatelessWidget {
|
|||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text("${part.name}"),
|
title: Text("${part.name}"),
|
||||||
subtitle: Text("${part.description}"),
|
subtitle: Text("${part.description}"),
|
||||||
leading: InvenTreeAPI().getImage(part.thumbnail),
|
leading: InvenTreeAPI().getImage(
|
||||||
|
part.thumbnail,
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_openPart(context, part.pk);
|
_openPart(context, part.pk);
|
||||||
},
|
},
|
||||||
|
@ -28,6 +28,18 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
|||||||
@override
|
@override
|
||||||
String getAppBarTitle(BuildContext context) { return "Stock Location"; }
|
String getAppBarTitle(BuildContext context) { return "Stock Location"; }
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Widget> getAppBarActions(BuildContext context) {
|
||||||
|
return <Widget>[
|
||||||
|
IconButton(
|
||||||
|
icon: FaIcon(FontAwesomeIcons.edit),
|
||||||
|
tooltip: "Edit",
|
||||||
|
// TODO - Edit stock location
|
||||||
|
onPressed: null,
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
_LocationDisplayState(this.location) {}
|
_LocationDisplayState(this.location) {}
|
||||||
|
|
||||||
List<InvenTreeStockLocation> _sublocations = List<InvenTreeStockLocation>();
|
List<InvenTreeStockLocation> _sublocations = List<InvenTreeStockLocation>();
|
||||||
@ -98,10 +110,6 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
title: Text("${location.name}"),
|
title: Text("${location.name}"),
|
||||||
subtitle: Text("${location.description}"),
|
subtitle: Text("${location.description}"),
|
||||||
trailing: IconButton(
|
|
||||||
icon: FaIcon(FontAwesomeIcons.edit),
|
|
||||||
onPressed: null,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text("Parent Category"),
|
title: Text("Parent Category"),
|
||||||
@ -245,7 +253,11 @@ class StockList extends StatelessWidget {
|
|||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text("${item.partName}"),
|
title: Text("${item.partName}"),
|
||||||
subtitle: Text("${item.partDescription}"),
|
subtitle: Text("${item.partDescription}"),
|
||||||
leading: InvenTreeAPI().getImage(item.partThumbnail),
|
leading: InvenTreeAPI().getImage(
|
||||||
|
item.partThumbnail,
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
),
|
||||||
trailing: Text("${item.displayQuantity}",
|
trailing: Text("${item.displayQuantity}",
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
|
@ -32,6 +32,18 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
|||||||
@override
|
@override
|
||||||
String getAppBarTitle(BuildContext context) { return "Part"; }
|
String getAppBarTitle(BuildContext context) { return "Part"; }
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Widget> getAppBarActions(BuildContext context) {
|
||||||
|
return <Widget>[
|
||||||
|
// TODO: Hide the 'edit' button if the user does not have permission!!
|
||||||
|
IconButton(
|
||||||
|
icon: FaIcon(FontAwesomeIcons.edit),
|
||||||
|
tooltip: 'Edit',
|
||||||
|
onPressed: _editPartDialog,
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
_PartDisplayState(this.part) {
|
_PartDisplayState(this.part) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
@ -75,6 +87,7 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
|||||||
var _description;
|
var _description;
|
||||||
var _ipn;
|
var _ipn;
|
||||||
var _revision;
|
var _revision;
|
||||||
|
var _keywords;
|
||||||
|
|
||||||
showFormDialog(context, "Edit Part",
|
showFormDialog(context, "Edit Part",
|
||||||
key: _editPartKey,
|
key: _editPartKey,
|
||||||
@ -95,6 +108,7 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
|||||||
"name": _name,
|
"name": _name,
|
||||||
"description": _description,
|
"description": _description,
|
||||||
"IPN": _ipn,
|
"IPN": _ipn,
|
||||||
|
"keywords": _keywords,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -114,7 +128,14 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
|||||||
StringField(
|
StringField(
|
||||||
label: "Internal Part Number",
|
label: "Internal Part Number",
|
||||||
initial: part.IPN,
|
initial: part.IPN,
|
||||||
|
allowEmpty: true,
|
||||||
onSaved: (value) => _ipn = value,
|
onSaved: (value) => _ipn = value,
|
||||||
|
),
|
||||||
|
StringField(
|
||||||
|
label: "Keywords",
|
||||||
|
initial: part.keywords,
|
||||||
|
allowEmpty: true,
|
||||||
|
onSaved: (value) => _keywords = value,
|
||||||
)
|
)
|
||||||
|
|
||||||
]
|
]
|
||||||
@ -136,10 +157,6 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
|||||||
title: Text("${part.fullname}"),
|
title: Text("${part.fullname}"),
|
||||||
subtitle: Text("${part.description}"),
|
subtitle: Text("${part.description}"),
|
||||||
leading: InvenTreeAPI().getImage(part.thumbnail),
|
leading: InvenTreeAPI().getImage(part.thumbnail),
|
||||||
trailing: IconButton(
|
|
||||||
icon: FaIcon(FontAwesomeIcons.edit),
|
|
||||||
onPressed: _editPartDialog,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -174,37 +191,25 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// External link?
|
|
||||||
if (part.link.isNotEmpty) {
|
|
||||||
tiles.add(
|
|
||||||
ListTile(
|
|
||||||
title: Text("${part.link}"),
|
|
||||||
leading: FaIcon(FontAwesomeIcons.link),
|
|
||||||
trailing: Text(""),
|
|
||||||
onTap: null,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stock information
|
// Stock information
|
||||||
tiles.add(
|
tiles.add(
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text("Stock"),
|
title: Text("Stock"),
|
||||||
leading: FaIcon(FontAwesomeIcons.boxes),
|
leading: FaIcon(FontAwesomeIcons.boxes),
|
||||||
trailing: Text("${part.inStock}"),
|
trailing: Text("${part.inStock}"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(builder: (context) => PartStockDetailWidget(part))
|
MaterialPageRoute(builder: (context) => PartStockDetailWidget(part))
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Parts on order
|
// Parts on order
|
||||||
if (part.isPurchaseable) {
|
if (part.isPurchaseable) {
|
||||||
tiles.add(
|
tiles.add(
|
||||||
ListTile(
|
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}"),
|
||||||
@ -218,11 +223,11 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
|||||||
if (false && part.isAssembly) {
|
if (false && part.isAssembly) {
|
||||||
|
|
||||||
tiles.add(ListTile(
|
tiles.add(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(
|
||||||
@ -238,9 +243,31 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
|||||||
// TODO - Do we want to use the app to display "used in"?
|
// TODO - Do we want to use the app to display "used in"?
|
||||||
if (false && part.isComponent) {
|
if (false && part.isComponent) {
|
||||||
tiles.add(ListTile(
|
tiles.add(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,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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),
|
||||||
|
trailing: Text(""),
|
||||||
onTap: null,
|
onTap: null,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -11,6 +11,10 @@ abstract class RefreshableState<T extends StatefulWidget> extends State<T> {
|
|||||||
// Storage for context once "Build" is called
|
// Storage for context once "Build" is called
|
||||||
BuildContext context;
|
BuildContext context;
|
||||||
|
|
||||||
|
List<Widget> getAppBarActions(BuildContext context) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
String getAppBarTitle(BuildContext context) { return "App Bar Title"; }
|
String getAppBarTitle(BuildContext context) { return "App Bar Title"; }
|
||||||
|
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -37,6 +41,7 @@ abstract class RefreshableState<T extends StatefulWidget> extends State<T> {
|
|||||||
AppBar getAppBar(BuildContext context) {
|
AppBar getAppBar(BuildContext context) {
|
||||||
return AppBar(
|
return AppBar(
|
||||||
title: Text(getAppBarTitle(context)),
|
title: Text(getAppBarTitle(context)),
|
||||||
|
actions: getAppBarActions(context),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,18 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
|||||||
@override
|
@override
|
||||||
String getAppBarTitle(BuildContext context) { return "Stock Item"; }
|
String getAppBarTitle(BuildContext context) { return "Stock Item"; }
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Widget> getAppBarActions(BuildContext context) {
|
||||||
|
return <Widget>[
|
||||||
|
// TODO: Hide the 'edit' button if the user does not have permission!!
|
||||||
|
IconButton(
|
||||||
|
icon: FaIcon(FontAwesomeIcons.edit),
|
||||||
|
tooltip: "Edit",
|
||||||
|
onPressed: _editStockItemDialog,
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
final TextEditingController _quantityController = TextEditingController();
|
final TextEditingController _quantityController = TextEditingController();
|
||||||
final TextEditingController _notesController = TextEditingController();
|
final TextEditingController _notesController = TextEditingController();
|
||||||
|
|
||||||
@ -333,10 +345,6 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
|||||||
title: Text("${item.partName}"),
|
title: Text("${item.partName}"),
|
||||||
subtitle: Text("${item.partDescription}"),
|
subtitle: Text("${item.partDescription}"),
|
||||||
leading: InvenTreeAPI().getImage(item.partImage),
|
leading: InvenTreeAPI().getImage(item.partImage),
|
||||||
trailing: IconButton(
|
|
||||||
icon: FaIcon(FontAwesomeIcons.edit),
|
|
||||||
onPressed: _editStockItemDialog,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -358,6 +366,33 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Location information
|
||||||
|
if ((item.locationId > 0) && (item.locationName != null) && (item.locationName.isNotEmpty)) {
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text("Stock Location"),
|
||||||
|
subtitle: Text("${item.locationPathString}"),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.mapMarkerAlt),
|
||||||
|
onTap: () {
|
||||||
|
if (item.locationId > 0) {
|
||||||
|
InvenTreeStockLocation().get(context, item.locationId).then((var loc) {
|
||||||
|
Navigator.push(context, MaterialPageRoute(
|
||||||
|
builder: (context) => LocationDisplayWidget(loc)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text("Stock Location"),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.mapMarkerAlt),
|
||||||
|
subtitle: Text("No location set"),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Quantity information
|
// Quantity information
|
||||||
if (item.isSerialized()) {
|
if (item.isSerialized()) {
|
||||||
tiles.add(
|
tiles.add(
|
||||||
@ -391,33 +426,6 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Location information
|
|
||||||
if ((item.locationId > 0) && (item.locationName != null) && (item.locationName.isNotEmpty)) {
|
|
||||||
tiles.add(
|
|
||||||
ListTile(
|
|
||||||
title: Text("Stock Location"),
|
|
||||||
subtitle: Text("${item.locationPathString}"),
|
|
||||||
leading: FaIcon(FontAwesomeIcons.mapMarkerAlt),
|
|
||||||
onTap: () {
|
|
||||||
if (item.locationId > 0) {
|
|
||||||
InvenTreeStockLocation().get(context, item.locationId).then((var loc) {
|
|
||||||
Navigator.push(context, MaterialPageRoute(
|
|
||||||
builder: (context) => LocationDisplayWidget(loc)));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
tiles.add(
|
|
||||||
ListTile(
|
|
||||||
title: Text("Stock Location"),
|
|
||||||
leading: FaIcon(FontAwesomeIcons.mapMarkerAlt),
|
|
||||||
subtitle: Text("No location set"),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Supplier part?
|
// Supplier part?
|
||||||
if (item.supplierPartId > 0) {
|
if (item.supplierPartId > 0) {
|
||||||
tiles.add(
|
tiles.add(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user