mirror of
https://github.com/inventree/inventree-app.git
synced 2025-12-03 18:59:50 +00:00
Attachments refactor (#737)
* refactor attachment code into its own file * Add getters * Remove custom models for each type of attachment * Refactor existing widgets * Fix double camera open bug * Remove dead code * Remove unused imports * Refactor common code * format * Update release notes
This commit is contained in:
@@ -1,3 +1,8 @@
|
|||||||
|
### x.xx.x - Month Year
|
||||||
|
---
|
||||||
|
|
||||||
|
- Fixes bug which launched camera twice when uploading an attachment
|
||||||
|
|
||||||
### 0.21.1 - November 2025
|
### 0.21.1 - November 2025
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
176
lib/inventree/attachment.dart
Normal file
176
lib/inventree/attachment.dart
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
import "dart:io";
|
||||||
|
|
||||||
|
import "package:flutter/cupertino.dart";
|
||||||
|
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
||||||
|
import "package:inventree/api.dart";
|
||||||
|
import "package:inventree/inventree/model.dart";
|
||||||
|
import "package:inventree/inventree/sentry.dart";
|
||||||
|
import "package:inventree/l10.dart";
|
||||||
|
import "package:inventree/widget/fields.dart";
|
||||||
|
import "package:inventree/widget/snacks.dart";
|
||||||
|
import "package:path/path.dart" as path;
|
||||||
|
|
||||||
|
class InvenTreeAttachment extends InvenTreeModel {
|
||||||
|
// Class representing an "attachment" file
|
||||||
|
InvenTreeAttachment() : super();
|
||||||
|
|
||||||
|
InvenTreeAttachment.fromJson(Map<String, dynamic> json)
|
||||||
|
: super.fromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
InvenTreeAttachment createFromJson(Map<String, dynamic> json) =>
|
||||||
|
InvenTreeAttachment.fromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get URL => "attachment/";
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, Map<String, dynamic>> formFields() {
|
||||||
|
Map<String, Map<String, dynamic>> fields = {"link": {}, "comment": {}};
|
||||||
|
|
||||||
|
if (!hasLink) {
|
||||||
|
fields.remove("link");
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The model type of the instance this attachment is associated with
|
||||||
|
String get modelType => getString("model_type");
|
||||||
|
|
||||||
|
// The ID of the instance this attachment is associated with
|
||||||
|
int get modelId => getInt("model_id");
|
||||||
|
|
||||||
|
String get attachment => getString("attachment");
|
||||||
|
|
||||||
|
bool get hasAttachment => attachment.isNotEmpty;
|
||||||
|
|
||||||
|
// Return the filename of the attachment
|
||||||
|
String get filename {
|
||||||
|
return attachment.split("/").last;
|
||||||
|
}
|
||||||
|
|
||||||
|
IconData get icon {
|
||||||
|
String fn = filename.toLowerCase();
|
||||||
|
|
||||||
|
if (fn.endsWith(".pdf")) {
|
||||||
|
return TablerIcons.file_type_pdf;
|
||||||
|
} else if (fn.endsWith(".csv")) {
|
||||||
|
return TablerIcons.file_type_csv;
|
||||||
|
} else if (fn.endsWith(".doc") || fn.endsWith(".docx")) {
|
||||||
|
return TablerIcons.file_type_doc;
|
||||||
|
} else if (fn.endsWith(".xls") || fn.endsWith(".xlsx")) {
|
||||||
|
return TablerIcons.file_type_xls;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Image formats
|
||||||
|
final List<String> img_formats = [".png", ".jpg", ".gif", ".bmp", ".svg"];
|
||||||
|
|
||||||
|
for (String fmt in img_formats) {
|
||||||
|
if (fn.endsWith(fmt)) {
|
||||||
|
return TablerIcons.file_type_jpg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TablerIcons.file;
|
||||||
|
}
|
||||||
|
|
||||||
|
String get comment => getString("comment");
|
||||||
|
|
||||||
|
DateTime? get uploadDate {
|
||||||
|
if (jsondata.containsKey("upload_date")) {
|
||||||
|
return DateTime.tryParse((jsondata["upload_date"] ?? "") as String);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a count of how many attachments exist against the specified model ID
|
||||||
|
Future<int> countAttachments(String modelType, int modelId) async {
|
||||||
|
Map<String, String> filters = {};
|
||||||
|
|
||||||
|
if (!api.supportsModernAttachments) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
filters["model_type"] = modelType;
|
||||||
|
filters["model_id"] = modelId.toString();
|
||||||
|
|
||||||
|
return count(filters: filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> uploadAttachment(
|
||||||
|
File attachment,
|
||||||
|
String modelType,
|
||||||
|
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);
|
||||||
|
|
||||||
|
String url = URL;
|
||||||
|
|
||||||
|
if (comment.isNotEmpty) {
|
||||||
|
data["comment"] = comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
data["model_type"] = modelType;
|
||||||
|
data["model_id"] = modelId.toString();
|
||||||
|
|
||||||
|
final APIResponse response = await InvenTreeAPI().uploadFile(
|
||||||
|
url,
|
||||||
|
attachment,
|
||||||
|
method: "POST",
|
||||||
|
name: "attachment",
|
||||||
|
fields: data,
|
||||||
|
);
|
||||||
|
|
||||||
|
return response.successful();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> uploadImage(
|
||||||
|
String modelType,
|
||||||
|
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(":", "-");
|
||||||
|
|
||||||
|
// Rename the file with a unique name
|
||||||
|
String filename = "${dir}/${prefix}_image_${now}${ext}";
|
||||||
|
|
||||||
|
try {
|
||||||
|
return file.rename(filename).then((File renamed) {
|
||||||
|
return uploadAttachment(renamed, modelType, modelId).then((
|
||||||
|
success,
|
||||||
|
) {
|
||||||
|
result = success;
|
||||||
|
showSnackIcon(
|
||||||
|
result ? L10().imageUploadSuccess : L10().imageUploadFailure,
|
||||||
|
success: result,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (error, stackTrace) {
|
||||||
|
sentryReportError("uploadImage", error, stackTrace);
|
||||||
|
showSnackIcon(L10().imageUploadFailure, success: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Download this attachment file
|
||||||
|
*/
|
||||||
|
Future<void> downloadAttachment() async {
|
||||||
|
await InvenTreeAPI().downloadFile(attachment);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -111,31 +111,6 @@ class InvenTreeCompany extends InvenTreeModel {
|
|||||||
InvenTreeCompany.fromJson(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);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get REFERENCE_FIELD => "company";
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get REF_MODEL_TYPE => "company";
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get URL => InvenTreeAPI().supportsModernAttachments
|
|
||||||
? "attachment/"
|
|
||||||
: "company/attachment/";
|
|
||||||
|
|
||||||
@override
|
|
||||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
|
||||||
InvenTreeCompanyAttachment.fromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The InvenTreeSupplierPart class represents the SupplierPart model in the InvenTree database
|
* The InvenTreeSupplierPart class represents the SupplierPart model in the InvenTree database
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import "dart:async";
|
import "dart:async";
|
||||||
import "dart:io";
|
|
||||||
|
|
||||||
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
@@ -15,7 +14,6 @@ import "package:inventree/helpers.dart";
|
|||||||
import "package:inventree/inventree/sentry.dart";
|
import "package:inventree/inventree/sentry.dart";
|
||||||
|
|
||||||
import "package:inventree/widget/dialogs.dart";
|
import "package:inventree/widget/dialogs.dart";
|
||||||
import "package:inventree/widget/fields.dart";
|
|
||||||
|
|
||||||
// Paginated response object
|
// Paginated response object
|
||||||
class InvenTreePageResponse {
|
class InvenTreePageResponse {
|
||||||
@@ -934,171 +932,3 @@ class InvenTreeUserSetting extends InvenTreeGlobalSetting {
|
|||||||
@override
|
@override
|
||||||
String get URL => "settings/user/";
|
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);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get URL => "attachment/";
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, Map<String, dynamic>> formFields() {
|
|
||||||
Map<String, Map<String, dynamic>> fields = {"link": {}, "comment": {}};
|
|
||||||
|
|
||||||
if (!hasLink) {
|
|
||||||
fields.remove("link");
|
|
||||||
}
|
|
||||||
|
|
||||||
return fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Override this reference field for any subclasses
|
|
||||||
// Note: This is used for the *legacy* attachment API
|
|
||||||
String get REFERENCE_FIELD => "";
|
|
||||||
|
|
||||||
// Override this reference field for any subclasses
|
|
||||||
// Note: This is used for the *modern* attachment API
|
|
||||||
String get REF_MODEL_TYPE => "";
|
|
||||||
|
|
||||||
String get attachment => getString("attachment");
|
|
||||||
|
|
||||||
bool get hasAttachment => attachment.isNotEmpty;
|
|
||||||
|
|
||||||
// Return the filename of the attachment
|
|
||||||
String get filename {
|
|
||||||
return attachment.split("/").last;
|
|
||||||
}
|
|
||||||
|
|
||||||
IconData get icon {
|
|
||||||
String fn = filename.toLowerCase();
|
|
||||||
|
|
||||||
if (fn.endsWith(".pdf")) {
|
|
||||||
return TablerIcons.file_type_pdf;
|
|
||||||
} else if (fn.endsWith(".csv")) {
|
|
||||||
return TablerIcons.file_type_csv;
|
|
||||||
} else if (fn.endsWith(".doc") || fn.endsWith(".docx")) {
|
|
||||||
return TablerIcons.file_type_doc;
|
|
||||||
} else if (fn.endsWith(".xls") || fn.endsWith(".xlsx")) {
|
|
||||||
return TablerIcons.file_type_xls;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Image formats
|
|
||||||
final List<String> img_formats = [".png", ".jpg", ".gif", ".bmp", ".svg"];
|
|
||||||
|
|
||||||
for (String fmt in img_formats) {
|
|
||||||
if (fn.endsWith(fmt)) {
|
|
||||||
return TablerIcons.file_type_jpg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return TablerIcons.file;
|
|
||||||
}
|
|
||||||
|
|
||||||
String get comment => getString("comment");
|
|
||||||
|
|
||||||
DateTime? get uploadDate {
|
|
||||||
if (jsondata.containsKey("upload_date")) {
|
|
||||||
return DateTime.tryParse((jsondata["upload_date"] ?? "") as String);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
filters["model_type"] = REF_MODEL_TYPE;
|
|
||||||
filters["model_id"] = modelId.toString();
|
|
||||||
} else {
|
|
||||||
filters[REFERENCE_FIELD] = modelId.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return count(filters: filters);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
String url = URL;
|
|
||||||
|
|
||||||
if (comment.isNotEmpty) {
|
|
||||||
data["comment"] = comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
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'",
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
data[REFERENCE_FIELD] = modelId.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
final APIResponse response = await InvenTreeAPI().uploadFile(
|
|
||||||
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(":", "-");
|
|
||||||
|
|
||||||
// Rename the file with a unique name
|
|
||||||
String filename = "${dir}/${prefix}_image_${now}${ext}";
|
|
||||||
|
|
||||||
try {
|
|
||||||
file.rename(filename).then((File renamed) {
|
|
||||||
uploadAttachment(renamed, modelId).then((success) {
|
|
||||||
result = success;
|
|
||||||
showSnackIcon(
|
|
||||||
result ? L10().imageUploadSuccess : L10().imageUploadFailure,
|
|
||||||
success: result,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} catch (error, stackTrace) {
|
|
||||||
sentryReportError("uploadImage", error, stackTrace);
|
|
||||||
showSnackIcon(L10().imageUploadFailure, success: false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Download this attachment file
|
|
||||||
*/
|
|
||||||
Future<void> downloadAttachment() async {
|
|
||||||
await InvenTreeAPI().downloadFile(attachment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -547,28 +547,3 @@ class InvenTreePartPricing extends InvenTreeModel {
|
|||||||
double? get saleHistoryMin => getDoubleOrNull("sale_history_min");
|
double? get saleHistoryMin => getDoubleOrNull("sale_history_min");
|
||||||
double? get saleHistoryMax => getDoubleOrNull("sale_history_max");
|
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);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get REFERENCE_FIELD => "part";
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get REF_MODEL_TYPE => "part";
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get URL => InvenTreeAPI().supportsModernAttachments
|
|
||||||
? "attachment/"
|
|
||||||
: "part/attachment/";
|
|
||||||
|
|
||||||
@override
|
|
||||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
|
||||||
InvenTreePartAttachment.fromJson(json);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -336,28 +336,3 @@ class InvenTreePOExtraLineItem extends InvenTreeExtraLineItem {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Class representing an attachment file against a PurchaseOrder object
|
|
||||||
*/
|
|
||||||
class InvenTreePurchaseOrderAttachment extends InvenTreeAttachment {
|
|
||||||
InvenTreePurchaseOrderAttachment() : super();
|
|
||||||
|
|
||||||
InvenTreePurchaseOrderAttachment.fromJson(Map<String, dynamic> json)
|
|
||||||
: super.fromJson(json);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get REFERENCE_FIELD => "order";
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get REF_MODEL_TYPE => "purchaseorder";
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get URL => InvenTreeAPI().supportsModernAttachments
|
|
||||||
? "attachment/"
|
|
||||||
: "order/po/attachment/";
|
|
||||||
|
|
||||||
@override
|
|
||||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
|
||||||
InvenTreePurchaseOrderAttachment.fromJson(json);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ class InvenTreeSalesOrderShipment extends InvenTreeModel {
|
|||||||
/*
|
/*
|
||||||
* Class representing an allocation of stock against a SalesOrderShipment
|
* Class representing an allocation of stock against a SalesOrderShipment
|
||||||
*/
|
*/
|
||||||
class InvenTreeSalesOrderAllocation extends InvenTreeAttachment {
|
class InvenTreeSalesOrderAllocation extends InvenTreeModel {
|
||||||
InvenTreeSalesOrderAllocation() : super();
|
InvenTreeSalesOrderAllocation() : super();
|
||||||
|
|
||||||
InvenTreeSalesOrderAllocation.fromJson(Map<String, dynamic> json)
|
InvenTreeSalesOrderAllocation.fromJson(Map<String, dynamic> json)
|
||||||
@@ -428,48 +428,3 @@ class InvenTreeSalesOrderAllocation extends InvenTreeAttachment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Class representing an attachment file against a SalesOrder object
|
|
||||||
*/
|
|
||||||
class InvenTreeSalesOrderAttachment extends InvenTreeAttachment {
|
|
||||||
InvenTreeSalesOrderAttachment() : super();
|
|
||||||
|
|
||||||
InvenTreeSalesOrderAttachment.fromJson(Map<String, dynamic> json)
|
|
||||||
: super.fromJson(json);
|
|
||||||
|
|
||||||
@override
|
|
||||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
|
||||||
InvenTreeSalesOrderAttachment.fromJson(json);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get REFERENCE_FIELD => "order";
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get REF_MODEL_TYPE => "salesorder";
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get URL => InvenTreeAPI().supportsModernAttachments
|
|
||||||
? "attachment/"
|
|
||||||
: "order/so/attachment/";
|
|
||||||
}
|
|
||||||
|
|
||||||
class InvenTreeSalesOrderShipmentAttachment extends InvenTreeAttachment {
|
|
||||||
InvenTreeSalesOrderShipmentAttachment() : super();
|
|
||||||
|
|
||||||
InvenTreeSalesOrderShipmentAttachment.fromJson(Map<String, dynamic> json)
|
|
||||||
: super.fromJson(json);
|
|
||||||
|
|
||||||
@override
|
|
||||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
|
||||||
InvenTreeSalesOrderShipmentAttachment.fromJson(json);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get REFERENCE_FIELD => "shipment";
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get REF_MODEL_TYPE => "salesordershipment";
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get URL => "attachment/";
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -575,31 +575,6 @@ class InvenTreeStockItem extends InvenTreeModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Class representing an attachment file against a StockItem object
|
|
||||||
*/
|
|
||||||
class InvenTreeStockItemAttachment extends InvenTreeAttachment {
|
|
||||||
InvenTreeStockItemAttachment() : super();
|
|
||||||
|
|
||||||
InvenTreeStockItemAttachment.fromJson(Map<String, dynamic> json)
|
|
||||||
: super.fromJson(json);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get REFERENCE_FIELD => "stock_item";
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get REF_MODEL_TYPE => "stockitem";
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get URL => InvenTreeAPI().supportsModernAttachments
|
|
||||||
? "attachment/"
|
|
||||||
: "stock/attachment/";
|
|
||||||
|
|
||||||
@override
|
|
||||||
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
|
|
||||||
InvenTreeStockItemAttachment.fromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
class InvenTreeStockLocation extends InvenTreeModel {
|
class InvenTreeStockLocation extends InvenTreeModel {
|
||||||
InvenTreeStockLocation() : super();
|
InvenTreeStockLocation() : super();
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,13 @@ import "dart:io";
|
|||||||
|
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
||||||
|
import "package:inventree/api.dart";
|
||||||
|
import "package:inventree/inventree/attachment.dart";
|
||||||
|
import "package:inventree/widget/link_icon.dart";
|
||||||
import "package:one_context/one_context.dart";
|
import "package:one_context/one_context.dart";
|
||||||
|
|
||||||
import "package:inventree/api.dart";
|
|
||||||
import "package:inventree/l10.dart";
|
import "package:inventree/l10.dart";
|
||||||
import "package:inventree/app_colors.dart";
|
import "package:inventree/app_colors.dart";
|
||||||
|
|
||||||
import "package:inventree/inventree/model.dart";
|
|
||||||
|
|
||||||
import "package:inventree/widget/fields.dart";
|
import "package:inventree/widget/fields.dart";
|
||||||
import "package:inventree/widget/progress.dart";
|
import "package:inventree/widget/progress.dart";
|
||||||
import "package:inventree/widget/snacks.dart";
|
import "package:inventree/widget/snacks.dart";
|
||||||
@@ -23,13 +22,13 @@ import "package:inventree/widget/refreshable_state.dart";
|
|||||||
*/
|
*/
|
||||||
class AttachmentWidget extends StatefulWidget {
|
class AttachmentWidget extends StatefulWidget {
|
||||||
const AttachmentWidget(
|
const AttachmentWidget(
|
||||||
this.attachmentClass,
|
this.modelType,
|
||||||
this.modelId,
|
this.modelId,
|
||||||
this.imagePrefix,
|
this.imagePrefix,
|
||||||
this.hasUploadPermission,
|
this.hasUploadPermission,
|
||||||
) : super();
|
) : super();
|
||||||
|
|
||||||
final InvenTreeAttachment attachmentClass;
|
final String modelType;
|
||||||
final int modelId;
|
final int modelId;
|
||||||
final bool hasUploadPermission;
|
final bool hasUploadPermission;
|
||||||
final String imagePrefix;
|
final String imagePrefix;
|
||||||
@@ -54,15 +53,15 @@ class _AttachmentWidgetState extends RefreshableState<AttachmentWidget> {
|
|||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(TablerIcons.camera),
|
icon: Icon(TablerIcons.camera),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
widget.attachmentClass.uploadImage(
|
InvenTreeAttachment()
|
||||||
widget.modelId,
|
.uploadImage(
|
||||||
prefix: widget.imagePrefix,
|
widget.modelType,
|
||||||
);
|
widget.modelId,
|
||||||
FilePickerDialog.pickImageFromCamera().then((File? file) {
|
prefix: widget.imagePrefix,
|
||||||
upload(context, file).then((_) {
|
)
|
||||||
refresh(context);
|
.then((_) {
|
||||||
});
|
refresh(context);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
@@ -83,8 +82,9 @@ class _AttachmentWidgetState extends RefreshableState<AttachmentWidget> {
|
|||||||
|
|
||||||
showLoadingOverlay();
|
showLoadingOverlay();
|
||||||
|
|
||||||
final bool result = await widget.attachmentClass.uploadAttachment(
|
final bool result = await InvenTreeAttachment().uploadAttachment(
|
||||||
file,
|
file,
|
||||||
|
widget.modelType,
|
||||||
widget.modelId,
|
widget.modelId,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -168,25 +168,24 @@ class _AttachmentWidgetState extends RefreshableState<AttachmentWidget> {
|
|||||||
Future<void> request(BuildContext context) async {
|
Future<void> request(BuildContext context) async {
|
||||||
Map<String, String> filters = {};
|
Map<String, String> filters = {};
|
||||||
|
|
||||||
if (InvenTreeAPI().supportsModernAttachments) {
|
filters["model_type"] = widget.modelType;
|
||||||
filters["model_type"] = widget.attachmentClass.REF_MODEL_TYPE;
|
filters["model_id"] = widget.modelId.toString();
|
||||||
filters["model_id"] = widget.modelId.toString();
|
|
||||||
} else {
|
|
||||||
filters[widget.attachmentClass.REFERENCE_FIELD] = widget.modelId
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
await widget.attachmentClass.list(filters: filters).then((var results) {
|
List<InvenTreeAttachment> _attachments = [];
|
||||||
attachments.clear();
|
|
||||||
|
|
||||||
|
InvenTreeAttachment().list(filters: filters).then((var results) {
|
||||||
for (var result in results) {
|
for (var result in results) {
|
||||||
if (result is InvenTreeAttachment) {
|
if (result is InvenTreeAttachment) {
|
||||||
attachments.add(result);
|
_attachments.add(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
setState(() {});
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
attachments = _attachments;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -240,3 +239,40 @@ class _AttachmentWidgetState extends RefreshableState<AttachmentWidget> {
|
|||||||
return tiles;
|
return tiles;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return a ListTile to display attachments for the specified model
|
||||||
|
*/
|
||||||
|
ListTile? ShowAttachmentsItem(
|
||||||
|
BuildContext context,
|
||||||
|
String modelType,
|
||||||
|
int modelId,
|
||||||
|
String imagePrefix,
|
||||||
|
int attachmentCount,
|
||||||
|
bool hasUploadPermission,
|
||||||
|
) {
|
||||||
|
if (!InvenTreeAPI().supportsModernAttachments) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
|
title: Text(L10().attachments),
|
||||||
|
leading: Icon(TablerIcons.file, color: COLOR_ACTION),
|
||||||
|
trailing: LinkIcon(
|
||||||
|
text: attachmentCount > 0 ? attachmentCount.toString() : null,
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => AttachmentWidget(
|
||||||
|
modelType,
|
||||||
|
modelId,
|
||||||
|
imagePrefix,
|
||||||
|
hasUploadPermission,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_speed_dial/flutter_speed_dial.dart";
|
import "package:flutter_speed_dial/flutter_speed_dial.dart";
|
||||||
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
||||||
|
import "package:inventree/inventree/attachment.dart";
|
||||||
|
|
||||||
import "package:inventree/l10.dart";
|
import "package:inventree/l10.dart";
|
||||||
import "package:inventree/api.dart";
|
import "package:inventree/api.dart";
|
||||||
@@ -184,15 +185,15 @@ class _CompanyDetailState extends RefreshableState<CompanyDetailWidget> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
InvenTreeCompanyAttachment().countAttachments(widget.company.pk).then((
|
InvenTreeAttachment()
|
||||||
value,
|
.countAttachments(InvenTreeCompany.MODEL_TYPE, widget.company.pk)
|
||||||
) {
|
.then((value) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
attachmentCount = value;
|
attachmentCount = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> editCompany(BuildContext context) async {
|
Future<void> editCompany(BuildContext context) async {
|
||||||
@@ -393,29 +394,19 @@ class _CompanyDetailState extends RefreshableState<CompanyDetailWidget> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
tiles.add(
|
ListTile? attachmentTile = ShowAttachmentsItem(
|
||||||
ListTile(
|
context,
|
||||||
title: Text(L10().attachments),
|
InvenTreeCompany.MODEL_TYPE,
|
||||||
leading: Icon(TablerIcons.file, color: COLOR_ACTION),
|
widget.company.pk,
|
||||||
trailing: LinkIcon(
|
widget.company.name,
|
||||||
text: attachmentCount > 0 ? attachmentCount.toString() : null,
|
attachmentCount,
|
||||||
),
|
widget.company.canEdit,
|
||||||
onTap: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => AttachmentWidget(
|
|
||||||
InvenTreeCompanyAttachment(),
|
|
||||||
widget.company.pk,
|
|
||||||
widget.company.name,
|
|
||||||
InvenTreeCompany().canEdit,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (attachmentTile != null) {
|
||||||
|
tiles.add(attachmentTile);
|
||||||
|
}
|
||||||
|
|
||||||
return tiles;
|
return tiles;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import "package:inventree/app_colors.dart";
|
|||||||
import "package:inventree/barcode/barcode.dart";
|
import "package:inventree/barcode/barcode.dart";
|
||||||
import "package:inventree/barcode/purchase_order.dart";
|
import "package:inventree/barcode/purchase_order.dart";
|
||||||
import "package:inventree/helpers.dart";
|
import "package:inventree/helpers.dart";
|
||||||
|
import "package:inventree/inventree/attachment.dart";
|
||||||
import "package:inventree/l10.dart";
|
import "package:inventree/l10.dart";
|
||||||
|
|
||||||
import "package:inventree/inventree/model.dart";
|
import "package:inventree/inventree/model.dart";
|
||||||
@@ -174,8 +175,12 @@ class _PurchaseOrderDetailState
|
|||||||
|
|
||||||
/// Upload an image against the current PurchaseOrder
|
/// Upload an image against the current PurchaseOrder
|
||||||
Future<void> _uploadImage(BuildContext context) async {
|
Future<void> _uploadImage(BuildContext context) async {
|
||||||
InvenTreePurchaseOrderAttachment()
|
InvenTreeAttachment()
|
||||||
.uploadImage(widget.order.pk, prefix: widget.order.reference)
|
.uploadImage(
|
||||||
|
InvenTreePurchaseOrder.MODEL_TYPE,
|
||||||
|
widget.order.pk,
|
||||||
|
prefix: widget.order.reference,
|
||||||
|
)
|
||||||
.then((result) => refresh(context));
|
.then((result) => refresh(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,15 +300,15 @@ class _PurchaseOrderDetailState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InvenTreePurchaseOrderAttachment().countAttachments(widget.order.pk).then((
|
InvenTreeAttachment()
|
||||||
int value,
|
.countAttachments(InvenTreePurchaseOrder.MODEL_TYPE, widget.order.pk)
|
||||||
) {
|
.then((int value) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
attachmentCount = value;
|
attachmentCount = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (api.supportsPurchaseOrderDestination &&
|
if (api.supportsPurchaseOrderDestination &&
|
||||||
widget.order.destinationId > 0) {
|
widget.order.destinationId > 0) {
|
||||||
@@ -565,30 +570,19 @@ class _PurchaseOrderDetailState
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Attachments
|
ListTile? attachmentTile = ShowAttachmentsItem(
|
||||||
tiles.add(
|
context,
|
||||||
ListTile(
|
InvenTreePurchaseOrder.MODEL_TYPE,
|
||||||
title: Text(L10().attachments),
|
widget.order.pk,
|
||||||
leading: Icon(TablerIcons.file, color: COLOR_ACTION),
|
widget.order.reference,
|
||||||
trailing: LinkIcon(
|
attachmentCount,
|
||||||
text: attachmentCount > 0 ? attachmentCount.toString() : null,
|
widget.order.canEdit,
|
||||||
),
|
|
||||||
onTap: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => AttachmentWidget(
|
|
||||||
InvenTreePurchaseOrderAttachment(),
|
|
||||||
widget.order.pk,
|
|
||||||
widget.order.reference,
|
|
||||||
widget.order.canEdit,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (attachmentTile != null) {
|
||||||
|
tiles.add(attachmentTile);
|
||||||
|
}
|
||||||
|
|
||||||
return tiles;
|
return tiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import "package:flutter_speed_dial/flutter_speed_dial.dart";
|
|||||||
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
||||||
import "package:inventree/barcode/barcode.dart";
|
import "package:inventree/barcode/barcode.dart";
|
||||||
import "package:inventree/barcode/sales_order.dart";
|
import "package:inventree/barcode/sales_order.dart";
|
||||||
|
import "package:inventree/inventree/attachment.dart";
|
||||||
import "package:inventree/inventree/company.dart";
|
import "package:inventree/inventree/company.dart";
|
||||||
import "package:inventree/inventree/sales_order.dart";
|
import "package:inventree/inventree/sales_order.dart";
|
||||||
import "package:inventree/preferences.dart";
|
import "package:inventree/preferences.dart";
|
||||||
@@ -108,8 +109,12 @@ class _SalesOrderDetailState extends RefreshableState<SalesOrderDetailWidget> {
|
|||||||
|
|
||||||
/// Upload an image for this order
|
/// Upload an image for this order
|
||||||
Future<void> _uploadImage(BuildContext context) async {
|
Future<void> _uploadImage(BuildContext context) async {
|
||||||
InvenTreeSalesOrderAttachment()
|
InvenTreeAttachment()
|
||||||
.uploadImage(widget.order.pk, prefix: widget.order.reference)
|
.uploadImage(
|
||||||
|
InvenTreeSalesOrder.MODEL_TYPE,
|
||||||
|
widget.order.pk,
|
||||||
|
prefix: widget.order.reference,
|
||||||
|
)
|
||||||
.then((result) => refresh(context));
|
.then((result) => refresh(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,15 +271,15 @@ class _SalesOrderDetailState extends RefreshableState<SalesOrderDetailWidget> {
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
InvenTreeSalesOrderAttachment().countAttachments(widget.order.pk).then((
|
InvenTreeAttachment()
|
||||||
int value,
|
.countAttachments(InvenTreeSalesOrder.MODEL_TYPE, widget.order.pk)
|
||||||
) {
|
.then((int value) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
attachmentCount = value;
|
attachmentCount = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Count number of "extra line items" against this order
|
// Count number of "extra line items" against this order
|
||||||
InvenTreeSOExtraLineItem()
|
InvenTreeSOExtraLineItem()
|
||||||
@@ -492,30 +497,19 @@ class _SalesOrderDetailState extends RefreshableState<SalesOrderDetailWidget> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Attachments
|
ListTile? attachmentTile = ShowAttachmentsItem(
|
||||||
tiles.add(
|
context,
|
||||||
ListTile(
|
InvenTreeSalesOrder.MODEL_TYPE,
|
||||||
title: Text(L10().attachments),
|
widget.order.pk,
|
||||||
leading: Icon(TablerIcons.file, color: COLOR_ACTION),
|
widget.order.reference,
|
||||||
trailing: LinkIcon(
|
attachmentCount,
|
||||||
text: attachmentCount > 0 ? attachmentCount.toString() : null,
|
widget.order.canEdit,
|
||||||
),
|
|
||||||
onTap: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => AttachmentWidget(
|
|
||||||
InvenTreeSalesOrderAttachment(),
|
|
||||||
widget.order.pk,
|
|
||||||
widget.order.reference,
|
|
||||||
widget.order.canEdit,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (attachmentTile != null) {
|
||||||
|
tiles.add(attachmentTile);
|
||||||
|
}
|
||||||
|
|
||||||
return tiles;
|
return tiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
|||||||
import "package:inventree/api.dart";
|
import "package:inventree/api.dart";
|
||||||
import "package:inventree/api_form.dart";
|
import "package:inventree/api_form.dart";
|
||||||
import "package:inventree/app_colors.dart";
|
import "package:inventree/app_colors.dart";
|
||||||
|
import "package:inventree/inventree/attachment.dart";
|
||||||
import "package:inventree/inventree/sales_order.dart";
|
import "package:inventree/inventree/sales_order.dart";
|
||||||
import "package:inventree/l10.dart";
|
import "package:inventree/l10.dart";
|
||||||
import "package:inventree/preferences.dart";
|
import "package:inventree/preferences.dart";
|
||||||
@@ -91,8 +92,11 @@ class _SOShipmentDetailWidgetState
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
InvenTreeSalesOrderShipmentAttachment()
|
InvenTreeAttachment()
|
||||||
.countAttachments(widget.shipment.pk)
|
.countAttachments(
|
||||||
|
InvenTreeSalesOrderShipment.MODEL_TYPE,
|
||||||
|
widget.shipment.pk,
|
||||||
|
)
|
||||||
.then((int value) {
|
.then((int value) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -104,8 +108,12 @@ class _SOShipmentDetailWidgetState
|
|||||||
|
|
||||||
/// Upload an image for this shipment
|
/// Upload an image for this shipment
|
||||||
Future<void> _uploadImage(BuildContext context) async {
|
Future<void> _uploadImage(BuildContext context) async {
|
||||||
InvenTreeSalesOrderShipmentAttachment()
|
InvenTreeAttachment()
|
||||||
.uploadImage(widget.shipment.pk, prefix: widget.shipment.reference)
|
.uploadImage(
|
||||||
|
InvenTreeSalesOrderShipment.MODEL_TYPE,
|
||||||
|
widget.shipment.pk,
|
||||||
|
prefix: widget.shipment.reference,
|
||||||
|
)
|
||||||
.then((result) => refresh(context));
|
.then((result) => refresh(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,30 +347,19 @@ class _SOShipmentDetailWidgetState
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Attachments
|
ListTile? attachmentTile = ShowAttachmentsItem(
|
||||||
tiles.add(
|
context,
|
||||||
ListTile(
|
InvenTreeSalesOrderShipment.MODEL_TYPE,
|
||||||
title: Text(L10().attachments),
|
widget.shipment.pk,
|
||||||
leading: Icon(TablerIcons.file, color: COLOR_ACTION),
|
widget.shipment.reference,
|
||||||
trailing: LinkIcon(
|
attachmentCount,
|
||||||
text: attachmentCount > 0 ? attachmentCount.toString() : null,
|
widget.shipment.canEdit,
|
||||||
),
|
|
||||||
onTap: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => AttachmentWidget(
|
|
||||||
InvenTreeSalesOrderShipmentAttachment(),
|
|
||||||
widget.shipment.pk,
|
|
||||||
widget.shipment.reference,
|
|
||||||
widget.shipment.canEdit,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (attachmentTile != null) {
|
||||||
|
tiles.add(attachmentTile);
|
||||||
|
}
|
||||||
|
|
||||||
return tiles;
|
return tiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
|||||||
|
|
||||||
import "package:inventree/app_colors.dart";
|
import "package:inventree/app_colors.dart";
|
||||||
import "package:inventree/barcode/barcode.dart";
|
import "package:inventree/barcode/barcode.dart";
|
||||||
|
import "package:inventree/inventree/attachment.dart";
|
||||||
import "package:inventree/l10.dart";
|
import "package:inventree/l10.dart";
|
||||||
import "package:inventree/helpers.dart";
|
import "package:inventree/helpers.dart";
|
||||||
|
|
||||||
@@ -212,13 +213,15 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Request the number of attachments
|
// Request the number of attachments
|
||||||
InvenTreePartAttachment().countAttachments(part.pk).then((int value) {
|
InvenTreeAttachment()
|
||||||
if (mounted) {
|
.countAttachments(InvenTreePart.MODEL_TYPE, part.pk)
|
||||||
setState(() {
|
.then((int value) {
|
||||||
attachmentCount = value;
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
attachmentCount = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// If show pricing information?
|
// If show pricing information?
|
||||||
if (showPricing) {
|
if (showPricing) {
|
||||||
@@ -596,29 +599,19 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
tiles.add(
|
ListTile? attachmentTile = ShowAttachmentsItem(
|
||||||
ListTile(
|
context,
|
||||||
title: Text(L10().attachments),
|
InvenTreePart.MODEL_TYPE,
|
||||||
leading: Icon(TablerIcons.file, color: COLOR_ACTION),
|
part.pk,
|
||||||
trailing: LinkIcon(
|
L10().part,
|
||||||
text: attachmentCount > 0 ? attachmentCount.toString() : null,
|
attachmentCount,
|
||||||
),
|
part.canEdit,
|
||||||
onTap: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => AttachmentWidget(
|
|
||||||
InvenTreePartAttachment(),
|
|
||||||
part.pk,
|
|
||||||
L10().part,
|
|
||||||
part.canEdit,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (attachmentTile != null) {
|
||||||
|
tiles.add(attachmentTile);
|
||||||
|
}
|
||||||
|
|
||||||
return tiles;
|
return tiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import "package:inventree/app_colors.dart";
|
|||||||
import "package:inventree/barcode/barcode.dart";
|
import "package:inventree/barcode/barcode.dart";
|
||||||
import "package:inventree/barcode/stock.dart";
|
import "package:inventree/barcode/stock.dart";
|
||||||
import "package:inventree/helpers.dart";
|
import "package:inventree/helpers.dart";
|
||||||
|
import "package:inventree/inventree/attachment.dart";
|
||||||
import "package:inventree/inventree/sales_order.dart";
|
import "package:inventree/inventree/sales_order.dart";
|
||||||
import "package:inventree/l10.dart";
|
import "package:inventree/l10.dart";
|
||||||
import "package:inventree/api.dart";
|
import "package:inventree/api.dart";
|
||||||
@@ -255,15 +256,15 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Request the number of attachments
|
// Request the number of attachments
|
||||||
InvenTreeStockItemAttachment().countAttachments(widget.item.pk).then((
|
InvenTreeAttachment()
|
||||||
int value,
|
.countAttachments(InvenTreeStockItem.MODEL_TYPE, widget.item.pk)
|
||||||
) {
|
.then((int value) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
attachmentCount = value;
|
attachmentCount = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Request SalesOrder information
|
// Request SalesOrder information
|
||||||
if (widget.item.hasSalesOrder) {
|
if (widget.item.hasSalesOrder) {
|
||||||
@@ -837,29 +838,19 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
tiles.add(
|
ListTile? attachmentTile = ShowAttachmentsItem(
|
||||||
ListTile(
|
context,
|
||||||
title: Text(L10().attachments),
|
InvenTreeStockItem.MODEL_TYPE,
|
||||||
leading: Icon(TablerIcons.file, color: COLOR_ACTION),
|
widget.item.pk,
|
||||||
trailing: LinkIcon(
|
L10().stockItem,
|
||||||
text: attachmentCount > 0 ? attachmentCount.toString() : null,
|
attachmentCount,
|
||||||
),
|
widget.item.canEdit,
|
||||||
onTap: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => AttachmentWidget(
|
|
||||||
InvenTreeStockItemAttachment(),
|
|
||||||
widget.item.pk,
|
|
||||||
L10().stockItem,
|
|
||||||
widget.item.canEdit,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (attachmentTile != null) {
|
||||||
|
tiles.add(attachmentTile);
|
||||||
|
}
|
||||||
|
|
||||||
return tiles;
|
return tiles;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user