2
0
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:
Ben Hagen
2025-06-24 01:55:01 +02:00
committed by GitHub
parent e9db6532e4
commit 4444884afa
100 changed files with 5332 additions and 5592 deletions

View File

@ -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");
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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/");
}
}
}
}

View File

@ -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": {},
};
}
}
}

View File

@ -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);
}

View File

@ -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");

View File

@ -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);
}

View File

@ -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/";
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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);
}