mirror of
https://github.com/inventree/inventree-app.git
synced 2025-06-15 03:35:28 +00:00
Order extra lines (#632)
* Define classes for extra line item * Display PO extra line items - Also, some refactoring * Support extra line items for sales order * linting fixes * Update release notes
This commit is contained in:
@ -6,19 +6,15 @@ import "package:inventree/l10.dart";
|
||||
import "package:inventree/api.dart";
|
||||
import "package:inventree/app_colors.dart";
|
||||
import "package:inventree/helpers.dart";
|
||||
|
||||
import "package:inventree/inventree/company.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/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/snacks.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";
|
||||
|
||||
|
||||
/*
|
||||
@ -121,13 +117,7 @@ class _CompanyDetailState extends RefreshableState<CompanyDetailWidget> {
|
||||
|
||||
if (data.containsKey("pk")) {
|
||||
var order = InvenTreeSalesOrder.fromJson(data);
|
||||
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SalesOrderDetailWidget(order)
|
||||
)
|
||||
);
|
||||
order.goToDetailPage(context);
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -150,13 +140,7 @@ class _CompanyDetailState extends RefreshableState<CompanyDetailWidget> {
|
||||
|
||||
if (data.containsKey("pk")) {
|
||||
var order = InvenTreePurchaseOrder.fromJson(data);
|
||||
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PurchaseOrderDetailWidget(order)
|
||||
)
|
||||
);
|
||||
order.goToDetailPage(context);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -11,7 +11,6 @@ import "package:inventree/inventree/model.dart";
|
||||
|
||||
import "package:inventree/widget/paginator.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
import "package:inventree/widget/company/company_detail.dart";
|
||||
|
||||
|
||||
/*
|
||||
@ -48,13 +47,7 @@ class _CompanyListWidgetState extends RefreshableState<CompanyListWidget> {
|
||||
|
||||
if (data.containsKey("pk")) {
|
||||
var company = InvenTreeCompany.fromJson(data);
|
||||
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CompanyDetailWidget(company)
|
||||
)
|
||||
);
|
||||
company.goToDetailPage(context);
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -137,7 +130,7 @@ class _CompanyListState extends PaginatedSearchState<PaginatedCompanyList> {
|
||||
subtitle: Text(company.description),
|
||||
leading: InvenTreeAPI().getThumbnail(company.image),
|
||||
onTap: () async {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CompanyDetailWidget(company)));
|
||||
company.goToDetailPage(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -13,9 +13,6 @@ import "package:inventree/inventree/part.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
import "package:inventree/widget/snacks.dart";
|
||||
import "package:inventree/widget/progress.dart";
|
||||
|
||||
import "package:inventree/widget/part/part_detail.dart";
|
||||
import "package:inventree/widget/company/company_detail.dart";
|
||||
import "package:url_launcher/url_launcher.dart";
|
||||
|
||||
/*
|
||||
@ -114,8 +111,7 @@ class _ManufacturerPartDisplayState extends RefreshableState<ManufacturerPartDet
|
||||
hideLoadingOverlay();
|
||||
|
||||
if (part is InvenTreePart) {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => PartDetailWidget(part)));
|
||||
part.goToDetailPage(context);
|
||||
}
|
||||
},
|
||||
)
|
||||
@ -134,9 +130,7 @@ class _ManufacturerPartDisplayState extends RefreshableState<ManufacturerPartDet
|
||||
hideLoadingOverlay();
|
||||
|
||||
if (supplier is InvenTreeCompany) {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => CompanyDetailWidget(supplier)
|
||||
));
|
||||
supplier.goToDetailPage(context);
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -15,9 +15,7 @@ import "package:inventree/inventree/company.dart";
|
||||
import "package:inventree/widget/progress.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
import "package:inventree/widget/snacks.dart";
|
||||
import "package:inventree/widget/company/company_detail.dart";
|
||||
import "package:inventree/widget/company/manufacturer_part_detail.dart";
|
||||
import "package:inventree/widget/part/part_detail.dart";
|
||||
|
||||
|
||||
/*
|
||||
@ -126,8 +124,7 @@ class _SupplierPartDisplayState extends RefreshableState<SupplierPartDetailWidge
|
||||
hideLoadingOverlay();
|
||||
|
||||
if (part is InvenTreePart) {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => PartDetailWidget(part)));
|
||||
part.goToDetailPage(context);
|
||||
}
|
||||
},
|
||||
)
|
||||
@ -169,9 +166,7 @@ class _SupplierPartDisplayState extends RefreshableState<SupplierPartDetailWidge
|
||||
hideLoadingOverlay();
|
||||
|
||||
if (supplier is InvenTreeCompany) {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => CompanyDetailWidget(supplier)
|
||||
));
|
||||
supplier.goToDetailPage(context);
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -200,9 +195,7 @@ class _SupplierPartDisplayState extends RefreshableState<SupplierPartDetailWidge
|
||||
hideLoadingOverlay();
|
||||
|
||||
if (supplier is InvenTreeCompany) {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => CompanyDetailWidget(supplier)
|
||||
));
|
||||
supplier.goToDetailPage(context);
|
||||
}
|
||||
}
|
||||
)
|
||||
|
112
lib/widget/order/extra_line_detail.dart
Normal file
112
lib/widget/order/extra_line_detail.dart
Normal file
@ -0,0 +1,112 @@
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
||||
import "package:inventree/helpers.dart";
|
||||
|
||||
import "package:inventree/l10.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
import "package:inventree/widget/snacks.dart";
|
||||
|
||||
import "package:inventree/inventree/orders.dart";
|
||||
|
||||
|
||||
class ExtraLineDetailWidget extends StatefulWidget {
|
||||
const ExtraLineDetailWidget(this.item, {Key? key}) : super(key: key);
|
||||
|
||||
final InvenTreeExtraLineItem item;
|
||||
|
||||
@override
|
||||
_ExtraLineDetailWidgetState createState() => _ExtraLineDetailWidgetState();
|
||||
}
|
||||
|
||||
class _ExtraLineDetailWidgetState extends RefreshableState<ExtraLineDetailWidget> {
|
||||
|
||||
_ExtraLineDetailWidgetState();
|
||||
|
||||
@override
|
||||
String getAppBarTitle() => L10().extraLineItem;
|
||||
|
||||
@override
|
||||
List<Widget> appBarActions(BuildContext context) {
|
||||
List<Widget> actions = [];
|
||||
|
||||
if (widget.item.canEdit) {
|
||||
actions.add(
|
||||
IconButton(
|
||||
icon: Icon(TablerIcons.edit),
|
||||
onPressed: () {
|
||||
_editLineItem(context);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
// Function to request data for this page
|
||||
@override
|
||||
Future<void> request(BuildContext context) async {
|
||||
await widget.item.reload();
|
||||
}
|
||||
|
||||
// Callback to edit this line item
|
||||
Future<void> _editLineItem(BuildContext context) async {
|
||||
var fields = widget.item.formFields();
|
||||
|
||||
widget.item.editForm(
|
||||
context,
|
||||
L10().editLineItem,
|
||||
fields: fields,
|
||||
onSuccess: (data) async {
|
||||
refresh(context);
|
||||
showSnackIcon(L10().lineItemUpdated, success: true);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Widget> getTiles(BuildContext context) {
|
||||
List<Widget> tiles = [];
|
||||
|
||||
tiles.add(
|
||||
ListTile(
|
||||
title: Text(L10().reference),
|
||||
trailing: Text(widget.item.reference),
|
||||
)
|
||||
);
|
||||
|
||||
tiles.add(
|
||||
ListTile(
|
||||
title: Text(L10().description),
|
||||
trailing: Text(widget.item.description),
|
||||
)
|
||||
);
|
||||
|
||||
tiles.add(
|
||||
ListTile(
|
||||
title: Text(L10().quantity),
|
||||
trailing: Text(widget.item.quantity.toString()),
|
||||
)
|
||||
);
|
||||
|
||||
tiles.add(
|
||||
ListTile(
|
||||
title: Text(L10().unitPrice),
|
||||
trailing: Text(
|
||||
renderCurrency(widget.item.price, widget.item.priceCurrency)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (widget.item.notes.isNotEmpty) {
|
||||
tiles.add(
|
||||
ListTile(
|
||||
title: Text(L10().notes),
|
||||
subtitle: Text(widget.item.notes),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return tiles;
|
||||
}
|
||||
}
|
116
lib/widget/order/po_extra_line_list.dart
Normal file
116
lib/widget/order/po_extra_line_list.dart
Normal file
@ -0,0 +1,116 @@
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_speed_dial/flutter_speed_dial.dart";
|
||||
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
||||
|
||||
import "package:inventree/l10.dart";
|
||||
import "package:inventree/inventree/model.dart";
|
||||
import "package:inventree/inventree/purchase_order.dart";
|
||||
import "package:inventree/widget/paginator.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
import "package:inventree/widget/snacks.dart";
|
||||
|
||||
|
||||
class POExtraLineListWidget extends StatefulWidget {
|
||||
|
||||
const POExtraLineListWidget(this.order, {this.filters = const {}, Key? key}) : super(key: key);
|
||||
|
||||
final InvenTreePurchaseOrder order;
|
||||
|
||||
final Map<String, String> filters;
|
||||
|
||||
@override
|
||||
_PurchaseOrderExtraLineListWidgetState createState() => _PurchaseOrderExtraLineListWidgetState();
|
||||
}
|
||||
|
||||
class _PurchaseOrderExtraLineListWidgetState extends RefreshableState<POExtraLineListWidget> {
|
||||
|
||||
_PurchaseOrderExtraLineListWidgetState();
|
||||
|
||||
@override
|
||||
String getAppBarTitle() => L10().extraLineItems;
|
||||
|
||||
Future<void> _addLineItem(BuildContext context) async {
|
||||
|
||||
var fields = InvenTreePOExtraLineItem().formFields();
|
||||
|
||||
fields["order"]?["value"] = widget.order.pk;
|
||||
|
||||
InvenTreePOExtraLineItem().createForm(
|
||||
context,
|
||||
L10().lineItemAdd,
|
||||
fields: fields,
|
||||
onSuccess: (data) async {
|
||||
refresh(context);
|
||||
showSnackIcon(L10().lineItemUpdated, success: true);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<SpeedDialChild> actionButtons(BuildContext context) {
|
||||
List<SpeedDialChild> actions = [];
|
||||
|
||||
if (widget.order.canEdit) {
|
||||
actions.add(
|
||||
SpeedDialChild(
|
||||
child: Icon(TablerIcons.circle_plus, color: Colors.green),
|
||||
label: L10().lineItemAdd,
|
||||
onTap: () {
|
||||
_addLineItem(context);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget getBody(BuildContext context) {
|
||||
return PaginatedPOExtraLineList(widget.filters);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PaginatedPOExtraLineList extends PaginatedSearchWidget {
|
||||
|
||||
const PaginatedPOExtraLineList(Map<String, String> filters) : super(filters: filters);
|
||||
|
||||
@override
|
||||
String get searchTitle => L10().extraLineItems;
|
||||
|
||||
@override
|
||||
_PaginatedPOExtraLineListState createState() => _PaginatedPOExtraLineListState();
|
||||
|
||||
}
|
||||
|
||||
class _PaginatedPOExtraLineListState extends PaginatedSearchState<PaginatedPOExtraLineList> {
|
||||
|
||||
_PaginatedPOExtraLineListState() : super();
|
||||
|
||||
@override
|
||||
String get prefix => "po_extra_line_";
|
||||
|
||||
@override
|
||||
Future<InvenTreePageResponse?> requestPage(int limit, int offset, Map<String, String> params) async {
|
||||
final page = await InvenTreePOExtraLineItem().listPaginated(limit, offset, filters: params);
|
||||
return page;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildItem(BuildContext context, InvenTreeModel model) {
|
||||
|
||||
InvenTreePOExtraLineItem line = model as InvenTreePOExtraLineItem;
|
||||
|
||||
return ListTile(
|
||||
title: Text(line.reference),
|
||||
subtitle: Text(line.description),
|
||||
trailing: Text(line.quantity.toString()),
|
||||
onTap: () {
|
||||
line.goToDetailPage(context).then((_) {
|
||||
refresh();
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -13,8 +13,6 @@ import "package:inventree/inventree/purchase_order.dart";
|
||||
import "package:inventree/inventree/stock.dart";
|
||||
|
||||
import "package:inventree/widget/progress.dart";
|
||||
import "package:inventree/widget/part/part_detail.dart";
|
||||
import "package:inventree/widget/stock/location_display.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
import "package:inventree/widget/snacks.dart";
|
||||
import "package:inventree/widget/company/supplier_part_detail.dart";
|
||||
@ -157,7 +155,7 @@ class _POLineDetailWidgetState extends RefreshableState<POLineDetailWidget> {
|
||||
hideLoadingOverlay();
|
||||
|
||||
if (part is InvenTreePart) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
|
||||
part.goToDetailPage(context);
|
||||
}
|
||||
},
|
||||
)
|
||||
@ -187,14 +185,8 @@ class _POLineDetailWidgetState extends RefreshableState<POLineDetailWidget> {
|
||||
title: Text(L10().destination),
|
||||
subtitle: Text(destination!.name),
|
||||
leading: Icon(TablerIcons.map_pin, color: COLOR_ACTION),
|
||||
onTap: () =>
|
||||
{
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LocationDisplayWidget(destination)
|
||||
)
|
||||
)
|
||||
onTap: () => {
|
||||
destination!.goToDetailPage(context)
|
||||
}
|
||||
));
|
||||
}
|
||||
|
@ -14,12 +14,12 @@ import "package:inventree/inventree/stock.dart";
|
||||
import "package:inventree/inventree/purchase_order.dart";
|
||||
|
||||
import "package:inventree/widget/dialogs.dart";
|
||||
import "package:inventree/widget/order/po_extra_line_list.dart";
|
||||
import "package:inventree/widget/stock/location_display.dart";
|
||||
import "package:inventree/widget/order/po_line_list.dart";
|
||||
|
||||
|
||||
import "package:inventree/widget/attachment_widget.dart";
|
||||
import "package:inventree/widget/company/company_detail.dart";
|
||||
import "package:inventree/widget/notes_widget.dart";
|
||||
import "package:inventree/widget/progress.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
@ -47,11 +47,11 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
||||
_PurchaseOrderDetailState();
|
||||
|
||||
List<InvenTreePOLineItem> lines = [];
|
||||
int extraLineCount = 0;
|
||||
|
||||
InvenTreeStockLocation? destination;
|
||||
|
||||
int completedLines = 0;
|
||||
|
||||
int attachmentCount = 0;
|
||||
|
||||
bool showCameraShortcut = true;
|
||||
@ -296,6 +296,15 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Count number of "extra line items" against this order
|
||||
InvenTreePOExtraLineItem().count(filters: {"order": widget.order.pk.toString() }).then((int value) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
extraLineCount = value;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Edit the currently displayed PurchaseOrder
|
||||
@ -368,12 +377,7 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
||||
subtitle: Text(supplier.name),
|
||||
leading: Icon(TablerIcons.building, color: COLOR_ACTION),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CompanyDetailWidget(supplier)
|
||||
)
|
||||
);
|
||||
supplier.goToDetailPage(context);
|
||||
},
|
||||
));
|
||||
}
|
||||
@ -415,6 +419,21 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
||||
trailing: Text("${completedLines} / ${widget.order.lineItemCount}", style: TextStyle(color: lineColor)),
|
||||
));
|
||||
|
||||
// Extra line items
|
||||
tiles.add(ListTile(
|
||||
title: Text(L10().extraLineItems),
|
||||
leading: Icon(TablerIcons.clipboard_list, color: COLOR_ACTION),
|
||||
trailing: Text(extraLineCount.toString()),
|
||||
onTap: () => {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => POExtraLineListWidget(widget.order, filters: {"order": widget.order.pk.toString()})
|
||||
)
|
||||
)
|
||||
},
|
||||
));
|
||||
|
||||
tiles.add(ListTile(
|
||||
title: Text(L10().totalPrice),
|
||||
leading: Icon(TablerIcons.currency_dollar),
|
||||
|
@ -5,7 +5,6 @@ import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
||||
import "package:inventree/inventree/company.dart";
|
||||
import "package:inventree/inventree/model.dart";
|
||||
import "package:inventree/widget/paginator.dart";
|
||||
import "package:inventree/widget/order/purchase_order_detail.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
import "package:inventree/l10.dart";
|
||||
import "package:inventree/api.dart";
|
||||
@ -69,13 +68,7 @@ class _PurchaseOrderListWidgetState extends RefreshableState<PurchaseOrderListWi
|
||||
|
||||
if (data.containsKey("pk")) {
|
||||
var order = InvenTreePurchaseOrder.fromJson(data);
|
||||
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PurchaseOrderDetailWidget(order)
|
||||
)
|
||||
);
|
||||
order.goToDetailPage(context);
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -184,12 +177,7 @@ class _PaginatedPurchaseOrderListState extends PaginatedSearchState<PaginatedPur
|
||||
),
|
||||
),
|
||||
onTap: () async {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PurchaseOrderDetailWidget(order)
|
||||
)
|
||||
);
|
||||
order.goToDetailPage(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import "package:inventree/barcode/sales_order.dart";
|
||||
import "package:inventree/inventree/company.dart";
|
||||
import "package:inventree/inventree/sales_order.dart";
|
||||
import "package:inventree/preferences.dart";
|
||||
import "package:inventree/widget/order/so_extra_line_list.dart";
|
||||
import "package:inventree/widget/order/so_line_list.dart";
|
||||
import "package:inventree/widget/order/so_shipment_list.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
@ -18,7 +19,6 @@ import "package:inventree/widget/attachment_widget.dart";
|
||||
import "package:inventree/widget/dialogs.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";
|
||||
|
||||
/*
|
||||
@ -40,6 +40,7 @@ class _SalesOrderDetailState extends RefreshableState<SalesOrderDetailWidget> {
|
||||
_SalesOrderDetailState();
|
||||
|
||||
List<InvenTreeSOLineItem> lines = [];
|
||||
int extraLineCount = 0;
|
||||
|
||||
bool showCameraShortcut = true;
|
||||
bool supportsProjectCodes = false;
|
||||
@ -270,6 +271,15 @@ class _SalesOrderDetailState extends RefreshableState<SalesOrderDetailWidget> {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Count number of "extra line items" against this order
|
||||
InvenTreeSOExtraLineItem().count(filters: {"order": widget.order.pk.toString() }).then((int value) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
extraLineCount = value;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Edit the current SalesOrder instance
|
||||
@ -340,12 +350,7 @@ class _SalesOrderDetailState extends RefreshableState<SalesOrderDetailWidget> {
|
||||
subtitle: Text(customer.name),
|
||||
leading: Icon(TablerIcons.user, color: COLOR_ACTION),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CompanyDetailWidget(customer)
|
||||
)
|
||||
);
|
||||
customer.goToDetailPage(context);
|
||||
}
|
||||
));
|
||||
}
|
||||
@ -370,6 +375,21 @@ class _SalesOrderDetailState extends RefreshableState<SalesOrderDetailWidget> {
|
||||
trailing: Text("${widget.order.completedLineItemCount} / ${widget.order.lineItemCount}", style: TextStyle(color: lineColor)),
|
||||
));
|
||||
|
||||
// Extra line items
|
||||
tiles.add(ListTile(
|
||||
title: Text(L10().extraLineItems),
|
||||
leading: Icon(TablerIcons.clipboard_list, color: COLOR_ACTION),
|
||||
trailing: Text(extraLineCount.toString()),
|
||||
onTap: () => {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SOExtraLineListWidget(widget.order, filters: {"order": widget.order.pk.toString()})
|
||||
)
|
||||
)
|
||||
},
|
||||
));
|
||||
|
||||
// Shipment progress
|
||||
if (widget.order.shipmentCount > 0) {
|
||||
tiles.add(ListTile(
|
||||
|
@ -3,7 +3,6 @@ import "package:flutter/material.dart";
|
||||
import "package:flutter_speed_dial/flutter_speed_dial.dart";
|
||||
import "package:flutter_tabler_icons/flutter_tabler_icons.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";
|
||||
@ -67,13 +66,7 @@ class _SalesOrderListWidgetState extends RefreshableState<SalesOrderListWidget>
|
||||
|
||||
if (data.containsKey("pk")) {
|
||||
var order = InvenTreeSalesOrder.fromJson(data);
|
||||
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SalesOrderDetailWidget(order)
|
||||
)
|
||||
);
|
||||
order.goToDetailPage(context);
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -167,12 +160,7 @@ class _PaginatedSalesOrderListState extends PaginatedSearchState<PaginatedSalesO
|
||||
)
|
||||
),
|
||||
onTap: () async {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SalesOrderDetailWidget(order)
|
||||
)
|
||||
);
|
||||
order.goToDetailPage(context);
|
||||
}
|
||||
);
|
||||
|
||||
|
118
lib/widget/order/so_extra_line_list.dart
Normal file
118
lib/widget/order/so_extra_line_list.dart
Normal file
@ -0,0 +1,118 @@
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_speed_dial/flutter_speed_dial.dart";
|
||||
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
|
||||
|
||||
import "package:inventree/l10.dart";
|
||||
|
||||
import "package:inventree/inventree/model.dart";
|
||||
import "package:inventree/inventree/sales_order.dart";
|
||||
|
||||
import "package:inventree/widget/paginator.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
import "package:inventree/widget/snacks.dart";
|
||||
|
||||
|
||||
class SOExtraLineListWidget extends StatefulWidget {
|
||||
|
||||
const SOExtraLineListWidget(this.order, {this.filters = const {}, Key? key}) : super(key: key);
|
||||
|
||||
final InvenTreeSalesOrder order;
|
||||
|
||||
final Map<String, String> filters;
|
||||
|
||||
@override
|
||||
_SalesOrderExtraLineListWidgetState createState() => _SalesOrderExtraLineListWidgetState();
|
||||
}
|
||||
|
||||
class _SalesOrderExtraLineListWidgetState extends RefreshableState<SOExtraLineListWidget> {
|
||||
|
||||
_SalesOrderExtraLineListWidgetState();
|
||||
|
||||
@override
|
||||
String getAppBarTitle() => L10().extraLineItems;
|
||||
|
||||
Future<void> _addLineItem(BuildContext context) async {
|
||||
|
||||
var fields = InvenTreeSOExtraLineItem().formFields();
|
||||
|
||||
fields["order"]?["value"] = widget.order.pk;
|
||||
|
||||
InvenTreeSOExtraLineItem().createForm(
|
||||
context,
|
||||
L10().lineItemAdd,
|
||||
fields: fields,
|
||||
onSuccess: (data) async {
|
||||
refresh(context);
|
||||
showSnackIcon(L10().lineItemUpdated, success: true);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<SpeedDialChild> actionButtons(BuildContext context) {
|
||||
List<SpeedDialChild> actions = [];
|
||||
|
||||
if (widget.order.canEdit) {
|
||||
actions.add(
|
||||
SpeedDialChild(
|
||||
child: Icon(TablerIcons.circle_plus, color: Colors.green),
|
||||
label: L10().lineItemAdd,
|
||||
onTap: () {
|
||||
_addLineItem(context);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget getBody(BuildContext context) {
|
||||
return PaginatedSOExtraLineList(widget.filters);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PaginatedSOExtraLineList extends PaginatedSearchWidget {
|
||||
|
||||
const PaginatedSOExtraLineList(Map<String, String> filters) : super(filters: filters);
|
||||
|
||||
@override
|
||||
String get searchTitle => L10().extraLineItems;
|
||||
|
||||
@override
|
||||
_PaginatedSOExtraLineListState createState() => _PaginatedSOExtraLineListState();
|
||||
|
||||
}
|
||||
|
||||
class _PaginatedSOExtraLineListState extends PaginatedSearchState<PaginatedSOExtraLineList> {
|
||||
|
||||
_PaginatedSOExtraLineListState() : super();
|
||||
|
||||
@override
|
||||
String get prefix => "so_extra_line_";
|
||||
|
||||
@override
|
||||
Future<InvenTreePageResponse?> requestPage(int limit, int offset, Map<String, String> params) async {
|
||||
final page = await InvenTreeSOExtraLineItem().listPaginated(limit, offset, filters: params);
|
||||
return page;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildItem(BuildContext context, InvenTreeModel model) {
|
||||
|
||||
InvenTreeSOExtraLineItem line = model as InvenTreeSOExtraLineItem;
|
||||
|
||||
return ListTile(
|
||||
title: Text(line.reference),
|
||||
subtitle: Text(line.description),
|
||||
trailing: Text(line.quantity.toString()),
|
||||
onTap: () {
|
||||
line.goToDetailPage(context).then((_) {
|
||||
refresh();
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -15,7 +15,6 @@ 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/widget/snacks.dart";
|
||||
|
||||
import "package:inventree/app_colors.dart";
|
||||
@ -192,7 +191,7 @@ class _SOLineDetailWidgetState extends RefreshableState<SoLineDetailWidget> {
|
||||
hideLoadingOverlay();
|
||||
|
||||
if (part is InvenTreePart) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
|
||||
part.goToDetailPage(context);
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -257,6 +257,10 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget> extends Sta
|
||||
// Pagination controller
|
||||
final PagingController<int, InvenTreeModel> _pagingController = PagingController(firstPageKey: 0);
|
||||
|
||||
void refresh() {
|
||||
_pagingController.refresh();
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_pagingController.addPageRequestListener((pageKey) {
|
||||
|
@ -11,7 +11,6 @@ import "package:inventree/inventree/model.dart";
|
||||
import "package:inventree/inventree/part.dart";
|
||||
|
||||
import "package:inventree/widget/paginator.dart";
|
||||
import "package:inventree/widget/part/part_detail.dart";
|
||||
import "package:inventree/widget/progress.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
|
||||
@ -159,7 +158,7 @@ class _PaginatedBomListState extends PaginatedSearchState<PaginatedBomList> {
|
||||
hideLoadingOverlay();
|
||||
|
||||
if (part is InvenTreePart) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
|
||||
part.goToDetailPage(context);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -11,7 +11,6 @@ import "package:inventree/widget/part/category_list.dart";
|
||||
import "package:inventree/widget/part/part_list.dart";
|
||||
import "package:inventree/widget/progress.dart";
|
||||
import "package:inventree/widget/snacks.dart";
|
||||
import "package:inventree/widget/part/part_detail.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
|
||||
|
||||
@ -164,7 +163,7 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
hideLoadingOverlay();
|
||||
|
||||
if (cat is InvenTreePartCategory) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(cat)));
|
||||
cat.goToDetailPage(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -255,13 +254,9 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
|
||||
if (data.containsKey("pk")) {
|
||||
var cat = InvenTreePartCategory.fromJson(data);
|
||||
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CategoryDisplayWidget(cat)
|
||||
)
|
||||
);
|
||||
cat.goToDetailPage(context).then((_) {
|
||||
refresh(context);
|
||||
});
|
||||
} else {
|
||||
refresh(context);
|
||||
}
|
||||
@ -285,13 +280,7 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
|
||||
if (data.containsKey("pk")) {
|
||||
var part = InvenTreePart.fromJson(data);
|
||||
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PartDetailWidget(part)
|
||||
)
|
||||
);
|
||||
part.goToDetailPage(context);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -2,7 +2,6 @@ import "package:flutter/material.dart";
|
||||
|
||||
import "package:inventree/inventree/model.dart";
|
||||
import "package:inventree/inventree/part.dart";
|
||||
import "package:inventree/widget/part/category_display.dart";
|
||||
import "package:inventree/widget/paginator.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
|
||||
@ -100,12 +99,7 @@ class _PaginatedPartCategoryListState extends PaginatedSearchState<PaginatedPart
|
||||
trailing: Text("${category.partcount}"),
|
||||
leading: category.customIcon == null ? null : Icon(category.customIcon),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CategoryDisplayWidget(category)
|
||||
)
|
||||
);
|
||||
category.goToDetailPage(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ import "package:inventree/widget/part/category_display.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
import "package:inventree/widget/part/part_image_widget.dart";
|
||||
import "package:inventree/widget/snacks.dart";
|
||||
import "package:inventree/widget/stock/stock_detail.dart";
|
||||
import "package:inventree/widget/stock/stock_list.dart";
|
||||
import "package:inventree/widget/company/supplier_part_list.dart";
|
||||
|
||||
@ -347,10 +346,7 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
||||
height: 32,
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => PartDetailWidget(parentPart!))
|
||||
);
|
||||
parentPart?.goToDetailPage(context);
|
||||
}
|
||||
)
|
||||
);
|
||||
@ -371,8 +367,7 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
||||
hideLoadingOverlay();
|
||||
|
||||
if (cat is InvenTreePartCategory) {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => CategoryDisplayWidget(cat)));
|
||||
cat.goToDetailPage(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -674,13 +669,7 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
||||
|
||||
if (data.containsKey("pk")) {
|
||||
var item = InvenTreeStockItem.fromJson(data);
|
||||
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StockDetailWidget(item)
|
||||
)
|
||||
);
|
||||
item.goToDetailPage(context);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -7,7 +7,6 @@ import "package:inventree/inventree/model.dart";
|
||||
import "package:inventree/inventree/part.dart";
|
||||
|
||||
import "package:inventree/widget/paginator.dart";
|
||||
import "package:inventree/widget/part/part_detail.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
|
||||
|
||||
@ -132,7 +131,7 @@ class _PaginatedPartListState extends PaginatedSearchState<PaginatedPartList> {
|
||||
),
|
||||
leading: InvenTreeAPI().getThumbnail(part.thumbnail),
|
||||
onTap: () {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
|
||||
part.goToDetailPage(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import "package:inventree/api.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:inventree/inventree/part.dart";
|
||||
import "package:inventree/inventree/company.dart";
|
||||
import "package:inventree/widget/company/company_detail.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
|
||||
class PartSupplierWidget extends StatefulWidget {
|
||||
@ -58,12 +57,7 @@ class _PartSupplierState extends RefreshableState<PartSupplierWidget> {
|
||||
var company = await InvenTreeCompany().get(_part.supplierId);
|
||||
|
||||
if (company != null && company is InvenTreeCompany) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CompanyDetailWidget(company)
|
||||
)
|
||||
);
|
||||
company.goToDetailPage(context);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -15,7 +15,6 @@ import "package:inventree/widget/stock/location_list.dart";
|
||||
import "package:inventree/widget/progress.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
import "package:inventree/widget/snacks.dart";
|
||||
import "package:inventree/widget/stock/stock_detail.dart";
|
||||
import "package:inventree/widget/stock/stock_list.dart";
|
||||
import "package:inventree/labels.dart";
|
||||
|
||||
@ -279,13 +278,7 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
|
||||
if (data.containsKey("pk")) {
|
||||
var loc = InvenTreeStockLocation.fromJson(data);
|
||||
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LocationDisplayWidget(loc)
|
||||
)
|
||||
);
|
||||
loc.goToDetailPage(context);
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -317,13 +310,7 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
|
||||
if (data.containsKey("pk")) {
|
||||
var item = InvenTreeStockItem.fromJson(data);
|
||||
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StockDetailWidget(item)
|
||||
)
|
||||
);
|
||||
item.goToDetailPage(context);
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -367,8 +354,7 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
hideLoadingOverlay();
|
||||
|
||||
if (loc is InvenTreeStockLocation) {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => LocationDisplayWidget(loc)));
|
||||
loc.goToDetailPage(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -2,7 +2,6 @@ import "package:flutter/material.dart";
|
||||
|
||||
import "package:inventree/inventree/model.dart";
|
||||
import "package:inventree/inventree/stock.dart";
|
||||
import "package:inventree/widget/stock/location_display.dart";
|
||||
import "package:inventree/widget/paginator.dart";
|
||||
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
@ -87,12 +86,7 @@ class _PaginatedStockLocationListState extends PaginatedSearchState<PaginatedSto
|
||||
trailing: Text("${location.itemcount}"),
|
||||
leading: location.customIcon == null ? null : Icon(location.customIcon),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LocationDisplayWidget(location)
|
||||
)
|
||||
);
|
||||
location.goToDetailPage(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -17,14 +17,10 @@ import "package:inventree/preferences.dart";
|
||||
import "package:inventree/inventree/company.dart";
|
||||
import "package:inventree/inventree/stock.dart";
|
||||
import "package:inventree/inventree/part.dart";
|
||||
import "package:inventree/widget/company/company_detail.dart";
|
||||
|
||||
import "package:inventree/widget/company/supplier_part_detail.dart";
|
||||
import "package:inventree/widget/dialogs.dart";
|
||||
import "package:inventree/widget/attachment_widget.dart";
|
||||
import "package:inventree/widget/order/sales_order_detail.dart";
|
||||
import "package:inventree/widget/stock/location_display.dart";
|
||||
import "package:inventree/widget/part/part_detail.dart";
|
||||
import "package:inventree/widget/progress.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
import "package:inventree/widget/snacks.dart";
|
||||
@ -531,7 +527,7 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
||||
hideLoadingOverlay();
|
||||
|
||||
if (part is InvenTreePart) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
|
||||
part.goToDetailPage(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -574,8 +570,7 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
||||
hideLoadingOverlay();
|
||||
|
||||
if (loc is InvenTreeStockLocation) {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => LocationDisplayWidget(loc)));
|
||||
loc.goToDetailPage(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -690,9 +685,7 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
||||
leading: Icon(TablerIcons.truck_delivery, color: COLOR_ACTION),
|
||||
trailing: Text(salesOrder?.reference ?? ""),
|
||||
onTap: () {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => SalesOrderDetailWidget(salesOrder!)
|
||||
));
|
||||
salesOrder?.goToDetailPage(context);
|
||||
}
|
||||
)
|
||||
);
|
||||
@ -706,9 +699,7 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
||||
leading: Icon(TablerIcons.building_store, color: COLOR_ACTION),
|
||||
trailing: Text(customer?.name ?? ""),
|
||||
onTap: () {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => CompanyDetailWidget(customer!)
|
||||
));
|
||||
customer?.goToDetailPage(context);
|
||||
},
|
||||
)
|
||||
);
|
||||
|
@ -5,7 +5,6 @@ import "package:inventree/inventree/stock.dart";
|
||||
import "package:inventree/widget/paginator.dart";
|
||||
import "package:inventree/widget/refreshable_state.dart";
|
||||
import "package:inventree/l10.dart";
|
||||
import "package:inventree/widget/stock/stock_detail.dart";
|
||||
import "package:inventree/api.dart";
|
||||
|
||||
|
||||
@ -146,7 +145,7 @@ class _PaginatedStockItemListState extends PaginatedSearchState<PaginatedStockIt
|
||||
)
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => StockDetailWidget(item)));
|
||||
item.goToDetailPage(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user