mirror of
https://github.com/inventree/inventree-app.git
synced 2025-07-09 15:20:48 +00:00
Format Code and Add Format Checks to CI (#643)
* Remove unused lib/generated/i18n.dart * Use `fvm dart format .` * Add contributing guidelines * Enforce dart format * Add `dart format off` directive to generated files
This commit is contained in:
@ -1,4 +1,3 @@
|
||||
|
||||
import "package:inventree/inventree/model.dart";
|
||||
import "package:inventree/inventree/part.dart";
|
||||
|
||||
@ -6,13 +5,13 @@ import "package:inventree/inventree/part.dart";
|
||||
* Class representing the BomItem database model
|
||||
*/
|
||||
class InvenTreeBomItem extends InvenTreeModel {
|
||||
|
||||
InvenTreeBomItem() : super();
|
||||
|
||||
InvenTreeBomItem.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeBomItem.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreeBomItem.fromJson(json);
|
||||
|
||||
@override
|
||||
String get URL => "bom/";
|
||||
@ -28,7 +27,7 @@ class InvenTreeBomItem extends InvenTreeModel {
|
||||
|
||||
// Extract the 'reference' value associated with this BomItem
|
||||
String get reference => getString("reference");
|
||||
|
||||
|
||||
// Extract the 'quantity' value associated with this BomItem
|
||||
double get quantity => getDouble("quantity");
|
||||
|
||||
@ -57,8 +56,8 @@ class InvenTreeBomItem extends InvenTreeModel {
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the ID of the related sub-part
|
||||
int get subPartId => getInt("sub_part");
|
||||
}
|
||||
}
|
||||
|
@ -6,13 +6,11 @@ import "package:inventree/inventree/model.dart";
|
||||
import "package:inventree/inventree/purchase_order.dart";
|
||||
import "package:inventree/widget/company/company_detail.dart";
|
||||
|
||||
|
||||
/*
|
||||
* The InvenTreeCompany class represents the Company model in the InvenTree database.
|
||||
*/
|
||||
|
||||
class InvenTreeCompany extends InvenTreeModel {
|
||||
|
||||
InvenTreeCompany() : super();
|
||||
|
||||
InvenTreeCompany.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
@ -26,14 +24,16 @@ class InvenTreeCompany extends InvenTreeModel {
|
||||
Future<Object?> goToDetailPage(BuildContext context) async {
|
||||
return Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CompanyDetailWidget(this)
|
||||
)
|
||||
MaterialPageRoute(builder: (context) => CompanyDetailWidget(this)),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<String> get rolesRequired => ["purchase_order", "sales_order", "return_order"];
|
||||
List<String> get rolesRequired => [
|
||||
"purchase_order",
|
||||
"sales_order",
|
||||
"return_order",
|
||||
];
|
||||
|
||||
@override
|
||||
Map<String, Map<String, dynamic>> formFields() {
|
||||
@ -54,12 +54,16 @@ class InvenTreeCompany extends InvenTreeModel {
|
||||
return fields;
|
||||
}
|
||||
|
||||
String get image => (jsondata["image"] ?? jsondata["thumbnail"] ?? InvenTreeAPI.staticImage) as String;
|
||||
String get image =>
|
||||
(jsondata["image"] ?? jsondata["thumbnail"] ?? InvenTreeAPI.staticImage)
|
||||
as String;
|
||||
|
||||
String get thumbnail => (jsondata["thumbnail"] ?? jsondata["image"] ?? InvenTreeAPI.staticThumb) as String;
|
||||
String get thumbnail =>
|
||||
(jsondata["thumbnail"] ?? jsondata["image"] ?? InvenTreeAPI.staticThumb)
|
||||
as String;
|
||||
|
||||
String get website => getString("website");
|
||||
|
||||
|
||||
String get phone => getString("phone");
|
||||
|
||||
String get email => getString("email");
|
||||
@ -73,22 +77,21 @@ class InvenTreeCompany extends InvenTreeModel {
|
||||
bool get active => getBool("active", backup: true);
|
||||
|
||||
int get partSuppliedCount => getInt("part_supplied");
|
||||
|
||||
int get partManufacturedCount => getInt("parts_manufactured");
|
||||
|
||||
// Request a list of purchase orders against this company
|
||||
Future<List<InvenTreePurchaseOrder>> getPurchaseOrders({bool? outstanding}) async {
|
||||
|
||||
Map<String, String> filters = {
|
||||
"supplier": "${pk}"
|
||||
};
|
||||
int get partManufacturedCount => getInt("parts_manufactured");
|
||||
|
||||
// Request a list of purchase orders against this company
|
||||
Future<List<InvenTreePurchaseOrder>> getPurchaseOrders({
|
||||
bool? outstanding,
|
||||
}) async {
|
||||
Map<String, String> filters = {"supplier": "${pk}"};
|
||||
|
||||
if (outstanding != null) {
|
||||
filters["outstanding"] = outstanding ? "true" : "false";
|
||||
}
|
||||
|
||||
final List<InvenTreeModel> results = await InvenTreePurchaseOrder().list(
|
||||
filters: filters
|
||||
filters: filters,
|
||||
);
|
||||
|
||||
List<InvenTreePurchaseOrder> orders = [];
|
||||
@ -103,18 +106,18 @@ class InvenTreeCompany extends InvenTreeModel {
|
||||
}
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeCompany.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreeCompany.fromJson(json);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class representing an attachment file against a Company object
|
||||
*/
|
||||
class InvenTreeCompanyAttachment extends InvenTreeAttachment {
|
||||
|
||||
InvenTreeCompanyAttachment() : super();
|
||||
|
||||
InvenTreeCompanyAttachment.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreeCompanyAttachment.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
String get REFERENCE_FIELD => "company";
|
||||
@ -123,21 +126,23 @@ class InvenTreeCompanyAttachment extends InvenTreeAttachment {
|
||||
String get REF_MODEL_TYPE => "company";
|
||||
|
||||
@override
|
||||
String get URL => InvenTreeAPI().supportsModernAttachments ? "attachment/" : "company/attachment/";
|
||||
String get URL => InvenTreeAPI().supportsModernAttachments
|
||||
? "attachment/"
|
||||
: "company/attachment/";
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeCompanyAttachment.fromJson(json);
|
||||
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreeCompanyAttachment.fromJson(json);
|
||||
}
|
||||
|
||||
/*
|
||||
* The InvenTreeSupplierPart class represents the SupplierPart model in the InvenTree database
|
||||
*/
|
||||
class InvenTreeSupplierPart extends InvenTreeModel {
|
||||
|
||||
InvenTreeSupplierPart() : super();
|
||||
|
||||
InvenTreeSupplierPart.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreeSupplierPart.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
String get URL => "company/part/";
|
||||
@ -180,37 +185,47 @@ class InvenTreeSupplierPart extends InvenTreeModel {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
int get manufacturerId => getInt("pk", subKey: "manufacturer_detail");
|
||||
|
||||
String get manufacturerName => getString("name", subKey: "manufacturer_detail");
|
||||
|
||||
|
||||
String get manufacturerName =>
|
||||
getString("name", subKey: "manufacturer_detail");
|
||||
|
||||
String get MPN => getString("MPN", subKey: "manufacturer_part_detail");
|
||||
|
||||
String get manufacturerImage => (jsondata["manufacturer_detail"]?["image"] ?? jsondata["manufacturer_detail"]?["thumbnail"] ?? InvenTreeAPI.staticThumb) as String;
|
||||
|
||||
String get manufacturerImage =>
|
||||
(jsondata["manufacturer_detail"]?["image"] ??
|
||||
jsondata["manufacturer_detail"]?["thumbnail"] ??
|
||||
InvenTreeAPI.staticThumb)
|
||||
as String;
|
||||
|
||||
int get manufacturerPartId => getInt("manufacturer_part");
|
||||
|
||||
|
||||
int get supplierId => getInt("supplier");
|
||||
|
||||
|
||||
String get supplierName => getString("name", subKey: "supplier_detail");
|
||||
|
||||
String get supplierImage => (jsondata["supplier_detail"]?["image"] ?? jsondata["supplier_detail"]?["thumbnail"] ?? InvenTreeAPI.staticThumb) as String;
|
||||
|
||||
String get supplierImage =>
|
||||
(jsondata["supplier_detail"]?["image"] ??
|
||||
jsondata["supplier_detail"]?["thumbnail"] ??
|
||||
InvenTreeAPI.staticThumb)
|
||||
as String;
|
||||
|
||||
String get SKU => getString("SKU");
|
||||
|
||||
bool get active => getBool("active", backup: true);
|
||||
|
||||
|
||||
int get partId => getInt("part");
|
||||
|
||||
String get partImage => (jsondata["part_detail"]?["thumbnail"] ?? InvenTreeAPI.staticThumb) as String;
|
||||
String get partImage =>
|
||||
(jsondata["part_detail"]?["thumbnail"] ?? InvenTreeAPI.staticThumb)
|
||||
as String;
|
||||
|
||||
String get partName => getString("name", subKey: "part_detail");
|
||||
|
||||
Map<String, dynamic> get partDetail => getMap("part_detail");
|
||||
|
||||
String get partDescription => getString("description", subKey: "part_detail");
|
||||
|
||||
|
||||
String get note => getString("note");
|
||||
|
||||
String get packaging => getString("packaging");
|
||||
@ -224,15 +239,15 @@ class InvenTreeSupplierPart extends InvenTreeModel {
|
||||
}
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeSupplierPart.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreeSupplierPart.fromJson(json);
|
||||
}
|
||||
|
||||
|
||||
class InvenTreeManufacturerPart extends InvenTreeModel {
|
||||
|
||||
InvenTreeManufacturerPart() : super();
|
||||
|
||||
InvenTreeManufacturerPart.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreeManufacturerPart.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
String URL = "company/part/manufacturer/";
|
||||
@ -255,10 +270,7 @@ class InvenTreeManufacturerPart extends InvenTreeModel {
|
||||
|
||||
@override
|
||||
Map<String, String> defaultFilters() {
|
||||
return {
|
||||
"manufacturer_detail": "true",
|
||||
"part_detail": "true",
|
||||
};
|
||||
return {"manufacturer_detail": "true", "part_detail": "true"};
|
||||
}
|
||||
|
||||
int get partId => getInt("part");
|
||||
@ -269,18 +281,27 @@ class InvenTreeManufacturerPart extends InvenTreeModel {
|
||||
|
||||
String get partIPN => getString("IPN", subKey: "part_detail");
|
||||
|
||||
String get partImage => (jsondata["part_detail"]?["thumbnail"] ?? InvenTreeAPI.staticThumb) as String;
|
||||
String get partImage =>
|
||||
(jsondata["part_detail"]?["thumbnail"] ?? InvenTreeAPI.staticThumb)
|
||||
as String;
|
||||
|
||||
int get manufacturerId => getInt("manufacturer");
|
||||
|
||||
String get manufacturerName => getString("name", subKey: "manufacturer_detail");
|
||||
String get manufacturerName =>
|
||||
getString("name", subKey: "manufacturer_detail");
|
||||
|
||||
String get manufacturerDescription => getString("description", subKey: "manufacturer_detail");
|
||||
String get manufacturerDescription =>
|
||||
getString("description", subKey: "manufacturer_detail");
|
||||
|
||||
String get manufacturerImage => (jsondata["manufacturer_detail"]?["image"] ?? jsondata["manufacturer_detail"]?["thumbnail"] ?? InvenTreeAPI.staticThumb) as String;
|
||||
String get manufacturerImage =>
|
||||
(jsondata["manufacturer_detail"]?["image"] ??
|
||||
jsondata["manufacturer_detail"]?["thumbnail"] ??
|
||||
InvenTreeAPI.staticThumb)
|
||||
as String;
|
||||
|
||||
String get MPN => getString("MPN");
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeManufacturerPart.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreeManufacturerPart.fromJson(json);
|
||||
}
|
||||
|
@ -17,10 +17,8 @@ import "package:inventree/inventree/sentry.dart";
|
||||
import "package:inventree/widget/dialogs.dart";
|
||||
import "package:inventree/widget/fields.dart";
|
||||
|
||||
|
||||
// Paginated response object
|
||||
class InvenTreePageResponse {
|
||||
|
||||
InvenTreePageResponse() {
|
||||
results = [];
|
||||
}
|
||||
@ -31,7 +29,7 @@ class InvenTreePageResponse {
|
||||
|
||||
// Total number of results in the dataset
|
||||
int count = 0;
|
||||
|
||||
|
||||
int get length => results.length;
|
||||
|
||||
List<InvenTreeModel> results = [];
|
||||
@ -42,7 +40,6 @@ class InvenTreePageResponse {
|
||||
* for interacting with InvenTree data.
|
||||
*/
|
||||
class InvenTreeModel {
|
||||
|
||||
InvenTreeModel();
|
||||
|
||||
// Construct an InvenTreeModel from a JSON data object
|
||||
@ -87,7 +84,6 @@ class InvenTreeModel {
|
||||
|
||||
// If a subKey is specified, we need to dig deeper into the JSON data
|
||||
if (subKey.isNotEmpty) {
|
||||
|
||||
if (!data.containsKey(subKey)) {
|
||||
debug("JSON data does not contain subKey '$subKey' for key '$key'");
|
||||
return backup;
|
||||
@ -98,7 +94,6 @@ class InvenTreeModel {
|
||||
if (sub_data is Map<String, dynamic>) {
|
||||
data = (data[subKey] ?? {}) as Map<String, dynamic>;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (data.containsKey(key)) {
|
||||
@ -109,7 +104,11 @@ class InvenTreeModel {
|
||||
}
|
||||
|
||||
// Helper function to get sub-map from JSON data
|
||||
Map<String, dynamic> getMap(String key, {Map<String, dynamic> backup = const {}, String subKey = ""}) {
|
||||
Map<String, dynamic> getMap(
|
||||
String key, {
|
||||
Map<String, dynamic> backup = const {},
|
||||
String subKey = "",
|
||||
}) {
|
||||
dynamic value = getValue(key, backup: backup, subKey: subKey);
|
||||
|
||||
if (value == null) {
|
||||
@ -152,7 +151,7 @@ class InvenTreeModel {
|
||||
return double.tryParse(value.toString()) ?? backup;
|
||||
}
|
||||
|
||||
double getDouble(String key, {double backup = 0.0, String subkey = "" }) {
|
||||
double getDouble(String key, {double backup = 0.0, String subkey = ""}) {
|
||||
double? value = getDoubleOrNull(key, backup: backup, subKey: subkey);
|
||||
return value ?? backup;
|
||||
}
|
||||
@ -194,7 +193,6 @@ class InvenTreeModel {
|
||||
|
||||
// Return the InvenTree web server URL for this object
|
||||
String get webUrl {
|
||||
|
||||
if (api.isConnected()) {
|
||||
String web = InvenTreeAPI().baseUrl;
|
||||
|
||||
@ -205,7 +203,6 @@ class InvenTreeModel {
|
||||
web = web.replaceAll("//", "/");
|
||||
|
||||
return web;
|
||||
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
@ -216,7 +213,9 @@ class InvenTreeModel {
|
||||
*/
|
||||
List<String> get rolesRequired {
|
||||
// Default implementation should not be called
|
||||
debug("rolesRequired() not implemented for model ${URL} - returning empty list");
|
||||
debug(
|
||||
"rolesRequired() not implemented for model ${URL} - returning empty list",
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -271,12 +270,17 @@ class InvenTreeModel {
|
||||
// Fields for editing / creating this model
|
||||
// Override per-model
|
||||
Map<String, Map<String, dynamic>> formFields() {
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Future<void> createForm(BuildContext context, String title, {String fileField = "", Map<String, dynamic> fields=const{}, Map<String, dynamic> data=const {}, Function(dynamic)? onSuccess}) async {
|
||||
|
||||
Future<void> createForm(
|
||||
BuildContext context,
|
||||
String title, {
|
||||
String fileField = "",
|
||||
Map<String, dynamic> fields = const {},
|
||||
Map<String, dynamic> data = const {},
|
||||
Function(dynamic)? onSuccess,
|
||||
}) async {
|
||||
if (fields.isEmpty) {
|
||||
fields = formFields();
|
||||
}
|
||||
@ -291,14 +295,17 @@ class InvenTreeModel {
|
||||
method: "POST",
|
||||
fileField: fileField,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Launch a modal form to edit the fields available to this model instance.
|
||||
*/
|
||||
Future<void> editForm(BuildContext context, String title, {Map<String, dynamic> fields=const {}, Function(dynamic)? onSuccess}) async {
|
||||
|
||||
Future<void> editForm(
|
||||
BuildContext context,
|
||||
String title, {
|
||||
Map<String, dynamic> fields = const {},
|
||||
Function(dynamic)? onSuccess,
|
||||
}) async {
|
||||
if (fields.isEmpty) {
|
||||
fields = formFields();
|
||||
}
|
||||
@ -310,9 +317,8 @@ class InvenTreeModel {
|
||||
fields,
|
||||
modelData: jsondata,
|
||||
onSuccess: onSuccess,
|
||||
method: "PATCH"
|
||||
method: "PATCH",
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
// JSON data which defines this object
|
||||
@ -324,12 +330,12 @@ class InvenTreeModel {
|
||||
int get pk => getInt("pk");
|
||||
|
||||
String get pkString => pk.toString();
|
||||
|
||||
|
||||
// Some common accessors
|
||||
String get name => getString("name");
|
||||
|
||||
String get description => getString("description");
|
||||
|
||||
|
||||
String get notes => getString("notes");
|
||||
|
||||
int get parentId => getInt("parent");
|
||||
@ -387,8 +393,7 @@ class InvenTreeModel {
|
||||
return "";
|
||||
}
|
||||
|
||||
Future <void> goToInvenTreePage() async {
|
||||
|
||||
Future<void> goToInvenTreePage() async {
|
||||
var uri = Uri.tryParse(webUrl);
|
||||
if (uri != null && await canLaunchUrl(uri)) {
|
||||
await launchUrl(uri);
|
||||
@ -397,8 +402,7 @@ class InvenTreeModel {
|
||||
}
|
||||
}
|
||||
|
||||
Future <void> openLink() async {
|
||||
|
||||
Future<void> openLink() async {
|
||||
if (link.isNotEmpty) {
|
||||
var uri = Uri.tryParse(link);
|
||||
if (uri != null && await canLaunchUrl(uri)) {
|
||||
@ -408,16 +412,21 @@ class InvenTreeModel {
|
||||
}
|
||||
|
||||
String get keywords => getString("keywords");
|
||||
|
||||
|
||||
// Create a new object from JSON data (not a constructor!)
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeModel.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreeModel.fromJson(json);
|
||||
|
||||
// Return the API detail endpoint for this Model object
|
||||
String get url => "${URL}/${pk}/".replaceAll("//", "/");
|
||||
|
||||
// Search this Model type in the database
|
||||
Future<List<InvenTreeModel>> search(String searchTerm, {Map<String, String> filters = const {}, int offset = 0, int limit = 25}) async {
|
||||
|
||||
Future<List<InvenTreeModel>> search(
|
||||
String searchTerm, {
|
||||
Map<String, String> filters = const {},
|
||||
int offset = 0,
|
||||
int limit = 25,
|
||||
}) async {
|
||||
Map<String, String> searchFilters = {};
|
||||
|
||||
for (String key in filters.keys) {
|
||||
@ -431,12 +440,13 @@ class InvenTreeModel {
|
||||
final results = list(filters: searchFilters);
|
||||
|
||||
return results;
|
||||
|
||||
}
|
||||
|
||||
// Return the number of results that would meet a particular "query"
|
||||
Future<int> count({Map<String, String> filters = const {}, String searchQuery = ""} ) async {
|
||||
|
||||
Future<int> count({
|
||||
Map<String, String> filters = const {},
|
||||
String searchQuery = "",
|
||||
}) async {
|
||||
var params = defaultListFilters();
|
||||
|
||||
filters.forEach((String key, String value) {
|
||||
@ -458,7 +468,7 @@ class InvenTreeModel {
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, String> defaultFilters() {
|
||||
return {};
|
||||
@ -476,8 +486,11 @@ class InvenTreeModel {
|
||||
/*
|
||||
* Report error information to sentry, when a model operation fails.
|
||||
*/
|
||||
Future<void> reportModelError(String title, APIResponse response, {Map<String, String> context = const {}}) async {
|
||||
|
||||
Future<void> reportModelError(
|
||||
String title,
|
||||
APIResponse response, {
|
||||
Map<String, String> context = const {},
|
||||
}) async {
|
||||
String dataString = response.data?.toString() ?? "null";
|
||||
|
||||
// If the response has "errorDetail" set, then the error has already been handled, and there is no need to continue
|
||||
@ -506,16 +519,12 @@ class InvenTreeModel {
|
||||
context["dataType"] = response.data?.runtimeType.toString() ?? "null";
|
||||
context["model"] = URL;
|
||||
|
||||
await sentryReportMessage(
|
||||
title,
|
||||
context: context,
|
||||
);
|
||||
await sentryReportMessage(title, context: context);
|
||||
}
|
||||
|
||||
/// Delete the instance on the remote server
|
||||
/// Returns true if the operation was successful, else false
|
||||
Future<bool> delete() async {
|
||||
|
||||
// Return if we do not have a valid pk
|
||||
if (pk < 0) {
|
||||
return false;
|
||||
@ -523,18 +532,15 @@ class InvenTreeModel {
|
||||
|
||||
var response = await api.delete(url);
|
||||
|
||||
if (!response.isValid() || response.data == null || (response.data is! Map)) {
|
||||
|
||||
if (!response.isValid() ||
|
||||
response.data == null ||
|
||||
(response.data is! Map)) {
|
||||
reportModelError(
|
||||
"InvenTreeModel.delete() returned invalid response",
|
||||
response,
|
||||
);
|
||||
|
||||
showServerError(
|
||||
url,
|
||||
L10().serverError,
|
||||
L10().errorDelete,
|
||||
);
|
||||
showServerError(url, L10().serverError, L10().errorDelete);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -547,52 +553,40 @@ class InvenTreeModel {
|
||||
* Reload this object, by requesting data from the server
|
||||
*/
|
||||
Future<bool> reload() async {
|
||||
|
||||
// If we do not have a valid pk (for some reason), exit immediately
|
||||
if (pk < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var response = await api.get(url, params: defaultGetFilters(), expectedStatusCode: 200);
|
||||
var response = await api.get(
|
||||
url,
|
||||
params: defaultGetFilters(),
|
||||
expectedStatusCode: 200,
|
||||
);
|
||||
|
||||
// A valid response has been returned
|
||||
if (response.isValid() && response.statusCode == 200) {
|
||||
|
||||
// Returned data was not a valid JSON object
|
||||
if (response.data == null || response.data is! Map) {
|
||||
reportModelError(
|
||||
"InvenTreeModel.reload() returned invalid response",
|
||||
response,
|
||||
context: {
|
||||
"pk": pk.toString(),
|
||||
}
|
||||
"InvenTreeModel.reload() returned invalid response",
|
||||
response,
|
||||
context: {"pk": pk.toString()},
|
||||
);
|
||||
|
||||
showServerError(
|
||||
url,
|
||||
L10().serverError,
|
||||
L10().responseInvalid,
|
||||
);
|
||||
showServerError(url, L10().serverError, L10().responseInvalid);
|
||||
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
||||
switch (response.statusCode) {
|
||||
case 404: // Object has been deleted
|
||||
showSnackIcon(
|
||||
L10().itemDeleted,
|
||||
success: false,
|
||||
);
|
||||
showSnackIcon(L10().itemDeleted, success: false);
|
||||
default:
|
||||
String detail = L10().errorFetch;
|
||||
detail += "\n${L10().statusCode}: ${response.statusCode}";
|
||||
|
||||
showServerError(
|
||||
url,
|
||||
L10().serverError,
|
||||
detail
|
||||
);
|
||||
showServerError(url, L10().serverError, detail);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -606,15 +600,15 @@ class InvenTreeModel {
|
||||
}
|
||||
|
||||
// POST data to update the model
|
||||
Future<APIResponse> update({Map<String, String> values = const {}, int? expectedStatusCode = 200}) async {
|
||||
|
||||
Future<APIResponse> update({
|
||||
Map<String, String> values = const {},
|
||||
int? expectedStatusCode = 200,
|
||||
}) async {
|
||||
var url = path.join(URL, pk.toString());
|
||||
|
||||
// Return if we do not have a valid pk
|
||||
if (pk < 0) {
|
||||
return APIResponse(
|
||||
url: url,
|
||||
);
|
||||
return APIResponse(url: url);
|
||||
}
|
||||
|
||||
if (!url.endsWith("/")) {
|
||||
@ -631,8 +625,10 @@ class InvenTreeModel {
|
||||
}
|
||||
|
||||
// Return the detail view for the associated pk
|
||||
Future<InvenTreeModel?> getModel(String pk, {Map<String, String> filters = const {}}) async {
|
||||
|
||||
Future<InvenTreeModel?> getModel(
|
||||
String pk, {
|
||||
Map<String, String> filters = const {},
|
||||
}) async {
|
||||
var url = path.join(URL, pk.toString());
|
||||
|
||||
if (!url.endsWith("/")) {
|
||||
@ -649,27 +645,18 @@ class InvenTreeModel {
|
||||
var response = await api.get(url, params: params);
|
||||
|
||||
if (!response.isValid() || response.data == null || response.data is! Map) {
|
||||
|
||||
if (response.statusCode != -1) {
|
||||
// Report error
|
||||
reportModelError(
|
||||
"InvenTreeModel.getModel() returned invalid response",
|
||||
response,
|
||||
context: {
|
||||
"filters": filters.toString(),
|
||||
"pk": pk,
|
||||
}
|
||||
"InvenTreeModel.getModel() returned invalid response",
|
||||
response,
|
||||
context: {"filters": filters.toString(), "pk": pk},
|
||||
);
|
||||
}
|
||||
|
||||
showServerError(
|
||||
url,
|
||||
L10().serverError,
|
||||
L10().errorFetch,
|
||||
);
|
||||
showServerError(url, L10().serverError, L10().errorFetch);
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
lastReload = DateTime.now();
|
||||
@ -677,8 +664,10 @@ class InvenTreeModel {
|
||||
return createFromJson(response.asMap());
|
||||
}
|
||||
|
||||
Future<InvenTreeModel?> get(int pk, {Map<String, String> filters = const {}}) async {
|
||||
|
||||
Future<InvenTreeModel?> get(
|
||||
int pk, {
|
||||
Map<String, String> filters = const {},
|
||||
}) async {
|
||||
if (pk < 0) {
|
||||
return null;
|
||||
}
|
||||
@ -687,7 +676,6 @@ class InvenTreeModel {
|
||||
}
|
||||
|
||||
Future<InvenTreeModel?> create(Map<String, dynamic> data) async {
|
||||
|
||||
if (data.containsKey("pk")) {
|
||||
data.remove("pk");
|
||||
}
|
||||
@ -700,20 +688,13 @@ class InvenTreeModel {
|
||||
|
||||
// Invalid response returned from server
|
||||
if (!response.isValid() || response.data == null || response.data is! Map) {
|
||||
|
||||
reportModelError(
|
||||
"InvenTreeModel.create() returned invalid response",
|
||||
response,
|
||||
context: {
|
||||
"pk": pk.toString(),
|
||||
}
|
||||
"InvenTreeModel.create() returned invalid response",
|
||||
response,
|
||||
context: {"pk": pk.toString()},
|
||||
);
|
||||
|
||||
showServerError(
|
||||
URL,
|
||||
L10().serverError,
|
||||
L10().errorCreate,
|
||||
);
|
||||
showServerError(URL, L10().serverError, L10().errorCreate);
|
||||
|
||||
return null;
|
||||
}
|
||||
@ -721,7 +702,11 @@ class InvenTreeModel {
|
||||
return createFromJson(response.asMap());
|
||||
}
|
||||
|
||||
Future<InvenTreePageResponse?> listPaginated(int limit, int offset, {Map<String, String> filters = const {}}) async {
|
||||
Future<InvenTreePageResponse?> listPaginated(
|
||||
int limit,
|
||||
int offset, {
|
||||
Map<String, String> filters = const {},
|
||||
}) async {
|
||||
var params = defaultListFilters();
|
||||
|
||||
for (String key in filters.keys) {
|
||||
@ -737,7 +722,6 @@ class InvenTreeModel {
|
||||
* - In such a case, we want to concatenate them together
|
||||
*/
|
||||
if (params.containsKey("original_search")) {
|
||||
|
||||
String search = params["search"] ?? "";
|
||||
String original = params["original_search"] ?? "";
|
||||
|
||||
@ -759,18 +743,20 @@ class InvenTreeModel {
|
||||
|
||||
// First attempt is to look for paginated data, returned as a map
|
||||
|
||||
if (dataMap.isNotEmpty && dataMap.containsKey("count") && dataMap.containsKey("results")) {
|
||||
if (dataMap.isNotEmpty &&
|
||||
dataMap.containsKey("count") &&
|
||||
dataMap.containsKey("results")) {
|
||||
page.count = (dataMap["count"] ?? 0) as int;
|
||||
|
||||
page.results = [];
|
||||
page.results = [];
|
||||
|
||||
List<dynamic> results = dataMap["results"] as List<dynamic>;
|
||||
List<dynamic> results = dataMap["results"] as List<dynamic>;
|
||||
|
||||
for (dynamic result in results) {
|
||||
page.addResult(createFromJson(result as Map<String, dynamic>));
|
||||
}
|
||||
for (dynamic result in results) {
|
||||
page.addResult(createFromJson(result as Map<String, dynamic>));
|
||||
}
|
||||
|
||||
return page;
|
||||
return page;
|
||||
}
|
||||
|
||||
// Second attempt is to look for a list of data (not paginated)
|
||||
@ -782,7 +768,7 @@ class InvenTreeModel {
|
||||
|
||||
for (var result in dataList) {
|
||||
page.addResult(createFromJson(result as Map<String, dynamic>));
|
||||
}
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
@ -792,7 +778,9 @@ class InvenTreeModel {
|
||||
}
|
||||
|
||||
// Return list of objects from the database, with optional filters
|
||||
Future<List<InvenTreeModel>> list({Map<String, String> filters = const {}}) async {
|
||||
Future<List<InvenTreeModel>> list({
|
||||
Map<String, String> filters = const {},
|
||||
}) async {
|
||||
var params = defaultListFilters();
|
||||
|
||||
for (String key in filters.keys) {
|
||||
@ -821,7 +809,6 @@ class InvenTreeModel {
|
||||
}
|
||||
|
||||
for (var d in data) {
|
||||
|
||||
// Create a new object (of the current class type
|
||||
InvenTreeModel obj = createFromJson(d as Map<String, dynamic>);
|
||||
|
||||
@ -847,7 +834,6 @@ class InvenTreeModel {
|
||||
// Each filter must be matched
|
||||
// Used for (e.g.) filtering returned results
|
||||
bool filter(String filterString) {
|
||||
|
||||
List<String> filters = filterString.trim().toLowerCase().split(" ");
|
||||
|
||||
for (var f in filters) {
|
||||
@ -860,22 +846,20 @@ class InvenTreeModel {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class representing a single plugin instance
|
||||
*/
|
||||
class InvenTreePlugin extends InvenTreeModel {
|
||||
|
||||
InvenTreePlugin() : super();
|
||||
|
||||
InvenTreePlugin.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePlugin.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreePlugin.fromJson(json);
|
||||
|
||||
@override
|
||||
String get URL {
|
||||
|
||||
/* Note: The plugin API endpoint changed at API version 90,
|
||||
* < 90 = 'plugin'
|
||||
* >= 90 = 'plugins'
|
||||
@ -889,23 +873,24 @@ class InvenTreePlugin extends InvenTreeModel {
|
||||
}
|
||||
|
||||
String get key => getString("key");
|
||||
|
||||
|
||||
bool get active => getBool("active");
|
||||
|
||||
|
||||
// Return the metadata struct for this plugin
|
||||
Map<String, dynamic> get _meta => (jsondata["meta"] ?? {}) as Map<String, dynamic>;
|
||||
Map<String, dynamic> get _meta =>
|
||||
(jsondata["meta"] ?? {}) as Map<String, dynamic>;
|
||||
|
||||
String get humanName => (_meta["human_name"] ?? "") as String;
|
||||
|
||||
// Return the mixins struct for this plugin
|
||||
Map<String, dynamic> get _mixins => (jsondata["mixins"] ?? {}) as Map<String, dynamic>;
|
||||
Map<String, dynamic> get _mixins =>
|
||||
(jsondata["mixins"] ?? {}) as Map<String, dynamic>;
|
||||
|
||||
bool supportsMixin(String mixin) {
|
||||
return _mixins.containsKey(mixin);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class representing a 'setting' object on the InvenTree server.
|
||||
* There are two sorts of settings available from the server, via the API:
|
||||
@ -913,10 +898,10 @@ class InvenTreePlugin extends InvenTreeModel {
|
||||
* - UserSetting (applicable only to the current user)
|
||||
*/
|
||||
class InvenTreeGlobalSetting extends InvenTreeModel {
|
||||
|
||||
InvenTreeGlobalSetting() : super();
|
||||
|
||||
InvenTreeGlobalSetting.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreeGlobalSetting.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
InvenTreeGlobalSetting createFromJson(Map<String, dynamic> json) {
|
||||
@ -927,18 +912,17 @@ class InvenTreeGlobalSetting extends InvenTreeModel {
|
||||
String get URL => "settings/global/";
|
||||
|
||||
String get key => getString("key");
|
||||
|
||||
String get value => getString("value");
|
||||
|
||||
String get type => getString("type");
|
||||
|
||||
String get value => getString("value");
|
||||
|
||||
String get type => getString("type");
|
||||
}
|
||||
|
||||
class InvenTreeUserSetting extends InvenTreeGlobalSetting {
|
||||
|
||||
InvenTreeUserSetting() : super();
|
||||
|
||||
InvenTreeUserSetting.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreeUserSetting.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
InvenTreeGlobalSetting createFromJson(Map<String, dynamic> json) {
|
||||
@ -949,22 +933,19 @@ class InvenTreeUserSetting extends InvenTreeGlobalSetting {
|
||||
String get URL => "settings/user/";
|
||||
}
|
||||
|
||||
|
||||
class InvenTreeAttachment extends InvenTreeModel {
|
||||
// Class representing an "attachment" file
|
||||
InvenTreeAttachment() : super();
|
||||
|
||||
InvenTreeAttachment.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreeAttachment.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
String get URL => "attachment/";
|
||||
|
||||
@override
|
||||
Map<String, Map<String, dynamic>> formFields() {
|
||||
Map<String, Map<String, dynamic>> fields = {
|
||||
"link": {},
|
||||
"comment": {}
|
||||
};
|
||||
Map<String, Map<String, dynamic>> fields = {"link": {}, "comment": {}};
|
||||
|
||||
if (!hasLink) {
|
||||
fields.remove("link");
|
||||
@ -1004,13 +985,7 @@ class InvenTreeAttachment extends InvenTreeModel {
|
||||
}
|
||||
|
||||
// Image formats
|
||||
final List<String> img_formats = [
|
||||
".png",
|
||||
".jpg",
|
||||
".gif",
|
||||
".bmp",
|
||||
".svg",
|
||||
];
|
||||
final List<String> img_formats = [".png", ".jpg", ".gif", ".bmp", ".svg"];
|
||||
|
||||
for (String fmt in img_formats) {
|
||||
if (fn.endsWith(fmt)) {
|
||||
@ -1022,7 +997,7 @@ class InvenTreeAttachment extends InvenTreeModel {
|
||||
}
|
||||
|
||||
String get comment => getString("comment");
|
||||
|
||||
|
||||
DateTime? get uploadDate {
|
||||
if (jsondata.containsKey("upload_date")) {
|
||||
return DateTime.tryParse((jsondata["upload_date"] ?? "") as String);
|
||||
@ -1033,7 +1008,6 @@ class InvenTreeAttachment extends InvenTreeModel {
|
||||
|
||||
// Return a count of how many attachments exist against the specified model ID
|
||||
Future<int> countAttachments(int modelId) {
|
||||
|
||||
Map<String, String> filters = {};
|
||||
|
||||
if (InvenTreeAPI().supportsModernAttachments) {
|
||||
@ -1046,8 +1020,12 @@ class InvenTreeAttachment extends InvenTreeModel {
|
||||
return count(filters: filters);
|
||||
}
|
||||
|
||||
Future<bool> uploadAttachment(File attachment, int modelId, {String comment = "", Map<String, String> fields = const {}}) async {
|
||||
|
||||
Future<bool> uploadAttachment(
|
||||
File attachment,
|
||||
int modelId, {
|
||||
String comment = "",
|
||||
Map<String, String> fields = const {},
|
||||
}) async {
|
||||
// Ensure that the correct reference field is set
|
||||
Map<String, String> data = Map<String, String>.from(fields);
|
||||
|
||||
@ -1058,15 +1036,14 @@ class InvenTreeAttachment extends InvenTreeModel {
|
||||
}
|
||||
|
||||
if (InvenTreeAPI().supportsModernAttachments) {
|
||||
|
||||
url = "attachment/";
|
||||
data["model_id"] = modelId.toString();
|
||||
data["model_type"] = REF_MODEL_TYPE;
|
||||
|
||||
} else {
|
||||
|
||||
if (REFERENCE_FIELD.isEmpty) {
|
||||
sentryReportMessage("uploadAttachment called with empty 'REFERENCE_FIELD'");
|
||||
sentryReportMessage(
|
||||
"uploadAttachment called with empty 'REFERENCE_FIELD'",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1074,24 +1051,21 @@ class InvenTreeAttachment extends InvenTreeModel {
|
||||
}
|
||||
|
||||
final APIResponse response = await InvenTreeAPI().uploadFile(
|
||||
url,
|
||||
attachment,
|
||||
method: "POST",
|
||||
name: "attachment",
|
||||
fields: data
|
||||
url,
|
||||
attachment,
|
||||
method: "POST",
|
||||
name: "attachment",
|
||||
fields: data,
|
||||
);
|
||||
|
||||
return response.successful();
|
||||
}
|
||||
|
||||
|
||||
Future<bool> uploadImage(int modelId, {String prefix = "InvenTree"}) async {
|
||||
|
||||
bool result = false;
|
||||
|
||||
await FilePickerDialog.pickImageFromCamera().then((File? file) {
|
||||
if (file != null) {
|
||||
|
||||
String dir = path.dirname(file.path);
|
||||
String ext = path.extension(file.path);
|
||||
String now = DateTime.now().toIso8601String().replaceAll(":", "-");
|
||||
@ -1104,8 +1078,9 @@ class InvenTreeAttachment extends InvenTreeModel {
|
||||
uploadAttachment(renamed, modelId).then((success) {
|
||||
result = success;
|
||||
showSnackIcon(
|
||||
result ? L10().imageUploadSuccess : L10().imageUploadFailure,
|
||||
success: result);
|
||||
result ? L10().imageUploadSuccess : L10().imageUploadFailure,
|
||||
success: result,
|
||||
);
|
||||
});
|
||||
});
|
||||
} catch (error, stackTrace) {
|
||||
@ -1118,14 +1093,10 @@ class InvenTreeAttachment extends InvenTreeModel {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Download this attachment file
|
||||
*/
|
||||
Future<void> downloadAttachment() async {
|
||||
await InvenTreeAPI().downloadFile(attachment);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -5,10 +5,10 @@ import "package:inventree/inventree/model.dart";
|
||||
*/
|
||||
|
||||
class InvenTreeNotification extends InvenTreeModel {
|
||||
|
||||
InvenTreeNotification() : super();
|
||||
|
||||
InvenTreeNotification.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreeNotification.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
InvenTreeNotification createFromJson(Map<String, dynamic> json) {
|
||||
@ -20,15 +20,12 @@ class InvenTreeNotification extends InvenTreeModel {
|
||||
|
||||
@override
|
||||
Map<String, String> defaultListFilters() {
|
||||
|
||||
// By default, only return 'unread' notifications
|
||||
return {
|
||||
"read": "false",
|
||||
};
|
||||
return {"read": "false"};
|
||||
}
|
||||
|
||||
String get message => getString("message");
|
||||
|
||||
|
||||
DateTime? get creationDate {
|
||||
if (jsondata.containsKey("creation")) {
|
||||
return DateTime.tryParse((jsondata["creation"] ?? "") as String);
|
||||
@ -41,7 +38,6 @@ class InvenTreeNotification extends InvenTreeModel {
|
||||
* Dismiss this notification (mark as read)
|
||||
*/
|
||||
Future<void> dismiss() async {
|
||||
|
||||
if (api.apiVersion >= 82) {
|
||||
// "Modern" API endpoint operates a little differently
|
||||
await update(values: {"read": "true"});
|
||||
@ -49,5 +45,4 @@ class InvenTreeNotification extends InvenTreeModel {
|
||||
await api.post("${url}read/");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -2,16 +2,13 @@
|
||||
* Base model for various "orders" which share common properties
|
||||
*/
|
||||
|
||||
|
||||
import "package:inventree/inventree/model.dart";
|
||||
import "package:inventree/inventree/part.dart";
|
||||
|
||||
|
||||
/*
|
||||
* Generic class representing an "order"
|
||||
*/
|
||||
class InvenTreeOrder extends InvenTreeModel {
|
||||
|
||||
InvenTreeOrder() : super();
|
||||
|
||||
InvenTreeOrder.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
@ -34,7 +31,8 @@ class InvenTreeOrder extends InvenTreeModel {
|
||||
|
||||
int get shipmentCount => getInt("shipments_count", backup: 0);
|
||||
|
||||
int get completedShipmentCount => getInt("completed_shipments_count", backup: 0);
|
||||
int get completedShipmentCount =>
|
||||
getInt("completed_shipments_count", backup: 0);
|
||||
|
||||
bool get complete => completedLineItemCount >= lineItemCount;
|
||||
|
||||
@ -46,14 +44,16 @@ class InvenTreeOrder extends InvenTreeModel {
|
||||
|
||||
String get responsibleName => getString("name", subKey: "responsible_detail");
|
||||
|
||||
String get responsibleLabel => getString("label", subKey: "responsible_detail");
|
||||
String get responsibleLabel =>
|
||||
getString("label", subKey: "responsible_detail");
|
||||
|
||||
// Project code information
|
||||
int get projectCodeId => getInt("project_code");
|
||||
|
||||
String get projectCode => getString("code", subKey: "project_code_detail");
|
||||
|
||||
String get projectCodeDescription => getString("description", subKey: "project_code_detail");
|
||||
String get projectCodeDescription =>
|
||||
getString("description", subKey: "project_code_detail");
|
||||
|
||||
bool get hasProjectCode => projectCode.isNotEmpty;
|
||||
|
||||
@ -84,12 +84,10 @@ class InvenTreeOrder extends InvenTreeModel {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Generic class representing an "order line"
|
||||
*/
|
||||
class InvenTreeOrderLine extends InvenTreeModel {
|
||||
|
||||
InvenTreeOrderLine() : super();
|
||||
|
||||
InvenTreeOrderLine.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
@ -121,15 +119,14 @@ class InvenTreeOrderLine extends InvenTreeModel {
|
||||
String get targetDate => getDateString("target_date");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Generic class representing an "ExtraLineItem"
|
||||
*/
|
||||
class InvenTreeExtraLineItem extends InvenTreeModel {
|
||||
|
||||
InvenTreeExtraLineItem() : super();
|
||||
|
||||
InvenTreeExtraLineItem.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreeExtraLineItem.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
int get orderId => getInt("order");
|
||||
|
||||
@ -157,5 +154,4 @@ class InvenTreeExtraLineItem extends InvenTreeModel {
|
||||
"notes": {},
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -14,15 +14,14 @@ import "package:inventree/inventree/model.dart";
|
||||
import "package:inventree/widget/part/category_display.dart";
|
||||
import "package:inventree/widget/part/part_detail.dart";
|
||||
|
||||
|
||||
/*
|
||||
* Class representing the PartCategory database model
|
||||
*/
|
||||
class InvenTreePartCategory extends InvenTreeModel {
|
||||
|
||||
InvenTreePartCategory() : super();
|
||||
|
||||
InvenTreePartCategory.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreePartCategory.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
String get URL => "part/category/";
|
||||
@ -37,16 +36,13 @@ class InvenTreePartCategory extends InvenTreeModel {
|
||||
Future<Object?> goToDetailPage(BuildContext context) async {
|
||||
// Default implementation does not do anything...
|
||||
return Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CategoryDisplayWidget(this)
|
||||
)
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => CategoryDisplayWidget(this)),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Map<String, dynamic>> formFields() {
|
||||
|
||||
Map<String, Map<String, dynamic>> fields = {
|
||||
"name": {},
|
||||
"description": {},
|
||||
@ -58,9 +54,8 @@ class InvenTreePartCategory extends InvenTreeModel {
|
||||
}
|
||||
|
||||
String get pathstring => getString("pathstring");
|
||||
|
||||
String get parentPathString {
|
||||
|
||||
String get parentPathString {
|
||||
List<String> psplit = pathstring.split("/");
|
||||
|
||||
if (psplit.isNotEmpty) {
|
||||
@ -78,21 +73,22 @@ class InvenTreePartCategory extends InvenTreeModel {
|
||||
|
||||
// Return the number of parts in this category
|
||||
// Note that the API changed from 'parts' to 'part_count' (v69)
|
||||
int get partcount => (jsondata["part_count"] ?? jsondata["parts"] ?? 0) as int;
|
||||
int get partcount =>
|
||||
(jsondata["part_count"] ?? jsondata["parts"] ?? 0) as int;
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePartCategory.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreePartCategory.fromJson(json);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class representing the PartTestTemplate database model
|
||||
*/
|
||||
class InvenTreePartTestTemplate extends InvenTreeModel {
|
||||
|
||||
InvenTreePartTestTemplate() : super();
|
||||
|
||||
InvenTreePartTestTemplate.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreePartTestTemplate.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
String get URL => "part/test-template/";
|
||||
@ -104,16 +100,16 @@ class InvenTreePartTestTemplate extends InvenTreeModel {
|
||||
String get testName => getString("test_name");
|
||||
|
||||
bool get required => getBool("required");
|
||||
|
||||
|
||||
bool get requiresValue => getBool("requires_value");
|
||||
|
||||
bool get requiresAttachment => getBool("requires_attachment");
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePartTestTemplate.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreePartTestTemplate.fromJson(json);
|
||||
|
||||
bool passFailStatus() {
|
||||
|
||||
var result = latestResult();
|
||||
|
||||
if (result == null) {
|
||||
@ -134,17 +130,16 @@ class InvenTreePartTestTemplate extends InvenTreeModel {
|
||||
|
||||
return results.last;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Class representing the PartParameter database model
|
||||
*/
|
||||
class InvenTreePartParameter extends InvenTreeModel {
|
||||
|
||||
InvenTreePartParameter() : super();
|
||||
|
||||
InvenTreePartParameter.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreePartParameter.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
String get URL => "part/parameter/";
|
||||
@ -153,11 +148,11 @@ class InvenTreePartParameter extends InvenTreeModel {
|
||||
List<String> get rolesRequired => ["part"];
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePartParameter.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreePartParameter.fromJson(json);
|
||||
|
||||
@override
|
||||
Map<String, Map<String, dynamic>> formFields() {
|
||||
|
||||
Map<String, Map<String, dynamic>> fields = {
|
||||
"header": {
|
||||
"type": "string",
|
||||
@ -166,9 +161,7 @@ class InvenTreePartParameter extends InvenTreeModel {
|
||||
"help_text": description,
|
||||
"value": "",
|
||||
},
|
||||
"data": {
|
||||
"type": "string",
|
||||
}
|
||||
"data": {"type": "string"},
|
||||
};
|
||||
|
||||
return fields;
|
||||
@ -179,9 +172,9 @@ class InvenTreePartParameter extends InvenTreeModel {
|
||||
|
||||
@override
|
||||
String get description => getString("description", subKey: "template_detail");
|
||||
|
||||
|
||||
String get value => getString("data");
|
||||
|
||||
|
||||
String get valueString {
|
||||
String v = value;
|
||||
|
||||
@ -196,15 +189,15 @@ class InvenTreePartParameter extends InvenTreeModel {
|
||||
bool get as_bool => value.toLowerCase() == "true";
|
||||
|
||||
String get units => getString("units", subKey: "template_detail");
|
||||
|
||||
bool get is_checkbox => getBool("checkbox", subKey: "template_detail", backup: false);
|
||||
|
||||
bool get is_checkbox =>
|
||||
getBool("checkbox", subKey: "template_detail", backup: false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class representing the Part database model
|
||||
*/
|
||||
class InvenTreePart extends InvenTreeModel {
|
||||
|
||||
InvenTreePart() : super();
|
||||
|
||||
InvenTreePart.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
@ -222,10 +215,8 @@ class InvenTreePart extends InvenTreeModel {
|
||||
Future<Object?> goToDetailPage(BuildContext context) async {
|
||||
// Default implementation does not do anything...
|
||||
return Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PartDetailWidget(this)
|
||||
)
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => PartDetailWidget(this)),
|
||||
);
|
||||
}
|
||||
|
||||
@ -259,9 +250,7 @@ class InvenTreePart extends InvenTreeModel {
|
||||
|
||||
@override
|
||||
Map<String, String> defaultFilters() {
|
||||
return {
|
||||
"category_detail": "true",
|
||||
};
|
||||
return {"category_detail": "true"};
|
||||
}
|
||||
|
||||
// Cached list of stock items
|
||||
@ -270,27 +259,25 @@ class InvenTreePart extends InvenTreeModel {
|
||||
int get stockItemCount => stockItems.length;
|
||||
|
||||
// Request stock items for this part
|
||||
Future<void> getStockItems(BuildContext context, {bool showDialog=false}) async {
|
||||
Future<void> getStockItems(
|
||||
BuildContext context, {
|
||||
bool showDialog = false,
|
||||
}) async {
|
||||
await InvenTreeStockItem()
|
||||
.list(filters: {"part": "${pk}", "in_stock": "true"})
|
||||
.then((var items) {
|
||||
stockItems.clear();
|
||||
|
||||
await InvenTreeStockItem().list(
|
||||
filters: {
|
||||
"part": "${pk}",
|
||||
"in_stock": "true",
|
||||
},
|
||||
).then((var items) {
|
||||
stockItems.clear();
|
||||
|
||||
for (var item in items) {
|
||||
if (item is InvenTreeStockItem) {
|
||||
stockItems.add(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
for (var item in items) {
|
||||
if (item is InvenTreeStockItem) {
|
||||
stockItems.add(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Request pricing data for this part
|
||||
Future<InvenTreePartPricing?> getPricing() async {
|
||||
|
||||
print("REQUEST PRICING FOR: ${pk}");
|
||||
|
||||
try {
|
||||
@ -311,15 +298,13 @@ class InvenTreePart extends InvenTreeModel {
|
||||
}
|
||||
|
||||
int get supplierCount => getInt("suppliers", backup: 0);
|
||||
|
||||
|
||||
// Request supplier parts for this part
|
||||
Future<List<InvenTreeSupplierPart>> getSupplierParts() async {
|
||||
List<InvenTreeSupplierPart> _supplierParts = [];
|
||||
|
||||
final parts = await InvenTreeSupplierPart().list(
|
||||
filters: {
|
||||
"part": "${pk}",
|
||||
}
|
||||
filters: {"part": "${pk}"},
|
||||
);
|
||||
|
||||
for (var result in parts) {
|
||||
@ -338,13 +323,9 @@ class InvenTreePart extends InvenTreeModel {
|
||||
|
||||
// Request test templates from the serve
|
||||
Future<void> getTestTemplates() async {
|
||||
|
||||
InvenTreePartTestTemplate().list(
|
||||
filters: {
|
||||
"part": "${pk}",
|
||||
},
|
||||
).then((var templates) {
|
||||
|
||||
InvenTreePartTestTemplate().list(filters: {"part": "${pk}"}).then((
|
||||
var templates,
|
||||
) {
|
||||
testingTemplates.clear();
|
||||
|
||||
for (var t in templates) {
|
||||
@ -373,12 +354,12 @@ class InvenTreePart extends InvenTreeModel {
|
||||
|
||||
// Get the 'available stock' for this Part
|
||||
double get unallocatedStock {
|
||||
|
||||
double unallocated = 0;
|
||||
|
||||
// Note that the 'available_stock' was not added until API v35
|
||||
if (jsondata.containsKey("unallocated_stock")) {
|
||||
unallocated = double.tryParse(jsondata["unallocated_stock"].toString()) ?? 0;
|
||||
unallocated =
|
||||
double.tryParse(jsondata["unallocated_stock"].toString()) ?? 0;
|
||||
} else {
|
||||
unallocated = inStock;
|
||||
}
|
||||
@ -386,148 +367,150 @@ class InvenTreePart extends InvenTreeModel {
|
||||
return max(0, unallocated);
|
||||
}
|
||||
|
||||
String get unallocatedStockString => simpleNumberString(unallocatedStock);
|
||||
String get unallocatedStockString => simpleNumberString(unallocatedStock);
|
||||
|
||||
String stockString({bool includeUnits = true}) {
|
||||
String q = unallocatedStockString;
|
||||
String stockString({bool includeUnits = true}) {
|
||||
String q = unallocatedStockString;
|
||||
|
||||
if (unallocatedStock != inStock) {
|
||||
q += " / ${inStockString}";
|
||||
}
|
||||
|
||||
if (includeUnits && units.isNotEmpty) {
|
||||
q += " ${units}";
|
||||
}
|
||||
|
||||
return q;
|
||||
if (unallocatedStock != inStock) {
|
||||
q += " / ${inStockString}";
|
||||
}
|
||||
|
||||
String get units => getString("units");
|
||||
|
||||
// Get the ID of the Part that this part is a variant of (or null)
|
||||
int? get variantOf => jsondata["variant_of"] as int?;
|
||||
|
||||
// Get the number of units being build for this Part
|
||||
double get building => getDouble("building");
|
||||
|
||||
// Get the number of BOMs this Part is used in (if it is a component)
|
||||
int get usedInCount => jsondata.containsKey("used_in") ? getInt("used_in", backup: 0) : 0;
|
||||
|
||||
bool get isAssembly => getBool("assembly");
|
||||
|
||||
bool get isComponent => getBool("component");
|
||||
|
||||
bool get isPurchaseable => getBool("purchaseable");
|
||||
|
||||
bool get isSalable => getBool("salable");
|
||||
|
||||
bool get isActive => getBool("active");
|
||||
|
||||
bool get isVirtual => getBool("virtual");
|
||||
|
||||
bool get isTemplate => getBool("is_template");
|
||||
|
||||
bool get isTrackable => getBool("trackable");
|
||||
|
||||
// Get the IPN (internal part number) for the Part instance
|
||||
String get IPN => getString("IPN");
|
||||
|
||||
// Get the revision string for the Part instance
|
||||
String get revision => getString("revision");
|
||||
|
||||
// Get the category ID for the Part instance (or "null" if does not exist)
|
||||
int get categoryId => getInt("category");
|
||||
|
||||
// Get the category name for the Part instance
|
||||
String get categoryName {
|
||||
// Inavlid category ID
|
||||
if (categoryId <= 0) return "";
|
||||
|
||||
if (!jsondata.containsKey("category_detail")) return "";
|
||||
|
||||
return (jsondata["category_detail"]?["name"] ?? "") as String;
|
||||
if (includeUnits && units.isNotEmpty) {
|
||||
q += " ${units}";
|
||||
}
|
||||
|
||||
// Get the category description for the Part instance
|
||||
String get categoryDescription {
|
||||
// Invalid category ID
|
||||
if (categoryId <= 0) return "";
|
||||
return q;
|
||||
}
|
||||
|
||||
if (!jsondata.containsKey("category_detail")) return "";
|
||||
String get units => getString("units");
|
||||
|
||||
return (jsondata["category_detail"]?["description"] ?? "") as String;
|
||||
}
|
||||
// Get the image URL for the Part instance
|
||||
String get _image => getString("image");
|
||||
// Get the ID of the Part that this part is a variant of (or null)
|
||||
int? get variantOf => jsondata["variant_of"] as int?;
|
||||
|
||||
// Get the thumbnail URL for the Part instance
|
||||
String get _thumbnail => getString("thumbnail");
|
||||
// Get the number of units being build for this Part
|
||||
double get building => getDouble("building");
|
||||
|
||||
// Return the fully-qualified name for the Part instance
|
||||
String get fullname {
|
||||
// Get the number of BOMs this Part is used in (if it is a component)
|
||||
int get usedInCount =>
|
||||
jsondata.containsKey("used_in") ? getInt("used_in", backup: 0) : 0;
|
||||
|
||||
String fn = getString("full_name");
|
||||
bool get isAssembly => getBool("assembly");
|
||||
|
||||
if (fn.isNotEmpty) return fn;
|
||||
bool get isComponent => getBool("component");
|
||||
|
||||
List<String> elements = [];
|
||||
bool get isPurchaseable => getBool("purchaseable");
|
||||
|
||||
if (IPN.isNotEmpty) elements.add(IPN);
|
||||
bool get isSalable => getBool("salable");
|
||||
|
||||
elements.add(name);
|
||||
bool get isActive => getBool("active");
|
||||
|
||||
if (revision.isNotEmpty) elements.add(revision);
|
||||
bool get isVirtual => getBool("virtual");
|
||||
|
||||
return elements.join(" | ");
|
||||
}
|
||||
bool get isTemplate => getBool("is_template");
|
||||
|
||||
// Return a path to the image for this Part
|
||||
String get image {
|
||||
// Use thumbnail as a backup
|
||||
String img = _image.isNotEmpty ? _image : _thumbnail;
|
||||
bool get isTrackable => getBool("trackable");
|
||||
|
||||
return img.isNotEmpty ? img : InvenTreeAPI.staticImage;
|
||||
}
|
||||
// Get the IPN (internal part number) for the Part instance
|
||||
String get IPN => getString("IPN");
|
||||
|
||||
// Return a path to the thumbnail for this part
|
||||
String get thumbnail {
|
||||
// Use image as a backup
|
||||
String img = _thumbnail.isNotEmpty ? _thumbnail : _image;
|
||||
// Get the revision string for the Part instance
|
||||
String get revision => getString("revision");
|
||||
|
||||
return img.isNotEmpty ? img : InvenTreeAPI.staticThumb;
|
||||
}
|
||||
// Get the category ID for the Part instance (or "null" if does not exist)
|
||||
int get categoryId => getInt("category");
|
||||
|
||||
Future<bool> uploadImage(File image) async {
|
||||
// Upload file against this part
|
||||
final APIResponse response = await InvenTreeAPI().uploadFile(
|
||||
url,
|
||||
image,
|
||||
method: "PATCH",
|
||||
name: "image",
|
||||
);
|
||||
// Get the category name for the Part instance
|
||||
String get categoryName {
|
||||
// Inavlid category ID
|
||||
if (categoryId <= 0) return "";
|
||||
|
||||
return response.successful();
|
||||
}
|
||||
if (!jsondata.containsKey("category_detail")) return "";
|
||||
|
||||
// Return the "starred" status of this part
|
||||
bool get starred => getBool("starred");
|
||||
return (jsondata["category_detail"]?["name"] ?? "") as String;
|
||||
}
|
||||
|
||||
// Get the category description for the Part instance
|
||||
String get categoryDescription {
|
||||
// Invalid category ID
|
||||
if (categoryId <= 0) return "";
|
||||
|
||||
if (!jsondata.containsKey("category_detail")) return "";
|
||||
|
||||
return (jsondata["category_detail"]?["description"] ?? "") as String;
|
||||
}
|
||||
|
||||
// Get the image URL for the Part instance
|
||||
String get _image => getString("image");
|
||||
|
||||
// Get the thumbnail URL for the Part instance
|
||||
String get _thumbnail => getString("thumbnail");
|
||||
|
||||
// Return the fully-qualified name for the Part instance
|
||||
String get fullname {
|
||||
String fn = getString("full_name");
|
||||
|
||||
if (fn.isNotEmpty) return fn;
|
||||
|
||||
List<String> elements = [];
|
||||
|
||||
if (IPN.isNotEmpty) elements.add(IPN);
|
||||
|
||||
elements.add(name);
|
||||
|
||||
if (revision.isNotEmpty) elements.add(revision);
|
||||
|
||||
return elements.join(" | ");
|
||||
}
|
||||
|
||||
// Return a path to the image for this Part
|
||||
String get image {
|
||||
// Use thumbnail as a backup
|
||||
String img = _image.isNotEmpty ? _image : _thumbnail;
|
||||
|
||||
return img.isNotEmpty ? img : InvenTreeAPI.staticImage;
|
||||
}
|
||||
|
||||
// Return a path to the thumbnail for this part
|
||||
String get thumbnail {
|
||||
// Use image as a backup
|
||||
String img = _thumbnail.isNotEmpty ? _thumbnail : _image;
|
||||
|
||||
return img.isNotEmpty ? img : InvenTreeAPI.staticThumb;
|
||||
}
|
||||
|
||||
Future<bool> uploadImage(File image) async {
|
||||
// Upload file against this part
|
||||
final APIResponse response = await InvenTreeAPI().uploadFile(
|
||||
url,
|
||||
image,
|
||||
method: "PATCH",
|
||||
name: "image",
|
||||
);
|
||||
|
||||
return response.successful();
|
||||
}
|
||||
|
||||
// Return the "starred" status of this part
|
||||
bool get starred => getBool("starred");
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePart.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreePart.fromJson(json);
|
||||
}
|
||||
|
||||
|
||||
class InvenTreePartPricing extends InvenTreeModel {
|
||||
|
||||
InvenTreePartPricing() : super();
|
||||
|
||||
InvenTreePartPricing.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreePartPricing.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
List<String> get rolesRequired => ["part"];
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePartPricing.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreePartPricing.fromJson(json);
|
||||
|
||||
// Price data accessors
|
||||
String get currency => getString("currency", backup: "USD");
|
||||
@ -538,8 +521,10 @@ class InvenTreePartPricing extends InvenTreeModel {
|
||||
double? get overrideMin => getDoubleOrNull("override_min");
|
||||
double? get overrideMax => getDoubleOrNull("override_max");
|
||||
|
||||
String get overrideMinCurrency => getString("override_min_currency", backup: currency);
|
||||
String get overrideMaxCurrency => getString("override_max_currency", backup: currency);
|
||||
String get overrideMinCurrency =>
|
||||
getString("override_min_currency", backup: currency);
|
||||
String get overrideMaxCurrency =>
|
||||
getString("override_max_currency", backup: currency);
|
||||
|
||||
double? get bomCostMin => getDoubleOrNull("bom_cost_min");
|
||||
double? get bomCostMax => getDoubleOrNull("bom_cost_max");
|
||||
@ -563,15 +548,14 @@ class InvenTreePartPricing extends InvenTreeModel {
|
||||
double? get saleHistoryMax => getDoubleOrNull("sale_history_max");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class representing an attachment file against a Part object
|
||||
*/
|
||||
class InvenTreePartAttachment extends InvenTreeAttachment {
|
||||
|
||||
InvenTreePartAttachment() : super();
|
||||
|
||||
InvenTreePartAttachment.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreePartAttachment.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
String get REFERENCE_FIELD => "part";
|
||||
@ -580,9 +564,11 @@ class InvenTreePartAttachment extends InvenTreeAttachment {
|
||||
String get REF_MODEL_TYPE => "part";
|
||||
|
||||
@override
|
||||
String get URL => InvenTreeAPI().supportsModernAttachments ? "attachment/" : "part/attachment/";
|
||||
String get URL => InvenTreeAPI().supportsModernAttachments
|
||||
? "attachment/"
|
||||
: "part/attachment/";
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePartAttachment.fromJson(json);
|
||||
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreePartAttachment.fromJson(json);
|
||||
}
|
||||
|
@ -1,17 +1,17 @@
|
||||
import "package:inventree/inventree/model.dart";
|
||||
|
||||
|
||||
/*
|
||||
* Class representing the ProjectCode database model
|
||||
*/
|
||||
class InvenTreeProjectCode extends InvenTreeModel {
|
||||
|
||||
InvenTreeProjectCode() : super();
|
||||
|
||||
InvenTreeProjectCode.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreeProjectCode.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeProjectCode.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreeProjectCode.fromJson(json);
|
||||
|
||||
@override
|
||||
String get URL => "project-code/";
|
||||
@ -20,10 +20,7 @@ class InvenTreeProjectCode extends InvenTreeModel {
|
||||
|
||||
@override
|
||||
Map<String, Map<String, dynamic>> formFields() {
|
||||
return {
|
||||
"code": {},
|
||||
"description": {},
|
||||
};
|
||||
return {"code": {}, "description": {}};
|
||||
}
|
||||
|
||||
String get code => getString("code");
|
||||
|
@ -12,18 +12,18 @@ import "package:inventree/widget/progress.dart";
|
||||
import "package:inventree/api_form.dart";
|
||||
import "package:inventree/l10.dart";
|
||||
|
||||
|
||||
/*
|
||||
* Class representing an individual PurchaseOrder instance
|
||||
*/
|
||||
class InvenTreePurchaseOrder extends InvenTreeOrder {
|
||||
|
||||
InvenTreePurchaseOrder() : super();
|
||||
|
||||
InvenTreePurchaseOrder.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreePurchaseOrder.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePurchaseOrder.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreePurchaseOrder.fromJson(json);
|
||||
|
||||
@override
|
||||
String get URL => "order/po/";
|
||||
@ -31,10 +31,8 @@ class InvenTreePurchaseOrder extends InvenTreeOrder {
|
||||
@override
|
||||
Future<Object?> goToDetailPage(BuildContext context) async {
|
||||
return Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PurchaseOrderDetailWidget(this)
|
||||
)
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => PurchaseOrderDetailWidget(this)),
|
||||
);
|
||||
}
|
||||
|
||||
@ -50,9 +48,7 @@ class InvenTreePurchaseOrder extends InvenTreeOrder {
|
||||
Map<String, Map<String, dynamic>> fields = {
|
||||
"reference": {},
|
||||
"supplier": {
|
||||
"filters": {
|
||||
"is_supplier": true,
|
||||
},
|
||||
"filters": {"is_supplier": true},
|
||||
},
|
||||
"supplier_reference": {},
|
||||
"description": {},
|
||||
@ -63,9 +59,7 @@ class InvenTreePurchaseOrder extends InvenTreeOrder {
|
||||
"link": {},
|
||||
"responsible": {},
|
||||
"contact": {
|
||||
"filters": {
|
||||
"company": supplierId,
|
||||
}
|
||||
"filters": {"company": supplierId},
|
||||
},
|
||||
};
|
||||
|
||||
@ -82,20 +76,16 @@ class InvenTreePurchaseOrder extends InvenTreeOrder {
|
||||
}
|
||||
|
||||
return fields;
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, String> defaultFilters() {
|
||||
return {
|
||||
"supplier_detail": "true",
|
||||
};
|
||||
return {"supplier_detail": "true"};
|
||||
}
|
||||
|
||||
int get supplierId => getInt("supplier");
|
||||
|
||||
InvenTreeCompany? get supplier {
|
||||
|
||||
dynamic supplier_detail = jsondata["supplier_detail"];
|
||||
|
||||
if (supplier_detail == null) {
|
||||
@ -109,20 +99,26 @@ class InvenTreePurchaseOrder extends InvenTreeOrder {
|
||||
|
||||
int get destinationId => getInt("destination");
|
||||
|
||||
bool get isOpen => api.PurchaseOrderStatus.isNameIn(status, ["PENDING", "PLACED", "ON_HOLD"]);
|
||||
bool get isOpen => api.PurchaseOrderStatus.isNameIn(status, [
|
||||
"PENDING",
|
||||
"PLACED",
|
||||
"ON_HOLD",
|
||||
]);
|
||||
|
||||
bool get isPending => api.PurchaseOrderStatus.isNameIn(status, ["PENDING", "ON_HOLD"]);
|
||||
bool get isPending =>
|
||||
api.PurchaseOrderStatus.isNameIn(status, ["PENDING", "ON_HOLD"]);
|
||||
|
||||
bool get isPlaced => api.PurchaseOrderStatus.isNameIn(status, ["PLACED"]);
|
||||
|
||||
bool get isFailed => api.PurchaseOrderStatus.isNameIn(status, ["CANCELLED", "LOST", "RETURNED"]);
|
||||
bool get isFailed => api.PurchaseOrderStatus.isNameIn(status, [
|
||||
"CANCELLED",
|
||||
"LOST",
|
||||
"RETURNED",
|
||||
]);
|
||||
|
||||
Future<List<InvenTreePOLineItem>> getLineItems() async {
|
||||
|
||||
final results = await InvenTreePOLineItem().list(
|
||||
filters: {
|
||||
"order": "${pk}",
|
||||
}
|
||||
filters: {"order": "${pk}"},
|
||||
);
|
||||
|
||||
List<InvenTreePOLineItem> items = [];
|
||||
@ -161,13 +157,14 @@ class InvenTreePurchaseOrder extends InvenTreeOrder {
|
||||
}
|
||||
|
||||
class InvenTreePOLineItem extends InvenTreeOrderLine {
|
||||
|
||||
InvenTreePOLineItem() : super();
|
||||
|
||||
InvenTreePOLineItem.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreePOLineItem.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePOLineItem.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreePOLineItem.fromJson(json);
|
||||
|
||||
@override
|
||||
String get URL => "order/po-line/";
|
||||
@ -198,10 +195,7 @@ class InvenTreePOLineItem extends InvenTreeOrderLine {
|
||||
|
||||
@override
|
||||
Map<String, String> defaultFilters() {
|
||||
return {
|
||||
"part_detail": "true",
|
||||
"order_detail": "true",
|
||||
};
|
||||
return {"part_detail": "true", "order_detail": "true"};
|
||||
}
|
||||
|
||||
double get received => getDouble("received");
|
||||
@ -216,14 +210,14 @@ class InvenTreePOLineItem extends InvenTreeOrderLine {
|
||||
return received / quantity;
|
||||
}
|
||||
|
||||
String get progressString => simpleNumberString(received) + " / " + simpleNumberString(quantity);
|
||||
String get progressString =>
|
||||
simpleNumberString(received) + " / " + simpleNumberString(quantity);
|
||||
|
||||
double get outstanding => quantity - received;
|
||||
|
||||
int get supplierPartId => getInt("part");
|
||||
|
||||
InvenTreeSupplierPart? get supplierPart {
|
||||
|
||||
dynamic detail = jsondata["supplier_part_detail"];
|
||||
|
||||
if (detail == null) {
|
||||
@ -246,7 +240,7 @@ class InvenTreePOLineItem extends InvenTreeOrderLine {
|
||||
String get SKU => getString("SKU", subKey: "supplier_part_detail");
|
||||
|
||||
double get purchasePrice => getDouble("purchase_price");
|
||||
|
||||
|
||||
String get purchasePriceCurrency => getString("purchase_price_currency");
|
||||
|
||||
int get destinationId => getInt("destination");
|
||||
@ -256,7 +250,13 @@ class InvenTreePOLineItem extends InvenTreeOrderLine {
|
||||
Map<String, dynamic> get destinationDetail => getMap("destination_detail");
|
||||
|
||||
// Receive this line item into stock
|
||||
Future<void> receive(BuildContext context, {int? destination, double? quantity, String? barcode, Function? onSuccess}) async {
|
||||
Future<void> receive(
|
||||
BuildContext context, {
|
||||
int? destination,
|
||||
double? quantity,
|
||||
String? barcode,
|
||||
Function? onSuccess,
|
||||
}) async {
|
||||
// Infer the destination location from the line item if not provided
|
||||
if (destinationId > 0) {
|
||||
destination = destinationId;
|
||||
@ -274,20 +274,10 @@ class InvenTreePOLineItem extends InvenTreeOrderLine {
|
||||
"hidden": true,
|
||||
"value": pk,
|
||||
},
|
||||
"quantity": {
|
||||
"parent": "items",
|
||||
"nested": true,
|
||||
"value": quantity,
|
||||
},
|
||||
"quantity": {"parent": "items", "nested": true, "value": quantity},
|
||||
"location": {},
|
||||
"status": {
|
||||
"parent": "items",
|
||||
"nested": true,
|
||||
},
|
||||
"batch_code": {
|
||||
"parent": "items",
|
||||
"nested": true,
|
||||
},
|
||||
"status": {"parent": "items", "nested": true},
|
||||
"batch_code": {"parent": "items", "nested": true},
|
||||
"barcode": {
|
||||
"parent": "items",
|
||||
"nested": true,
|
||||
@ -295,7 +285,7 @@ class InvenTreePOLineItem extends InvenTreeOrderLine {
|
||||
"label": L10().barcodeAssign,
|
||||
"value": barcode,
|
||||
"required": false,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if (destination != null && destination > 0) {
|
||||
@ -306,31 +296,31 @@ class InvenTreePOLineItem extends InvenTreeOrderLine {
|
||||
|
||||
if (order != null) {
|
||||
await launchApiForm(
|
||||
context,
|
||||
L10().receiveItem,
|
||||
order.receive_url,
|
||||
fields,
|
||||
method: "POST",
|
||||
icon: TablerIcons.transition_right,
|
||||
onSuccess: (data) {
|
||||
if (onSuccess != null) {
|
||||
onSuccess();
|
||||
}
|
||||
context,
|
||||
L10().receiveItem,
|
||||
order.receive_url,
|
||||
fields,
|
||||
method: "POST",
|
||||
icon: TablerIcons.transition_right,
|
||||
onSuccess: (data) {
|
||||
if (onSuccess != null) {
|
||||
onSuccess();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class InvenTreePOExtraLineItem extends InvenTreeExtraLineItem {
|
||||
|
||||
InvenTreePOExtraLineItem() : super();
|
||||
|
||||
InvenTreePOExtraLineItem.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreePOExtraLineItem.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePOExtraLineItem.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreePOExtraLineItem.fromJson(json);
|
||||
|
||||
@override
|
||||
String get URL => "order/po-extra-line/";
|
||||
@ -342,23 +332,19 @@ class InvenTreePOExtraLineItem extends InvenTreeExtraLineItem {
|
||||
Future<Object?> goToDetailPage(BuildContext context) async {
|
||||
return Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ExtraLineDetailWidget(this)
|
||||
)
|
||||
MaterialPageRoute(builder: (context) => ExtraLineDetailWidget(this)),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class representing an attachment file against a PurchaseOrder object
|
||||
*/
|
||||
class InvenTreePurchaseOrderAttachment extends InvenTreeAttachment {
|
||||
|
||||
InvenTreePurchaseOrderAttachment() : super();
|
||||
|
||||
InvenTreePurchaseOrderAttachment.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreePurchaseOrderAttachment.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
String get REFERENCE_FIELD => "order";
|
||||
@ -367,9 +353,11 @@ class InvenTreePurchaseOrderAttachment extends InvenTreeAttachment {
|
||||
String get REF_MODEL_TYPE => "purchaseorder";
|
||||
|
||||
@override
|
||||
String get URL => InvenTreeAPI().supportsModernAttachments ? "attachment/" : "order/po/attachment/";
|
||||
String get URL => InvenTreeAPI().supportsModernAttachments
|
||||
? "attachment/"
|
||||
: "order/po/attachment/";
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePurchaseOrderAttachment.fromJson(json);
|
||||
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreePurchaseOrderAttachment.fromJson(json);
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
import "package:flutter/material.dart";
|
||||
import "package:inventree/api.dart";
|
||||
import "package:inventree/helpers.dart";
|
||||
@ -11,18 +9,18 @@ import "package:inventree/widget/progress.dart";
|
||||
import "package:inventree/widget/order/extra_line_detail.dart";
|
||||
import "package:inventree/widget/order/sales_order_detail.dart";
|
||||
|
||||
|
||||
/*
|
||||
* Class representing an individual SalesOrder
|
||||
*/
|
||||
class InvenTreeSalesOrder extends InvenTreeOrder {
|
||||
|
||||
InvenTreeSalesOrder() : super();
|
||||
|
||||
InvenTreeSalesOrder.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreeSalesOrder.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeSalesOrder.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreeSalesOrder.fromJson(json);
|
||||
|
||||
@override
|
||||
String get URL => "order/so/";
|
||||
@ -38,9 +36,7 @@ class InvenTreeSalesOrder extends InvenTreeOrder {
|
||||
Future<Object?> goToDetailPage(BuildContext context) async {
|
||||
return Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SalesOrderDetailWidget(this)
|
||||
)
|
||||
MaterialPageRoute(builder: (context) => SalesOrderDetailWidget(this)),
|
||||
);
|
||||
}
|
||||
|
||||
@ -49,9 +45,7 @@ class InvenTreeSalesOrder extends InvenTreeOrder {
|
||||
Map<String, Map<String, dynamic>> fields = {
|
||||
"reference": {},
|
||||
"customer": {
|
||||
"filters": {
|
||||
"is_customer": true,
|
||||
}
|
||||
"filters": {"is_customer": true},
|
||||
},
|
||||
"customer_reference": {},
|
||||
"description": {},
|
||||
@ -61,10 +55,8 @@ class InvenTreeSalesOrder extends InvenTreeOrder {
|
||||
"link": {},
|
||||
"responsible": {},
|
||||
"contact": {
|
||||
"filters": {
|
||||
"company": customerId,
|
||||
}
|
||||
}
|
||||
"filters": {"company": customerId},
|
||||
},
|
||||
};
|
||||
|
||||
if (!InvenTreeAPI().supportsProjectCodes) {
|
||||
@ -84,9 +76,7 @@ class InvenTreeSalesOrder extends InvenTreeOrder {
|
||||
|
||||
@override
|
||||
Map<String, String> defaultFilters() {
|
||||
return {
|
||||
"customer_detail": "true",
|
||||
};
|
||||
return {"customer_detail": "true"};
|
||||
}
|
||||
|
||||
Future<void> issueOrder() async {
|
||||
@ -124,28 +114,33 @@ class InvenTreeSalesOrder extends InvenTreeOrder {
|
||||
|
||||
String get customerReference => getString("customer_reference");
|
||||
|
||||
bool get isOpen => api.SalesOrderStatus.isNameIn(status, ["PENDING", "IN_PROGRESS", "ON_HOLD"]);
|
||||
bool get isOpen => api.SalesOrderStatus.isNameIn(status, [
|
||||
"PENDING",
|
||||
"IN_PROGRESS",
|
||||
"ON_HOLD",
|
||||
]);
|
||||
|
||||
bool get isPending => api.SalesOrderStatus.isNameIn(status, ["PENDING", "ON_HOLD"]);
|
||||
bool get isPending =>
|
||||
api.SalesOrderStatus.isNameIn(status, ["PENDING", "ON_HOLD"]);
|
||||
|
||||
bool get isInProgress => api.SalesOrderStatus.isNameIn(status, ["IN_PROGRESS"]);
|
||||
bool get isInProgress =>
|
||||
api.SalesOrderStatus.isNameIn(status, ["IN_PROGRESS"]);
|
||||
|
||||
bool get isComplete => api.SalesOrderStatus.isNameIn(status, ["SHIPPED"]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class representing an individual line item in a SalesOrder
|
||||
*/
|
||||
class InvenTreeSOLineItem extends InvenTreeOrderLine {
|
||||
|
||||
InvenTreeSOLineItem() : super();
|
||||
|
||||
InvenTreeSOLineItem.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreeSOLineItem.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeSOLineItem.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreeSOLineItem.fromJson(json);
|
||||
|
||||
@override
|
||||
String get URL => "order/so-line/";
|
||||
@ -156,13 +151,9 @@ class InvenTreeSOLineItem extends InvenTreeOrderLine {
|
||||
@override
|
||||
Map<String, Map<String, dynamic>> formFields() {
|
||||
return {
|
||||
"order": {
|
||||
"hidden": true,
|
||||
},
|
||||
"order": {"hidden": true},
|
||||
"part": {
|
||||
"filters": {
|
||||
"salable": true,
|
||||
}
|
||||
"filters": {"salable": true},
|
||||
},
|
||||
"quantity": {},
|
||||
"reference": {},
|
||||
@ -172,33 +163,17 @@ class InvenTreeSOLineItem extends InvenTreeOrderLine {
|
||||
}
|
||||
|
||||
Map<String, Map<String, dynamic>> allocateFormFields() {
|
||||
|
||||
return {
|
||||
"line_item": {
|
||||
"parent": "items",
|
||||
"nested": true,
|
||||
"hidden": true,
|
||||
},
|
||||
"stock_item": {
|
||||
"parent": "items",
|
||||
"nested": true,
|
||||
"filters": {},
|
||||
},
|
||||
"quantity": {
|
||||
"parent": "items",
|
||||
"nested": true,
|
||||
},
|
||||
"shipment": {
|
||||
"filters": {}
|
||||
}
|
||||
"line_item": {"parent": "items", "nested": true, "hidden": true},
|
||||
"stock_item": {"parent": "items", "nested": true, "filters": {}},
|
||||
"quantity": {"parent": "items", "nested": true},
|
||||
"shipment": {"filters": {}},
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, String> defaultFilters() {
|
||||
return {
|
||||
"part_detail": "true",
|
||||
};
|
||||
return {"part_detail": "true"};
|
||||
}
|
||||
|
||||
double get allocated => getDouble("allocated");
|
||||
@ -223,7 +198,8 @@ class InvenTreeSOLineItem extends InvenTreeOrderLine {
|
||||
return unallocated;
|
||||
}
|
||||
|
||||
String get allocatedString => simpleNumberString(allocated) + " / " + simpleNumberString(quantity);
|
||||
String get allocatedString =>
|
||||
simpleNumberString(allocated) + " / " + simpleNumberString(quantity);
|
||||
|
||||
double get shipped => getDouble("shipped");
|
||||
|
||||
@ -239,26 +215,28 @@ class InvenTreeSOLineItem extends InvenTreeOrderLine {
|
||||
return shipped / quantity;
|
||||
}
|
||||
|
||||
String get progressString => simpleNumberString(shipped) + " / " + simpleNumberString(quantity);
|
||||
String get progressString =>
|
||||
simpleNumberString(shipped) + " / " + simpleNumberString(quantity);
|
||||
|
||||
bool get isComplete => shipped >= quantity;
|
||||
|
||||
double get available => getDouble("available_stock") + getDouble("available_variant_stock");
|
||||
double get available =>
|
||||
getDouble("available_stock") + getDouble("available_variant_stock");
|
||||
|
||||
double get salePrice => getDouble("sale_price");
|
||||
|
||||
String get salePriceCurrency => getString("sale_price_currency");
|
||||
|
||||
}
|
||||
|
||||
|
||||
class InvenTreeSOExtraLineItem extends InvenTreeExtraLineItem {
|
||||
InvenTreeSOExtraLineItem() : super();
|
||||
|
||||
InvenTreeSOExtraLineItem.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreeSOExtraLineItem.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeSOExtraLineItem.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreeSOExtraLineItem.fromJson(json);
|
||||
|
||||
@override
|
||||
String get URL => "order/so-extra-line/";
|
||||
@ -269,10 +247,8 @@ class InvenTreeSOExtraLineItem extends InvenTreeExtraLineItem {
|
||||
@override
|
||||
Future<Object?> goToDetailPage(BuildContext context) async {
|
||||
return Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ExtraLineDetailWidget(this)
|
||||
)
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => ExtraLineDetailWidget(this)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -281,13 +257,14 @@ class InvenTreeSOExtraLineItem extends InvenTreeExtraLineItem {
|
||||
* Class representing a sales order shipment
|
||||
*/
|
||||
class InvenTreeSalesOrderShipment extends InvenTreeModel {
|
||||
|
||||
InvenTreeSalesOrderShipment() : super();
|
||||
|
||||
InvenTreeSalesOrderShipment.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreeSalesOrderShipment.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeSalesOrderShipment.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreeSalesOrderShipment.fromJson(json);
|
||||
|
||||
@override
|
||||
String get URL => "/order/so/shipment/";
|
||||
@ -318,19 +295,18 @@ class InvenTreeSalesOrderShipment extends InvenTreeModel {
|
||||
bool get shipped => shipment_date != null && shipment_date!.isNotEmpty;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Class representing an attachment file against a SalesOrder object
|
||||
*/
|
||||
class InvenTreeSalesOrderAttachment extends InvenTreeAttachment {
|
||||
|
||||
InvenTreeSalesOrderAttachment() : super();
|
||||
|
||||
InvenTreeSalesOrderAttachment.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreeSalesOrderAttachment.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeSalesOrderAttachment.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreeSalesOrderAttachment.fromJson(json);
|
||||
|
||||
@override
|
||||
String get REFERENCE_FIELD => "order";
|
||||
@ -339,6 +315,7 @@ class InvenTreeSalesOrderAttachment extends InvenTreeAttachment {
|
||||
String get REF_MODEL_TYPE => "salesorder";
|
||||
|
||||
@override
|
||||
String get URL => InvenTreeAPI().supportsModernAttachments ? "attachment/" : "order/so/attachment/";
|
||||
|
||||
String get URL => InvenTreeAPI().supportsModernAttachments
|
||||
? "attachment/"
|
||||
: "order/so/attachment/";
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import "package:inventree/dsn.dart";
|
||||
import "package:inventree/preferences.dart";
|
||||
|
||||
Future<Map<String, dynamic>> getDeviceInfo() async {
|
||||
|
||||
// Extract device information
|
||||
final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
||||
|
||||
@ -31,7 +30,6 @@ Future<Map<String, dynamic>> getDeviceInfo() async {
|
||||
"identifierForVendor": iosDeviceInfo.identifierForVendor,
|
||||
"isPhysicalDevice": iosDeviceInfo.isPhysicalDevice,
|
||||
};
|
||||
|
||||
} else if (Platform.isAndroid) {
|
||||
final androidDeviceInfo = await deviceInfo.androidInfo;
|
||||
|
||||
@ -57,13 +55,11 @@ Future<Map<String, dynamic>> getDeviceInfo() async {
|
||||
return device_info;
|
||||
}
|
||||
|
||||
|
||||
Map<String, dynamic> getServerInfo() => {
|
||||
"version": InvenTreeAPI().serverVersion,
|
||||
"apiVersion": InvenTreeAPI().apiVersion,
|
||||
};
|
||||
|
||||
|
||||
Future<Map<String, dynamic>> getAppInfo() async {
|
||||
// Add app info
|
||||
final package_info = await PackageInfo.fromPlatform();
|
||||
@ -76,7 +72,6 @@ Future<Map<String, dynamic>> getAppInfo() async {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
bool isInDebugMode() {
|
||||
bool inDebugMode = false;
|
||||
|
||||
@ -85,8 +80,10 @@ bool isInDebugMode() {
|
||||
return inDebugMode;
|
||||
}
|
||||
|
||||
Future<bool> sentryReportMessage(String message, {Map<String, String>? context}) async {
|
||||
|
||||
Future<bool> sentryReportMessage(
|
||||
String message, {
|
||||
Map<String, String>? context,
|
||||
}) async {
|
||||
if (SENTRY_DSN_KEY.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
@ -106,23 +103,22 @@ Future<bool> sentryReportMessage(String message, {Map<String, String>? context})
|
||||
// We don't care about the server address, only the path and query parameters!
|
||||
// Overwrite the provided URL
|
||||
context["url"] = uri.path + "?" + uri.query;
|
||||
|
||||
} catch (error) {
|
||||
// Ignore if any errors are thrown here
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
print("Sending user message to Sentry: ${message}, ${context}");
|
||||
|
||||
if (isInDebugMode()) {
|
||||
|
||||
print("----- In dev mode. Not sending message to Sentry.io -----");
|
||||
return true;
|
||||
}
|
||||
|
||||
final upload = await InvenTreeSettingsManager().getValue(INV_REPORT_ERRORS, true) as bool;
|
||||
final upload =
|
||||
await InvenTreeSettingsManager().getValue(INV_REPORT_ERRORS, true)
|
||||
as bool;
|
||||
|
||||
if (!upload) {
|
||||
print("----- Error reporting disabled -----");
|
||||
@ -152,12 +148,15 @@ Future<bool> sentryReportMessage(String message, {Map<String, String>? context})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Report an error message to sentry.io
|
||||
*/
|
||||
Future<void> sentryReportError(String source, dynamic error, StackTrace? stackTrace, {Map<String, String> context = const {}}) async {
|
||||
|
||||
Future<void> sentryReportError(
|
||||
String source,
|
||||
dynamic error,
|
||||
StackTrace? stackTrace, {
|
||||
Map<String, String> context = const {},
|
||||
}) async {
|
||||
if (sentryIgnoreError(error)) {
|
||||
// No action on this error
|
||||
return;
|
||||
@ -170,7 +169,6 @@ Future<void> sentryReportError(String source, dynamic error, StackTrace? stackTr
|
||||
// check if you are running in dev mode using an assertion and omit sending
|
||||
// the report.
|
||||
if (isInDebugMode()) {
|
||||
|
||||
print("----- In dev mode. Not sending report to Sentry.io -----");
|
||||
return;
|
||||
}
|
||||
@ -179,7 +177,9 @@ Future<void> sentryReportError(String source, dynamic error, StackTrace? stackTr
|
||||
return;
|
||||
}
|
||||
|
||||
final upload = await InvenTreeSettingsManager().getValue(INV_REPORT_ERRORS, true) as bool;
|
||||
final upload =
|
||||
await InvenTreeSettingsManager().getValue(INV_REPORT_ERRORS, true)
|
||||
as bool;
|
||||
|
||||
if (!upload) {
|
||||
print("----- Error reporting disabled -----");
|
||||
@ -188,11 +188,12 @@ Future<void> sentryReportError(String source, dynamic error, StackTrace? stackTr
|
||||
|
||||
// Some errors are outside our control, and we do not want to "pollute" the uploaded data
|
||||
if (source == "FlutterError.onError") {
|
||||
|
||||
String errorString = error.toString();
|
||||
|
||||
// Missing media file
|
||||
if (errorString.contains("HttpException") && errorString.contains("404") && errorString.contains("/media/")) {
|
||||
if (errorString.contains("HttpException") &&
|
||||
errorString.contains("404") &&
|
||||
errorString.contains("/media/")) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -225,26 +226,28 @@ Future<void> sentryReportError(String source, dynamic error, StackTrace? stackTr
|
||||
scope.setContexts("context", context);
|
||||
});
|
||||
|
||||
Sentry.captureException(error, stackTrace: stackTrace).catchError((error) {
|
||||
print("Error uploading information to Sentry.io:");
|
||||
print(error);
|
||||
return SentryId.empty();
|
||||
}).then((response) {
|
||||
print("Uploaded information to Sentry.io : ${response.toString()}");
|
||||
});
|
||||
Sentry.captureException(error, stackTrace: stackTrace)
|
||||
.catchError((error) {
|
||||
print("Error uploading information to Sentry.io:");
|
||||
print(error);
|
||||
return SentryId.empty();
|
||||
})
|
||||
.then((response) {
|
||||
print("Uploaded information to Sentry.io : ${response.toString()}");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Test if a certain error should be ignored by Sentry
|
||||
*/
|
||||
bool sentryIgnoreError(dynamic error) {
|
||||
// Ignore 404 errors for media files
|
||||
if (error is HttpException) {
|
||||
if (error.uri.toString().contains("/media/") && error.message.contains("404")) {
|
||||
if (error.uri.toString().contains("/media/") &&
|
||||
error.message.contains("404")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -11,12 +11,10 @@ import "package:inventree/api.dart";
|
||||
import "package:inventree/app_colors.dart";
|
||||
import "package:inventree/helpers.dart";
|
||||
|
||||
|
||||
/*
|
||||
* Base class definition for a "status code" definition.
|
||||
*/
|
||||
class InvenTreeStatusCode {
|
||||
|
||||
InvenTreeStatusCode(this.URL);
|
||||
|
||||
final String URL;
|
||||
@ -34,10 +32,7 @@ class InvenTreeStatusCode {
|
||||
dynamic _entry = data[key];
|
||||
|
||||
if (_entry is Map<String, dynamic>) {
|
||||
_choices.add({
|
||||
"value": _entry["key"],
|
||||
"display_name": _entry["label"]
|
||||
});
|
||||
_choices.add({"value": _entry["key"], "display_name": _entry["label"]});
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,7 +41,6 @@ class InvenTreeStatusCode {
|
||||
|
||||
// Load status code information from the server
|
||||
Future<void> load({bool forceReload = false}) async {
|
||||
|
||||
// Return internally cached data
|
||||
if (data.isNotEmpty && !forceReload) {
|
||||
return;
|
||||
|
@ -10,16 +10,14 @@ import "package:inventree/inventree/model.dart";
|
||||
import "package:inventree/widget/stock/location_display.dart";
|
||||
import "package:inventree/widget/stock/stock_detail.dart";
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Class representing a test result for a single stock item
|
||||
*/
|
||||
class InvenTreeStockItemTestResult extends InvenTreeModel {
|
||||
|
||||
InvenTreeStockItemTestResult() : super();
|
||||
|
||||
InvenTreeStockItemTestResult.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreeStockItemTestResult.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
String get URL => "stock/test/";
|
||||
@ -29,22 +27,16 @@ class InvenTreeStockItemTestResult extends InvenTreeModel {
|
||||
|
||||
@override
|
||||
Map<String, String> defaultFilters() {
|
||||
return {
|
||||
"user_detail": "true",
|
||||
"template_detail": "true",
|
||||
};
|
||||
return {"user_detail": "true", "template_detail": "true"};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Map<String, dynamic>> formFields() {
|
||||
|
||||
Map<String, Map<String, dynamic>> fields = {
|
||||
"stock_item": {"hidden": true},
|
||||
"test": {},
|
||||
"template": {
|
||||
"filters": {
|
||||
"enabled": "true",
|
||||
}
|
||||
"filters": {"enabled": "true"},
|
||||
},
|
||||
"result": {},
|
||||
"value": {},
|
||||
@ -68,44 +60,39 @@ class InvenTreeStockItemTestResult extends InvenTreeModel {
|
||||
String get testName => getString("test");
|
||||
|
||||
bool get result => getBool("result");
|
||||
|
||||
|
||||
String get value => getString("value");
|
||||
|
||||
|
||||
String get attachment => getString("attachment");
|
||||
|
||||
String get username => getString("username", subKey: "user_detail");
|
||||
|
||||
String get date => getString("date");
|
||||
|
||||
|
||||
@override
|
||||
InvenTreeStockItemTestResult createFromJson(Map<String, dynamic> json) {
|
||||
var result = InvenTreeStockItemTestResult.fromJson(json);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class InvenTreeStockItemHistory extends InvenTreeModel {
|
||||
|
||||
InvenTreeStockItemHistory() : super();
|
||||
|
||||
InvenTreeStockItemHistory.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreeStockItemHistory.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeStockItemHistory.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreeStockItemHistory.fromJson(json);
|
||||
|
||||
@override
|
||||
String get URL => "stock/track/";
|
||||
|
||||
@override
|
||||
Map<String, String> defaultFilters() {
|
||||
|
||||
// By default, order by decreasing date
|
||||
return {
|
||||
"ordering": "-date",
|
||||
"user_detail": "true",
|
||||
};
|
||||
return {"ordering": "-date", "user_detail": "true"};
|
||||
}
|
||||
|
||||
DateTime? get date => getDate("date");
|
||||
@ -113,7 +100,7 @@ class InvenTreeStockItemHistory extends InvenTreeModel {
|
||||
String get dateString => getDateString("date");
|
||||
|
||||
String get label => getString("label");
|
||||
|
||||
|
||||
// Return the "deltas" associated with this historical object
|
||||
Map<String, dynamic> get deltas => getMap("deltas");
|
||||
|
||||
@ -133,7 +120,6 @@ class InvenTreeStockItemHistory extends InvenTreeModel {
|
||||
int? get user => getValue("user") as int?;
|
||||
|
||||
String get userString {
|
||||
|
||||
if (user != null) {
|
||||
return getString("username", subKey: "user_detail");
|
||||
} else {
|
||||
@ -142,12 +128,10 @@ class InvenTreeStockItemHistory extends InvenTreeModel {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class representing a StockItem database instance
|
||||
*/
|
||||
class InvenTreeStockItem extends InvenTreeModel {
|
||||
|
||||
InvenTreeStockItem() : super();
|
||||
|
||||
InvenTreeStockItem.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
@ -164,39 +148,18 @@ class InvenTreeStockItem extends InvenTreeModel {
|
||||
Future<Object?> goToDetailPage(BuildContext context) async {
|
||||
return Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StockDetailWidget(this)
|
||||
)
|
||||
MaterialPageRoute(builder: (context) => StockDetailWidget(this)),
|
||||
);
|
||||
}
|
||||
|
||||
// Return a set of fields to transfer this stock item via dialog
|
||||
Map<String, dynamic> transferFields() {
|
||||
Map<String, dynamic> fields = {
|
||||
"pk": {
|
||||
"parent": "items",
|
||||
"nested": true,
|
||||
"hidden": true,
|
||||
"value": pk,
|
||||
},
|
||||
"quantity": {
|
||||
"parent": "items",
|
||||
"nested": true,
|
||||
"value": quantity,
|
||||
},
|
||||
"location": {
|
||||
"value": locationId,
|
||||
},
|
||||
"status": {
|
||||
"parent": "items",
|
||||
"nested": true,
|
||||
"value": status,
|
||||
},
|
||||
"packaging": {
|
||||
"parent": "items",
|
||||
"nested": true,
|
||||
"value": packaging,
|
||||
},
|
||||
"pk": {"parent": "items", "nested": true, "hidden": true, "value": pk},
|
||||
"quantity": {"parent": "items", "nested": true, "value": quantity},
|
||||
"location": {"value": locationId},
|
||||
"status": {"parent": "items", "nested": true, "value": status},
|
||||
"packaging": {"parent": "items", "nested": true, "value": packaging},
|
||||
"notes": {},
|
||||
};
|
||||
|
||||
@ -233,10 +196,7 @@ class InvenTreeStockItem extends InvenTreeModel {
|
||||
"location": {},
|
||||
"quantity": {},
|
||||
"serial": {},
|
||||
"serial_numbers": {
|
||||
"label": L10().serialNumbers,
|
||||
"type": "string",
|
||||
},
|
||||
"serial_numbers": {"label": L10().serialNumbers, "type": "string"},
|
||||
"status": {},
|
||||
"batch": {},
|
||||
"purchase_price": {},
|
||||
@ -250,13 +210,12 @@ class InvenTreeStockItem extends InvenTreeModel {
|
||||
|
||||
@override
|
||||
Map<String, String> defaultFilters() {
|
||||
|
||||
return {
|
||||
"part_detail": "true",
|
||||
"location_detail": "true",
|
||||
"supplier_detail": "true",
|
||||
"supplier_part_detail": "true",
|
||||
"cascade": "false"
|
||||
"cascade": "false",
|
||||
};
|
||||
}
|
||||
|
||||
@ -265,21 +224,18 @@ class InvenTreeStockItem extends InvenTreeModel {
|
||||
int get testTemplateCount => testTemplates.length;
|
||||
|
||||
// Get all the test templates associated with this StockItem
|
||||
Future<void> getTestTemplates({bool showDialog=false}) async {
|
||||
await InvenTreePartTestTemplate().list(
|
||||
filters: {
|
||||
"part": "${partId}",
|
||||
"enabled": "true",
|
||||
},
|
||||
).then((var templates) {
|
||||
testTemplates.clear();
|
||||
Future<void> getTestTemplates({bool showDialog = false}) async {
|
||||
await InvenTreePartTestTemplate()
|
||||
.list(filters: {"part": "${partId}", "enabled": "true"})
|
||||
.then((var templates) {
|
||||
testTemplates.clear();
|
||||
|
||||
for (var t in templates) {
|
||||
if (t is InvenTreePartTestTemplate) {
|
||||
testTemplates.add(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
for (var t in templates) {
|
||||
if (t is InvenTreePartTestTemplate) {
|
||||
testTemplates.add(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
List<InvenTreeStockItemTestResult> testResults = [];
|
||||
@ -287,21 +243,17 @@ class InvenTreeStockItem extends InvenTreeModel {
|
||||
int get testResultCount => testResults.length;
|
||||
|
||||
Future<void> getTestResults() async {
|
||||
await InvenTreeStockItemTestResult()
|
||||
.list(filters: {"stock_item": "${pk}", "user_detail": "true"})
|
||||
.then((var results) {
|
||||
testResults.clear();
|
||||
|
||||
await InvenTreeStockItemTestResult().list(
|
||||
filters: {
|
||||
"stock_item": "${pk}",
|
||||
"user_detail": "true",
|
||||
},
|
||||
).then((var results) {
|
||||
testResults.clear();
|
||||
|
||||
for (var r in results) {
|
||||
if (r is InvenTreeStockItemTestResult) {
|
||||
testResults.add(r);
|
||||
}
|
||||
}
|
||||
});
|
||||
for (var r in results) {
|
||||
if (r is InvenTreeStockItemTestResult) {
|
||||
testResults.add(r);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
int get status => getInt("status");
|
||||
@ -313,7 +265,7 @@ class InvenTreeStockItem extends InvenTreeModel {
|
||||
String get batch => getString("batch");
|
||||
|
||||
int get partId => getInt("part");
|
||||
|
||||
|
||||
double? get purchasePrice {
|
||||
String pp = getString("purchase_price");
|
||||
|
||||
@ -334,7 +286,7 @@ class InvenTreeStockItem extends InvenTreeModel {
|
||||
int get purchaseOrderId => getInt("purchase_order");
|
||||
|
||||
int get trackingItemCount => getInt("tracking_items", backup: 0);
|
||||
|
||||
|
||||
bool get isBuilding => getBool("is_building");
|
||||
|
||||
int get salesOrderId => getInt("sales_order");
|
||||
@ -362,274 +314,275 @@ class InvenTreeStockItem extends InvenTreeModel {
|
||||
|
||||
String get stocktakeDateString => getDateString("stocktake_date");
|
||||
|
||||
String get partName {
|
||||
String get partName {
|
||||
String nm = "";
|
||||
|
||||
String nm = "";
|
||||
|
||||
// Use the detailed part information as priority
|
||||
if (jsondata.containsKey("part_detail")) {
|
||||
nm = (jsondata["part_detail"]?["full_name"] ?? "") as String;
|
||||
}
|
||||
|
||||
// Backup if first value fails
|
||||
if (nm.isEmpty) {
|
||||
nm = getString("part__name");
|
||||
}
|
||||
|
||||
return nm;
|
||||
// Use the detailed part information as priority
|
||||
if (jsondata.containsKey("part_detail")) {
|
||||
nm = (jsondata["part_detail"]?["full_name"] ?? "") as String;
|
||||
}
|
||||
|
||||
String get partDescription {
|
||||
String desc = "";
|
||||
|
||||
// Use the detailed part description as priority
|
||||
if (jsondata.containsKey("part_detail")) {
|
||||
desc = (jsondata["part_detail"]?["description"] ?? "") as String;
|
||||
}
|
||||
|
||||
if (desc.isEmpty) {
|
||||
desc = getString("part__description");
|
||||
}
|
||||
|
||||
return desc;
|
||||
// Backup if first value fails
|
||||
if (nm.isEmpty) {
|
||||
nm = getString("part__name");
|
||||
}
|
||||
|
||||
String get partImage {
|
||||
String img = "";
|
||||
return nm;
|
||||
}
|
||||
|
||||
if (jsondata.containsKey("part_detail")) {
|
||||
img = (jsondata["part_detail"]?["thumbnail"] ?? "") as String;
|
||||
}
|
||||
String get partDescription {
|
||||
String desc = "";
|
||||
|
||||
if (img.isEmpty) {
|
||||
img = getString("part__thumbnail");
|
||||
}
|
||||
|
||||
return img;
|
||||
// Use the detailed part description as priority
|
||||
if (jsondata.containsKey("part_detail")) {
|
||||
desc = (jsondata["part_detail"]?["description"] ?? "") as String;
|
||||
}
|
||||
|
||||
/*
|
||||
if (desc.isEmpty) {
|
||||
desc = getString("part__description");
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
String get partImage {
|
||||
String img = "";
|
||||
|
||||
if (jsondata.containsKey("part_detail")) {
|
||||
img = (jsondata["part_detail"]?["thumbnail"] ?? "") as String;
|
||||
}
|
||||
|
||||
if (img.isEmpty) {
|
||||
img = getString("part__thumbnail");
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the Part thumbnail for this stock item.
|
||||
*/
|
||||
String get partThumbnail {
|
||||
String get partThumbnail {
|
||||
String thumb = "";
|
||||
|
||||
String thumb = "";
|
||||
thumb = (jsondata["part_detail"]?["thumbnail"] ?? "") as String;
|
||||
|
||||
thumb = (jsondata["part_detail"]?["thumbnail"] ?? "") as String;
|
||||
|
||||
// Use "image" as a backup
|
||||
if (thumb.isEmpty) {
|
||||
thumb = (jsondata["part_detail"]?["image"] ?? "") as String;
|
||||
}
|
||||
|
||||
// Try a different approach
|
||||
if (thumb.isEmpty) {
|
||||
thumb = getString("part__thumbnail");
|
||||
}
|
||||
|
||||
// Still no thumbnail? Use the "no image" image
|
||||
if (thumb.isEmpty) thumb = InvenTreeAPI.staticThumb;
|
||||
|
||||
return thumb;
|
||||
// Use "image" as a backup
|
||||
if (thumb.isEmpty) {
|
||||
thumb = (jsondata["part_detail"]?["image"] ?? "") as String;
|
||||
}
|
||||
|
||||
int get supplierPartId => getInt("supplier_part");
|
||||
|
||||
String get supplierImage {
|
||||
String thumb = "";
|
||||
|
||||
if (jsondata.containsKey("supplier_part_detail")) {
|
||||
thumb = (jsondata["supplier_part_detail"]?["supplier_detail"]?["image"] ?? "") as String;
|
||||
} else if (jsondata.containsKey("supplier_detail")) {
|
||||
thumb = (jsondata["supplier_detail"]?["image"] ?? "") as String;
|
||||
}
|
||||
|
||||
return thumb;
|
||||
// Try a different approach
|
||||
if (thumb.isEmpty) {
|
||||
thumb = getString("part__thumbnail");
|
||||
}
|
||||
|
||||
String get supplierName => getString("supplier_name", subKey: "supplier_detail");
|
||||
// Still no thumbnail? Use the "no image" image
|
||||
if (thumb.isEmpty) thumb = InvenTreeAPI.staticThumb;
|
||||
|
||||
String get units => getString("units", subKey: "part_detail");
|
||||
return thumb;
|
||||
}
|
||||
|
||||
String get supplierSKU => getString("SKU", subKey: "supplier_part_detail");
|
||||
int get supplierPartId => getInt("supplier_part");
|
||||
|
||||
String get serialNumber => getString("serial");
|
||||
String get supplierImage {
|
||||
String thumb = "";
|
||||
|
||||
double get quantity => getDouble("quantity");
|
||||
if (jsondata.containsKey("supplier_part_detail")) {
|
||||
thumb =
|
||||
(jsondata["supplier_part_detail"]?["supplier_detail"]?["image"] ?? "")
|
||||
as String;
|
||||
} else if (jsondata.containsKey("supplier_detail")) {
|
||||
thumb = (jsondata["supplier_detail"]?["image"] ?? "") as String;
|
||||
}
|
||||
|
||||
String quantityString({bool includeUnits = true}){
|
||||
return thumb;
|
||||
}
|
||||
|
||||
String q = "";
|
||||
String get supplierName =>
|
||||
getString("supplier_name", subKey: "supplier_detail");
|
||||
|
||||
if (allocated > 0) {
|
||||
q += simpleNumberString(available);
|
||||
q += " / ";
|
||||
}
|
||||
String get units => getString("units", subKey: "part_detail");
|
||||
|
||||
q += simpleNumberString(quantity);
|
||||
String get supplierSKU => getString("SKU", subKey: "supplier_part_detail");
|
||||
|
||||
if (includeUnits && units.isNotEmpty) {
|
||||
String get serialNumber => getString("serial");
|
||||
|
||||
double get quantity => getDouble("quantity");
|
||||
|
||||
String quantityString({bool includeUnits = true}) {
|
||||
String q = "";
|
||||
|
||||
if (allocated > 0) {
|
||||
q += simpleNumberString(available);
|
||||
q += " / ";
|
||||
}
|
||||
|
||||
q += simpleNumberString(quantity);
|
||||
|
||||
if (includeUnits && units.isNotEmpty) {
|
||||
q += " ${units}";
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
double get allocated => getDouble("allocated");
|
||||
|
||||
double get available => quantity - allocated;
|
||||
|
||||
int get locationId => getInt("location");
|
||||
|
||||
bool isSerialized() => serialNumber.isNotEmpty && quantity.toInt() == 1;
|
||||
|
||||
String serialOrQuantityDisplay() {
|
||||
if (isSerialized()) {
|
||||
return "SN ${serialNumber}";
|
||||
} else if (allocated > 0) {
|
||||
return "${available} / ${quantity}";
|
||||
} else {
|
||||
return simpleNumberString(quantity);
|
||||
}
|
||||
}
|
||||
|
||||
String get locationName {
|
||||
if (locationId == -1 || !jsondata.containsKey("location_detail")) {
|
||||
return "Unknown Location";
|
||||
}
|
||||
|
||||
String loc = getString("name", subKey: "location_detail");
|
||||
|
||||
// Old-style name
|
||||
if (loc.isEmpty) {
|
||||
loc = getString("location__name");
|
||||
}
|
||||
|
||||
return loc;
|
||||
}
|
||||
|
||||
String get locationPathString {
|
||||
if (locationId == -1 || !jsondata.containsKey("location_detail")) {
|
||||
return L10().locationNotSet;
|
||||
}
|
||||
|
||||
String _loc = getString("pathstring", subKey: "location_detail");
|
||||
if (_loc.isNotEmpty) {
|
||||
return _loc;
|
||||
} else {
|
||||
return locationName;
|
||||
}
|
||||
}
|
||||
|
||||
String get displayQuantity {
|
||||
// Display either quantity or serial number!
|
||||
|
||||
if (serialNumber.isNotEmpty) {
|
||||
return "SN: $serialNumber";
|
||||
} else {
|
||||
String q = simpleNumberString(quantity);
|
||||
|
||||
if (units.isNotEmpty) {
|
||||
q += " ${units}";
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
}
|
||||
|
||||
double get allocated => getDouble("allocated");
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreeStockItem.fromJson(json);
|
||||
|
||||
double get available => quantity - allocated;
|
||||
|
||||
int get locationId => getInt("location");
|
||||
|
||||
bool isSerialized() => serialNumber.isNotEmpty && quantity.toInt() == 1;
|
||||
|
||||
String serialOrQuantityDisplay() {
|
||||
if (isSerialized()) {
|
||||
return "SN ${serialNumber}";
|
||||
} else if (allocated > 0) {
|
||||
return "${available} / ${quantity}";
|
||||
} else {
|
||||
return simpleNumberString(quantity);
|
||||
}
|
||||
}
|
||||
|
||||
String get locationName {
|
||||
|
||||
if (locationId == -1 || !jsondata.containsKey("location_detail")) return "Unknown Location";
|
||||
|
||||
String loc = getString("name", subKey: "location_detail");
|
||||
|
||||
// Old-style name
|
||||
if (loc.isEmpty) {
|
||||
loc = getString("location__name");
|
||||
}
|
||||
|
||||
return loc;
|
||||
}
|
||||
|
||||
String get locationPathString {
|
||||
|
||||
if (locationId == -1 || !jsondata.containsKey("location_detail")) return L10().locationNotSet;
|
||||
|
||||
String _loc = getString("pathstring", subKey: "location_detail");
|
||||
if (_loc.isNotEmpty) {
|
||||
return _loc;
|
||||
} else {
|
||||
return locationName;
|
||||
}
|
||||
}
|
||||
|
||||
String get displayQuantity {
|
||||
// Display either quantity or serial number!
|
||||
|
||||
if (serialNumber.isNotEmpty) {
|
||||
return "SN: $serialNumber";
|
||||
} else {
|
||||
String q = simpleNumberString(quantity);
|
||||
|
||||
if (units.isNotEmpty) {
|
||||
q += " ${units}";
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeStockItem.fromJson(json);
|
||||
|
||||
/*
|
||||
/*
|
||||
* Perform stocktake action:
|
||||
*
|
||||
* - Add
|
||||
* - Remove
|
||||
* - Count
|
||||
*/
|
||||
Future<bool> adjustStock(String endpoint, double q, {String? notes, int? location}) async {
|
||||
|
||||
// Serialized stock cannot be adjusted (unless it is a "transfer")
|
||||
if (isSerialized() && location == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cannot handle negative stock
|
||||
if (q < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Map<String, dynamic> data = {};
|
||||
|
||||
data = {
|
||||
"items": [
|
||||
{
|
||||
"pk": "${pk}",
|
||||
"quantity": "${quantity}",
|
||||
}
|
||||
],
|
||||
"notes": notes ?? "",
|
||||
};
|
||||
|
||||
if (location != null) {
|
||||
data["location"] = location;
|
||||
}
|
||||
|
||||
var response = await api.post(
|
||||
endpoint,
|
||||
body: data,
|
||||
);
|
||||
|
||||
return response.isValid() && (response.statusCode == 200 || response.statusCode == 201);
|
||||
Future<bool> adjustStock(
|
||||
String endpoint,
|
||||
double q, {
|
||||
String? notes,
|
||||
int? location,
|
||||
}) async {
|
||||
// Serialized stock cannot be adjusted (unless it is a "transfer")
|
||||
if (isSerialized() && location == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<bool> countStock(double q, {String? notes}) async {
|
||||
|
||||
final bool result = await adjustStock("/stock/count/", q, notes: notes);
|
||||
|
||||
return result;
|
||||
// Cannot handle negative stock
|
||||
if (q < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<bool> addStock(double q, {String? notes}) async {
|
||||
Map<String, dynamic> data = {};
|
||||
|
||||
final bool result = await adjustStock("/stock/add/", q, notes: notes);
|
||||
data = {
|
||||
"items": [
|
||||
{"pk": "${pk}", "quantity": "${quantity}"},
|
||||
],
|
||||
"notes": notes ?? "",
|
||||
};
|
||||
|
||||
return result;
|
||||
if (location != null) {
|
||||
data["location"] = location;
|
||||
}
|
||||
|
||||
Future<bool> removeStock(double q, {String? notes}) async {
|
||||
var response = await api.post(endpoint, body: data);
|
||||
|
||||
final bool result = await adjustStock("/stock/remove/", q, notes: notes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<bool> transferStock(int location, {double? quantity, String? notes}) async {
|
||||
|
||||
double q = this.quantity;
|
||||
|
||||
if (quantity != null) {
|
||||
q = quantity;
|
||||
}
|
||||
|
||||
final bool result = await adjustStock(
|
||||
"/stock/transfer/",
|
||||
q,
|
||||
notes: notes,
|
||||
location: location,
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
return response.isValid() &&
|
||||
(response.statusCode == 200 || response.statusCode == 201);
|
||||
}
|
||||
|
||||
Future<bool> countStock(double q, {String? notes}) async {
|
||||
final bool result = await adjustStock("/stock/count/", q, notes: notes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<bool> addStock(double q, {String? notes}) async {
|
||||
final bool result = await adjustStock("/stock/add/", q, notes: notes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<bool> removeStock(double q, {String? notes}) async {
|
||||
final bool result = await adjustStock("/stock/remove/", q, notes: notes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<bool> transferStock(
|
||||
int location, {
|
||||
double? quantity,
|
||||
String? notes,
|
||||
}) async {
|
||||
double q = this.quantity;
|
||||
|
||||
if (quantity != null) {
|
||||
q = quantity;
|
||||
}
|
||||
|
||||
final bool result = await adjustStock(
|
||||
"/stock/transfer/",
|
||||
q,
|
||||
notes: notes,
|
||||
location: location,
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class representing an attachment file against a StockItem object
|
||||
*/
|
||||
class InvenTreeStockItemAttachment extends InvenTreeAttachment {
|
||||
|
||||
InvenTreeStockItemAttachment() : super();
|
||||
|
||||
InvenTreeStockItemAttachment.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreeStockItemAttachment.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
String get REFERENCE_FIELD => "stock_item";
|
||||
@ -638,18 +591,20 @@ class InvenTreeStockItemAttachment extends InvenTreeAttachment {
|
||||
String get REF_MODEL_TYPE => "stockitem";
|
||||
|
||||
@override
|
||||
String get URL => InvenTreeAPI().supportsModernAttachments ? "attachment/" : "stock/attachment/";
|
||||
String get URL => InvenTreeAPI().supportsModernAttachments
|
||||
? "attachment/"
|
||||
: "stock/attachment/";
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeStockItemAttachment.fromJson(json);
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreeStockItemAttachment.fromJson(json);
|
||||
}
|
||||
|
||||
|
||||
class InvenTreeStockLocation extends InvenTreeModel {
|
||||
|
||||
InvenTreeStockLocation() : super();
|
||||
|
||||
InvenTreeStockLocation.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||
InvenTreeStockLocation.fromJson(Map<String, dynamic> json)
|
||||
: super.fromJson(json);
|
||||
|
||||
@override
|
||||
String get URL => "stock/location/";
|
||||
@ -665,9 +620,7 @@ class InvenTreeStockLocation extends InvenTreeModel {
|
||||
Future<Object?> goToDetailPage(BuildContext context) async {
|
||||
return Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LocationDisplayWidget(this)
|
||||
)
|
||||
MaterialPageRoute(builder: (context) => LocationDisplayWidget(this)),
|
||||
);
|
||||
}
|
||||
|
||||
@ -684,7 +637,6 @@ class InvenTreeStockLocation extends InvenTreeModel {
|
||||
}
|
||||
|
||||
String get parentPathString {
|
||||
|
||||
List<String> psplit = pathstring.split("/");
|
||||
|
||||
if (psplit.isNotEmpty) {
|
||||
@ -703,6 +655,6 @@ class InvenTreeStockLocation extends InvenTreeModel {
|
||||
int get itemcount => (jsondata["items"] ?? 0) as int;
|
||||
|
||||
@override
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeStockLocation.fromJson(json);
|
||||
|
||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
||||
InvenTreeStockLocation.fromJson(json);
|
||||
}
|
||||
|
Reference in New Issue
Block a user