mirror of
				https://github.com/inventree/inventree-app.git
				synced 2025-10-31 21:35:42 +00:00 
			
		
		
		
	Project code support (#336)
* Determine if project codes are supported * Add helpers for boolean functions * Adds helper methods for generic "model" class - Will allow us to do some good refactoring * Refactor the refactor * Add debug support and getMap function * Major refactoring for model data accessors * Handle null values * Add sentry reporting if key is used incorrectly * Fix typo * Refactor createFromJson function * Add model for ProjectCode * Display and edit project code for purchase orders
This commit is contained in:
		
							
								
								
									
										15
									
								
								lib/api.dart
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								lib/api.dart
									
									
									
									
									
								
							| @@ -300,6 +300,9 @@ class InvenTreeAPI { | ||||
|   // Order barcodes API v107 or newer | ||||
|   bool get supportsOrderBarcodes => isConnected() && apiVersion >= 107; | ||||
|  | ||||
|   // Project codes require v109 or newer | ||||
|   bool get supportsProjectCodes => isConnected() && apiVersion >= 109; | ||||
|  | ||||
|   // Are plugins enabled on the server? | ||||
|   bool _pluginsEnabled = false; | ||||
|  | ||||
| @@ -1362,6 +1365,12 @@ class InvenTreeAPI { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Return a boolean global setting value | ||||
|   Future<bool> getGlobalBooleanSetting(String key) async { | ||||
|     String value = await getGlobalSetting(key); | ||||
|     return value.toLowerCase() == "true"; | ||||
|   } | ||||
|  | ||||
|   Future<String> getUserSetting(String key) async { | ||||
|     if (!supportsSettings) return ""; | ||||
|  | ||||
| @@ -1382,6 +1391,12 @@ class InvenTreeAPI { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Return a boolean user setting value | ||||
|   Future<bool> getUserBooleanSetting(String key) async { | ||||
|     String value = await getUserSetting(key); | ||||
|     return value.toLowerCase() == "true"; | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|    * Send a request to the server to locate / identify either a StockItem or StockLocation | ||||
|    */ | ||||
|   | ||||
| @@ -15,6 +15,7 @@ import "package:inventree/l10.dart"; | ||||
|  | ||||
| import "package:inventree/inventree/company.dart"; | ||||
| import "package:inventree/inventree/part.dart"; | ||||
| import "package:inventree/inventree/project_code.dart"; | ||||
| import "package:inventree/inventree/sentry.dart"; | ||||
| import "package:inventree/inventree/stock.dart"; | ||||
|  | ||||
| @@ -667,6 +668,13 @@ class APIFormField { | ||||
|             height: 40 | ||||
|           ) | ||||
|         ); | ||||
|       case "projectcode": | ||||
|         var project_code = InvenTreeProjectCode.fromJson(data); | ||||
|         return ListTile( | ||||
|           title: Text(project_code.code), | ||||
|           subtitle: Text(project_code.description), | ||||
|           leading: FaIcon(FontAwesomeIcons.list) | ||||
|         ); | ||||
|       default: | ||||
|         return ListTile( | ||||
|           title: Text( | ||||
|   | ||||
| @@ -12,9 +12,7 @@ class InvenTreeBomItem extends InvenTreeModel { | ||||
|   InvenTreeBomItem.fromJson(Map<String, dynamic> json) : super.fromJson(json); | ||||
|  | ||||
|   @override | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) { | ||||
|     return InvenTreeBomItem.fromJson(json); | ||||
|   } | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeBomItem.fromJson(json); | ||||
|  | ||||
|   @override | ||||
|   String get URL => "bom/"; | ||||
| @@ -36,13 +34,13 @@ class InvenTreeBomItem extends InvenTreeModel { | ||||
|   } | ||||
|  | ||||
|   // Extract the 'reference' value associated with this BomItem | ||||
|   String get reference => (jsondata["reference"] ?? "") as String; | ||||
|  | ||||
|   String get reference => getString("reference"); | ||||
|    | ||||
|   // Extract the 'quantity' value associated with this BomItem | ||||
|   double get quantity => double.tryParse(jsondata["quantity"].toString()) ?? 0; | ||||
|   double get quantity => getDouble("quantity"); | ||||
|  | ||||
|   // Extract the ID of the related part | ||||
|   int get partId => int.tryParse(jsondata["part"].toString()) ?? -1; | ||||
|   int get partId => getInt("part"); | ||||
|  | ||||
|   // Return a Part instance for the referenced part | ||||
|   InvenTreePart? get part { | ||||
| @@ -69,5 +67,5 @@ class InvenTreeBomItem extends InvenTreeModel { | ||||
| } | ||||
|  | ||||
|   // Extract the ID of the related sub-part | ||||
|   int get subPartId => int.tryParse(jsondata["sub_part"].toString()) ?? -1; | ||||
|   int get subPartId => getInt("sub_part"); | ||||
| } | ||||
| @@ -38,22 +38,22 @@ class InvenTreeCompany extends InvenTreeModel { | ||||
|  | ||||
|   String get thumbnail => (jsondata["thumbnail"] ?? jsondata["image"] ?? InvenTreeAPI.staticThumb) as String; | ||||
|  | ||||
|   String get website => (jsondata["website"] ?? "") as String; | ||||
|   String get website => getString("website"); | ||||
|    | ||||
|   String get phone => getString("phone"); | ||||
|  | ||||
|   String get phone => (jsondata["phone"] ?? "") as String; | ||||
|   String get email => getString("email"); | ||||
|  | ||||
|   String get email => (jsondata["email"] ?? "") as String; | ||||
|   bool get isSupplier => getBool("is_supplier"); | ||||
|  | ||||
|   bool get isSupplier => (jsondata["is_supplier"] ?? false) as bool; | ||||
|   bool get isManufacturer => getBool("is_manufacturer"); | ||||
|  | ||||
|   bool get isManufacturer => (jsondata["is_manufacturer"] ?? false)  as bool; | ||||
|  | ||||
|   bool get isCustomer => (jsondata["is_customer"] ?? false) as bool; | ||||
|  | ||||
|   int get partSuppliedCount => (jsondata["parts_supplied"] ?? 0) as int; | ||||
|  | ||||
|   int get partManufacturedCount => (jsondata["parts_manufactured"] ?? 0) as int; | ||||
|   bool get isCustomer => getBool("is_customer"); | ||||
|  | ||||
|   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 { | ||||
|  | ||||
| @@ -81,11 +81,7 @@ class InvenTreeCompany extends InvenTreeModel { | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) { | ||||
|     var company = InvenTreeCompany.fromJson(json); | ||||
|  | ||||
|     return company; | ||||
|   } | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeCompany.fromJson(json); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -154,40 +150,36 @@ class InvenTreeSupplierPart extends InvenTreeModel { | ||||
|     return _filters(); | ||||
|   } | ||||
|  | ||||
|   int get manufacturerId => (jsondata["manufacturer_detail"]["pk"] ?? -1) as int; | ||||
|  | ||||
|   String get manufacturerName => (jsondata["manufacturer_detail"]?["name"] ?? "") as String; | ||||
|  | ||||
|   String get MPN => (jsondata["manufacturer_part_detail"]?["MPN"] ?? "") as String; | ||||
|  | ||||
|   int get manufacturerId => getInt("pk", 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; | ||||
|  | ||||
|   int get manufacturerPartId => (jsondata["manufacturer_part"] ?? -1) as int; | ||||
|  | ||||
|   int get supplierId => (jsondata["supplier"] ?? -1) as int; | ||||
|  | ||||
|   String get supplierName => (jsondata["supplier_detail"]?["name"] ?? "") 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 SKU => (jsondata["SKU"] ?? "") as String; | ||||
|  | ||||
|   int get partId => (jsondata["part"] ?? -1) as int; | ||||
|  | ||||
|   String get SKU => getString("SKU"); | ||||
|    | ||||
|   int get partId => getInt("part"); | ||||
|    | ||||
|   String get partImage => (jsondata["part_detail"]?["thumbnail"] ?? InvenTreeAPI.staticThumb) as String; | ||||
|  | ||||
|   String get partName => (jsondata["part_detail"]?["full_name"] ?? "") as String; | ||||
|  | ||||
|   String get partDescription => (jsondata["part_detail"]?["description"] ?? "") as String; | ||||
|  | ||||
|   String get note => (jsondata["note"] ?? "") as String; | ||||
|   String get partName => getString("full_name", subKey: "part_detail"); | ||||
|    | ||||
|   String get partDescription => getString("description", subKey: "part_detail"); | ||||
|    | ||||
|   String get note => getString("note"); | ||||
|  | ||||
|   @override | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) { | ||||
|     var part = InvenTreeSupplierPart.fromJson(json); | ||||
|  | ||||
|     return part; | ||||
|   } | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeSupplierPart.fromJson(json); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -207,16 +199,12 @@ class InvenTreeManufacturerPart extends InvenTreeModel { | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   int get partId => (jsondata["part"] ?? -1) as int; | ||||
|  | ||||
|   int get manufacturerId => (jsondata["manufacturer"] ?? -1) as int; | ||||
|  | ||||
|   String get MPN => (jsondata["MPN"] ?? "") as String; | ||||
|  | ||||
|   int get partId => getInt("part"); | ||||
|    | ||||
|   int get manufacturerId => getInt("manufacturer"); | ||||
|    | ||||
|   String get MPN => getString("MPN"); | ||||
|    | ||||
|   @override | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) { | ||||
|     var part = InvenTreeManufacturerPart.fromJson(json); | ||||
|  | ||||
|     return part; | ||||
|   } | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeManufacturerPart.fromJson(json); | ||||
| } | ||||
|   | ||||
| @@ -63,6 +63,96 @@ class InvenTreeModel { | ||||
|   // Note: If the WEB_URL is the same (except for /api/) as URL then just leave blank | ||||
|   String get WEB_URL => ""; | ||||
|  | ||||
|   // Helper function to set a value in the JSON data | ||||
|   void setValue(String key, dynamic value) { | ||||
|     jsondata[key] = value; | ||||
|   } | ||||
|  | ||||
|   // return a dynamic value from the JSON data | ||||
|   // optionally we can specifiy a "subKey" to get a value from a sub-dictionary | ||||
|   dynamic getValue(String key, {dynamic backup, String subKey = ""}) { | ||||
|     Map<String, dynamic> data = jsondata; | ||||
|  | ||||
|     // 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; | ||||
|       } | ||||
|  | ||||
|       dynamic sub_data = data[subKey]; | ||||
|  | ||||
|       if (sub_data is Map<String, dynamic>) { | ||||
|         data = (data[subKey] ?? {}) as Map<String, dynamic>; | ||||
|       } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     if (data.containsKey(key)) { | ||||
|       return data[key]; | ||||
|     } else { | ||||
|       debug("JSON data does not contain key '$key' (subKey '${subKey}')"); | ||||
|       return backup; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Helper function to get sub-map from JSON data | ||||
|   Map<String, dynamic> getMap(String key, {Map<String, dynamic> backup = const {}, String subKey = ""}) { | ||||
|     dynamic value = getValue(key, backup: backup, subKey: subKey); | ||||
|  | ||||
|     if (value == null) { | ||||
|       return backup; | ||||
|     } | ||||
|  | ||||
|     return value as Map<String, dynamic>; | ||||
|   } | ||||
|  | ||||
|   // Helper function to get string value from JSON data | ||||
|   String getString(String key, {String backup = "", String subKey = ""}) { | ||||
|     dynamic value = getValue(key, backup: backup, subKey: subKey); | ||||
|  | ||||
|     if (value == null) { | ||||
|       return backup; | ||||
|     } | ||||
|  | ||||
|     return value.toString(); | ||||
|   } | ||||
|  | ||||
|   // Helper function to get integer value from JSON data | ||||
|   int getInt(String key, {int backup = -1, String subKey = ""}) { | ||||
|     dynamic value = getValue(key, backup: backup, subKey: subKey); | ||||
|  | ||||
|     if (value == null) { | ||||
|       return backup; | ||||
|     } | ||||
|  | ||||
|     return int.tryParse(value.toString()) ?? backup; | ||||
|   } | ||||
|  | ||||
|   // Helper function to get double value from JSON data | ||||
|   double getDouble(String key, {double backup = 0.0, String subKey = ""}) { | ||||
|     dynamic value = getValue(key, backup: backup, subKey: subKey); | ||||
|  | ||||
|     if (value == null) { | ||||
|       return backup; | ||||
|     } | ||||
|  | ||||
|     return double.tryParse(value.toString()) ?? backup; | ||||
|   } | ||||
|  | ||||
|   // Helper function to get boolean value from json data | ||||
|   bool getBool(String key, {bool backup = false, String subKey = ""}) { | ||||
|     dynamic value = getValue(key, backup: backup, subKey: subKey); | ||||
|  | ||||
|     if (value == null) { | ||||
|       return backup; | ||||
|     } | ||||
|  | ||||
|     return value.toString().toLowerCase() == "true"; | ||||
|   } | ||||
|  | ||||
|   // Return the InvenTree web server URL for this object | ||||
|   String get webUrl { | ||||
|  | ||||
|     if (api.isConnected()) { | ||||
| @@ -191,16 +281,16 @@ class InvenTreeModel { | ||||
|   // Accessor for the API | ||||
|   InvenTreeAPI get api => InvenTreeAPI(); | ||||
|  | ||||
|   int get pk => (jsondata["pk"] ?? -1) as int; | ||||
|  | ||||
|   int get pk => getInt("pk"); | ||||
|    | ||||
|   // Some common accessors | ||||
|   String get name => (jsondata["name"] ?? "") as String; | ||||
|   String get name => getString("name"); | ||||
|  | ||||
|   String get description => (jsondata["description"] ?? "") as String; | ||||
|   String get description => getString("description"); | ||||
|    | ||||
|   String get notes => getString("notes"); | ||||
|  | ||||
|   String get notes => (jsondata["notes"] ?? "") as String; | ||||
|  | ||||
|   int get parentId => (jsondata["parent"] ?? -1) as int; | ||||
|   int get parentId => getInt("parent"); | ||||
|  | ||||
|   // Legacy API provided external link as "URL", while newer API uses "link" | ||||
|   String get link => (jsondata["link"] ?? jsondata["URL"] ?? "") as String; | ||||
| @@ -297,15 +387,10 @@ class InvenTreeModel { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   String get keywords => (jsondata["keywords"] ?? "") as String; | ||||
|  | ||||
|   String get keywords => getString("keywords"); | ||||
|    | ||||
|   // Create a new object from JSON data (not a constructor!) | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) { | ||||
|  | ||||
|       var obj = InvenTreeModel.fromJson(json); | ||||
|  | ||||
|       return obj; | ||||
|   } | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeModel.fromJson(json); | ||||
|  | ||||
|   // Return the API detail endpoint for this Model object | ||||
|   String get url => "${URL}/${pk}/".replaceAll("//", "/"); | ||||
| @@ -746,9 +831,7 @@ class InvenTreePlugin extends InvenTreeModel { | ||||
|   InvenTreePlugin.fromJson(Map<String, dynamic> json) : super.fromJson(json); | ||||
|  | ||||
|   @override | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) { | ||||
|     return InvenTreePlugin.fromJson(json); | ||||
|   } | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePlugin.fromJson(json); | ||||
|  | ||||
|   @override | ||||
|   String get URL { | ||||
| @@ -765,10 +848,10 @@ class InvenTreePlugin extends InvenTreeModel { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   String get key => (jsondata["key"] ?? "") as String; | ||||
|  | ||||
|   bool get active => (jsondata["active"] ?? false) as bool; | ||||
|  | ||||
|   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>; | ||||
|  | ||||
| @@ -803,11 +886,11 @@ class InvenTreeGlobalSetting extends InvenTreeModel { | ||||
|   @override | ||||
|   String get URL => "settings/global/"; | ||||
|  | ||||
|   String get key => (jsondata["key"] ?? "") as String; | ||||
|  | ||||
|   String get value => (jsondata["value"] ?? "") as String; | ||||
|  | ||||
|   String get type => (jsondata["type"] ?? "") as String; | ||||
|   String get key => getString("key"); | ||||
|    | ||||
|   String get value => getString("value"); | ||||
|    | ||||
|   String get type => getString("type"); | ||||
|  | ||||
| } | ||||
|  | ||||
| @@ -836,8 +919,8 @@ class InvenTreeAttachment extends InvenTreeModel { | ||||
|   // Override this reference field for any subclasses | ||||
|   String get REFERENCE_FIELD => ""; | ||||
|  | ||||
|   String get attachment => (jsondata["attachment"] ?? "") as String; | ||||
|  | ||||
|   String get attachment => getString("attachment"); | ||||
|    | ||||
|   // Return the filename of the attachment | ||||
|   String get filename { | ||||
|     return attachment.split("/").last; | ||||
| @@ -874,8 +957,8 @@ class InvenTreeAttachment extends InvenTreeModel { | ||||
|     return FontAwesomeIcons.fileLines; | ||||
|   } | ||||
|  | ||||
|   String get comment => (jsondata["comment"] ?? "") as String; | ||||
|  | ||||
|   String get comment => getString("comment"); | ||||
|    | ||||
|   DateTime? get uploadDate { | ||||
|     if (jsondata.containsKey("upload_date")) { | ||||
|       return DateTime.tryParse((jsondata["upload_date"] ?? "") as String); | ||||
|   | ||||
| @@ -27,8 +27,8 @@ class InvenTreeNotification extends InvenTreeModel { | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   String get message => (jsondata["message"] ?? "") as String; | ||||
|  | ||||
|   String get message => getString("message"); | ||||
|    | ||||
|   DateTime? get creationDate { | ||||
|     if (jsondata.containsKey("creation")) { | ||||
|       return DateTime.tryParse((jsondata["creation"] ?? "") as String); | ||||
|   | ||||
| @@ -43,8 +43,8 @@ class InvenTreePartCategory extends InvenTreeModel { | ||||
|     return fields; | ||||
|   } | ||||
|  | ||||
|   String get pathstring => (jsondata["pathstring"] ?? "") as String; | ||||
|  | ||||
|   String get pathstring => getString("pathstring"); | ||||
|    | ||||
|   String get parentPathString { | ||||
|  | ||||
|     List<String> psplit = pathstring.split("/"); | ||||
| @@ -67,11 +67,7 @@ class InvenTreePartCategory extends InvenTreeModel { | ||||
|   int get partcount => (jsondata["part_count"] ?? jsondata["parts"] ?? 0) as int; | ||||
|  | ||||
|   @override | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) { | ||||
|     var cat = InvenTreePartCategory.fromJson(json); | ||||
|  | ||||
|     return cat; | ||||
|   } | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePartCategory.fromJson(json); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -87,22 +83,18 @@ class InvenTreePartTestTemplate extends InvenTreeModel { | ||||
|   @override | ||||
|   String get URL => "part/test-template/"; | ||||
|  | ||||
|   String get key => (jsondata["key"] ?? "") as String; | ||||
|   String get key => getString("key"); | ||||
|  | ||||
|   String get testName => (jsondata["test_name"] ?? "") as String; | ||||
|   String get testName => getString("test_name"); | ||||
|  | ||||
|   bool get required => (jsondata["required"] ?? false) as bool; | ||||
|   bool get required => getBool("required"); | ||||
|    | ||||
|   bool get requiresValue => getBool("requires_value"); | ||||
|  | ||||
|   bool get requiresValue => (jsondata["requires_value"] ?? false) as bool; | ||||
|  | ||||
|   bool get requiresAttachment => (jsondata["requires_attachment"] ?? false) as bool; | ||||
|   bool get requiresAttachment => getBool("requires_attachment"); | ||||
|  | ||||
|   @override | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) { | ||||
|     var template = InvenTreePartTestTemplate.fromJson(json); | ||||
|  | ||||
|     return template; | ||||
|   } | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePartTestTemplate.fromJson(json); | ||||
|  | ||||
|   bool passFailStatus() { | ||||
|  | ||||
| @@ -142,9 +134,7 @@ class InvenTreePartParameter extends InvenTreeModel { | ||||
|   String get URL => "part/parameter/"; | ||||
|  | ||||
|   @override | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) { | ||||
|     return InvenTreePartParameter.fromJson(json); | ||||
|   } | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePartParameter.fromJson(json); | ||||
|  | ||||
|   @override | ||||
|   Map<String, dynamic> formFields() { | ||||
| @@ -152,13 +142,13 @@ class InvenTreePartParameter extends InvenTreeModel { | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   String get name => (jsondata["template_detail"]?["name"] ?? "") as String; | ||||
|   String get name => getString("name", subKey: "template_detail"); | ||||
|  | ||||
|   @override | ||||
|   String get description => (jsondata["template_detail"]?["description"] ?? "") as String; | ||||
|  | ||||
|   String get value => jsondata["data"] as String; | ||||
|  | ||||
|   String get description => getString("description", subKey: "template_detail"); | ||||
|    | ||||
|   String get value => getString("data"); | ||||
|    | ||||
|   String get valueString { | ||||
|     String v = value; | ||||
|  | ||||
| @@ -170,7 +160,7 @@ class InvenTreePartParameter extends InvenTreeModel { | ||||
|     return v; | ||||
|   } | ||||
|  | ||||
|   String get units => (jsondata["template_detail"]?["units"] ?? "") as String; | ||||
|   String get units => getString("units", subKey: "template_detail"); | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -254,8 +244,8 @@ class InvenTreePart extends InvenTreeModel { | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   int get supplierCount => (jsondata["suppliers"] ?? 0) as int; | ||||
|  | ||||
|   int get supplierCount => getInt("suppliers", backup: 0); | ||||
|    | ||||
|   // Request supplier parts for this part | ||||
|   Future<List<InvenTreeSupplierPart>> getSupplierParts() async { | ||||
|     List<InvenTreeSupplierPart> _supplierParts = []; | ||||
| @@ -301,40 +291,26 @@ class InvenTreePart extends InvenTreeModel { | ||||
|  | ||||
|   int? get defaultLocation => jsondata["default_location"] as int?; | ||||
|  | ||||
|     // Get the number of stock on order for this Part | ||||
|     double get onOrder => double.tryParse(jsondata["ordering"].toString()) ?? 0; | ||||
|   double get onOrder => getDouble("ordering"); | ||||
|  | ||||
|     String get onOrderString { | ||||
|   String get onOrderString => simpleNumberString(onOrder); | ||||
|  | ||||
|       return simpleNumberString(onOrder); | ||||
|   double get inStock => getDouble("in_stock"); | ||||
|  | ||||
|   String get inStockString => simpleNumberString(inStock); | ||||
|  | ||||
|   // Get the 'available stock' for this Part | ||||
|   double get unallocatedStock { | ||||
|  | ||||
|     // Note that the 'available_stock' was not added until API v35 | ||||
|     if (jsondata.containsKey("unallocated_stock")) { | ||||
|       return double.tryParse(jsondata["unallocated_stock"].toString()) ?? 0; | ||||
|     } else { | ||||
|       return inStock; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|     // Get the stock count for this Part | ||||
|     double get inStock => double.tryParse(jsondata["in_stock"].toString()) ?? 0; | ||||
|  | ||||
|     String get inStockString { | ||||
|  | ||||
|       String q = simpleNumberString(inStock); | ||||
|  | ||||
|       return q; | ||||
|     } | ||||
|  | ||||
|     // Get the 'available stock' for this Part | ||||
|     double get unallocatedStock { | ||||
|  | ||||
|       // Note that the 'available_stock' was not added until API v35 | ||||
|       if (jsondata.containsKey("unallocated_stock")) { | ||||
|         return double.tryParse(jsondata["unallocated_stock"].toString()) ?? 0; | ||||
|       } else { | ||||
|         return inStock; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     String get unallocatedStockString { | ||||
|       String q = simpleNumberString(unallocatedStock); | ||||
|  | ||||
|       return q; | ||||
|     } | ||||
|     String get unallocatedStockString => simpleNumberString(unallocatedStock); | ||||
|  | ||||
|     String stockString({bool includeUnits = true}) { | ||||
|       String q = unallocatedStockString; | ||||
| @@ -350,39 +326,39 @@ class InvenTreePart extends InvenTreeModel { | ||||
|       return q; | ||||
|     } | ||||
|  | ||||
|     String get units => (jsondata["units"] ?? "") as String; | ||||
|     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 => double.tryParse(jsondata["building"].toString()) ?? 0; | ||||
|     double get building => getDouble("building"); | ||||
|  | ||||
|     // Get the number of BOMs this Part is used in (if it is a component) | ||||
|     int get usedInCount => (jsondata["used_in"] ?? 0) as int; | ||||
|     int get usedInCount => jsondata.containsKey("used_in") ? getInt("used_in", backup: 0) : 0; | ||||
|  | ||||
|     bool get isAssembly => (jsondata["assembly"] ?? false) as bool; | ||||
|     bool get isAssembly => getBool("assembly"); | ||||
|  | ||||
|     bool get isComponent => (jsondata["component"] ?? false) as bool; | ||||
|     bool get isComponent => getBool("component"); | ||||
|  | ||||
|     bool get isPurchaseable => (jsondata["purchaseable"] ?? false) as bool; | ||||
|     bool get isPurchaseable => getBool("purchaseable"); | ||||
|  | ||||
|     bool get isSalable => (jsondata["salable"] ?? false) as bool; | ||||
|     bool get isSalable => getBool("salable"); | ||||
|  | ||||
|     bool get isActive => (jsondata["active"] ?? false) as bool; | ||||
|     bool get isActive => getBool("active"); | ||||
|  | ||||
|     bool get isVirtual => (jsondata["virtual"] ?? false) as bool; | ||||
|     bool get isVirtual => getBool("virtual"); | ||||
|  | ||||
|     bool get isTrackable => (jsondata["trackable"] ?? false) as bool; | ||||
|     bool get isTrackable => getBool("trackable"); | ||||
|  | ||||
|     // Get the IPN (internal part number) for the Part instance | ||||
|     String get IPN => (jsondata["IPN"] ?? "") as String; | ||||
|     String get IPN => getString("IPN"); | ||||
|  | ||||
|     // Get the revision string for the Part instance | ||||
|     String get revision => (jsondata["revision"] ?? "") as String; | ||||
|     String get revision => getString("revision"); | ||||
|  | ||||
|     // Get the category ID for the Part instance (or "null" if does not exist) | ||||
|     int get categoryId => (jsondata["category"] ?? -1) as int; | ||||
|     int get categoryId => getInt("category"); | ||||
|  | ||||
|     // Get the category name for the Part instance | ||||
|     String get categoryName { | ||||
| @@ -404,15 +380,15 @@ class InvenTreePart extends InvenTreeModel { | ||||
|       return (jsondata["category_detail"]?["description"] ?? "") as String; | ||||
|     } | ||||
|     // Get the image URL for the Part instance | ||||
|     String get _image  => (jsondata["image"] ?? "") as String; | ||||
|     String get _image  => getString("image"); | ||||
|  | ||||
|     // Get the thumbnail URL for the Part instance | ||||
|     String get _thumbnail => (jsondata["thumbnail"] ?? "") as String; | ||||
|     String get _thumbnail => getString("thumbnail"); | ||||
|  | ||||
|     // Return the fully-qualified name for the Part instance | ||||
|     String get fullname { | ||||
|  | ||||
|       String fn = (jsondata["full_name"] ?? "") as String; | ||||
|       String fn = getString("full_name"); | ||||
|  | ||||
|       if (fn.isNotEmpty) return fn; | ||||
|  | ||||
| @@ -456,15 +432,10 @@ class InvenTreePart extends InvenTreeModel { | ||||
|     } | ||||
|  | ||||
|     // Return the "starred" status of this part | ||||
|     bool get starred => (jsondata["starred"] ?? false) as bool; | ||||
|     bool get starred => getBool("starred"); | ||||
|  | ||||
|   @override | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) { | ||||
|  | ||||
|     var part = InvenTreePart.fromJson(json); | ||||
|  | ||||
|     return part; | ||||
|   } | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePart.fromJson(json); | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -483,8 +454,6 @@ class InvenTreePartAttachment extends InvenTreeAttachment { | ||||
|   String get URL => "part/attachment/"; | ||||
|  | ||||
|   @override | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) { | ||||
|     return InvenTreePartAttachment.fromJson(json); | ||||
|   } | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePartAttachment.fromJson(json); | ||||
|  | ||||
| } | ||||
|   | ||||
							
								
								
									
										28
									
								
								lib/inventree/project_code.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								lib/inventree/project_code.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| 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); | ||||
|  | ||||
|   @override | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeProjectCode.fromJson(json); | ||||
|  | ||||
|   @override | ||||
|   String get URL => "project-code/"; | ||||
|  | ||||
|   @override | ||||
|   Map<String, dynamic> formFields() { | ||||
|     return { | ||||
|       "code": {}, | ||||
|       "description": {}, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   String get code => getString("code"); | ||||
| } | ||||
| @@ -34,6 +34,7 @@ class InvenTreePurchaseOrder extends InvenTreeModel { | ||||
|       }, | ||||
|       "supplier_reference": {}, | ||||
|       "description": {}, | ||||
|       "project_code": {}, | ||||
|       "target_date": {}, | ||||
|       "link": {}, | ||||
|       "responsible": {}, | ||||
| @@ -59,23 +60,32 @@ class InvenTreePurchaseOrder extends InvenTreeModel { | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   String get issueDate => (jsondata["issue_date"] ?? "") as String; | ||||
|   String get issueDate => getString("issue_date"); | ||||
|  | ||||
|   String get completeDate => (jsondata["complete_date"] ?? "") as String; | ||||
|   String get completeDate => getString("complete_date"); | ||||
|  | ||||
|   String get creationDate => (jsondata["creation_date"] ?? "") as String; | ||||
|   String get creationDate => getString("creation_date"); | ||||
|  | ||||
|   String get targetDate => (jsondata["target_date"] ?? "") as String; | ||||
|   String get targetDate => getString("target_date"); | ||||
|  | ||||
|   int get lineItemCount => (jsondata["line_items"] ?? 0) as int; | ||||
|   int get lineItemCount => getInt("line_items", backup: 0); | ||||
|    | ||||
|   bool get overdue => getBool("overdue"); | ||||
|  | ||||
|   bool get overdue => (jsondata["overdue"] ?? false) as bool; | ||||
|   String get reference => getString("reference"); | ||||
|  | ||||
|   String get reference => (jsondata["reference"] ?? "") as String; | ||||
|   int get responsibleId => getInt("responsible"); | ||||
|  | ||||
|   int get responsibleId => (jsondata["responsible"] ?? -1) as int; | ||||
|   int get supplierId => getInt("supplier"); | ||||
|  | ||||
|   int get supplierId => (jsondata["supplier"] ?? -1) as int; | ||||
|   // 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"); | ||||
|  | ||||
|   bool get hasProjectCode => projectCode.isNotEmpty; | ||||
|  | ||||
|   InvenTreeCompany? get supplier { | ||||
|  | ||||
| @@ -88,11 +98,11 @@ class InvenTreePurchaseOrder extends InvenTreeModel { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   String get supplierReference => (jsondata["supplier_reference"] ?? "") as String; | ||||
|   String get supplierReference => getString("supplier_reference"); | ||||
|  | ||||
|   int get status => (jsondata["status"] ?? -1) as int; | ||||
|   int get status => getInt("status"); | ||||
|  | ||||
|   String get statusText => (jsondata["status_text"] ?? "") as String; | ||||
|   String get statusText => getString("status_text"); | ||||
|  | ||||
|   bool get isOpen => status == PO_STATUS_PENDING || status == PO_STATUS_PLACED; | ||||
|  | ||||
| @@ -103,7 +113,7 @@ class InvenTreePurchaseOrder extends InvenTreeModel { | ||||
|   bool get isFailed => status == PO_STATUS_CANCELLED || status == PO_STATUS_LOST || status == PO_STATUS_RETURNED; | ||||
|  | ||||
|   double? get totalPrice { | ||||
|     String price = (jsondata["total_price"] ?? "") as String; | ||||
|     String price = getString("total_price"); | ||||
|  | ||||
|     if (price.isEmpty) { | ||||
|       return null; | ||||
| @@ -112,7 +122,7 @@ class InvenTreePurchaseOrder extends InvenTreeModel { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   String get totalPriceCurrency => (jsondata["total_price_currency"] ?? "") as String; | ||||
|   String get totalPriceCurrency => getString("total_price_currency"); | ||||
|  | ||||
|   Future<List<InvenTreePOLineItem>> getLineItems() async { | ||||
|  | ||||
| @@ -134,9 +144,7 @@ class InvenTreePurchaseOrder extends InvenTreeModel { | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) { | ||||
|     return InvenTreePurchaseOrder.fromJson(json); | ||||
|   } | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePurchaseOrder.fromJson(json); | ||||
|  | ||||
|   /// Mark this order as "placed" / "issued" | ||||
|   Future<void> issueOrder() async { | ||||
| @@ -199,17 +207,17 @@ class InvenTreePOLineItem extends InvenTreeModel { | ||||
|  | ||||
|   bool get isComplete => received >= quantity; | ||||
|  | ||||
|   double get quantity => (jsondata["quantity"] ?? 0) as double; | ||||
|   double get quantity => getDouble("quantity"); | ||||
|  | ||||
|   double get received => (jsondata["received"] ?? 0) as double; | ||||
|   double get received => getDouble("received"); | ||||
|  | ||||
|   double get outstanding => quantity - received; | ||||
|  | ||||
|   String get reference => (jsondata["reference"] ?? "") as String; | ||||
|   String get reference => getString("reference"); | ||||
|  | ||||
|   int get orderId => (jsondata["order"] ?? -1) as int; | ||||
|   int get orderId => getInt("order"); | ||||
|  | ||||
|   int get supplierPartId => (jsondata["part"] ?? -1) as int; | ||||
|   int get supplierPartId => getInt("part"); | ||||
|  | ||||
|   InvenTreePart? get part { | ||||
|     dynamic part_detail = jsondata["part_detail"]; | ||||
| @@ -232,20 +240,19 @@ class InvenTreePOLineItem extends InvenTreeModel { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   double get purchasePrice => double.parse((jsondata["purchase_price"] ?? "") as String); | ||||
|   double get purchasePrice => getDouble("purchase_price"); | ||||
|    | ||||
|   String get purchasePriceCurrency => getString("purchase_price_currency"); | ||||
|  | ||||
|   String get purchasePriceCurrency => (jsondata["purchase_price_currency"] ?? "") as String; | ||||
|   String get purchasePriceString => getString("purchase_price_string"); | ||||
|  | ||||
|   String get purchasePriceString => (jsondata["purchase_price_string"] ?? "") as String; | ||||
|  | ||||
|   int get destination => (jsondata["destination"] ?? -1) as int; | ||||
|  | ||||
|   Map<String, dynamic> get destinationDetail => (jsondata["destination_detail"] ?? {}) as Map<String, dynamic>; | ||||
|   int get destination => getInt("destination"); | ||||
|  | ||||
|   Map<String, dynamic> get destinationDetail => getMap("destination_detail"); | ||||
|    | ||||
|   @override | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) { | ||||
|     return InvenTreePOLineItem.fromJson(json); | ||||
|   } | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePOLineItem.fromJson(json); | ||||
|  | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -264,7 +271,6 @@ class InvenTreePurchaseOrderAttachment extends InvenTreeAttachment { | ||||
|   String get URL => "order/po/attachment/"; | ||||
|  | ||||
|   @override | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) { | ||||
|     return InvenTreePurchaseOrderAttachment.fromJson(json); | ||||
|   } | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePurchaseOrderAttachment.fromJson(json); | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -35,18 +35,18 @@ class InvenTreeStockItemTestResult extends InvenTreeModel { | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   String get key => (jsondata["key"] ?? "") as String; | ||||
|  | ||||
|   String get testName => (jsondata["test"] ?? "") as String; | ||||
|  | ||||
|   bool get result => (jsondata["result"] ?? false) as bool; | ||||
|  | ||||
|   String get value => (jsondata["value"] ?? "") as String; | ||||
|  | ||||
|   String get attachment => (jsondata["attachment"] ?? "") as String; | ||||
|  | ||||
|   String get date => (jsondata["date"] ?? "") as String; | ||||
|   String get key => getString("key"); | ||||
|    | ||||
|   String get testName => getString("test"); | ||||
|  | ||||
|   bool get result => getBool("result"); | ||||
|    | ||||
|   String get value => getString("value"); | ||||
|    | ||||
|   String get attachment => getString("attachment"); | ||||
|    | ||||
|   String get date => getString("date"); | ||||
|    | ||||
|   @override | ||||
|   InvenTreeStockItemTestResult createFromJson(Map<String, dynamic> json) { | ||||
|     var result = InvenTreeStockItemTestResult.fromJson(json); | ||||
| @@ -63,9 +63,7 @@ class InvenTreeStockItemHistory extends InvenTreeModel { | ||||
|   InvenTreeStockItemHistory.fromJson(Map<String, dynamic> json) : super.fromJson(json); | ||||
|  | ||||
|   @override | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) { | ||||
|     return InvenTreeStockItemHistory.fromJson(json); | ||||
|   } | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeStockItemHistory.fromJson(json); | ||||
|  | ||||
|   @override | ||||
|   String get URL => "stock/track/"; | ||||
| @@ -98,16 +96,10 @@ class InvenTreeStockItemHistory extends InvenTreeModel { | ||||
|     return DateFormat("yyyy-MM-dd").format(d); | ||||
|   } | ||||
|  | ||||
|   String get label => (jsondata["label"] ?? "") as String; | ||||
|  | ||||
|   String get label => getString("label"); | ||||
|    | ||||
|   // Return the "deltas" associated with this historical object | ||||
|   Map<String, dynamic> get deltas { | ||||
|     if (jsondata.containsKey("deltas")) { | ||||
|       return jsondata["deltas"] as Map<String, dynamic>; | ||||
|     } else { | ||||
|       return {}; | ||||
|     } | ||||
|   } | ||||
|   Map<String, dynamic> get deltas => getMap("deltas"); | ||||
|  | ||||
|   // Return the quantity string for this historical object | ||||
|   String get quantityString { | ||||
| @@ -122,12 +114,13 @@ class InvenTreeStockItemHistory extends InvenTreeModel { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   String get userString { | ||||
|     return (jsondata["user_detail"]?["username"] ?? "") as String; | ||||
|   } | ||||
|   String get userString => getString("username", subKey: "user_detail"); | ||||
| } | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Class representing a StockItem database instance | ||||
|  */ | ||||
| class InvenTreeStockItem extends InvenTreeModel { | ||||
|  | ||||
|   InvenTreeStockItem() : super(); | ||||
| @@ -237,16 +230,16 @@ class InvenTreeStockItem extends InvenTreeModel { | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   int get status => (jsondata["status"] ?? -1) as int; | ||||
|   int get status => getInt("status"); | ||||
|    | ||||
|   String get packaging => getString("packaging"); | ||||
|  | ||||
|   String get packaging => (jsondata["packaging"] ?? "") as String; | ||||
|   String get batch => getString("batch"); | ||||
|  | ||||
|   String get batch => (jsondata["batch"] ?? "") as String; | ||||
|  | ||||
|   int get partId => (jsondata["part"] ?? -1) as int; | ||||
|   int get partId => getInt("part"); | ||||
|    | ||||
|   double? get purchasePrice { | ||||
|     String pp = (jsondata["purchase_price"] ?? "") as String; | ||||
|     String pp = getString("purchase_price"); | ||||
|  | ||||
|     if (pp.isEmpty) { | ||||
|       return null; | ||||
| @@ -255,18 +248,18 @@ class InvenTreeStockItem extends InvenTreeModel { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   String get purchasePriceCurrency => (jsondata["purchase_price_currency"] ?? "") as String; | ||||
|   String get purchasePriceCurrency => getString("purchase_price_currency"); | ||||
|  | ||||
|   bool get hasPurchasePrice { | ||||
|     double? pp = purchasePrice; | ||||
|     return pp != null && pp > 0; | ||||
|   } | ||||
|  | ||||
|   int get purchaseOrderId => (jsondata["purchase_order"] ?? -1) as int; | ||||
|   int get purchaseOrderId => getInt("purchase_order"); | ||||
|  | ||||
|   int get trackingItemCount => (jsondata["tracking_items"] ?? 0) as int; | ||||
|  | ||||
|   bool get isBuilding => (jsondata["is_building"] ?? false) as bool; | ||||
|   int get trackingItemCount => getInt("tracking_items", backup: 0); | ||||
|    | ||||
|   bool get isBuilding => getBool("is_building"); | ||||
|  | ||||
|     // Date of last update | ||||
|     DateTime? get updatedDate { | ||||
| @@ -320,7 +313,7 @@ class InvenTreeStockItem extends InvenTreeModel { | ||||
|  | ||||
|       // Backup if first value fails | ||||
|       if (nm.isEmpty) { | ||||
|         nm = (jsondata["part__name"] ?? "") as String; | ||||
|         nm = getString("part__name"); | ||||
|       } | ||||
|  | ||||
|       return nm; | ||||
| @@ -335,7 +328,7 @@ class InvenTreeStockItem extends InvenTreeModel { | ||||
|       } | ||||
|  | ||||
|       if (desc.isEmpty) { | ||||
|         desc = (jsondata["part__description"] ?? "") as String; | ||||
|         desc = getString("part__description"); | ||||
|       } | ||||
|  | ||||
|       return desc; | ||||
| @@ -349,7 +342,7 @@ class InvenTreeStockItem extends InvenTreeModel { | ||||
|       } | ||||
|  | ||||
|       if (img.isEmpty) { | ||||
|         img = (jsondata["part__thumbnail"] ?? "") as String; | ||||
|         img = getString("part__thumbnail"); | ||||
|       } | ||||
|  | ||||
|       return img; | ||||
| @@ -371,7 +364,7 @@ class InvenTreeStockItem extends InvenTreeModel { | ||||
|  | ||||
|       // Try a different approach | ||||
|       if (thumb.isEmpty) { | ||||
|         thumb = (jsondata["part__thumbnail"] ?? "") as String; | ||||
|         thumb = getString("part__thumbnail"); | ||||
|       } | ||||
|  | ||||
|       // Still no thumbnail? Use the "no image" image | ||||
| @@ -380,7 +373,7 @@ class InvenTreeStockItem extends InvenTreeModel { | ||||
|       return thumb; | ||||
|     } | ||||
|  | ||||
|     int get supplierPartId => (jsondata["supplier_part"] ?? -1) as int; | ||||
|     int get supplierPartId => getInt("supplier_part"); | ||||
|  | ||||
|     String get supplierImage { | ||||
|       String thumb = ""; | ||||
| @@ -394,33 +387,15 @@ class InvenTreeStockItem extends InvenTreeModel { | ||||
|       return thumb; | ||||
|     } | ||||
|  | ||||
|     String get supplierName { | ||||
|       String sname = ""; | ||||
|     String get supplierName => getString("supplier_name", subKey: "supplier_detail"); | ||||
|  | ||||
|       if (jsondata.containsKey("supplier_detail")) { | ||||
|         sname = (jsondata["supplier_detail"]["supplier_name"] ?? "") as String; | ||||
|       } | ||||
|     String get units => getString("units", subKey: "part_detail"); | ||||
|  | ||||
|       return sname; | ||||
|     } | ||||
|     String get supplierSKU => getString("SKU", subKey: "supplier_part_detail"); | ||||
|  | ||||
|     String get units { | ||||
|       return (jsondata["part_detail"]?["units"] ?? "") as String; | ||||
|     } | ||||
|     String get serialNumber => getString("serial"); | ||||
|  | ||||
|     String get supplierSKU { | ||||
|       String sku = ""; | ||||
|  | ||||
|       if (jsondata.containsKey("supplier_part_detail")) { | ||||
|         sku = (jsondata["supplier_part_detail"]["SKU"] ?? "") as String; | ||||
|       } | ||||
|  | ||||
|       return sku; | ||||
|     } | ||||
|  | ||||
|     String get serialNumber => (jsondata["serial"] ?? "") as String; | ||||
|  | ||||
|     double get quantity => double.tryParse(jsondata["quantity"].toString()) ?? 0; | ||||
|     double get quantity => getDouble("quantity"); | ||||
|  | ||||
|     String quantityString({bool includeUnits = false}){ | ||||
|  | ||||
| @@ -440,11 +415,11 @@ class InvenTreeStockItem extends InvenTreeModel { | ||||
|       return q; | ||||
|     } | ||||
|  | ||||
|     double get allocated => double.tryParse(jsondata["allocated"].toString()) ?? 0; | ||||
|     double get allocated => getDouble("allocated"); | ||||
|  | ||||
|     double get available => quantity - allocated; | ||||
|  | ||||
|     int get locationId => (jsondata["location"] ?? -1) as int; | ||||
|     int get locationId => getInt("location"); | ||||
|  | ||||
|     bool isSerialized() => serialNumber.isNotEmpty && quantity.toInt() == 1; | ||||
|  | ||||
| @@ -459,15 +434,14 @@ class InvenTreeStockItem extends InvenTreeModel { | ||||
|     } | ||||
|  | ||||
|     String get locationName { | ||||
|       String loc = ""; | ||||
|  | ||||
|       if (locationId == -1 || !jsondata.containsKey("location_detail")) return "Unknown Location"; | ||||
|  | ||||
|       loc = (jsondata["location_detail"]["name"] ?? "") as String; | ||||
|       String loc = getString("name", subKey: "location_detail"); | ||||
|  | ||||
|       // Old-style name | ||||
|       if (loc.isEmpty) { | ||||
|         loc = (jsondata["location__name"] ?? "") as String; | ||||
|         loc = getString("location__name"); | ||||
|       } | ||||
|  | ||||
|       return loc; | ||||
| @@ -477,8 +451,7 @@ class InvenTreeStockItem extends InvenTreeModel { | ||||
|  | ||||
|       if (locationId == -1 || !jsondata.containsKey("location_detail")) return L10().locationNotSet; | ||||
|  | ||||
|       String _loc = (jsondata["location_detail"]["pathstring"] ?? "") as String; | ||||
|  | ||||
|       String _loc = getString("pathstring", subKey: "location_detail"); | ||||
|       if (_loc.isNotEmpty) { | ||||
|         return _loc; | ||||
|       } else { | ||||
| @@ -497,9 +470,7 @@ class InvenTreeStockItem extends InvenTreeModel { | ||||
|     } | ||||
|  | ||||
|     @override | ||||
|     InvenTreeModel createFromJson(Map<String, dynamic> json) { | ||||
|       return InvenTreeStockItem.fromJson(json); | ||||
|     } | ||||
|     InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeStockItem.fromJson(json); | ||||
|  | ||||
|     /* | ||||
|    * Perform stocktake action: | ||||
| @@ -601,9 +572,7 @@ class InvenTreeStockItemAttachment extends InvenTreeAttachment { | ||||
|   String get URL => "stock/attachment/"; | ||||
|  | ||||
|   @override | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) { | ||||
|     return InvenTreeStockItemAttachment.fromJson(json); | ||||
|   } | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeStockItemAttachment.fromJson(json); | ||||
|  | ||||
| } | ||||
|  | ||||
| @@ -620,7 +589,7 @@ class InvenTreeStockLocation extends InvenTreeModel { | ||||
|   @override | ||||
|   List<String> get rolesRequired => ["stock_location"]; | ||||
|  | ||||
|   String get pathstring => (jsondata["pathstring"] ?? "") as String; | ||||
|   String get pathstring => getString("pathstring"); | ||||
|  | ||||
|   @override | ||||
|   Map<String, dynamic> formFields() { | ||||
| @@ -658,10 +627,6 @@ class InvenTreeStockLocation extends InvenTreeModel { | ||||
|   int get itemcount => (jsondata["items"] ?? 0) as int; | ||||
|  | ||||
|   @override | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) { | ||||
|   InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeStockLocation.fromJson(json); | ||||
|  | ||||
|     var loc = InvenTreeStockLocation.fromJson(json); | ||||
|  | ||||
|     return loc; | ||||
|   } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -759,6 +759,9 @@ | ||||
|   "profileTapToCreate": "Tap to create or select a profile", | ||||
|   "@profileTapToCreate": {}, | ||||
|  | ||||
|   "projectCode": "Project Code", | ||||
|   "@projectCode": {}, | ||||
|  | ||||
|   "purchaseOrder": "Purchase Order", | ||||
|   "@purchaseOrder": {}, | ||||
|  | ||||
|   | ||||
| @@ -43,6 +43,8 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg | ||||
|  | ||||
|   int attachmentCount = 0; | ||||
|  | ||||
|   bool supportProjectCodes = false; | ||||
|  | ||||
|   @override | ||||
|   String getAppBarTitle() => L10().purchaseOrder; | ||||
|  | ||||
| @@ -139,6 +141,8 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg | ||||
|  | ||||
|     lines = await order.getLineItems(); | ||||
|  | ||||
|     supportProjectCodes = api.supportsProjectCodes && await api.getGlobalBooleanSetting("PROJECT_CODES_ENABLED"); | ||||
|  | ||||
|     completedLines = 0; | ||||
|  | ||||
|     for (var line in lines) { | ||||
| @@ -157,12 +161,20 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg | ||||
|   // Edit the currently displayed PurchaseOrder | ||||
|   Future <void> editOrder(BuildContext context) async { | ||||
|     var fields = order.formFields(); | ||||
|  | ||||
|     // Cannot edit supplier field from here | ||||
|     fields.remove("supplier"); | ||||
|  | ||||
|     // Contact model not supported by server | ||||
|     if (!api.supportsContactModel) { | ||||
|       fields.remove("contact"); | ||||
|     } | ||||
|  | ||||
|     // ProjectCode model not supported by server | ||||
|     if (!supportProjectCodes) { | ||||
|       fields.remove("project_code"); | ||||
|     } | ||||
|  | ||||
|     order.editForm( | ||||
|       context, | ||||
|       L10().purchaseOrderEdit, | ||||
| @@ -202,6 +214,14 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg | ||||
|  | ||||
|     tiles.add(headerTile(context)); | ||||
|  | ||||
|     if (supportProjectCodes && order.hasProjectCode) { | ||||
|       tiles.add(ListTile( | ||||
|         title: Text(L10().projectCode), | ||||
|         subtitle: Text("${order.projectCode} - ${order.projectCodeDescription}"), | ||||
|         leading: FaIcon(FontAwesomeIcons.list), | ||||
|       )); | ||||
|     } | ||||
|  | ||||
|     if (supplier != null) { | ||||
|       tiles.add(ListTile( | ||||
|         title: Text(L10().supplier), | ||||
|   | ||||
		Reference in New Issue
	
	Block a user