mirror of
https://github.com/inventree/inventree-app.git
synced 2025-04-27 21:16:48 +00:00
Sales order support (#438)
* Add new models for SalesOrder - Create generic Order and OrderLine models with common functionality * Refactor - Move some widgets around - Cleanup directory structure * Add link to home screen and nav drawer * Add SalesOrder list widget * Linting fixes * Fix string * Refactor PurchaseOrderDetailWidget * Tweaks to existing code * linting * Fixes for drawer widget * Add "detail" page for SalesOrder * Add more tiles to SalesOrder detail * Allow editing of salesorder * add list filters for sales orders * Display list of line items * Customer updates - Display customer icon on home screen - Fetch sales orders for customer detail page * Cleanup company detail view * Create new sales order from list * Stricter typing for formFields method * Create new PurchaseOrder and SalesOrder from company deatil * Status code updates - Add function for name comparison - Remove hard-coded values * Update view permission checks for home widget * Add ability to manually add SalesOrderLineItem * Add nice progress bar widgets * Display detail view for sales order line item * edit SalesOrderLineItem * Fix unused import * Hide "shipped items" tab - Will be added in a future update
This commit is contained in:
parent
c1c0d46957
commit
bdd5470e68
17
lib/api.dart
17
lib/api.dart
@ -620,6 +620,8 @@ class InvenTreeAPI {
|
|||||||
_globalSettings.clear();
|
_globalSettings.clear();
|
||||||
_userSettings.clear();
|
_userSettings.clear();
|
||||||
|
|
||||||
|
roles.clear();
|
||||||
|
_plugins.clear();
|
||||||
serverInfo.clear();
|
serverInfo.clear();
|
||||||
_connectionStatusChanged();
|
_connectionStatusChanged();
|
||||||
}
|
}
|
||||||
@ -672,6 +674,8 @@ class InvenTreeAPI {
|
|||||||
|
|
||||||
_connectionStatusChanged();
|
_connectionStatusChanged();
|
||||||
|
|
||||||
|
fetchStatusCodeData();
|
||||||
|
|
||||||
return _connected;
|
return _connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -735,6 +739,10 @@ class InvenTreeAPI {
|
|||||||
*/
|
*/
|
||||||
bool checkPermission(String role, String permission) {
|
bool checkPermission(String role, String permission) {
|
||||||
|
|
||||||
|
if (!_connected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// If we do not have enough information, assume permission is allowed
|
// If we do not have enough information, assume permission is allowed
|
||||||
if (roles.isEmpty) {
|
if (roles.isEmpty) {
|
||||||
debug("checkPermission - no roles defined!");
|
debug("checkPermission - no roles defined!");
|
||||||
@ -1624,11 +1632,20 @@ class InvenTreeAPI {
|
|||||||
InvenTreeStatusCode get StockHistoryStatus => _get_status_class("stock/track/status/");
|
InvenTreeStatusCode get StockHistoryStatus => _get_status_class("stock/track/status/");
|
||||||
InvenTreeStatusCode get StockStatus => _get_status_class("stock/status/");
|
InvenTreeStatusCode get StockStatus => _get_status_class("stock/status/");
|
||||||
InvenTreeStatusCode get PurchaseOrderStatus => _get_status_class("order/po/status/");
|
InvenTreeStatusCode get PurchaseOrderStatus => _get_status_class("order/po/status/");
|
||||||
|
InvenTreeStatusCode get SalesOrderStatus => _get_status_class("order/so/status/");
|
||||||
|
|
||||||
void clearStatusCodeData() {
|
void clearStatusCodeData() {
|
||||||
StockHistoryStatus.data.clear();
|
StockHistoryStatus.data.clear();
|
||||||
StockStatus.data.clear();
|
StockStatus.data.clear();
|
||||||
PurchaseOrderStatus.data.clear();
|
PurchaseOrderStatus.data.clear();
|
||||||
|
SalesOrderStatus.data.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> fetchStatusCodeData({bool forceReload = true}) async {
|
||||||
|
StockHistoryStatus.load(forceReload: forceReload);
|
||||||
|
StockStatus.load(forceReload: forceReload);
|
||||||
|
PurchaseOrderStatus.load(forceReload: forceReload);
|
||||||
|
SalesOrderStatus.load(forceReload: forceReload);
|
||||||
}
|
}
|
||||||
|
|
||||||
int notification_counter = 0;
|
int notification_counter = 0;
|
||||||
|
@ -23,13 +23,13 @@ import "package:inventree/inventree/purchase_order.dart";
|
|||||||
import "package:inventree/inventree/stock.dart";
|
import "package:inventree/inventree/stock.dart";
|
||||||
|
|
||||||
import "package:inventree/widget/dialogs.dart";
|
import "package:inventree/widget/dialogs.dart";
|
||||||
import "package:inventree/widget/location_display.dart";
|
import "package:inventree/widget/stock/location_display.dart";
|
||||||
import "package:inventree/widget/part_detail.dart";
|
import "package:inventree/widget/part/part_detail.dart";
|
||||||
import "package:inventree/widget/purchase_order_detail.dart";
|
import "package:inventree/widget/order/purchase_order_detail.dart";
|
||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
import "package:inventree/widget/snacks.dart";
|
import "package:inventree/widget/snacks.dart";
|
||||||
import "package:inventree/widget/stock_detail.dart";
|
import "package:inventree/widget/stock/stock_detail.dart";
|
||||||
import "package:inventree/widget/supplier_part_detail.dart";
|
import "package:inventree/widget/company/supplier_part_detail.dart";
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
import 'dart:async';
|
import "dart:async';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import "package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import "package:flutter/material.dart';
|
||||||
// ignore_for_file: non_constant_identifier_names
|
// ignore_for_file: non_constant_identifier_names
|
||||||
// ignore_for_file: camel_case_types
|
// ignore_for_file: camel_case_types
|
||||||
// ignore_for_file: prefer_single_quotes
|
// ignore_for_file: prefer_single_quotes
|
||||||
|
@ -22,7 +22,7 @@ class InvenTreeCompany extends InvenTreeModel {
|
|||||||
List<String> get rolesRequired => ["purchase_order", "sales_order", "return_order"];
|
List<String> get rolesRequired => ["purchase_order", "sales_order", "return_order"];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> formFields() {
|
Map<String, Map<String, dynamic>> formFields() {
|
||||||
return {
|
return {
|
||||||
"name": {},
|
"name": {},
|
||||||
"description": {},
|
"description": {},
|
||||||
@ -121,8 +121,8 @@ class InvenTreeSupplierPart extends InvenTreeModel {
|
|||||||
List<String> get rolesRequired => ["part", "purchase_order"];
|
List<String> get rolesRequired => ["part", "purchase_order"];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> formFields() {
|
Map<String, Map<String, dynamic>> formFields() {
|
||||||
Map<String, dynamic> fields = {
|
Map<String, Map<String, dynamic>> fields = {
|
||||||
"supplier": {},
|
"supplier": {},
|
||||||
"SKU": {},
|
"SKU": {},
|
||||||
"link": {},
|
"link": {},
|
||||||
|
@ -230,7 +230,7 @@ class InvenTreeModel {
|
|||||||
|
|
||||||
// Fields for editing / creating this model
|
// Fields for editing / creating this model
|
||||||
// Override per-model
|
// Override per-model
|
||||||
Map<String, dynamic> formFields() {
|
Map<String, Map<String, dynamic>> formFields() {
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
107
lib/inventree/orders.dart
Normal file
107
lib/inventree/orders.dart
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Base model for various "orders" which share common properties
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import "package:inventree/inventree/model.dart";
|
||||||
|
import "package:inventree/inventree/part.dart";
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generic class representing an "order"
|
||||||
|
*/
|
||||||
|
class InvenTreeOrder extends InvenTreeModel {
|
||||||
|
|
||||||
|
InvenTreeOrder() : super();
|
||||||
|
|
||||||
|
InvenTreeOrder.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||||
|
|
||||||
|
String get issueDate => getString("issue_date");
|
||||||
|
|
||||||
|
String get completeDate => getString("complete_date");
|
||||||
|
|
||||||
|
String get creationDate => getString("creation_date");
|
||||||
|
|
||||||
|
String get targetDate => getString("target_date");
|
||||||
|
|
||||||
|
int get lineItemCount => getInt("line_items", backup: 0);
|
||||||
|
|
||||||
|
bool get overdue => getBool("overdue");
|
||||||
|
|
||||||
|
String get reference => getString("reference");
|
||||||
|
|
||||||
|
int get responsibleId => getInt("responsible");
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
int get status => getInt("status");
|
||||||
|
|
||||||
|
String get statusText => getString("status_text");
|
||||||
|
|
||||||
|
double? get totalPrice {
|
||||||
|
String price = getString("total_price");
|
||||||
|
|
||||||
|
if (price.isEmpty) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return double.tryParse(price);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the currency for this order
|
||||||
|
// Note that the nomenclature in the API changed at some point
|
||||||
|
String get totalPriceCurrency {
|
||||||
|
if (jsondata.containsKey("order_currency")) {
|
||||||
|
return getString("order_currency");
|
||||||
|
} else if (jsondata.containsKey("total_price_currency")) {
|
||||||
|
return getString("total_price_currency");
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generic class representing an "order line"
|
||||||
|
*/
|
||||||
|
class InvenTreeOrderLine extends InvenTreeModel {
|
||||||
|
|
||||||
|
InvenTreeOrderLine() : super();
|
||||||
|
|
||||||
|
InvenTreeOrderLine.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||||
|
|
||||||
|
bool get overdue => getBool("overdue");
|
||||||
|
|
||||||
|
double get quantity => getDouble("quantity");
|
||||||
|
|
||||||
|
String get reference => getString("reference");
|
||||||
|
|
||||||
|
int get orderId => getInt("order");
|
||||||
|
|
||||||
|
InvenTreePart? get part {
|
||||||
|
dynamic part_detail = jsondata["part_detail"];
|
||||||
|
|
||||||
|
if (part_detail == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return InvenTreePart.fromJson(part_detail as Map<String, dynamic>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int get partId => getInt("pk", subKey: "part_detail");
|
||||||
|
|
||||||
|
String get partName => getString("name", subKey: "part_detail");
|
||||||
|
|
||||||
|
String get partImage => getString("thumbnail", subKey: "part_detail");
|
||||||
|
|
||||||
|
// TODO: Perhaps parse this as an actual date?
|
||||||
|
String get targetDate => getString("target_date");
|
||||||
|
}
|
@ -27,9 +27,9 @@ class InvenTreePartCategory extends InvenTreeModel {
|
|||||||
List<String> get rolesRequired => ["part_category"];
|
List<String> get rolesRequired => ["part_category"];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> formFields() {
|
Map<String, Map<String, dynamic>> formFields() {
|
||||||
|
|
||||||
Map<String, dynamic> fields = {
|
Map<String, Map<String, dynamic>> fields = {
|
||||||
"name": {},
|
"name": {},
|
||||||
"description": {},
|
"description": {},
|
||||||
"parent": {},
|
"parent": {},
|
||||||
@ -140,9 +140,9 @@ class InvenTreePartParameter extends InvenTreeModel {
|
|||||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePartParameter.fromJson(json);
|
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePartParameter.fromJson(json);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> formFields() {
|
Map<String, Map<String, dynamic>> formFields() {
|
||||||
|
|
||||||
Map<String, dynamic> fields = {
|
Map<String, Map<String, dynamic>> fields = {
|
||||||
"header": {
|
"header": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"read_only": true,
|
"read_only": true,
|
||||||
@ -200,7 +200,7 @@ class InvenTreePart extends InvenTreeModel {
|
|||||||
List<String> get rolesRequired => ["part"];
|
List<String> get rolesRequired => ["part"];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> formFields() {
|
Map<String, Map<String, dynamic>> formFields() {
|
||||||
return {
|
return {
|
||||||
"name": {},
|
"name": {},
|
||||||
"description": {},
|
"description": {},
|
||||||
|
@ -17,7 +17,7 @@ class InvenTreeProjectCode extends InvenTreeModel {
|
|||||||
String get URL => "project-code/";
|
String get URL => "project-code/";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> formFields() {
|
Map<String, Map<String, dynamic>> formFields() {
|
||||||
return {
|
return {
|
||||||
"code": {},
|
"code": {},
|
||||||
"description": {},
|
"description": {},
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
import "package:inventree/api.dart";
|
import "package:inventree/api.dart";
|
||||||
import "package:inventree/helpers.dart";
|
import "package:inventree/helpers.dart";
|
||||||
import "package:inventree/inventree/company.dart";
|
import "package:inventree/inventree/company.dart";
|
||||||
import "package:inventree/inventree/part.dart";
|
|
||||||
import "package:inventree/inventree/model.dart";
|
import "package:inventree/inventree/model.dart";
|
||||||
|
import "package:inventree/inventree/orders.dart";
|
||||||
|
|
||||||
const int PO_STATUS_PENDING = 10;
|
|
||||||
const int PO_STATUS_PLACED = 20;
|
|
||||||
const int PO_STATUS_COMPLETE = 30;
|
|
||||||
const int PO_STATUS_CANCELLED = 40;
|
|
||||||
const int PO_STATUS_LOST = 50;
|
|
||||||
const int PO_STATUS_RETURNED = 60;
|
|
||||||
|
|
||||||
class InvenTreePurchaseOrder extends InvenTreeModel {
|
/*
|
||||||
|
* Class representing an individual PurchaseOrder instance
|
||||||
|
*/
|
||||||
|
class InvenTreePurchaseOrder extends InvenTreeOrder {
|
||||||
|
|
||||||
InvenTreePurchaseOrder() : super();
|
InvenTreePurchaseOrder() : super();
|
||||||
|
|
||||||
InvenTreePurchaseOrder.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
InvenTreePurchaseOrder.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePurchaseOrder.fromJson(json);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get URL => "order/po/";
|
String get URL => "order/po/";
|
||||||
|
|
||||||
@ -26,8 +26,8 @@ class InvenTreePurchaseOrder extends InvenTreeModel {
|
|||||||
String get receive_url => "${url}receive/";
|
String get receive_url => "${url}receive/";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> formFields() {
|
Map<String, Map<String, dynamic>> formFields() {
|
||||||
var fields = {
|
Map<String, Map<String, dynamic>> fields = {
|
||||||
"reference": {},
|
"reference": {},
|
||||||
"supplier": {
|
"supplier": {
|
||||||
"filters": {
|
"filters": {
|
||||||
@ -69,33 +69,8 @@ class InvenTreePurchaseOrder extends InvenTreeModel {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
String get issueDate => getString("issue_date");
|
|
||||||
|
|
||||||
String get completeDate => getString("complete_date");
|
|
||||||
|
|
||||||
String get creationDate => getString("creation_date");
|
|
||||||
|
|
||||||
String get targetDate => getString("target_date");
|
|
||||||
|
|
||||||
int get lineItemCount => getInt("line_items", backup: 0);
|
|
||||||
|
|
||||||
bool get overdue => getBool("overdue");
|
|
||||||
|
|
||||||
String get reference => getString("reference");
|
|
||||||
|
|
||||||
int get responsibleId => getInt("responsible");
|
|
||||||
|
|
||||||
int get supplierId => getInt("supplier");
|
int get supplierId => getInt("supplier");
|
||||||
|
|
||||||
// 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 {
|
InvenTreeCompany? get supplier {
|
||||||
|
|
||||||
dynamic supplier_detail = jsondata["supplier_detail"];
|
dynamic supplier_detail = jsondata["supplier_detail"];
|
||||||
@ -109,39 +84,13 @@ class InvenTreePurchaseOrder extends InvenTreeModel {
|
|||||||
|
|
||||||
String get supplierReference => getString("supplier_reference");
|
String get supplierReference => getString("supplier_reference");
|
||||||
|
|
||||||
int get status => getInt("status");
|
bool get isOpen => api.PurchaseOrderStatus.isNameIn(status, ["PENDING", "PLACED"]);
|
||||||
|
|
||||||
String get statusText => getString("status_text");
|
bool get isPending => api.PurchaseOrderStatus.isNameIn(status, ["PENDING"]);
|
||||||
|
|
||||||
bool get isOpen => status == PO_STATUS_PENDING || status == PO_STATUS_PLACED;
|
bool get isPlaced => api.PurchaseOrderStatus.isNameIn(status, ["PLACED"]);
|
||||||
|
|
||||||
bool get isPending => status == PO_STATUS_PENDING;
|
bool get isFailed => api.PurchaseOrderStatus.isNameIn(status, ["CANCELLED", "LOST", "RETURNED"]);
|
||||||
|
|
||||||
bool get isPlaced => status == PO_STATUS_PLACED;
|
|
||||||
|
|
||||||
bool get isFailed => status == PO_STATUS_CANCELLED || status == PO_STATUS_LOST || status == PO_STATUS_RETURNED;
|
|
||||||
|
|
||||||
double? get totalPrice {
|
|
||||||
String price = getString("total_price");
|
|
||||||
|
|
||||||
if (price.isEmpty) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
return double.tryParse(price);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the currency for this order
|
|
||||||
// Note that the nomenclature in the API changed at some point
|
|
||||||
String get totalPriceCurrency {
|
|
||||||
if (jsondata.containsKey("order_currency")) {
|
|
||||||
return getString("order_currency");
|
|
||||||
} else if (jsondata.containsKey("total_price_currency")) {
|
|
||||||
return getString("total_price_currency");
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<InvenTreePOLineItem>> getLineItems() async {
|
Future<List<InvenTreePOLineItem>> getLineItems() async {
|
||||||
|
|
||||||
@ -162,9 +111,6 @@ class InvenTreePurchaseOrder extends InvenTreeModel {
|
|||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePurchaseOrder.fromJson(json);
|
|
||||||
|
|
||||||
/// Mark this order as "placed" / "issued"
|
/// Mark this order as "placed" / "issued"
|
||||||
Future<void> issueOrder() async {
|
Future<void> issueOrder() async {
|
||||||
// Order can only be placed when the order is 'pending'
|
// Order can only be placed when the order is 'pending'
|
||||||
@ -185,12 +131,15 @@ class InvenTreePurchaseOrder extends InvenTreeModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InvenTreePOLineItem extends InvenTreeModel {
|
class InvenTreePOLineItem extends InvenTreeOrderLine {
|
||||||
|
|
||||||
InvenTreePOLineItem() : super();
|
InvenTreePOLineItem() : super();
|
||||||
|
|
||||||
InvenTreePOLineItem.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
InvenTreePOLineItem.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePOLineItem.fromJson(json);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get URL => "order/po-line/";
|
String get URL => "order/po-line/";
|
||||||
|
|
||||||
@ -198,7 +147,7 @@ class InvenTreePOLineItem extends InvenTreeModel {
|
|||||||
List<String> get rolesRequired => ["purchase_order"];
|
List<String> get rolesRequired => ["purchase_order"];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> formFields() {
|
Map<String, Map<String, dynamic>> formFields() {
|
||||||
return {
|
return {
|
||||||
"part": {
|
"part": {
|
||||||
// We cannot edit the supplier part field here
|
// We cannot edit the supplier part field here
|
||||||
@ -232,38 +181,24 @@ class InvenTreePOLineItem extends InvenTreeModel {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double get received => getDouble("received");
|
||||||
|
|
||||||
bool get isComplete => received >= quantity;
|
bool get isComplete => received >= quantity;
|
||||||
|
|
||||||
double get quantity => getDouble("quantity");
|
double get progressRatio {
|
||||||
|
if (quantity <= 0 || received <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
double get received => getDouble("received");
|
return received / quantity;
|
||||||
|
}
|
||||||
|
|
||||||
String get progressString => simpleNumberString(received) + " / " + simpleNumberString(quantity);
|
String get progressString => simpleNumberString(received) + " / " + simpleNumberString(quantity);
|
||||||
|
|
||||||
double get outstanding => quantity - received;
|
double get outstanding => quantity - received;
|
||||||
|
|
||||||
String get reference => getString("reference");
|
|
||||||
|
|
||||||
int get orderId => getInt("order");
|
|
||||||
|
|
||||||
int get supplierPartId => getInt("part");
|
int get supplierPartId => getInt("part");
|
||||||
|
|
||||||
InvenTreePart? get part {
|
|
||||||
dynamic part_detail = jsondata["part_detail"];
|
|
||||||
|
|
||||||
if (part_detail == null) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
return InvenTreePart.fromJson(part_detail as Map<String, dynamic>);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int get partId => getInt("pk", subKey: "part_detail");
|
|
||||||
|
|
||||||
String get partName => getString("name", subKey: "part_detail");
|
|
||||||
|
|
||||||
String get partImage => getString("thumbnail", subKey: "part_detail");
|
|
||||||
|
|
||||||
InvenTreeSupplierPart? get supplierPart {
|
InvenTreeSupplierPart? get supplierPart {
|
||||||
|
|
||||||
dynamic detail = jsondata["supplier_part_detail"];
|
dynamic detail = jsondata["supplier_part_detail"];
|
||||||
@ -281,19 +216,13 @@ class InvenTreePOLineItem extends InvenTreeModel {
|
|||||||
|
|
||||||
String get purchasePriceCurrency => getString("purchase_price_currency");
|
String get purchasePriceCurrency => getString("purchase_price_currency");
|
||||||
|
|
||||||
String get purchasePriceString => getString("purchase_price_string");
|
|
||||||
|
|
||||||
int get destination => getInt("destination");
|
int get destination => getInt("destination");
|
||||||
|
|
||||||
Map<String, dynamic> get destinationDetail => getMap("destination_detail");
|
Map<String, dynamic> get destinationDetail => getMap("destination_detail");
|
||||||
|
|
||||||
@override
|
|
||||||
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePOLineItem.fromJson(json);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Class representing an attachment file against a StockItem object
|
* Class representing an attachment file against a PurchaseOrder object
|
||||||
*/
|
*/
|
||||||
class InvenTreePurchaseOrderAttachment extends InvenTreeAttachment {
|
class InvenTreePurchaseOrderAttachment extends InvenTreeAttachment {
|
||||||
|
|
||||||
|
190
lib/inventree/sales_order.dart
Normal file
190
lib/inventree/sales_order.dart
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import "package:inventree/helpers.dart";
|
||||||
|
import "package:inventree/inventree/company.dart";
|
||||||
|
import "package:inventree/inventree/model.dart";
|
||||||
|
import "package:inventree/inventree/orders.dart";
|
||||||
|
|
||||||
|
import "package:inventree/api.dart";
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class representing an individual SalesOrder
|
||||||
|
*/
|
||||||
|
class InvenTreeSalesOrder extends InvenTreeOrder {
|
||||||
|
|
||||||
|
InvenTreeSalesOrder() : super();
|
||||||
|
|
||||||
|
InvenTreeSalesOrder.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeSalesOrder.fromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get URL => "order/so/";
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<String> get rolesRequired => ["sales_order"];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, Map<String, dynamic>> formFields() {
|
||||||
|
Map<String, Map<String, dynamic>> fields = {
|
||||||
|
"reference": {},
|
||||||
|
"customer": {
|
||||||
|
"filters": {
|
||||||
|
"is_customer": true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"customer_reference": {},
|
||||||
|
"description": {},
|
||||||
|
"project_code": {},
|
||||||
|
"target_date": {},
|
||||||
|
"link": {},
|
||||||
|
"responsible": {},
|
||||||
|
"contact": {
|
||||||
|
"filters": {
|
||||||
|
"company": customerId,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!InvenTreeAPI().supportsProjectCodes) {
|
||||||
|
fields.remove("project_code");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!InvenTreeAPI().supportsContactModel) {
|
||||||
|
fields.remove("contact");
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, String> defaultGetFilters() {
|
||||||
|
return {
|
||||||
|
"customer_detail": "true",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, String> defaultListFilters() {
|
||||||
|
return {
|
||||||
|
"customer_detail": "true",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
int get customerId => getInt("customer");
|
||||||
|
|
||||||
|
InvenTreeCompany? get customer {
|
||||||
|
dynamic customer_detail = jsondata["customer_detail"];
|
||||||
|
|
||||||
|
if (customer_detail == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return InvenTreeCompany.fromJson(customer_detail as Map<String, dynamic>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String get customerReference => getString("customer_reference");
|
||||||
|
|
||||||
|
bool get isOpen => api.SalesOrderStatus.isNameIn(status, ["PENDING", "IN_PROGRESS"]);
|
||||||
|
|
||||||
|
bool get isComplete => api.SalesOrderStatus.isNameIn(status, ["SHIPPED"]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class representing an individual line item in a SalesOrder
|
||||||
|
*/
|
||||||
|
class InvenTreeSOLineItem extends InvenTreeOrderLine {
|
||||||
|
|
||||||
|
InvenTreeSOLineItem() : super();
|
||||||
|
|
||||||
|
InvenTreeSOLineItem.fromJson(Map<String, dynamic> json) : super.fromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreeSOLineItem.fromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get URL => "order/so-line/";
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<String> get rolesRequired => ["sales_order"];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, Map<String, dynamic>> formFields() {
|
||||||
|
return {
|
||||||
|
"order": {
|
||||||
|
"hidden": true,
|
||||||
|
},
|
||||||
|
"part": {},
|
||||||
|
"quantity": {},
|
||||||
|
"reference": {},
|
||||||
|
"notes": {},
|
||||||
|
"link": {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, String> defaultGetFilters() {
|
||||||
|
return {
|
||||||
|
"part_detail": "true",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, String> defaultListFilters() {
|
||||||
|
return {
|
||||||
|
"part_detail": "true",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
double get allocated => getDouble("allocated");
|
||||||
|
|
||||||
|
bool get isAllocated => allocated >= quantity;
|
||||||
|
|
||||||
|
double get shipped => getDouble("shipped");
|
||||||
|
|
||||||
|
double get outstanding => quantity - shipped;
|
||||||
|
|
||||||
|
double get progressRatio {
|
||||||
|
if (quantity <= 0 || shipped <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shipped / quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
String get progressString => simpleNumberString(shipped) + " / " + simpleNumberString(quantity);
|
||||||
|
|
||||||
|
bool get isComplete => shipped >= quantity;
|
||||||
|
|
||||||
|
double get available => getDouble("available_stock") + getDouble("available_variant_stock");
|
||||||
|
|
||||||
|
double get salePrice => getDouble("sale_price");
|
||||||
|
|
||||||
|
String get salePriceCurrency => getString("sale_price_currency");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 URL => "order/po/attachment/";
|
||||||
|
|
||||||
|
}
|
@ -105,6 +105,24 @@ class InvenTreeStatusCode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the 'name' (untranslated) associated with a given status code
|
||||||
|
String name(int status) {
|
||||||
|
Map<String, dynamic> _entry = entry(status);
|
||||||
|
|
||||||
|
String _name = (_entry["name"] ?? "") as String;
|
||||||
|
|
||||||
|
if (_name.isEmpty) {
|
||||||
|
debug("No match for status code ${status} at '${URL}'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return _name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if the name associated with the given code is in the provided list
|
||||||
|
bool isNameIn(int code, List<String> names) {
|
||||||
|
return names.contains(name(code));
|
||||||
|
}
|
||||||
|
|
||||||
// Return the 'color' associated with a given status code
|
// Return the 'color' associated with a given status code
|
||||||
Color color(int status) {
|
Color color(int status) {
|
||||||
Map<String, dynamic> _entry = entry(status);
|
Map<String, dynamic> _entry = entry(status);
|
||||||
|
@ -27,7 +27,7 @@ class InvenTreeStockItemTestResult extends InvenTreeModel {
|
|||||||
List<String> get rolesRequired => ["stock"];
|
List<String> get rolesRequired => ["stock"];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> formFields() {
|
Map<String, Map<String, dynamic>> formFields() {
|
||||||
return {
|
return {
|
||||||
"stock_item": {"hidden": true},
|
"stock_item": {"hidden": true},
|
||||||
"test": {},
|
"test": {},
|
||||||
@ -158,8 +158,8 @@ class InvenTreeStockItem extends InvenTreeModel {
|
|||||||
String get WEB_URL => "stock/item/";
|
String get WEB_URL => "stock/item/";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> formFields() {
|
Map<String, Map<String, dynamic>> formFields() {
|
||||||
return {
|
Map<String, Map<String, dynamic>> fields = {
|
||||||
"part": {},
|
"part": {},
|
||||||
"location": {},
|
"location": {},
|
||||||
"quantity": {},
|
"quantity": {},
|
||||||
@ -175,6 +175,8 @@ class InvenTreeStockItem extends InvenTreeModel {
|
|||||||
"packaging": {},
|
"packaging": {},
|
||||||
"link": {},
|
"link": {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -609,8 +611,8 @@ class InvenTreeStockLocation extends InvenTreeModel {
|
|||||||
String get pathstring => getString("pathstring");
|
String get pathstring => getString("pathstring");
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> formFields() {
|
Map<String, Map<String, dynamic>> formFields() {
|
||||||
Map<String, dynamic> fields = {
|
Map<String, Map<String, dynamic>> fields = {
|
||||||
"name": {},
|
"name": {},
|
||||||
"description": {},
|
"description": {},
|
||||||
"parent": {},
|
"parent": {},
|
||||||
|
@ -235,9 +235,15 @@
|
|||||||
"credits": "Credits",
|
"credits": "Credits",
|
||||||
"@credits": {},
|
"@credits": {},
|
||||||
|
|
||||||
|
"customer": "Customer",
|
||||||
|
"@customer": {},
|
||||||
|
|
||||||
"customers": "Customers",
|
"customers": "Customers",
|
||||||
"@customers": {},
|
"@customers": {},
|
||||||
|
|
||||||
|
"customerReference": "Customer Reference",
|
||||||
|
"@customerReference": {},
|
||||||
|
|
||||||
"damaged": "Damaged",
|
"damaged": "Damaged",
|
||||||
"@damaged": {},
|
"@damaged": {},
|
||||||
|
|
||||||
@ -440,15 +446,21 @@
|
|||||||
"homeShowPo": "Show Purchase Orders",
|
"homeShowPo": "Show Purchase Orders",
|
||||||
"@homeShowPo": {},
|
"@homeShowPo": {},
|
||||||
|
|
||||||
|
"homeShowPoDescription": "Show purchase order button on home screen",
|
||||||
|
"@homeShowPoDescription": {},
|
||||||
|
|
||||||
|
"homeShowSo": "Show Sales Orders",
|
||||||
|
"@homeShowSo": {},
|
||||||
|
|
||||||
|
"homeShowSoDescription": "Show sales order button on home screen",
|
||||||
|
"@homeShowSoDescription": {},
|
||||||
|
|
||||||
"homeShowSubscribed": "Subscribed Parts",
|
"homeShowSubscribed": "Subscribed Parts",
|
||||||
"@homeShowSubscribed": {},
|
"@homeShowSubscribed": {},
|
||||||
|
|
||||||
"homeShowSubscribedDescription": "Show subscribed parts on home screen",
|
"homeShowSubscribedDescription": "Show subscribed parts on home screen",
|
||||||
"@homeShowSubscsribedDescription": {},
|
"@homeShowSubscsribedDescription": {},
|
||||||
|
|
||||||
"homeShowPoDescription": "Show purchase order button on home screen",
|
|
||||||
"@homeShowPoDescription": {},
|
|
||||||
|
|
||||||
"homeShowSuppliers": "Show Suppliers",
|
"homeShowSuppliers": "Show Suppliers",
|
||||||
"@homeShowSuppliers": {},
|
"@homeShowSuppliers": {},
|
||||||
|
|
||||||
@ -576,6 +588,9 @@
|
|||||||
"level": "Level",
|
"level": "Level",
|
||||||
"@level": {},
|
"@level": {},
|
||||||
|
|
||||||
|
"lineItemAdd": "Add Line Item",
|
||||||
|
"@lineItemAdd": {},
|
||||||
|
|
||||||
"lineItem": "Line Item",
|
"lineItem": "Line Item",
|
||||||
"@lineItem": {},
|
"@lineItem": {},
|
||||||
|
|
||||||
@ -687,9 +702,15 @@
|
|||||||
"outstanding": "Outstanding",
|
"outstanding": "Outstanding",
|
||||||
"@outstanding": {},
|
"@outstanding": {},
|
||||||
|
|
||||||
"outstandingOrderDetail": "Show outstanding items",
|
"outstandingOrderDetail": "Show outstanding orders",
|
||||||
"@outstandingOrderDetail": {},
|
"@outstandingOrderDetail": {},
|
||||||
|
|
||||||
|
"overdue": "Overdue",
|
||||||
|
"@overdue": {},
|
||||||
|
|
||||||
|
"overdueDetail": "Show overdue orders",
|
||||||
|
"@overdueDetail": {},
|
||||||
|
|
||||||
"packaging": "Packaging",
|
"packaging": "Packaging",
|
||||||
"@packaging": {},
|
"@packaging": {},
|
||||||
|
|
||||||
@ -997,9 +1018,21 @@
|
|||||||
"returned": "Returned",
|
"returned": "Returned",
|
||||||
"@returned": {},
|
"@returned": {},
|
||||||
|
|
||||||
|
"salesOrder": "Sales Order",
|
||||||
|
"@salesOrder": {},
|
||||||
|
|
||||||
"salesOrders": "Sales Orders",
|
"salesOrders": "Sales Orders",
|
||||||
"@salesOrders": {},
|
"@salesOrders": {},
|
||||||
|
|
||||||
|
"salesOrderCreate": "New Sales Order",
|
||||||
|
"@saleOrderCreate": {},
|
||||||
|
|
||||||
|
"salesOrderEdit": "Edit Sales Order",
|
||||||
|
"@salesOrderEdit": {},
|
||||||
|
|
||||||
|
"salesOrderUpdated": "Sales order updated",
|
||||||
|
"@salesOrderUpdated": {},
|
||||||
|
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"@save": {
|
"@save": {
|
||||||
"description": "Save"
|
"description": "Save"
|
||||||
@ -1125,6 +1158,9 @@
|
|||||||
"serverNotSelected": "Server not selected",
|
"serverNotSelected": "Server not selected",
|
||||||
"@serverNotSelected": {},
|
"@serverNotSelected": {},
|
||||||
|
|
||||||
|
"shipped": "Shipped",
|
||||||
|
"@shipped": {},
|
||||||
|
|
||||||
"sku": "SKU",
|
"sku": "SKU",
|
||||||
"@sku": {},
|
"@sku": {},
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import "package:path/path.dart";
|
|||||||
// Settings key values
|
// Settings key values
|
||||||
const String INV_HOME_SHOW_SUBSCRIBED = "homeShowSubscribed";
|
const String INV_HOME_SHOW_SUBSCRIBED = "homeShowSubscribed";
|
||||||
const String INV_HOME_SHOW_PO = "homeShowPo";
|
const String INV_HOME_SHOW_PO = "homeShowPo";
|
||||||
|
const String INV_HOME_SHOW_SO = "homeShowSo";
|
||||||
const String INV_HOME_SHOW_MANUFACTURERS = "homeShowManufacturers";
|
const String INV_HOME_SHOW_MANUFACTURERS = "homeShowManufacturers";
|
||||||
const String INV_HOME_SHOW_CUSTOMERS = "homeShowCustomers";
|
const String INV_HOME_SHOW_CUSTOMERS = "homeShowCustomers";
|
||||||
const String INV_HOME_SHOW_SUPPLIERS = "homeShowSuppliers";
|
const String INV_HOME_SHOW_SUPPLIERS = "homeShowSuppliers";
|
||||||
|
@ -21,6 +21,7 @@ class _HomeScreenSettingsState extends State<HomeScreenSettingsWidget> {
|
|||||||
// Home screen settings
|
// Home screen settings
|
||||||
bool homeShowSubscribed = true;
|
bool homeShowSubscribed = true;
|
||||||
bool homeShowPo = true;
|
bool homeShowPo = true;
|
||||||
|
bool homeShowSo = true;
|
||||||
bool homeShowSuppliers = true;
|
bool homeShowSuppliers = true;
|
||||||
bool homeShowManufacturers = true;
|
bool homeShowManufacturers = true;
|
||||||
bool homeShowCustomers = true;
|
bool homeShowCustomers = true;
|
||||||
@ -38,6 +39,7 @@ class _HomeScreenSettingsState extends State<HomeScreenSettingsWidget> {
|
|||||||
|
|
||||||
homeShowSubscribed = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_SUBSCRIBED, true) as bool;
|
homeShowSubscribed = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_SUBSCRIBED, true) as bool;
|
||||||
homeShowPo = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_PO, true) as bool;
|
homeShowPo = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_PO, true) as bool;
|
||||||
|
homeShowSo = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_SO, true) as bool;
|
||||||
homeShowManufacturers = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_MANUFACTURERS, true) as bool;
|
homeShowManufacturers = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_MANUFACTURERS, true) as bool;
|
||||||
homeShowCustomers = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_CUSTOMERS, true) as bool;
|
homeShowCustomers = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_CUSTOMERS, true) as bool;
|
||||||
homeShowSuppliers = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_SUPPLIERS, true) as bool;
|
homeShowSuppliers = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_SUPPLIERS, true) as bool;
|
||||||
@ -85,6 +87,20 @@ class _HomeScreenSettingsState extends State<HomeScreenSettingsWidget> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10().homeShowSo),
|
||||||
|
subtitle: Text(L10().homeShowSoDescription),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.truck),
|
||||||
|
trailing: Switch(
|
||||||
|
value: homeShowSo,
|
||||||
|
onChanged: (bool value) {
|
||||||
|
InvenTreeSettingsManager().setValue(INV_HOME_SHOW_SO, value);
|
||||||
|
setState(() {
|
||||||
|
homeShowSo = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(L10().homeShowSuppliers),
|
title: Text(L10().homeShowSuppliers),
|
||||||
subtitle: Text(L10().homeShowSuppliersDescription),
|
subtitle: Text(L10().homeShowSuppliersDescription),
|
||||||
@ -116,6 +132,7 @@ class _HomeScreenSettingsState extends State<HomeScreenSettingsWidget> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
*/
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(L10().homeShowCustomers),
|
title: Text(L10().homeShowCustomers),
|
||||||
subtitle: Text(L10().homeShowCustomersDescription),
|
subtitle: Text(L10().homeShowCustomersDescription),
|
||||||
@ -130,7 +147,6 @@ class _HomeScreenSettingsState extends State<HomeScreenSettingsWidget> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
*/
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -9,12 +9,16 @@ import "package:inventree/helpers.dart";
|
|||||||
|
|
||||||
import "package:inventree/inventree/company.dart";
|
import "package:inventree/inventree/company.dart";
|
||||||
import "package:inventree/inventree/purchase_order.dart";
|
import "package:inventree/inventree/purchase_order.dart";
|
||||||
|
import "package:inventree/inventree/sales_order.dart";
|
||||||
|
|
||||||
import "package:inventree/widget/attachment_widget.dart";
|
import "package:inventree/widget/attachment_widget.dart";
|
||||||
import "package:inventree/widget/purchase_order_list.dart";
|
import "package:inventree/widget/order/purchase_order_list.dart";
|
||||||
|
import "package:inventree/widget/order/sales_order_list.dart";
|
||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
import "package:inventree/widget/snacks.dart";
|
import "package:inventree/widget/snacks.dart";
|
||||||
import "package:inventree/widget/supplier_part_list.dart";
|
import "package:inventree/widget/company/supplier_part_list.dart";
|
||||||
|
import "package:inventree/widget/order/sales_order_detail.dart";
|
||||||
|
import "package:inventree/widget/order/purchase_order_detail.dart";
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -36,10 +40,11 @@ class _CompanyDetailState extends RefreshableState<CompanyDetailWidget> {
|
|||||||
|
|
||||||
_CompanyDetailState();
|
_CompanyDetailState();
|
||||||
|
|
||||||
List<InvenTreePurchaseOrder> outstandingOrders = [];
|
|
||||||
|
|
||||||
int supplierPartCount = 0;
|
int supplierPartCount = 0;
|
||||||
|
|
||||||
|
int outstandingPurchaseOrders = 0;
|
||||||
|
int outstandingSalesOrders = 0;
|
||||||
|
|
||||||
int attachmentCount = 0;
|
int attachmentCount = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -68,11 +73,87 @@ class _CompanyDetailState extends RefreshableState<CompanyDetailWidget> {
|
|||||||
List<SpeedDialChild> actionButtons(BuildContext context) {
|
List<SpeedDialChild> actionButtons(BuildContext context) {
|
||||||
List<SpeedDialChild> actions = [];
|
List<SpeedDialChild> actions = [];
|
||||||
|
|
||||||
// TODO - Actions for this company
|
if (widget.company.isCustomer && InvenTreeSalesOrder().canCreate) {
|
||||||
|
actions.add(SpeedDialChild(
|
||||||
|
child: FaIcon(FontAwesomeIcons.truck),
|
||||||
|
label: L10().salesOrderCreate,
|
||||||
|
onTap: () async {
|
||||||
|
_createSalesOrder(context);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget.company.isSupplier && InvenTreePurchaseOrder().canCreate) {
|
||||||
|
actions.add(SpeedDialChild(
|
||||||
|
child: FaIcon(FontAwesomeIcons.cartShopping),
|
||||||
|
label: L10().purchaseOrderCreate,
|
||||||
|
onTap: () async {
|
||||||
|
_createPurchaseOrder(context);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _createSalesOrder(BuildContext context) async {
|
||||||
|
var fields = InvenTreeSalesOrder().formFields();
|
||||||
|
|
||||||
|
// Cannot set contact until company is locked in
|
||||||
|
fields.remove("contact");
|
||||||
|
|
||||||
|
fields["customer"]?["value"] = widget.company.pk;
|
||||||
|
|
||||||
|
InvenTreeSalesOrder().createForm(
|
||||||
|
context,
|
||||||
|
L10().salesOrderCreate,
|
||||||
|
fields: fields,
|
||||||
|
onSuccess: (result) async {
|
||||||
|
Map<String, dynamic> data = result as Map<String, dynamic>;
|
||||||
|
|
||||||
|
if (data.containsKey("pk")) {
|
||||||
|
var order = InvenTreeSalesOrder.fromJson(data);
|
||||||
|
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => SalesOrderDetailWidget(order)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _createPurchaseOrder(BuildContext context) async {
|
||||||
|
var fields = InvenTreePurchaseOrder().formFields();
|
||||||
|
|
||||||
|
// Cannot set contact until company is locked in
|
||||||
|
fields.remove("contact");
|
||||||
|
|
||||||
|
fields["supplier"]?["value"] = widget.company.pk;
|
||||||
|
|
||||||
|
InvenTreePurchaseOrder().createForm(
|
||||||
|
context,
|
||||||
|
L10().purchaseOrderCreate,
|
||||||
|
fields: fields,
|
||||||
|
onSuccess: (result) async {
|
||||||
|
Map<String, dynamic> data = result as Map<String, dynamic>;
|
||||||
|
|
||||||
|
if (data.containsKey("pk")) {
|
||||||
|
var order = InvenTreePurchaseOrder.fromJson(data);
|
||||||
|
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => PurchaseOrderDetailWidget(order)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> request(BuildContext context) async {
|
Future<void> request(BuildContext context) async {
|
||||||
final bool result = await widget.company.reload();
|
final bool result = await widget.company.reload();
|
||||||
@ -83,11 +164,18 @@ class _CompanyDetailState extends RefreshableState<CompanyDetailWidget> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widget.company.isSupplier) {
|
outstandingPurchaseOrders = widget.company.isSupplier ?
|
||||||
outstandingOrders =
|
await InvenTreePurchaseOrder().count(filters: {
|
||||||
await widget.company.getPurchaseOrders(outstanding: true);
|
"supplier": widget.company.pk.toString(),
|
||||||
}
|
"outstanding": "true"
|
||||||
|
}) : 0;
|
||||||
|
|
||||||
|
outstandingSalesOrders = widget.company.isCustomer ?
|
||||||
|
await InvenTreeSalesOrder().count(filters: {
|
||||||
|
"customer": widget.company.pk.toString(),
|
||||||
|
"outstanding": "true"
|
||||||
|
}) : 0;
|
||||||
|
|
||||||
InvenTreeSupplierPart().count(
|
InvenTreeSupplierPart().count(
|
||||||
filters: {
|
filters: {
|
||||||
"supplier": widget.company.pk.toString()
|
"supplier": widget.company.pk.toString()
|
||||||
@ -224,7 +312,7 @@ class _CompanyDetailState extends RefreshableState<CompanyDetailWidget> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
title: Text(L10().purchaseOrders),
|
title: Text(L10().purchaseOrders),
|
||||||
leading: FaIcon(FontAwesomeIcons.cartShopping, color: COLOR_ACTION),
|
leading: FaIcon(FontAwesomeIcons.cartShopping, color: COLOR_ACTION),
|
||||||
trailing: Text("${outstandingOrders.length}"),
|
trailing: Text("${outstandingPurchaseOrders}"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
@ -257,7 +345,25 @@ class _CompanyDetailState extends RefreshableState<CompanyDetailWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (widget.company.isCustomer) {
|
if (widget.company.isCustomer) {
|
||||||
// TODO - Add list of sales orders
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10().salesOrders),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.truck, color: COLOR_ACTION),
|
||||||
|
trailing: Text("${outstandingSalesOrders}"),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => SalesOrderListWidget(
|
||||||
|
filters: {
|
||||||
|
"customer": widget.company.pk.toString()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widget.company.notes.isNotEmpty) {
|
if (widget.company.notes.isNotEmpty) {
|
@ -8,7 +8,7 @@ import "package:inventree/inventree/model.dart";
|
|||||||
|
|
||||||
import "package:inventree/widget/paginator.dart";
|
import "package:inventree/widget/paginator.dart";
|
||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
import "package:inventree/widget/company_detail.dart";
|
import "package:inventree/widget/company/company_detail.dart";
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
@ -10,8 +10,8 @@ import "package:inventree/l10.dart";
|
|||||||
import "package:inventree/inventree/part.dart";
|
import "package:inventree/inventree/part.dart";
|
||||||
import "package:inventree/inventree/company.dart";
|
import "package:inventree/inventree/company.dart";
|
||||||
|
|
||||||
import "package:inventree/widget/company_detail.dart";
|
import "package:inventree/widget/company/company_detail.dart";
|
||||||
import "package:inventree/widget/part_detail.dart";
|
import "package:inventree/widget/part/part_detail.dart";
|
||||||
import "package:inventree/widget/progress.dart";
|
import "package:inventree/widget/progress.dart";
|
||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
import "package:inventree/widget/snacks.dart";
|
import "package:inventree/widget/snacks.dart";
|
@ -8,7 +8,7 @@ import "package:inventree/inventree/model.dart";
|
|||||||
|
|
||||||
import "package:inventree/widget/paginator.dart";
|
import "package:inventree/widget/paginator.dart";
|
||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
import "package:inventree/widget/supplier_part_detail.dart";
|
import "package:inventree/widget/company/supplier_part_detail.dart";
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
@ -3,15 +3,17 @@ import "package:font_awesome_flutter/font_awesome_flutter.dart";
|
|||||||
|
|
||||||
import "package:inventree/api.dart";
|
import "package:inventree/api.dart";
|
||||||
import "package:inventree/app_colors.dart";
|
import "package:inventree/app_colors.dart";
|
||||||
import "package:inventree/inventree/company.dart";
|
import "package:inventree/inventree/part.dart";
|
||||||
import "package:inventree/inventree/purchase_order.dart";
|
import "package:inventree/inventree/purchase_order.dart";
|
||||||
|
import "package:inventree/inventree/sales_order.dart";
|
||||||
import "package:inventree/inventree/stock.dart";
|
import "package:inventree/inventree/stock.dart";
|
||||||
import "package:inventree/l10.dart";
|
import "package:inventree/l10.dart";
|
||||||
import "package:inventree/settings/settings.dart";
|
import "package:inventree/settings/settings.dart";
|
||||||
import "package:inventree/widget/category_display.dart";
|
import "package:inventree/widget/order/sales_order_list.dart";
|
||||||
|
import "package:inventree/widget/part/category_display.dart";
|
||||||
import "package:inventree/widget/notifications.dart";
|
import "package:inventree/widget/notifications.dart";
|
||||||
import "package:inventree/widget/purchase_order_list.dart";
|
import "package:inventree/widget/order/purchase_order_list.dart";
|
||||||
import "package:inventree/widget/location_display.dart";
|
import "package:inventree/widget/stock/location_display.dart";
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -28,6 +30,10 @@ class InvenTreeDrawer extends StatelessWidget {
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _checkConnection() {
|
||||||
|
return InvenTreeAPI().checkConnection();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return to the 'home' screen.
|
* Return to the 'home' screen.
|
||||||
* This will empty the navigation stack.
|
* This will empty the navigation stack.
|
||||||
@ -43,37 +49,63 @@ class InvenTreeDrawer extends StatelessWidget {
|
|||||||
// Load "parts" page
|
// Load "parts" page
|
||||||
void _parts() {
|
void _parts() {
|
||||||
_closeDrawer();
|
_closeDrawer();
|
||||||
Navigator.push(
|
|
||||||
context,
|
if (_checkConnection()) {
|
||||||
MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null))
|
Navigator.push(
|
||||||
);
|
context,
|
||||||
|
MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null))
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load "stock" page
|
// Load "stock" page
|
||||||
void _stock() {
|
void _stock() {
|
||||||
_closeDrawer();
|
_closeDrawer();
|
||||||
Navigator.push(
|
|
||||||
context,
|
if (_checkConnection()) {
|
||||||
MaterialPageRoute(builder: (context) => LocationDisplayWidget(null))
|
Navigator.push(
|
||||||
);
|
context,
|
||||||
|
MaterialPageRoute(builder: (context) => LocationDisplayWidget(null))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load "sales orders" page
|
||||||
|
void _salesOrders() {
|
||||||
|
_closeDrawer();
|
||||||
|
|
||||||
|
if (_checkConnection()) {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => SalesOrderListWidget(filters: {})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load "purchase orders" page
|
// Load "purchase orders" page
|
||||||
void _purchaseOrders() {
|
void _purchaseOrders() {
|
||||||
_closeDrawer();
|
_closeDrawer();
|
||||||
|
|
||||||
Navigator.push(
|
if (_checkConnection()) {
|
||||||
context,
|
Navigator.push(
|
||||||
MaterialPageRoute(
|
context,
|
||||||
builder: (context) => PurchaseOrderListWidget(filters: {})
|
MaterialPageRoute(
|
||||||
)
|
builder: (context) => PurchaseOrderListWidget(filters: {})
|
||||||
);
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load notifications screen
|
// Load notifications screen
|
||||||
void _notifications() {
|
void _notifications() {
|
||||||
_closeDrawer();
|
_closeDrawer();
|
||||||
Navigator.push(context, MaterialPageRoute(builder: (context) => NotificationWidget()));
|
|
||||||
|
if (_checkConnection()) {
|
||||||
|
Navigator.push(context,
|
||||||
|
MaterialPageRoute(builder: (context) => NotificationWidget()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load settings widget
|
// Load settings widget
|
||||||
@ -98,7 +130,7 @@ class InvenTreeDrawer extends StatelessWidget {
|
|||||||
|
|
||||||
tiles.add(Divider());
|
tiles.add(Divider());
|
||||||
|
|
||||||
if (InvenTreeCompany().canView) {
|
if (InvenTreePart().canView) {
|
||||||
tiles.add(
|
tiles.add(
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(L10().parts),
|
title: Text(L10().parts),
|
||||||
@ -128,6 +160,16 @@ class InvenTreeDrawer extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (InvenTreeSalesOrder().canView) {
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10().salesOrders),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.truck, color: COLOR_ACTION),
|
||||||
|
onTap: _salesOrders,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (tiles.length > 2) {
|
if (tiles.length > 2) {
|
||||||
tiles.add(Divider());
|
tiles.add(Divider());
|
||||||
}
|
}
|
||||||
|
@ -6,20 +6,25 @@ import "package:font_awesome_flutter/font_awesome_flutter.dart";
|
|||||||
|
|
||||||
import "package:inventree/api.dart";
|
import "package:inventree/api.dart";
|
||||||
import "package:inventree/app_colors.dart";
|
import "package:inventree/app_colors.dart";
|
||||||
|
import "package:inventree/inventree/part.dart";
|
||||||
|
import "package:inventree/inventree/purchase_order.dart";
|
||||||
|
import "package:inventree/inventree/sales_order.dart";
|
||||||
|
import "package:inventree/inventree/stock.dart";
|
||||||
import "package:inventree/preferences.dart";
|
import "package:inventree/preferences.dart";
|
||||||
import "package:inventree/l10.dart";
|
import "package:inventree/l10.dart";
|
||||||
import "package:inventree/settings/select_server.dart";
|
import "package:inventree/settings/select_server.dart";
|
||||||
import "package:inventree/user_profile.dart";
|
import "package:inventree/user_profile.dart";
|
||||||
|
|
||||||
import "package:inventree/widget/category_display.dart";
|
import "package:inventree/widget/part/category_display.dart";
|
||||||
import "package:inventree/widget/drawer.dart";
|
import "package:inventree/widget/drawer.dart";
|
||||||
import "package:inventree/widget/location_display.dart";
|
import "package:inventree/widget/stock/location_display.dart";
|
||||||
import "package:inventree/widget/part_list.dart";
|
import "package:inventree/widget/part/part_list.dart";
|
||||||
import "package:inventree/widget/purchase_order_list.dart";
|
import "package:inventree/widget/order/purchase_order_list.dart";
|
||||||
|
import "package:inventree/widget/order/sales_order_list.dart";
|
||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
import "package:inventree/widget/snacks.dart";
|
import "package:inventree/widget/snacks.dart";
|
||||||
import "package:inventree/widget/spinner.dart";
|
import "package:inventree/widget/spinner.dart";
|
||||||
import "package:inventree/widget/company_list.dart";
|
import "package:inventree/widget/company/company_list.dart";
|
||||||
|
|
||||||
|
|
||||||
class InvenTreeHomePage extends StatefulWidget {
|
class InvenTreeHomePage extends StatefulWidget {
|
||||||
@ -53,6 +58,7 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> with BaseWidgetPr
|
|||||||
final homeKey = GlobalKey<ScaffoldState>();
|
final homeKey = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
bool homeShowPo = false;
|
bool homeShowPo = false;
|
||||||
|
bool homeShowSo = false;
|
||||||
bool homeShowSubscribed = false;
|
bool homeShowSubscribed = false;
|
||||||
bool homeShowManufacturers = false;
|
bool homeShowManufacturers = false;
|
||||||
bool homeShowCustomers = false;
|
bool homeShowCustomers = false;
|
||||||
@ -97,6 +103,17 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> with BaseWidgetPr
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _showSalesOrders(BuildContext context) {
|
||||||
|
if (!InvenTreeAPI().checkConnection()) return;
|
||||||
|
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => SalesOrderListWidget(filters: {})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _showSuppliers(BuildContext context) {
|
void _showSuppliers(BuildContext context) {
|
||||||
if (!InvenTreeAPI().checkConnection()) return;
|
if (!InvenTreeAPI().checkConnection()) return;
|
||||||
|
|
||||||
@ -110,12 +127,12 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> with BaseWidgetPr
|
|||||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CompanyListWidget(L10().manufacturers, {"is_manufacturer": "true"})));
|
Navigator.push(context, MaterialPageRoute(builder: (context) => CompanyListWidget(L10().manufacturers, {"is_manufacturer": "true"})));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
void _showCustomers(BuildContext context) {
|
void _showCustomers(BuildContext context) {
|
||||||
if (!InvenTreeAPI().checkConnection()) return;
|
if (!InvenTreeAPI().checkConnection()) return;
|
||||||
|
|
||||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CompanyListWidget(L10().customers, {"is_customer": "true"})));
|
Navigator.push(context, MaterialPageRoute(builder: (context) => CompanyListWidget(L10().customers, {"is_customer": "true"})));
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
void _selectProfile() {
|
void _selectProfile() {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
@ -130,6 +147,7 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> with BaseWidgetPr
|
|||||||
|
|
||||||
homeShowSubscribed = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_SUBSCRIBED, true) as bool;
|
homeShowSubscribed = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_SUBSCRIBED, true) as bool;
|
||||||
homeShowPo = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_PO, true) as bool;
|
homeShowPo = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_PO, true) as bool;
|
||||||
|
homeShowSo = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_SO, true) as bool;
|
||||||
homeShowManufacturers = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_MANUFACTURERS, true) as bool;
|
homeShowManufacturers = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_MANUFACTURERS, true) as bool;
|
||||||
homeShowCustomers = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_CUSTOMERS, true) as bool;
|
homeShowCustomers = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_CUSTOMERS, true) as bool;
|
||||||
homeShowSuppliers = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_SUPPLIERS, true) as bool;
|
homeShowSuppliers = await InvenTreeSettingsManager().getValue(INV_HOME_SHOW_SUPPLIERS, true) as bool;
|
||||||
@ -207,17 +225,19 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> with BaseWidgetPr
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Parts
|
// Parts
|
||||||
tiles.add(_listTile(
|
if (InvenTreePart().canView) {
|
||||||
context,
|
tiles.add(_listTile(
|
||||||
L10().parts,
|
context,
|
||||||
FontAwesomeIcons.shapes,
|
L10().parts,
|
||||||
callback: () {
|
FontAwesomeIcons.shapes,
|
||||||
_showParts(context);
|
callback: () {
|
||||||
},
|
_showParts(context);
|
||||||
));
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
// Starred parts
|
// Starred parts
|
||||||
if (homeShowSubscribed) {
|
if (homeShowSubscribed && InvenTreePart().canView) {
|
||||||
tiles.add(_listTile(
|
tiles.add(_listTile(
|
||||||
context,
|
context,
|
||||||
L10().partsStarred,
|
L10().partsStarred,
|
||||||
@ -229,17 +249,19 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> with BaseWidgetPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Stock button
|
// Stock button
|
||||||
tiles.add(_listTile(
|
if (InvenTreeStockItem().canView) {
|
||||||
context,
|
tiles.add(_listTile(
|
||||||
L10().stock,
|
context,
|
||||||
FontAwesomeIcons.boxesStacked,
|
L10().stock,
|
||||||
callback: () {
|
FontAwesomeIcons.boxesStacked,
|
||||||
_showStock(context);
|
callback: () {
|
||||||
}
|
_showStock(context);
|
||||||
));
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
// Purchase orders
|
// Purchase orders
|
||||||
if (homeShowPo) {
|
if (homeShowPo && InvenTreePurchaseOrder().canView) {
|
||||||
tiles.add(_listTile(
|
tiles.add(_listTile(
|
||||||
context,
|
context,
|
||||||
L10().purchaseOrders,
|
L10().purchaseOrders,
|
||||||
@ -250,8 +272,19 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> with BaseWidgetPr
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (homeShowSo && InvenTreeSalesOrder().canView) {
|
||||||
|
tiles.add(_listTile(
|
||||||
|
context,
|
||||||
|
L10().salesOrders,
|
||||||
|
FontAwesomeIcons.truck,
|
||||||
|
callback: () {
|
||||||
|
_showSalesOrders(context);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
// Suppliers
|
// Suppliers
|
||||||
if (homeShowSuppliers) {
|
if (homeShowSuppliers && InvenTreePurchaseOrder().canView) {
|
||||||
tiles.add(_listTile(
|
tiles.add(_listTile(
|
||||||
context,
|
context,
|
||||||
L10().suppliers,
|
L10().suppliers,
|
||||||
@ -277,7 +310,7 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> with BaseWidgetPr
|
|||||||
}
|
}
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
// Customers
|
// Customers
|
||||||
if (homeShowCustomers) {
|
if (homeShowCustomers) {
|
||||||
tiles.add(_listTile(
|
tiles.add(_listTile(
|
||||||
@ -289,7 +322,6 @@ class _InvenTreeHomePageState extends State<InvenTreeHomePage> with BaseWidgetPr
|
|||||||
}
|
}
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
return tiles;
|
return tiles;
|
||||||
}
|
}
|
||||||
|
@ -7,17 +7,17 @@ import "package:inventree/app_colors.dart";
|
|||||||
import "package:inventree/helpers.dart";
|
import "package:inventree/helpers.dart";
|
||||||
import "package:inventree/l10.dart";
|
import "package:inventree/l10.dart";
|
||||||
import "package:inventree/widget/progress.dart";
|
import "package:inventree/widget/progress.dart";
|
||||||
import "package:inventree/widget/part_detail.dart";
|
import "package:inventree/widget/part/part_detail.dart";
|
||||||
|
|
||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
import "package:inventree/inventree/company.dart";
|
import "package:inventree/inventree/company.dart";
|
||||||
import "package:inventree/inventree/part.dart";
|
import "package:inventree/inventree/part.dart";
|
||||||
import "package:inventree/inventree/purchase_order.dart";
|
import "package:inventree/inventree/purchase_order.dart";
|
||||||
import "package:inventree/widget/snacks.dart";
|
import "package:inventree/widget/snacks.dart";
|
||||||
import "package:inventree/widget/supplier_part_detail.dart";
|
import "package:inventree/widget/company/supplier_part_detail.dart";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Widget for displaying detail view of a purchase order line item
|
* Widget for displaying detail view of a single PurchaseOrderLineItem
|
||||||
*/
|
*/
|
||||||
class POLineDetailWidget extends StatefulWidget {
|
class POLineDetailWidget extends StatefulWidget {
|
||||||
|
|
||||||
@ -171,7 +171,6 @@ class _POLineDetailWidgetState extends RefreshableState<POLineDetailWidget> {
|
|||||||
trailing: api.getThumbnail(widget.item.partImage),
|
trailing: api.getThumbnail(widget.item.partImage),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
showLoadingOverlay(context);
|
showLoadingOverlay(context);
|
||||||
print("part id: ${widget.item.partId}");
|
|
||||||
var part = await InvenTreePart().get(widget.item.partId);
|
var part = await InvenTreePart().get(widget.item.partId);
|
||||||
hideLoadingOverlay();
|
hideLoadingOverlay();
|
||||||
|
|
||||||
@ -200,16 +199,32 @@ class _POLineDetailWidgetState extends RefreshableState<POLineDetailWidget> {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Recevied
|
// Received quantity
|
||||||
tiles.add(
|
tiles.add(
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(L10().received),
|
title: Text(L10().received),
|
||||||
subtitle: Text(widget.item.received.toString()),
|
subtitle: ProgressBar(widget.item.progressRatio),
|
||||||
trailing: Text(widget.item.progressString, style: TextStyle(color: widget.item.isComplete ? COLOR_SUCCESS: COLOR_WARNING)),
|
trailing: Text(
|
||||||
|
widget.item.progressString,
|
||||||
|
style: TextStyle(
|
||||||
|
color: widget.item.isComplete ? COLOR_SUCCESS: COLOR_WARNING
|
||||||
|
)
|
||||||
|
),
|
||||||
leading: FaIcon(FontAwesomeIcons.boxOpen),
|
leading: FaIcon(FontAwesomeIcons.boxOpen),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Reference
|
||||||
|
if (widget.item.reference.isNotEmpty) {
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10().reference),
|
||||||
|
subtitle: Text(widget.item.reference),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.hashtag),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Pricing information
|
// Pricing information
|
||||||
tiles.add(
|
tiles.add(
|
||||||
ListTile(
|
ListTile(
|
@ -9,7 +9,7 @@ import "package:inventree/inventree/model.dart";
|
|||||||
import "package:inventree/inventree/purchase_order.dart";
|
import "package:inventree/inventree/purchase_order.dart";
|
||||||
|
|
||||||
import "package:inventree/widget/paginator.dart";
|
import "package:inventree/widget/paginator.dart";
|
||||||
import "package:inventree/widget/po_line_detail.dart";
|
import "package:inventree/widget/order/po_line_detail.dart";
|
||||||
import "package:inventree/widget/progress.dart";
|
import "package:inventree/widget/progress.dart";
|
||||||
|
|
||||||
/*
|
/*
|
@ -2,9 +2,8 @@ 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:font_awesome_flutter/font_awesome_flutter.dart";
|
import "package:font_awesome_flutter/font_awesome_flutter.dart";
|
||||||
import "package:inventree/widget/dialogs.dart";
|
import "package:inventree/widget/dialogs.dart";
|
||||||
import "package:inventree/widget/po_line_list.dart";
|
import "package:inventree/widget/order/po_line_list.dart";
|
||||||
|
|
||||||
import "package:inventree/api.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/helpers.dart";
|
import "package:inventree/helpers.dart";
|
||||||
@ -13,13 +12,17 @@ import "package:inventree/l10.dart";
|
|||||||
import "package:inventree/inventree/company.dart";
|
import "package:inventree/inventree/company.dart";
|
||||||
import "package:inventree/inventree/purchase_order.dart";
|
import "package:inventree/inventree/purchase_order.dart";
|
||||||
import "package:inventree/widget/attachment_widget.dart";
|
import "package:inventree/widget/attachment_widget.dart";
|
||||||
import "package:inventree/widget/company_detail.dart";
|
import "package:inventree/widget/company/company_detail.dart";
|
||||||
import "package:inventree/widget/notes_widget.dart";
|
import "package:inventree/widget/notes_widget.dart";
|
||||||
|
import "package:inventree/widget/progress.dart";
|
||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
import "package:inventree/widget/snacks.dart";
|
import "package:inventree/widget/snacks.dart";
|
||||||
import "package:inventree/widget/stock_list.dart";
|
import "package:inventree/widget/stock/stock_list.dart";
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Widget for viewing a single PurchaseOrder instance
|
||||||
|
*/
|
||||||
class PurchaseOrderDetailWidget extends StatefulWidget {
|
class PurchaseOrderDetailWidget extends StatefulWidget {
|
||||||
|
|
||||||
const PurchaseOrderDetailWidget(this.order, {Key? key}): super(key: key);
|
const PurchaseOrderDetailWidget(this.order, {Key? key}): super(key: key);
|
||||||
@ -27,16 +30,14 @@ class PurchaseOrderDetailWidget extends StatefulWidget {
|
|||||||
final InvenTreePurchaseOrder order;
|
final InvenTreePurchaseOrder order;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_PurchaseOrderDetailState createState() => _PurchaseOrderDetailState(order);
|
_PurchaseOrderDetailState createState() => _PurchaseOrderDetailState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidget> {
|
class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidget> {
|
||||||
|
|
||||||
_PurchaseOrderDetailState(this.order);
|
_PurchaseOrderDetailState();
|
||||||
|
|
||||||
final InvenTreePurchaseOrder order;
|
|
||||||
|
|
||||||
List<InvenTreePOLineItem> lines = [];
|
List<InvenTreePOLineItem> lines = [];
|
||||||
|
|
||||||
int completedLines = 0;
|
int completedLines = 0;
|
||||||
@ -52,7 +53,7 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
|||||||
List<Widget> appBarActions(BuildContext context) {
|
List<Widget> appBarActions(BuildContext context) {
|
||||||
List<Widget> actions = [];
|
List<Widget> actions = [];
|
||||||
|
|
||||||
if (order.canEdit) {
|
if (widget.order.canEdit) {
|
||||||
actions.add(
|
actions.add(
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.edit_square),
|
icon: Icon(Icons.edit_square),
|
||||||
@ -71,8 +72,8 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
|||||||
List<SpeedDialChild> actionButtons(BuildContext context) {
|
List<SpeedDialChild> actionButtons(BuildContext context) {
|
||||||
List<SpeedDialChild> actions = [];
|
List<SpeedDialChild> actions = [];
|
||||||
|
|
||||||
if (order.canCreate) {
|
if (widget.order.canCreate) {
|
||||||
if (order.isPending) {
|
if (widget.order.isPending) {
|
||||||
actions.add(
|
actions.add(
|
||||||
SpeedDialChild(
|
SpeedDialChild(
|
||||||
child: FaIcon(FontAwesomeIcons.paperPlane, color: Colors.blue),
|
child: FaIcon(FontAwesomeIcons.paperPlane, color: Colors.blue),
|
||||||
@ -84,7 +85,7 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (order.isOpen) {
|
if (widget.order.isOpen) {
|
||||||
actions.add(
|
actions.add(
|
||||||
SpeedDialChild(
|
SpeedDialChild(
|
||||||
child: FaIcon(FontAwesomeIcons.circleXmark, color: Colors.red),
|
child: FaIcon(FontAwesomeIcons.circleXmark, color: Colors.red),
|
||||||
@ -109,7 +110,7 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
|||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
acceptText: L10().issue,
|
acceptText: L10().issue,
|
||||||
onAccept: () async {
|
onAccept: () async {
|
||||||
await order.issueOrder().then((dynamic) {
|
await widget.order.issueOrder().then((dynamic) {
|
||||||
refresh(context);
|
refresh(context);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -125,7 +126,7 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
|||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
acceptText: L10().cancel,
|
acceptText: L10().cancel,
|
||||||
onAccept: () async {
|
onAccept: () async {
|
||||||
await order.cancelOrder().then((dynamic) {
|
await widget.order.cancelOrder().then((dynamic) {
|
||||||
print("callback");
|
print("callback");
|
||||||
refresh(context);
|
refresh(context);
|
||||||
});
|
});
|
||||||
@ -145,7 +146,7 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
|||||||
onTap:() async {
|
onTap:() async {
|
||||||
scanBarcode(
|
scanBarcode(
|
||||||
context,
|
context,
|
||||||
handler: POReceiveBarcodeHandler(purchaseOrder: order),
|
handler: POReceiveBarcodeHandler(purchaseOrder: widget.order),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -158,11 +159,11 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> request(BuildContext context) async {
|
Future<void> request(BuildContext context) async {
|
||||||
await order.reload();
|
await widget.order.reload();
|
||||||
|
|
||||||
await api.PurchaseOrderStatus.load();
|
await api.PurchaseOrderStatus.load();
|
||||||
|
|
||||||
lines = await order.getLineItems();
|
lines = await widget.order.getLineItems();
|
||||||
|
|
||||||
supportProjectCodes = api.supportsProjectCodes && await api.getGlobalBooleanSetting("PROJECT_CODES_ENABLED");
|
supportProjectCodes = api.supportsProjectCodes && await api.getGlobalBooleanSetting("PROJECT_CODES_ENABLED");
|
||||||
|
|
||||||
@ -174,16 +175,20 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
attachmentCount = await InvenTreePurchaseOrderAttachment().count(
|
InvenTreePurchaseOrderAttachment().count(filters: {
|
||||||
filters: {
|
"order": widget.order.pk.toString()
|
||||||
"order": order.pk.toString()
|
}).then((int value) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
attachmentCount = value;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edit the currently displayed PurchaseOrder
|
// Edit the currently displayed PurchaseOrder
|
||||||
Future <void> editOrder(BuildContext context) async {
|
Future <void> editOrder(BuildContext context) async {
|
||||||
var fields = order.formFields();
|
var fields = widget.order.formFields();
|
||||||
|
|
||||||
// Cannot edit supplier field from here
|
// Cannot edit supplier field from here
|
||||||
fields.remove("supplier");
|
fields.remove("supplier");
|
||||||
@ -198,7 +203,7 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
|||||||
fields.remove("project_code");
|
fields.remove("project_code");
|
||||||
}
|
}
|
||||||
|
|
||||||
order.editForm(
|
widget.order.editForm(
|
||||||
context,
|
context,
|
||||||
L10().purchaseOrderEdit,
|
L10().purchaseOrderEdit,
|
||||||
fields: fields,
|
fields: fields,
|
||||||
@ -211,17 +216,17 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
|||||||
|
|
||||||
Widget headerTile(BuildContext context) {
|
Widget headerTile(BuildContext context) {
|
||||||
|
|
||||||
InvenTreeCompany? supplier = order.supplier;
|
InvenTreeCompany? supplier = widget.order.supplier;
|
||||||
|
|
||||||
return Card(
|
return Card(
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: Text(order.reference),
|
title: Text(widget.order.reference),
|
||||||
subtitle: Text(order.description),
|
subtitle: Text(widget.order.description),
|
||||||
leading: supplier == null ? null : InvenTreeAPI().getThumbnail(supplier.thumbnail),
|
leading: supplier == null ? null : api.getThumbnail(supplier.thumbnail),
|
||||||
trailing: Text(
|
trailing: Text(
|
||||||
api.PurchaseOrderStatus.label(order.status),
|
api.PurchaseOrderStatus.label(widget.order.status),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: api.PurchaseOrderStatus.color(order.status)
|
color: api.PurchaseOrderStatus.color(widget.order.status)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -233,14 +238,14 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
|||||||
|
|
||||||
List<Widget> tiles = [];
|
List<Widget> tiles = [];
|
||||||
|
|
||||||
InvenTreeCompany? supplier = order.supplier;
|
InvenTreeCompany? supplier = widget.order.supplier;
|
||||||
|
|
||||||
tiles.add(headerTile(context));
|
tiles.add(headerTile(context));
|
||||||
|
|
||||||
if (supportProjectCodes && order.hasProjectCode) {
|
if (supportProjectCodes && widget.order.hasProjectCode) {
|
||||||
tiles.add(ListTile(
|
tiles.add(ListTile(
|
||||||
title: Text(L10().projectCode),
|
title: Text(L10().projectCode),
|
||||||
subtitle: Text("${order.projectCode} - ${order.projectCodeDescription}"),
|
subtitle: Text("${widget.order.projectCode} - ${widget.order.projectCodeDescription}"),
|
||||||
leading: FaIcon(FontAwesomeIcons.list),
|
leading: FaIcon(FontAwesomeIcons.list),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -261,20 +266,24 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (order.supplierReference.isNotEmpty) {
|
if (widget.order.supplierReference.isNotEmpty) {
|
||||||
tiles.add(ListTile(
|
tiles.add(ListTile(
|
||||||
title: Text(L10().supplierReference),
|
title: Text(L10().supplierReference),
|
||||||
subtitle: Text(order.supplierReference),
|
subtitle: Text(widget.order.supplierReference),
|
||||||
leading: FaIcon(FontAwesomeIcons.hashtag),
|
leading: FaIcon(FontAwesomeIcons.hashtag),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Color lineColor = completedLines < order.lineItemCount ? COLOR_WARNING : COLOR_SUCCESS;
|
Color lineColor = completedLines < widget.order.lineItemCount ? COLOR_WARNING : COLOR_SUCCESS;
|
||||||
|
|
||||||
tiles.add(ListTile(
|
tiles.add(ListTile(
|
||||||
title: Text(L10().lineItems),
|
title: Text(L10().lineItems),
|
||||||
|
subtitle: ProgressBar(
|
||||||
|
completedLines.toDouble(),
|
||||||
|
maximum: widget.order.lineItemCount.toDouble(),
|
||||||
|
),
|
||||||
leading: FaIcon(FontAwesomeIcons.clipboardCheck),
|
leading: FaIcon(FontAwesomeIcons.clipboardCheck),
|
||||||
trailing: Text("${completedLines} / ${order.lineItemCount}", style: TextStyle(color: lineColor)),
|
trailing: Text("${completedLines} / ${widget.order.lineItemCount}", style: TextStyle(color: lineColor)),
|
||||||
));
|
));
|
||||||
|
|
||||||
tiles.add(ListTile(
|
tiles.add(ListTile(
|
||||||
@ -285,18 +294,18 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
|||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
if (order.issueDate.isNotEmpty) {
|
if (widget.order.issueDate.isNotEmpty) {
|
||||||
tiles.add(ListTile(
|
tiles.add(ListTile(
|
||||||
title: Text(L10().issueDate),
|
title: Text(L10().issueDate),
|
||||||
subtitle: Text(order.issueDate),
|
subtitle: Text(widget.order.issueDate),
|
||||||
leading: FaIcon(FontAwesomeIcons.calendarDays),
|
leading: FaIcon(FontAwesomeIcons.calendarDays),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (order.targetDate.isNotEmpty) {
|
if (widget.order.targetDate.isNotEmpty) {
|
||||||
tiles.add(ListTile(
|
tiles.add(ListTile(
|
||||||
title: Text(L10().targetDate),
|
title: Text(L10().targetDate),
|
||||||
subtitle: Text(order.targetDate),
|
subtitle: Text(widget.order.targetDate),
|
||||||
leading: FaIcon(FontAwesomeIcons.calendarDays),
|
leading: FaIcon(FontAwesomeIcons.calendarDays),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -310,7 +319,7 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => NotesWidget(order)
|
builder: (context) => NotesWidget(widget.order)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -329,8 +338,8 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => AttachmentWidget(
|
builder: (context) => AttachmentWidget(
|
||||||
InvenTreePurchaseOrderAttachment(),
|
InvenTreePurchaseOrderAttachment(),
|
||||||
order.pk,
|
widget.order.pk,
|
||||||
order.canEdit
|
widget.order.canEdit
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -355,9 +364,9 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
|||||||
List<Widget> getTabs(BuildContext context) {
|
List<Widget> getTabs(BuildContext context) {
|
||||||
return [
|
return [
|
||||||
ListView(children: orderTiles(context)),
|
ListView(children: orderTiles(context)),
|
||||||
PaginatedPOLineList({"order": order.pk.toString()}),
|
PaginatedPOLineList({"order": widget.order.pk.toString()}),
|
||||||
// ListView(children: lineTiles(context)),
|
// ListView(children: lineTiles(context)),
|
||||||
PaginatedStockItemList({"purchase_order": order.pk.toString()}),
|
PaginatedStockItemList({"purchase_order": widget.order.pk.toString()}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
@ -5,7 +5,7 @@ import "package:font_awesome_flutter/font_awesome_flutter.dart";
|
|||||||
import "package:inventree/inventree/company.dart";
|
import "package:inventree/inventree/company.dart";
|
||||||
import "package:inventree/inventree/model.dart";
|
import "package:inventree/inventree/model.dart";
|
||||||
import "package:inventree/widget/paginator.dart";
|
import "package:inventree/widget/paginator.dart";
|
||||||
import "package:inventree/widget/purchase_order_detail.dart";
|
import "package:inventree/widget/order/purchase_order_detail.dart";
|
||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
import "package:inventree/l10.dart";
|
import "package:inventree/l10.dart";
|
||||||
import "package:inventree/api.dart";
|
import "package:inventree/api.dart";
|
||||||
@ -22,15 +22,13 @@ class PurchaseOrderListWidget extends StatefulWidget {
|
|||||||
final Map<String, String> filters;
|
final Map<String, String> filters;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_PurchaseOrderListWidgetState createState() => _PurchaseOrderListWidgetState(filters);
|
_PurchaseOrderListWidgetState createState() => _PurchaseOrderListWidgetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class _PurchaseOrderListWidgetState extends RefreshableState<PurchaseOrderListWidget> {
|
class _PurchaseOrderListWidgetState extends RefreshableState<PurchaseOrderListWidget> {
|
||||||
|
|
||||||
_PurchaseOrderListWidgetState(this.filters);
|
_PurchaseOrderListWidgetState();
|
||||||
|
|
||||||
final Map<String, String> filters;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String getAppBarTitle() => L10().purchaseOrders;
|
String getAppBarTitle() => L10().purchaseOrders;
|
||||||
@ -45,7 +43,7 @@ class _PurchaseOrderListWidgetState extends RefreshableState<PurchaseOrderListWi
|
|||||||
child: FaIcon(FontAwesomeIcons.circlePlus),
|
child: FaIcon(FontAwesomeIcons.circlePlus),
|
||||||
label: L10().purchaseOrderCreate,
|
label: L10().purchaseOrderCreate,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
createPurchaseOrder(context);
|
_createPurchaseOrder(context);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -54,9 +52,11 @@ class _PurchaseOrderListWidgetState extends RefreshableState<PurchaseOrderListWi
|
|||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> createPurchaseOrder(BuildContext context) async {
|
// Launch form to create a new PurchaseOrder
|
||||||
|
Future<void> _createPurchaseOrder(BuildContext context) async {
|
||||||
var fields = InvenTreePurchaseOrder().formFields();
|
var fields = InvenTreePurchaseOrder().formFields();
|
||||||
|
|
||||||
|
// Cannot set contact until company is locked in
|
||||||
fields.remove("contact");
|
fields.remove("contact");
|
||||||
|
|
||||||
InvenTreePurchaseOrder().createForm(
|
InvenTreePurchaseOrder().createForm(
|
||||||
@ -104,7 +104,7 @@ class _PurchaseOrderListWidgetState extends RefreshableState<PurchaseOrderListWi
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget getBody(BuildContext context) {
|
Widget getBody(BuildContext context) {
|
||||||
return PaginatedPurchaseOrderList(filters);
|
return PaginatedPurchaseOrderList(widget.filters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,6 +143,11 @@ class _PaginatedPurchaseOrderListState extends PaginatedSearchState<PaginatedPur
|
|||||||
"label": L10().outstanding,
|
"label": L10().outstanding,
|
||||||
"help_text": L10().outstandingOrderDetail,
|
"help_text": L10().outstandingOrderDetail,
|
||||||
"tristate": true,
|
"tristate": true,
|
||||||
|
},
|
||||||
|
"overdue": {
|
||||||
|
"label": L10().overdue,
|
||||||
|
"help_text": L10().overdueDetail,
|
||||||
|
"tristate": true,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -153,7 +158,6 @@ class _PaginatedPurchaseOrderListState extends PaginatedSearchState<PaginatedPur
|
|||||||
final page = await InvenTreePurchaseOrder().listPaginated(limit, offset, filters: params);
|
final page = await InvenTreePurchaseOrder().listPaginated(limit, offset, filters: params);
|
||||||
|
|
||||||
return page;
|
return page;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
303
lib/widget/order/sales_order_detail.dart
Normal file
303
lib/widget/order/sales_order_detail.dart
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
|
||||||
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_speed_dial/flutter_speed_dial.dart";
|
||||||
|
import "package:font_awesome_flutter/font_awesome_flutter.dart";
|
||||||
|
import "package:inventree/inventree/company.dart";
|
||||||
|
import "package:inventree/inventree/sales_order.dart";
|
||||||
|
import "package:inventree/widget/order/so_line_list.dart";
|
||||||
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
|
|
||||||
|
import "package:inventree/l10.dart";
|
||||||
|
|
||||||
|
import "package:inventree/app_colors.dart";
|
||||||
|
import "package:inventree/widget/attachment_widget.dart";
|
||||||
|
import "package:inventree/widget/notes_widget.dart";
|
||||||
|
import "package:inventree/widget/snacks.dart";
|
||||||
|
import "package:inventree/widget/company/company_detail.dart";
|
||||||
|
import "package:inventree/widget/progress.dart";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Widget for viewing a single SalesOrder instance
|
||||||
|
*/
|
||||||
|
class SalesOrderDetailWidget extends StatefulWidget {
|
||||||
|
|
||||||
|
const SalesOrderDetailWidget(this.order, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
final InvenTreeSalesOrder order;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_SalesOrderDetailState createState() => _SalesOrderDetailState();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class _SalesOrderDetailState extends RefreshableState<SalesOrderDetailWidget> {
|
||||||
|
|
||||||
|
_SalesOrderDetailState();
|
||||||
|
|
||||||
|
List<InvenTreeSOLineItem> lines = [];
|
||||||
|
|
||||||
|
bool supportsProjectCodes = false;
|
||||||
|
int completedLines = 0;
|
||||||
|
int attachmentCount = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String getAppBarTitle() => L10().salesOrder;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Widget> appBarActions(BuildContext context) {
|
||||||
|
List<Widget> actions = [];
|
||||||
|
|
||||||
|
if (widget.order.canEdit) {
|
||||||
|
actions.add(
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.edit_square),
|
||||||
|
onPressed: () {
|
||||||
|
editOrder(context);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new line item to this sales order
|
||||||
|
Future<void> _addLineItem(BuildContext context) async {
|
||||||
|
var fields = InvenTreeSOLineItem().formFields();
|
||||||
|
|
||||||
|
fields["order"]?["value"] = widget.order.pk;
|
||||||
|
fields["order"]?["hidden"] = true;
|
||||||
|
|
||||||
|
InvenTreeSOLineItem().createForm(
|
||||||
|
context,
|
||||||
|
L10().lineItemAdd,
|
||||||
|
fields: fields,
|
||||||
|
onSuccess: (result) async {
|
||||||
|
refresh(context);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<SpeedDialChild> actionButtons(BuildContext context) {
|
||||||
|
List<SpeedDialChild> actions = [];
|
||||||
|
|
||||||
|
// Add line item
|
||||||
|
if (widget.order.isOpen && InvenTreeSOLineItem().canCreate) {
|
||||||
|
actions.add(
|
||||||
|
SpeedDialChild(
|
||||||
|
child: FaIcon(FontAwesomeIcons.circlePlus),
|
||||||
|
label: L10().lineItemAdd,
|
||||||
|
onTap: () async {
|
||||||
|
_addLineItem(context);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<SpeedDialChild> barcodeButtons(BuildContext context) {
|
||||||
|
List<SpeedDialChild> actions = [];
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
return actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> request(BuildContext context) async {
|
||||||
|
await widget.order.reload();
|
||||||
|
await api.SalesOrderStatus.load();
|
||||||
|
|
||||||
|
supportsProjectCodes = api.supportsProjectCodes && await api.getGlobalBooleanSetting("PROJECT_CODES_ENABLED");
|
||||||
|
|
||||||
|
completedLines = 0;
|
||||||
|
|
||||||
|
for (var line in lines) {
|
||||||
|
if (line.isComplete) {
|
||||||
|
completedLines += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InvenTreeSalesOrderAttachment().count(filters: {
|
||||||
|
"order": widget.order.pk.toString()
|
||||||
|
}).then((int value) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
attachmentCount = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edit the current SalesOrder instance
|
||||||
|
Future<void> editOrder(BuildContext context) async {
|
||||||
|
var fields = widget.order.formFields();
|
||||||
|
|
||||||
|
fields.remove("customer");
|
||||||
|
|
||||||
|
// Contact model not supported by server
|
||||||
|
if (!api.supportsContactModel) {
|
||||||
|
fields.remove("contact");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectCode model not supported by server
|
||||||
|
if (!supportsProjectCodes) {
|
||||||
|
fields.remove("project_code");
|
||||||
|
}
|
||||||
|
|
||||||
|
widget.order.editForm(
|
||||||
|
context,
|
||||||
|
L10().salesOrderEdit,
|
||||||
|
fields: fields,
|
||||||
|
onSuccess: (data) async {
|
||||||
|
refresh(context);
|
||||||
|
showSnackIcon(L10().salesOrderUpdated, success: true);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct header tile
|
||||||
|
Widget headerTile(BuildContext context) {
|
||||||
|
InvenTreeCompany? customer = widget.order.customer;
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
child: ListTile(
|
||||||
|
title: Text(widget.order.reference),
|
||||||
|
subtitle: Text(widget.order.description),
|
||||||
|
leading: customer == null ? null : api.getThumbnail(customer.thumbnail),
|
||||||
|
trailing: Text(
|
||||||
|
api.SalesOrderStatus.label(widget.order.status),
|
||||||
|
style: TextStyle(
|
||||||
|
color: api.SalesOrderStatus.color(widget.order.status)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> orderTiles(BuildContext context) {
|
||||||
|
|
||||||
|
List<Widget> tiles = [
|
||||||
|
headerTile(context)
|
||||||
|
];
|
||||||
|
|
||||||
|
InvenTreeCompany? customer = widget.order.customer;
|
||||||
|
|
||||||
|
if (supportsProjectCodes && widget.order.hasProjectCode) {
|
||||||
|
tiles.add(ListTile(
|
||||||
|
title: Text(L10().projectCode),
|
||||||
|
subtitle: Text("${widget.order.projectCode} - ${widget.order.projectCodeDescription}"),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.list),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (customer != null) {
|
||||||
|
tiles.add(ListTile(
|
||||||
|
title: Text(L10().customer),
|
||||||
|
subtitle: Text(customer.name),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.userTie, color: COLOR_ACTION),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => CompanyDetailWidget(customer)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget.order.customerReference.isNotEmpty) {
|
||||||
|
tiles.add(ListTile(
|
||||||
|
title: Text(L10().customerReference),
|
||||||
|
subtitle: Text(widget.order.customerReference),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.hashtag),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Color lineColor = completedLines < widget.order.lineItemCount ? COLOR_WARNING : COLOR_SUCCESS;
|
||||||
|
|
||||||
|
tiles.add(ListTile(
|
||||||
|
title: Text(L10().lineItems),
|
||||||
|
subtitle: ProgressBar(
|
||||||
|
completedLines.toDouble(),
|
||||||
|
maximum: widget.order.lineItemCount.toDouble()
|
||||||
|
),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.clipboardCheck),
|
||||||
|
trailing: Text("${completedLines} / ${widget.order.lineItemCount}", style: TextStyle(color: lineColor)),
|
||||||
|
));
|
||||||
|
|
||||||
|
// TODO: total price
|
||||||
|
|
||||||
|
if (widget.order.targetDate.isNotEmpty) {
|
||||||
|
tiles.add(ListTile(
|
||||||
|
title: Text(L10().targetDate),
|
||||||
|
subtitle: Text(widget.order.targetDate),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.calendarDays),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notes tile
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10().notes),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.noteSticky, color: COLOR_ACTION),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => NotesWidget(widget.order)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Attachments
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10().attachments),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.fileLines, color: COLOR_ACTION),
|
||||||
|
trailing: attachmentCount > 0 ? Text(attachmentCount.toString()) : null,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => AttachmentWidget(
|
||||||
|
InvenTreeSalesOrderAttachment(),
|
||||||
|
widget.order.pk,
|
||||||
|
widget.order.canEdit
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return tiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Widget> getTabIcons(BuildContext context) {
|
||||||
|
return [
|
||||||
|
Tab(text: L10().details),
|
||||||
|
Tab(text: L10().lineItems),
|
||||||
|
// TODO: Add in the "shipped items" tab
|
||||||
|
// Tab(text: L10().shipped)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Widget> getTabs(BuildContext context) {
|
||||||
|
return [
|
||||||
|
ListView(children: orderTiles(context)),
|
||||||
|
PaginatedSOLineList({"order": widget.order.pk.toString()}),
|
||||||
|
// Center(), // TODO: Delivered stock
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
176
lib/widget/order/sales_order_list.dart
Normal file
176
lib/widget/order/sales_order_list.dart
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
|
||||||
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_speed_dial/flutter_speed_dial.dart";
|
||||||
|
import "package:font_awesome_flutter/font_awesome_flutter.dart";
|
||||||
|
import "package:inventree/inventree/sales_order.dart";
|
||||||
|
import "package:inventree/widget/order/sales_order_detail.dart";
|
||||||
|
import "package:inventree/widget/paginator.dart";
|
||||||
|
|
||||||
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
|
import "package:inventree/l10.dart";
|
||||||
|
|
||||||
|
import "package:inventree/api.dart";
|
||||||
|
import "package:inventree/inventree/company.dart";
|
||||||
|
import "package:inventree/inventree/model.dart";
|
||||||
|
|
||||||
|
|
||||||
|
class SalesOrderListWidget extends StatefulWidget {
|
||||||
|
|
||||||
|
const SalesOrderListWidget({this.filters = const {}, Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
final Map<String, String> filters;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_SalesOrderListWidgetState createState() => _SalesOrderListWidgetState();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SalesOrderListWidgetState extends RefreshableState<SalesOrderListWidget> {
|
||||||
|
|
||||||
|
_SalesOrderListWidgetState();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String getAppBarTitle() => L10().salesOrders;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<SpeedDialChild> actionButtons(BuildContext context) {
|
||||||
|
List<SpeedDialChild> actions = [];
|
||||||
|
|
||||||
|
if (InvenTreeSalesOrder().canCreate) {
|
||||||
|
actions.add(
|
||||||
|
SpeedDialChild(
|
||||||
|
child: FaIcon(FontAwesomeIcons.circlePlus),
|
||||||
|
label: L10().salesOrderCreate,
|
||||||
|
onTap: () {
|
||||||
|
_createSalesOrder(context);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Launch form to create a new SalesOrder
|
||||||
|
Future<void> _createSalesOrder(BuildContext context) async {
|
||||||
|
var fields = InvenTreeSalesOrder().formFields();
|
||||||
|
|
||||||
|
// Cannot set contact until company is locked in
|
||||||
|
fields.remove("contact");
|
||||||
|
|
||||||
|
InvenTreeSalesOrder().createForm(
|
||||||
|
context,
|
||||||
|
L10().salesOrderCreate,
|
||||||
|
fields: fields,
|
||||||
|
onSuccess: (result) async {
|
||||||
|
Map<String, dynamic> data = result as Map<String, dynamic>;
|
||||||
|
|
||||||
|
if (data.containsKey("pk")) {
|
||||||
|
var order = InvenTreeSalesOrder.fromJson(data);
|
||||||
|
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => SalesOrderDetailWidget(order)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<SpeedDialChild> barcodeButtons(BuildContext context) {
|
||||||
|
// TODO: return custom barcode actions
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget getBody(BuildContext context) {
|
||||||
|
return PaginatedSalesOrderList(widget.filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PaginatedSalesOrderList extends PaginatedSearchWidget {
|
||||||
|
|
||||||
|
const PaginatedSalesOrderList(Map<String, String> filters) : super(filters: filters);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get searchTitle => L10().salesOrders;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_PaginatedSalesOrderListState createState() => _PaginatedSalesOrderListState();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class _PaginatedSalesOrderListState extends PaginatedSearchState<PaginatedSalesOrderList> {
|
||||||
|
|
||||||
|
_PaginatedSalesOrderListState() : super();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get prefix => "so_";
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, String> get orderingOptions => {
|
||||||
|
"reference": L10().reference,
|
||||||
|
"status": L10().status,
|
||||||
|
"target_date": L10().targetDate,
|
||||||
|
"customer__name": L10().customer,
|
||||||
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, Map<String, dynamic>> get filterOptions => {
|
||||||
|
"outstanding": {
|
||||||
|
"label": L10().outstanding,
|
||||||
|
"help_text": L10().outstandingOrderDetail,
|
||||||
|
"tristate": true,
|
||||||
|
},
|
||||||
|
"overdue": {
|
||||||
|
"label": L10().overdue,
|
||||||
|
"help_text": L10().overdueDetail,
|
||||||
|
"tristate": true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<InvenTreePageResponse?> requestPage(int limit, int offset, Map<String, String> params) async {
|
||||||
|
|
||||||
|
await InvenTreeAPI().SalesOrderStatus.load();
|
||||||
|
final page = await InvenTreeSalesOrder().listPaginated(limit, offset, filters: params);
|
||||||
|
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildItem(BuildContext context, InvenTreeModel model) {
|
||||||
|
|
||||||
|
InvenTreeSalesOrder order = model as InvenTreeSalesOrder;
|
||||||
|
|
||||||
|
InvenTreeCompany? customer = order.customer;
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
|
title: Text(order.reference),
|
||||||
|
subtitle: Text(order.description),
|
||||||
|
leading: customer == null ? null : InvenTreeAPI().getThumbnail(customer.thumbnail),
|
||||||
|
trailing: Text(
|
||||||
|
InvenTreeAPI().SalesOrderStatus.label(order.status),
|
||||||
|
style: TextStyle(
|
||||||
|
color: InvenTreeAPI().SalesOrderStatus.color(order.status),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
onTap: () async {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => SalesOrderDetailWidget(order)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
164
lib/widget/order/so_line_detail.dart
Normal file
164
lib/widget/order/so_line_detail.dart
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Widget for displaying detail view of a single SalesOrderLineItem
|
||||||
|
*/
|
||||||
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_speed_dial/flutter_speed_dial.dart";
|
||||||
|
import "package:font_awesome_flutter/font_awesome_flutter.dart";
|
||||||
|
|
||||||
|
import "package:inventree/app_colors.dart";
|
||||||
|
import "package:inventree/l10.dart";
|
||||||
|
import "package:inventree/inventree/part.dart";
|
||||||
|
import "package:inventree/inventree/sales_order.dart";
|
||||||
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
|
import "package:inventree/widget/progress.dart";
|
||||||
|
import "package:inventree/widget/part/part_detail.dart";
|
||||||
|
|
||||||
|
import "package:inventree/helpers.dart";
|
||||||
|
import "package:inventree/widget/snacks.dart";
|
||||||
|
|
||||||
|
|
||||||
|
class SoLineDetailWidget extends StatefulWidget {
|
||||||
|
|
||||||
|
const SoLineDetailWidget(this.item, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
final InvenTreeSOLineItem item;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_SOLineDetailWidgetState createState() => _SOLineDetailWidgetState();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class _SOLineDetailWidgetState extends RefreshableState<SoLineDetailWidget> {
|
||||||
|
|
||||||
|
_SOLineDetailWidgetState();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String getAppBarTitle() => L10().lineItem;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Widget> appBarActions(BuildContext context) {
|
||||||
|
List<Widget> actions = [];
|
||||||
|
|
||||||
|
if (widget.item.canEdit) {
|
||||||
|
actions.add(
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.edit_square),
|
||||||
|
onPressed: () {
|
||||||
|
_editLineItem(context);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _editLineItem(BuildContext context) async {
|
||||||
|
var fields = widget.item.formFields();
|
||||||
|
|
||||||
|
// Prevent editing of the line item
|
||||||
|
if (widget.item.shipped > 0) {
|
||||||
|
fields["part"]?["hidden"] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
widget.item.editForm(
|
||||||
|
context,
|
||||||
|
L10().editLineItem,
|
||||||
|
fields: fields,
|
||||||
|
onSuccess: (data) async {
|
||||||
|
refresh(context);
|
||||||
|
showSnackIcon(L10().lineItemUpdated, success: true);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<SpeedDialChild> actionButtons(BuildContext context) {
|
||||||
|
// TODO
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> request(BuildContext context) async {
|
||||||
|
await widget.item.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Widget> getTiles(BuildContext context) {
|
||||||
|
List<Widget> tiles = [];
|
||||||
|
|
||||||
|
// Reference to the part
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10().part),
|
||||||
|
subtitle: Text(widget.item.partName),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.shapes, color: COLOR_ACTION),
|
||||||
|
trailing: api.getThumbnail(widget.item.partImage),
|
||||||
|
onTap: () async {
|
||||||
|
showLoadingOverlay(context);
|
||||||
|
var part = await InvenTreePart().get(widget.item.partId);
|
||||||
|
hideLoadingOverlay();
|
||||||
|
|
||||||
|
if (part is InvenTreePart) {
|
||||||
|
Navigator.push(context, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Shipped quantity
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10().shipped),
|
||||||
|
subtitle: ProgressBar(widget.item.progressRatio),
|
||||||
|
trailing: Text(
|
||||||
|
widget.item.progressString,
|
||||||
|
style: TextStyle(
|
||||||
|
color: widget.item.isComplete ? COLOR_SUCCESS : COLOR_WARNING
|
||||||
|
),
|
||||||
|
),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.truck)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reference
|
||||||
|
if (widget.item.reference.isNotEmpty) {
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10().reference),
|
||||||
|
subtitle: Text(widget.item.reference),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.hashtag)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note
|
||||||
|
if (widget.item.notes.isNotEmpty) {
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10().notes),
|
||||||
|
subtitle: Text(widget.item.notes),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.noteSticky),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// External link
|
||||||
|
if (widget.item.link.isNotEmpty) {
|
||||||
|
tiles.add(
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10().link),
|
||||||
|
subtitle: Text(widget.item.link),
|
||||||
|
leading: FaIcon(FontAwesomeIcons.link, color: COLOR_ACTION),
|
||||||
|
onTap: () async {
|
||||||
|
await openLink(widget.item.link);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tiles;
|
||||||
|
}
|
||||||
|
}
|
86
lib/widget/order/so_line_list.dart
Normal file
86
lib/widget/order/so_line_list.dart
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import "package:flutter/material.dart";
|
||||||
|
import "package:inventree/l10.dart";
|
||||||
|
import "package:inventree/widget/order/so_line_detail.dart";
|
||||||
|
import "package:inventree/widget/paginator.dart";
|
||||||
|
import "package:inventree/inventree/model.dart";
|
||||||
|
import "package:inventree/inventree/sales_order.dart";
|
||||||
|
import "package:inventree/inventree/part.dart";
|
||||||
|
import "package:inventree/api.dart";
|
||||||
|
import "package:inventree/app_colors.dart";
|
||||||
|
import "package:inventree/widget/progress.dart";
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Paginated widget class for displaying a list of sales order line items
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PaginatedSOLineList extends PaginatedSearchWidget {
|
||||||
|
const PaginatedSOLineList(Map<String, String> filters) : super(filters: filters);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get searchTitle => L10().lineItems;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_PaginatedSOLineListState createState() => _PaginatedSOLineListState();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* State class for PaginatedSOLineList
|
||||||
|
*/
|
||||||
|
class _PaginatedSOLineListState extends PaginatedSearchState<PaginatedSOLineList> {
|
||||||
|
|
||||||
|
_PaginatedSOLineListState() : super();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get prefix => "so_line_";
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, String> get orderingOptions => {
|
||||||
|
"part": L10().part,
|
||||||
|
"quantity": L10().quantity,
|
||||||
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, Map<String, dynamic>> get filterOptions => {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<InvenTreePageResponse?> requestPage(int limit, int offset, Map<String, String> params) async {
|
||||||
|
final page = await InvenTreeSOLineItem().listPaginated(limit, offset, filters: params);
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildItem(BuildContext context, InvenTreeModel model) {
|
||||||
|
InvenTreeSOLineItem item = model as InvenTreeSOLineItem;
|
||||||
|
InvenTreePart? part = item.part;
|
||||||
|
|
||||||
|
if (part != null) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(part.name),
|
||||||
|
subtitle: Text(part.description),
|
||||||
|
leading: InvenTreeAPI().getThumbnail(part.thumbnail),
|
||||||
|
trailing: Text(item.progressString),
|
||||||
|
onTap: () async {
|
||||||
|
showLoadingOverlay(context);
|
||||||
|
await item.reload();
|
||||||
|
hideLoadingOverlay();
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => SoLineDetailWidget(item))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(L10().error),
|
||||||
|
subtitle: Text("Missing part detail", style: TextStyle(color: COLOR_DANGER)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -11,7 +11,7 @@ import "package:inventree/inventree/model.dart";
|
|||||||
import "package:inventree/inventree/part.dart";
|
import "package:inventree/inventree/part.dart";
|
||||||
|
|
||||||
import "package:inventree/widget/paginator.dart";
|
import "package:inventree/widget/paginator.dart";
|
||||||
import "package:inventree/widget/part_detail.dart";
|
import "package:inventree/widget/part/part_detail.dart";
|
||||||
import "package:inventree/widget/progress.dart";
|
import "package:inventree/widget/progress.dart";
|
||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
|
|
@ -7,11 +7,11 @@ import "package:inventree/l10.dart";
|
|||||||
|
|
||||||
import "package:inventree/inventree/part.dart";
|
import "package:inventree/inventree/part.dart";
|
||||||
|
|
||||||
import "package:inventree/widget/category_list.dart";
|
import "package:inventree/widget/part/category_list.dart";
|
||||||
import "package:inventree/widget/part_list.dart";
|
import "package:inventree/widget/part/part_list.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";
|
||||||
import "package:inventree/widget/part_detail.dart";
|
import "package:inventree/widget/part/part_detail.dart";
|
||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
|
|
||||||
|
|
@ -2,7 +2,7 @@ import "package:flutter/material.dart";
|
|||||||
|
|
||||||
import "package:inventree/inventree/model.dart";
|
import "package:inventree/inventree/model.dart";
|
||||||
import "package:inventree/inventree/part.dart";
|
import "package:inventree/inventree/part.dart";
|
||||||
import "package:inventree/widget/category_display.dart";
|
import "package:inventree/widget/part/category_display.dart";
|
||||||
import "package:inventree/widget/paginator.dart";
|
import "package:inventree/widget/paginator.dart";
|
||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
|
|
@ -15,18 +15,18 @@ import "package:inventree/labels.dart";
|
|||||||
import "package:inventree/preferences.dart";
|
import "package:inventree/preferences.dart";
|
||||||
|
|
||||||
import "package:inventree/widget/attachment_widget.dart";
|
import "package:inventree/widget/attachment_widget.dart";
|
||||||
import "package:inventree/widget/bom_list.dart";
|
import "package:inventree/widget/part/bom_list.dart";
|
||||||
import "package:inventree/widget/part_list.dart";
|
import "package:inventree/widget/part/part_list.dart";
|
||||||
import "package:inventree/widget/notes_widget.dart";
|
import "package:inventree/widget/notes_widget.dart";
|
||||||
import "package:inventree/widget/part_parameter_widget.dart";
|
import "package:inventree/widget/part/part_parameter_widget.dart";
|
||||||
import "package:inventree/widget/progress.dart";
|
import "package:inventree/widget/progress.dart";
|
||||||
import "package:inventree/widget/category_display.dart";
|
import "package:inventree/widget/part/category_display.dart";
|
||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
import "package:inventree/widget/part_image_widget.dart";
|
import "package:inventree/widget/part/part_image_widget.dart";
|
||||||
import "package:inventree/widget/snacks.dart";
|
import "package:inventree/widget/snacks.dart";
|
||||||
import "package:inventree/widget/stock_detail.dart";
|
import "package:inventree/widget/stock/stock_detail.dart";
|
||||||
import "package:inventree/widget/stock_list.dart";
|
import "package:inventree/widget/stock/stock_list.dart";
|
||||||
import "package:inventree/widget/supplier_part_list.dart";
|
import "package:inventree/widget/company/supplier_part_list.dart";
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -634,7 +634,7 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
|||||||
fields.remove("serial");
|
fields.remove("serial");
|
||||||
|
|
||||||
// Hide the "part" field
|
// Hide the "part" field
|
||||||
fields["part"]["hidden"] = true;
|
fields["part"]?["hidden"] = true;
|
||||||
|
|
||||||
int? default_location = part.defaultLocation;
|
int? default_location = part.defaultLocation;
|
||||||
|
|
@ -7,7 +7,7 @@ import "package:inventree/inventree/model.dart";
|
|||||||
import "package:inventree/inventree/part.dart";
|
import "package:inventree/inventree/part.dart";
|
||||||
|
|
||||||
import "package:inventree/widget/paginator.dart";
|
import "package:inventree/widget/paginator.dart";
|
||||||
import "package:inventree/widget/part_detail.dart";
|
import "package:inventree/widget/part/part_detail.dart";
|
||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
|
|
||||||
|
|
@ -7,7 +7,7 @@ import "package:inventree/api.dart";
|
|||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:inventree/inventree/part.dart";
|
import "package:inventree/inventree/part.dart";
|
||||||
import "package:inventree/inventree/company.dart";
|
import "package:inventree/inventree/company.dart";
|
||||||
import "package:inventree/widget/company_detail.dart";
|
import "package:inventree/widget/company/company_detail.dart";
|
||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
|
|
||||||
class PartSupplierWidget extends StatefulWidget {
|
class PartSupplierWidget extends StatefulWidget {
|
@ -2,6 +2,34 @@
|
|||||||
|
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_overlay_loader/flutter_overlay_loader.dart";
|
import "package:flutter_overlay_loader/flutter_overlay_loader.dart";
|
||||||
|
import "package:inventree/app_colors.dart";
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A simplified linear progress bar widget,
|
||||||
|
* with standardized color depiction
|
||||||
|
*/
|
||||||
|
Widget ProgressBar(
|
||||||
|
double value,
|
||||||
|
{
|
||||||
|
double maximum = 1.0
|
||||||
|
}) {
|
||||||
|
|
||||||
|
double v = 0;
|
||||||
|
|
||||||
|
if (value <= 0 || maximum <= 0) {
|
||||||
|
v = 0;
|
||||||
|
} else {
|
||||||
|
v = value / maximum;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LinearProgressIndicator(
|
||||||
|
value: v,
|
||||||
|
backgroundColor: Colors.grey,
|
||||||
|
color: v >= 1 ? COLOR_SUCCESS : COLOR_WARNING,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Construct a circular progress indicator
|
* Construct a circular progress indicator
|
||||||
|
@ -11,13 +11,13 @@ import "package:inventree/inventree/part.dart";
|
|||||||
import "package:inventree/inventree/purchase_order.dart";
|
import "package:inventree/inventree/purchase_order.dart";
|
||||||
import "package:inventree/inventree/stock.dart";
|
import "package:inventree/inventree/stock.dart";
|
||||||
|
|
||||||
import "package:inventree/widget/part_list.dart";
|
import "package:inventree/widget/part/part_list.dart";
|
||||||
import "package:inventree/widget/purchase_order_list.dart";
|
import "package:inventree/widget/order/purchase_order_list.dart";
|
||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
import "package:inventree/widget/stock_list.dart";
|
import "package:inventree/widget/stock/stock_list.dart";
|
||||||
import "package:inventree/widget/category_list.dart";
|
import "package:inventree/widget/part/category_list.dart";
|
||||||
import "package:inventree/widget/company_list.dart";
|
import "package:inventree/widget/company/company_list.dart";
|
||||||
import "package:inventree/widget/location_list.dart";
|
import "package:inventree/widget/stock/location_list.dart";
|
||||||
|
|
||||||
|
|
||||||
// Widget for performing database-wide search
|
// Widget for performing database-wide search
|
||||||
|
@ -10,12 +10,12 @@ import "package:inventree/l10.dart";
|
|||||||
import "package:inventree/inventree/stock.dart";
|
import "package:inventree/inventree/stock.dart";
|
||||||
import "package:inventree/preferences.dart";
|
import "package:inventree/preferences.dart";
|
||||||
|
|
||||||
import "package:inventree/widget/location_list.dart";
|
import "package:inventree/widget/stock/location_list.dart";
|
||||||
import "package:inventree/widget/progress.dart";
|
import "package:inventree/widget/progress.dart";
|
||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
import "package:inventree/widget/snacks.dart";
|
import "package:inventree/widget/snacks.dart";
|
||||||
import "package:inventree/widget/stock_detail.dart";
|
import "package:inventree/widget/stock/stock_detail.dart";
|
||||||
import "package:inventree/widget/stock_list.dart";
|
import "package:inventree/widget/stock/stock_list.dart";
|
||||||
import "package:inventree/labels.dart";
|
import "package:inventree/labels.dart";
|
||||||
|
|
||||||
|
|
@ -2,7 +2,7 @@ import "package:flutter/material.dart";
|
|||||||
|
|
||||||
import "package:inventree/inventree/model.dart";
|
import "package:inventree/inventree/model.dart";
|
||||||
import "package:inventree/inventree/stock.dart";
|
import "package:inventree/inventree/stock.dart";
|
||||||
import "package:inventree/widget/location_display.dart";
|
import "package:inventree/widget/stock/location_display.dart";
|
||||||
import "package:inventree/widget/paginator.dart";
|
import "package:inventree/widget/paginator.dart";
|
||||||
|
|
||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
@ -16,16 +16,16 @@ import "package:inventree/inventree/company.dart";
|
|||||||
import "package:inventree/inventree/stock.dart";
|
import "package:inventree/inventree/stock.dart";
|
||||||
import "package:inventree/inventree/part.dart";
|
import "package:inventree/inventree/part.dart";
|
||||||
|
|
||||||
import "package:inventree/widget/supplier_part_detail.dart";
|
import "package:inventree/widget/company/supplier_part_detail.dart";
|
||||||
import "package:inventree/widget/dialogs.dart";
|
import "package:inventree/widget/dialogs.dart";
|
||||||
import "package:inventree/widget/attachment_widget.dart";
|
import "package:inventree/widget/attachment_widget.dart";
|
||||||
import "package:inventree/widget/location_display.dart";
|
import "package:inventree/widget/stock/location_display.dart";
|
||||||
import "package:inventree/widget/part_detail.dart";
|
import "package:inventree/widget/part/part_detail.dart";
|
||||||
import "package:inventree/widget/progress.dart";
|
import "package:inventree/widget/progress.dart";
|
||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
import "package:inventree/widget/snacks.dart";
|
import "package:inventree/widget/snacks.dart";
|
||||||
import "package:inventree/widget/stock_item_history.dart";
|
import "package:inventree/widget/stock/stock_item_history.dart";
|
||||||
import "package:inventree/widget/stock_item_test_results.dart";
|
import "package:inventree/widget/stock/stock_item_test_results.dart";
|
||||||
import "package:inventree/widget/notes_widget.dart";
|
import "package:inventree/widget/notes_widget.dart";
|
||||||
|
|
||||||
|
|
@ -5,7 +5,7 @@ import "package:inventree/inventree/stock.dart";
|
|||||||
import "package:inventree/widget/paginator.dart";
|
import "package:inventree/widget/paginator.dart";
|
||||||
import "package:inventree/widget/refreshable_state.dart";
|
import "package:inventree/widget/refreshable_state.dart";
|
||||||
import "package:inventree/l10.dart";
|
import "package:inventree/l10.dart";
|
||||||
import "package:inventree/widget/stock_detail.dart";
|
import "package:inventree/widget/stock/stock_detail.dart";
|
||||||
import "package:inventree/api.dart";
|
import "package:inventree/api.dart";
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user