From b9ffabd56127674ed2c503ad0747555c13d0578e Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 19 Apr 2023 21:57:28 +1000 Subject: [PATCH] Make notes widget "generic" (#327) * Make notes widget "generic" - No longer tied to the "part" model - Will allow us to use it elsewhere * Update release notes * Add helper methods for checking model permissions * Refactoring of permissions checks * Add notes to the "purchase order" widget * Fix typos * remove bom tab from part view * linting fixes --- assets/release_notes.md | 1 + lib/api.dart | 4 + lib/inventree/company.dart | 6 ++ lib/inventree/model.dart | 57 ++++++++++++++ lib/inventree/part.dart | 6 ++ lib/inventree/purchase_order.dart | 3 + lib/inventree/stock.dart | 9 +++ lib/widget/category_display.dart | 6 +- lib/widget/company_detail.dart | 20 +++-- lib/widget/drawer.dart | 9 ++- lib/widget/location_display.dart | 10 +-- .../{part_notes.dart => notes_widget.dart} | 37 +++++---- lib/widget/part_detail.dart | 23 ++---- lib/widget/part_image_widget.dart | 2 +- lib/widget/purchase_order_detail.dart | 34 ++++++-- lib/widget/purchase_order_list.dart | 2 +- lib/widget/search.dart | 20 ++--- lib/widget/stock_detail.dart | 25 +++--- lib/widget/stock_notes.dart | 77 ------------------- lib/widget/supplier_part_detail.dart | 9 +-- 20 files changed, 192 insertions(+), 168 deletions(-) rename lib/widget/{part_notes.dart => notes_widget.dart} (59%) delete mode 100644 lib/widget/stock_notes.dart diff --git a/assets/release_notes.md b/assets/release_notes.md index 7ada37f6..a2983274 100644 --- a/assets/release_notes.md +++ b/assets/release_notes.md @@ -4,6 +4,7 @@ - Fix background image transparency for dark mode - Fix link to Bill of Materials from Part screen - Improvements to supplier part detail screen +- Add "notes" field to more models ### 0.11.4 - April 2023 diff --git a/lib/api.dart b/lib/api.dart index fb883fa1..0950eeb1 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -650,16 +650,20 @@ class InvenTreeAPI { * e.g. "part", "change" */ bool checkPermission(String role, String permission) { + // If we do not have enough information, assume permission is allowed if (roles.isEmpty) { + debug("checkPermission - no roles defined!"); return true; } if (!roles.containsKey(role)) { + debug("checkPermission - role '$role' not found!"); return true; } if (roles[role] == null) { + debug("checkPermission - role '$role' is null!"); return true; } diff --git a/lib/inventree/company.dart b/lib/inventree/company.dart index ae711b7b..7af1229a 100644 --- a/lib/inventree/company.dart +++ b/lib/inventree/company.dart @@ -18,6 +18,9 @@ class InvenTreeCompany extends InvenTreeModel { @override String get URL => "company/"; + @override + List get rolesRequired => ["purchase_order", "sales_order", "return_order"]; + @override Map formFields() { return { @@ -118,6 +121,9 @@ class InvenTreeSupplierPart extends InvenTreeModel { @override String get URL => "company/part/"; + @override + List get rolesRequired => ["part", "purchase_order"]; + @override Map formFields() { return { diff --git a/lib/inventree/model.dart b/lib/inventree/model.dart index 7c75129a..576329fd 100644 --- a/lib/inventree/model.dart +++ b/lib/inventree/model.dart @@ -10,6 +10,7 @@ import "package:inventree/api.dart"; import "package:inventree/api_form.dart"; import "package:inventree/fa_icon_mapping.dart"; import "package:inventree/l10.dart"; +import "package:inventree/helpers.dart"; import "package:inventree/inventree/sentry.dart"; import "package:inventree/widget/dialogs.dart"; @@ -78,7 +79,63 @@ class InvenTreeModel { } else { return ""; } + } + /* Return a list of roles which may be required for this model + * If multiple roles are required, *any* role which passes the check is sufficient + */ + List get rolesRequired { + // Default implementation should not be called + debug("rolesRequired() not implemented for model ${URL} - returning empty list"); + return []; + } + + // Test if the user can "edit" this model + bool get canEdit { + for (String role in rolesRequired) { + if (InvenTreeAPI().checkPermission(role, "change")) { + return true; + } + } + + // Fallback + return false; + } + + // Test if the user can "create" this model + bool get canCreate { + for (String role in rolesRequired) { + if (InvenTreeAPI().checkPermission(role, "add")) { + return true; + } + } + + // Fallback + return false; + } + + // Test if the user can "delete" this model + bool get canDelete { + for (String role in rolesRequired) { + if (InvenTreeAPI().checkPermission(role, "delete")) { + return true; + } + } + + // Fallback + return false; + } + + // Test if the user can "view" this model + bool get canView { + for (String role in rolesRequired) { + if (InvenTreeAPI().checkPermission(role, "view")) { + return true; + } + } + + // Fallback + return false; } // Fields for editing / creating this model diff --git a/lib/inventree/part.dart b/lib/inventree/part.dart index 339382b2..e2ef2d91 100644 --- a/lib/inventree/part.dart +++ b/lib/inventree/part.dart @@ -23,6 +23,9 @@ class InvenTreePartCategory extends InvenTreeModel { @override String get URL => "part/category/"; + @override + List get rolesRequired => ["part_category"]; + @override Map formFields() { @@ -182,6 +185,9 @@ class InvenTreePart extends InvenTreeModel { @override String get URL => "part/"; + @override + List get rolesRequired => ["part"]; + @override Map formFields() { return { diff --git a/lib/inventree/purchase_order.dart b/lib/inventree/purchase_order.dart index abfd0ba3..53a297a0 100644 --- a/lib/inventree/purchase_order.dart +++ b/lib/inventree/purchase_order.dart @@ -18,6 +18,9 @@ class InvenTreePurchaseOrder extends InvenTreeModel { @override String get URL => "order/po/"; + @override + List get rolesRequired => ["purchase_order"]; + String get receive_url => "${url}receive/"; @override diff --git a/lib/inventree/stock.dart b/lib/inventree/stock.dart index ab6cc146..cd22e3fb 100644 --- a/lib/inventree/stock.dart +++ b/lib/inventree/stock.dart @@ -20,6 +20,9 @@ class InvenTreeStockItemTestResult extends InvenTreeModel { @override String get URL => "stock/test/"; + @override + List get rolesRequired => ["stock"]; + @override Map formFields() { return { @@ -134,6 +137,9 @@ class InvenTreeStockItem extends InvenTreeModel { @override String get URL => "stock/"; + @override + List get rolesRequired => ["stock"]; + // URLs for performing stock actions static String transferStockUrl() => "stock/transfer/"; @@ -611,6 +617,9 @@ class InvenTreeStockLocation extends InvenTreeModel { @override String get URL => "stock/location/"; + @override + List get rolesRequired => ["stock_location"]; + String get pathstring => (jsondata["pathstring"] ?? "") as String; @override diff --git a/lib/widget/category_display.dart b/lib/widget/category_display.dart index 404150d8..31ead036 100644 --- a/lib/widget/category_display.dart +++ b/lib/widget/category_display.dart @@ -40,7 +40,7 @@ class _CategoryDisplayState extends RefreshableState { List actions = []; if (widget.category != null) { - if (api.checkPermission("part_category", "change")) { + if (InvenTreePartCategory().canEdit) { actions.add( IconButton( icon: Icon(Icons.edit_square), @@ -60,7 +60,7 @@ class _CategoryDisplayState extends RefreshableState { List actionButtons(BuildContext context) { List actions = []; - if (api.checkPermission("part", "add")) { + if (InvenTreePart().canCreate) { actions.add( SpeedDialChild( child: FaIcon(FontAwesomeIcons.shapes), @@ -70,7 +70,7 @@ class _CategoryDisplayState extends RefreshableState { ); } - if (api.checkPermission("part_category", "add")) { + if (InvenTreePartCategory().canCreate) { actions.add( SpeedDialChild( child: FaIcon(FontAwesomeIcons.sitemap), diff --git a/lib/widget/company_detail.dart b/lib/widget/company_detail.dart index ff62fd46..c1d98a5c 100644 --- a/lib/widget/company_detail.dart +++ b/lib/widget/company_detail.dart @@ -49,17 +49,15 @@ class _CompanyDetailState extends RefreshableState { List appBarActions(BuildContext context) { List actions = []; - if (api.checkPermission("purchase_order", "change") || - api.checkPermission("sales_order", "change") || - api.checkPermission("return_order", "change")) { + if (InvenTreeCompany().canEdit) { actions.add( - IconButton( - icon: Icon(Icons.edit_square), - tooltip: L10().companyEdit, - onPressed: () { - editCompany(context); - } - ) + IconButton( + icon: Icon(Icons.edit_square), + tooltip: L10().companyEdit, + onPressed: () { + editCompany(context); + } + ) ); } @@ -281,7 +279,7 @@ class _CompanyDetailState extends RefreshableState { builder: (context) => AttachmentWidget( InvenTreeCompanyAttachment(), widget.company.pk, - api.checkPermission("purchase_order", "change") || api.checkPermission("sales_order", "change") + InvenTreeCompany().canEdit ) ) ); diff --git a/lib/widget/drawer.dart b/lib/widget/drawer.dart index a745d2bf..2ca8d88e 100644 --- a/lib/widget/drawer.dart +++ b/lib/widget/drawer.dart @@ -3,6 +3,9 @@ import "package:font_awesome_flutter/font_awesome_flutter.dart"; import "package:inventree/api.dart"; import "package:inventree/app_colors.dart"; +import "package:inventree/inventree/company.dart"; +import "package:inventree/inventree/purchase_order.dart"; +import "package:inventree/inventree/stock.dart"; import "package:inventree/l10.dart"; import "package:inventree/settings/settings.dart"; import "package:inventree/widget/category_display.dart"; @@ -95,7 +98,7 @@ class InvenTreeDrawer extends StatelessWidget { tiles.add(Divider()); - if (InvenTreeAPI().checkPermission("part_category", "view")) { + if (InvenTreeCompany().canView) { tiles.add( ListTile( title: Text(L10().parts), @@ -105,7 +108,7 @@ class InvenTreeDrawer extends StatelessWidget { ); } - if (InvenTreeAPI().checkPermission("stock_location", "view")) { + if (InvenTreeStockLocation().canView) { tiles.add( ListTile( title: Text(L10().stock), @@ -115,7 +118,7 @@ class InvenTreeDrawer extends StatelessWidget { ); } - if (InvenTreeAPI().checkPermission("purchase_order", "view")) { + if (InvenTreePurchaseOrder().canView) { tiles.add( ListTile( title: Text(L10().purchaseOrders), diff --git a/lib/widget/location_display.dart b/lib/widget/location_display.dart index dec7be66..38cc63d2 100644 --- a/lib/widget/location_display.dart +++ b/lib/widget/location_display.dart @@ -63,7 +63,7 @@ class _LocationDisplayState extends RefreshableState { } // Add "edit" button - if (location != null && api.checkPermission("stock_location", "change")) { + if (location != null && InvenTreeStockLocation().canEdit) { actions.add( IconButton( icon: Icon(Icons.edit_square), @@ -85,7 +85,7 @@ class _LocationDisplayState extends RefreshableState { if (location != null) { // Scan items into this location - if (api.checkPermission("stock", "change")) { + if (InvenTreeStockItem().canEdit) { actions.add( SpeedDialChild( child: FaIcon(FontAwesomeIcons.qrcode), @@ -105,7 +105,7 @@ class _LocationDisplayState extends RefreshableState { } // Scan this location into another one - if (api.checkPermission("stock_location", "change")) { + if (InvenTreeStockLocation().canEdit) { actions.add( SpeedDialChild( child: FaIcon(FontAwesomeIcons.qrcode), @@ -144,7 +144,7 @@ class _LocationDisplayState extends RefreshableState { List actions = []; // Create new location - if (api.checkPermission("stock_location", "add")) { + if (InvenTreeStockLocation().canCreate) { actions.add( SpeedDialChild( child: FaIcon(FontAwesomeIcons.sitemap), @@ -157,7 +157,7 @@ class _LocationDisplayState extends RefreshableState { } // Create new item - if (location != null && api.checkPermission("stock", "add")) { + if (location != null && InvenTreeStockItem().canCreate) { actions.add( SpeedDialChild( child: FaIcon(FontAwesomeIcons.boxesStacked), diff --git a/lib/widget/part_notes.dart b/lib/widget/notes_widget.dart similarity index 59% rename from lib/widget/part_notes.dart rename to lib/widget/notes_widget.dart index 15b9ee76..a5c11465 100644 --- a/lib/widget/part_notes.dart +++ b/lib/widget/notes_widget.dart @@ -1,49 +1,56 @@ import "package:flutter/material.dart"; import "package:font_awesome_flutter/font_awesome_flutter.dart"; -import "package:inventree/api.dart"; -import "package:inventree/inventree/part.dart"; +import "package:inventree/inventree/model.dart"; import "package:inventree/widget/refreshable_state.dart"; import "package:flutter_markdown/flutter_markdown.dart"; import "package:inventree/l10.dart"; -class PartNotesWidget extends StatefulWidget { +/* + * A widget for displaying the notes associated with a given model. + * We need to pass in the following parameters: + * + * - Model instance + * - Title for the app bar + */ +class NotesWidget extends StatefulWidget { - const PartNotesWidget(this.part, {Key? key}) : super(key: key); + const NotesWidget(this.model, {Key? key}) : super(key: key); - final InvenTreePart part; + final InvenTreeModel model; @override - _PartNotesState createState() => _PartNotesState(part); + _NotesState createState() => _NotesState(); } -class _PartNotesState extends RefreshableState { +/* + * Class representing the state of the NotesWidget + */ +class _NotesState extends RefreshableState { - _PartNotesState(this.part); - - final InvenTreePart part; + _NotesState(); @override Future request(BuildContext context) async { - await part.reload(); + await widget.model.reload(); } @override - String getAppBarTitle() => L10().partNotes; + String getAppBarTitle() => L10().editNotes; @override List appBarActions(BuildContext context) { List actions = []; - if (InvenTreeAPI().checkPermission("part", "change")) { + if (widget.model.canEdit) { actions.add( IconButton( icon: FaIcon(FontAwesomeIcons.penToSquare), tooltip: L10().edit, onPressed: () { - part.editForm( + widget.model.editForm( context, L10().editNotes, fields: { @@ -67,7 +74,7 @@ class _PartNotesState extends RefreshableState { Widget getBody(BuildContext context) { return Markdown( selectable: false, - data: part.notes, + data: widget.model.notes, ); } diff --git a/lib/widget/part_detail.dart b/lib/widget/part_detail.dart index 485b35b0..bcfb733c 100644 --- a/lib/widget/part_detail.dart +++ b/lib/widget/part_detail.dart @@ -16,7 +16,7 @@ import "package:inventree/preferences.dart"; import "package:inventree/widget/attachment_widget.dart"; import "package:inventree/widget/bom_list.dart"; import "package:inventree/widget/part_list.dart"; -import "package:inventree/widget/part_notes.dart"; +import "package:inventree/widget/notes_widget.dart"; import "package:inventree/widget/part_parameter_widget.dart"; import "package:inventree/widget/progress.dart"; import "package:inventree/widget/category_display.dart"; @@ -72,7 +72,7 @@ class _PartDisplayState extends RefreshableState { List appBarActions(BuildContext context) { List actions = []; - if (api.checkPermission("part", "change")) { + if (InvenTreePart().canEdit) { actions.add( IconButton( icon: Icon(Icons.edit_square), @@ -90,7 +90,7 @@ class _PartDisplayState extends RefreshableState { List barcodeButtons(BuildContext context) { List actions = []; - if (api.checkPermission("part", "change")) { + if (InvenTreePart().canEdit) { if (api.supportModernBarcodes) { actions.add( customBarcodeAction( @@ -109,7 +109,7 @@ class _PartDisplayState extends RefreshableState { List actionButtons(BuildContext context) { List actions = []; - if (api.checkPermission("stock", "add")) { + if (InvenTreeStockItem().canCreate) { actions.add( SpeedDialChild( child: FaIcon(FontAwesomeIcons.box), @@ -234,7 +234,7 @@ class _PartDisplayState extends RefreshableState { */ Future _toggleStar(BuildContext context) async { - if (api.checkPermission("part", "view")) { + if (InvenTreePart().canView) { showLoadingOverlay(context); await part.update(values: {"starred": "${!part.starred}"}); hideLoadingOverlay(); @@ -557,7 +557,7 @@ class _PartDisplayState extends RefreshableState { onTap: () { Navigator.push( context, - MaterialPageRoute(builder: (context) => PartNotesWidget(part)) + MaterialPageRoute(builder: (context) => NotesWidget(part)) ); }, ) @@ -575,7 +575,8 @@ class _PartDisplayState extends RefreshableState { builder: (context) => AttachmentWidget( InvenTreePartAttachment(), part.pk, - api.checkPermission("part", "change")) + part.canEdit + ) ) ); }, @@ -678,10 +679,6 @@ class _PartDisplayState extends RefreshableState { Tab(text: L10().stock) ]; - if (showBom && part.isAssembly) { - icons.add(Tab(text: L10().bom)); - } - if (showParameters) { icons.add(Tab(text: L10().parameters)); } @@ -703,10 +700,6 @@ class _PartDisplayState extends RefreshableState { PaginatedStockItemList({"part": part.pk.toString()}, true) ]; - if (showBom && part.isAssembly) { - tabs.add(PaginatedBomList({"part": part.pk.toString()}, showSearch: true, isParentPart: true)); - } - if (showParameters) { tabs.add(PaginatedParameterList({"part": part.pk.toString()}, true)); } diff --git a/lib/widget/part_image_widget.dart b/lib/widget/part_image_widget.dart index c548e8e0..68b12bde 100644 --- a/lib/widget/part_image_widget.dart +++ b/lib/widget/part_image_widget.dart @@ -42,7 +42,7 @@ class _PartImageState extends RefreshableState { List actions = []; - if (InvenTreeAPI().checkPermission("part", "change")) { + if (part.canEdit) { // File upload actions.add( diff --git a/lib/widget/purchase_order_detail.dart b/lib/widget/purchase_order_detail.dart index 06dd8a63..1e405f07 100644 --- a/lib/widget/purchase_order_detail.dart +++ b/lib/widget/purchase_order_detail.dart @@ -14,6 +14,7 @@ import "package:inventree/inventree/company.dart"; import "package:inventree/inventree/purchase_order.dart"; import "package:inventree/widget/attachment_widget.dart"; import "package:inventree/widget/company_detail.dart"; +import "package:inventree/widget/notes_widget.dart"; import "package:inventree/widget/refreshable_state.dart"; import "package:inventree/widget/snacks.dart"; import "package:inventree/widget/stock_list.dart"; @@ -49,7 +50,7 @@ class _PurchaseOrderDetailState extends RefreshableState appBarActions(BuildContext context) { List actions = []; - if (InvenTreeAPI().checkPermission("purchase_order", "change")) { + if (order.canEdit) { actions.add( IconButton( icon: Icon(Icons.edit_square), @@ -68,7 +69,7 @@ class _PurchaseOrderDetailState extends RefreshableState actionButtons(BuildContext context) { List actions = []; - if (api.checkPermission("purchase_order", "add")) { + if (order.canCreate) { if (order.isPending) { actions.add( SpeedDialChild( @@ -255,6 +256,22 @@ class _PurchaseOrderDetailState extends RefreshableState NotesWidget(order) + ) + ); + }, + ) + ); + // Attachments tiles.add( ListTile( @@ -263,13 +280,14 @@ class _PurchaseOrderDetailState extends RefreshableState 0 ? Text(attachmentCount.toString()) : null, onTap: () { Navigator.push( - context, - MaterialPageRoute( - builder: (context) => AttachmentWidget( - InvenTreePurchaseOrderAttachment(), - order.pk, - InvenTreeAPI().checkPermission("purchase_order", "change")) + context, + MaterialPageRoute( + builder: (context) => AttachmentWidget( + InvenTreePurchaseOrderAttachment(), + order.pk, + order.canEdit ) + ) ); }, ) diff --git a/lib/widget/purchase_order_list.dart b/lib/widget/purchase_order_list.dart index f2e51419..a2281985 100644 --- a/lib/widget/purchase_order_list.dart +++ b/lib/widget/purchase_order_list.dart @@ -52,7 +52,7 @@ class _PurchaseOrderListWidgetState extends RefreshableState actionButtons(BuildContext context) { List actions = []; - if (api.checkPermission("purchase_order", "add")) { + if (InvenTreePurchaseOrder().canCreate) { actions.add( SpeedDialChild( child: FaIcon(FontAwesomeIcons.circlePlus), diff --git a/lib/widget/search.dart b/lib/widget/search.dart index 750717e5..fd8fcabb 100644 --- a/lib/widget/search.dart +++ b/lib/widget/search.dart @@ -207,29 +207,29 @@ class _SearchDisplayState extends RefreshableState { }; // Part search - if (api.checkPermission("part", "view")) { + if (InvenTreePart().canView) { body["part"] = {}; } // PartCategory search - if (api.checkPermission("part_category", "view")) { + if (InvenTreePartCategory().canView) { body["partcategory"] = {}; } // StockItem search - if (api.checkPermission("stock", "view")) { + if (InvenTreeStockItem().canView) { body["stockitem"] = { "in_stock": true, }; } // StockLocation search - if (api.checkPermission("stock_location", "view")) { + if (InvenTreeStockLocation().canView) { body["stocklocation"] = {}; } // PurchaseOrder search - if (api.checkPermission("purchase_order", "view")) { + if (InvenTreePurchaseOrder().canView) { body["purchaseorder"] = { "outstanding": true }; @@ -253,7 +253,7 @@ class _SearchDisplayState extends RefreshableState { Future legacySearch(String term) async { // Search parts - if (api.checkPermission("part", "view")) { + if (InvenTreePart().canView) { nPendingSearches++; InvenTreePart().count(searchQuery: term).then((int n) { if (term == searchController.text) { @@ -268,7 +268,7 @@ class _SearchDisplayState extends RefreshableState { } // Search part categories - if (api.checkPermission("part_category", "view")) { + if (InvenTreePartCategory().canView) { nPendingSearches++; InvenTreePartCategory().count(searchQuery: term,).then((int n) { if (term == searchController.text) { @@ -283,7 +283,7 @@ class _SearchDisplayState extends RefreshableState { } // Search stock items - if (api.checkPermission("stock", "view")) { + if (InvenTreeStockItem().canView) { nPendingSearches++; InvenTreeStockItem().count(searchQuery: term).then((int n) { if (term == searchController.text) { @@ -298,7 +298,7 @@ class _SearchDisplayState extends RefreshableState { } // Search stock locations - if (api.checkPermission("stock_location", "view")) { + if (InvenTreeStockLocation().canView) { nPendingSearches++; InvenTreeStockLocation().count(searchQuery: term).then((int n) { if (term == searchController.text) { @@ -313,7 +313,7 @@ class _SearchDisplayState extends RefreshableState { } // Search purchase orders - if (api.checkPermission("purchase_order", "view")) { + if (InvenTreePurchaseOrder().canView) { nPendingSearches++; InvenTreePurchaseOrder().count( searchQuery: term, diff --git a/lib/widget/stock_detail.dart b/lib/widget/stock_detail.dart index 0149e097..b2dcc2c8 100644 --- a/lib/widget/stock_detail.dart +++ b/lib/widget/stock_detail.dart @@ -25,7 +25,7 @@ import "package:inventree/widget/refreshable_state.dart"; import "package:inventree/widget/snacks.dart"; import "package:inventree/widget/stock_item_history.dart"; import "package:inventree/widget/stock_item_test_results.dart"; -import "package:inventree/widget/stock_notes.dart"; +import "package:inventree/widget/notes_widget.dart"; class StockDetailWidget extends StatefulWidget { @@ -64,7 +64,7 @@ class _StockItemDisplayState extends RefreshableState { ); } - if (api.checkPermission("stock", "change")) { + if (widget.item.canEdit) { actions.add( IconButton( icon: Icon(Icons.edit_square), @@ -84,7 +84,7 @@ class _StockItemDisplayState extends RefreshableState { List actions = []; - if (api.checkPermission("stock", "change")) { + if (widget.item.canEdit) { // Stock adjustment actions available if item is *not* serialized if (!widget.item.isSerialized()) { @@ -138,7 +138,7 @@ class _StockItemDisplayState extends RefreshableState { ); } - if (api.checkPermission("stock", "delete")) { + if (widget.item.canDelete) { actions.add( SpeedDialChild( child: FaIcon(FontAwesomeIcons.trashCan, color: Colors.red), @@ -157,7 +157,7 @@ class _StockItemDisplayState extends RefreshableState { List barcodeButtons(BuildContext context) { List actions = []; - if (api.checkPermission("stock", "change")) { + if (widget.item.canEdit) { // Scan item into location actions.add( SpeedDialChild( @@ -816,7 +816,7 @@ class _StockItemDisplayState extends RefreshableState { onTap: () { Navigator.push( context, - MaterialPageRoute(builder: (context) => StockNotesWidget(widget.item)) + MaterialPageRoute(builder: (context) => NotesWidget(widget.item)) ); } ) @@ -829,13 +829,14 @@ class _StockItemDisplayState extends RefreshableState { trailing: attachmentCount > 0 ? Text(attachmentCount.toString()) : null, onTap: () { Navigator.push( - context, - MaterialPageRoute( - builder: (context) => AttachmentWidget( - InvenTreeStockItemAttachment(), - widget.item.pk, - InvenTreeAPI().checkPermission("stock", "change")) + context, + MaterialPageRoute( + builder: (context) => AttachmentWidget( + InvenTreeStockItemAttachment(), + widget.item.pk, + widget.item.canEdit, ) + ) ); }, ) diff --git a/lib/widget/stock_notes.dart b/lib/widget/stock_notes.dart deleted file mode 100644 index c990d0ef..00000000 --- a/lib/widget/stock_notes.dart +++ /dev/null @@ -1,77 +0,0 @@ - -import "package:flutter/material.dart"; -import "package:font_awesome_flutter/font_awesome_flutter.dart"; -import "package:inventree/inventree/stock.dart"; -import "package:inventree/widget/refreshable_state.dart"; -import "package:flutter_markdown/flutter_markdown.dart"; -import "package:inventree/l10.dart"; - -import "package:inventree/api.dart"; - - -class StockNotesWidget extends StatefulWidget { - - const StockNotesWidget(this.item, {Key? key}) : super(key: key); - - final InvenTreeStockItem item; - - @override - _StockNotesState createState() => _StockNotesState(item); -} - - -class _StockNotesState extends RefreshableState { - - _StockNotesState(this.item); - - final InvenTreeStockItem item; - - @override - String getAppBarTitle() => L10().stockItemNotes; - - @override - Future request(BuildContext context) async { - if (item.pk > 0) { - await item.reload(); - } - } - - @override - List appBarActions(BuildContext context) { - List actions = []; - - if (InvenTreeAPI().checkPermission("stock", "change")) { - actions.add( - IconButton( - icon: FaIcon(FontAwesomeIcons.penToSquare), - tooltip: L10().edit, - onPressed: () { - item.editForm( - context, - L10().editNotes, - fields: { - "notes": { - "multiline": true, - } - }, - onSuccess: (data) async { - refresh(context); - } - ); - } - ) - ); - } - - return actions; - } - - @override - Widget getBody(BuildContext context) { - return Markdown( - selectable: false, - data: item.notes, - ); - } - -} \ No newline at end of file diff --git a/lib/widget/supplier_part_detail.dart b/lib/widget/supplier_part_detail.dart index 85661d45..662e266d 100644 --- a/lib/widget/supplier_part_detail.dart +++ b/lib/widget/supplier_part_detail.dart @@ -57,10 +57,7 @@ class _SupplierPartDisplayState extends RefreshableState barcodeButtons(BuildContext context) { List actions = []; - if (api.checkPermission("purchase_order", "change") || - api.checkPermission("sales_order", "change") || - api.checkPermission("return_order", "change")) { - + if (widget.supplierPart.canEdit) { actions.add( customBarcodeAction( context, this, @@ -78,9 +75,7 @@ class _SupplierPartDisplayState extends RefreshableState appBarActions(BuildContext context) { List actions = []; - if (api.checkPermission("purchase_order", "change") || - api.checkPermission("sales_order", "change") || - api.checkPermission("return_order", "change")) { + if (widget.supplierPart.canEdit) { actions.add( IconButton( icon: Icon(Icons.edit_square),