mirror of
https://github.com/inventree/inventree-app.git
synced 2025-04-28 05:26:47 +00:00
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
This commit is contained in:
parent
28ed1ed545
commit
b9ffabd561
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,9 @@ class InvenTreeCompany extends InvenTreeModel {
|
||||
@override
|
||||
String get URL => "company/";
|
||||
|
||||
@override
|
||||
List<String> get rolesRequired => ["purchase_order", "sales_order", "return_order"];
|
||||
|
||||
@override
|
||||
Map<String, dynamic> formFields() {
|
||||
return {
|
||||
@ -118,6 +121,9 @@ class InvenTreeSupplierPart extends InvenTreeModel {
|
||||
@override
|
||||
String get URL => "company/part/";
|
||||
|
||||
@override
|
||||
List<String> get rolesRequired => ["part", "purchase_order"];
|
||||
|
||||
@override
|
||||
Map<String, dynamic> formFields() {
|
||||
return {
|
||||
|
@ -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<String> 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
|
||||
|
@ -23,6 +23,9 @@ class InvenTreePartCategory extends InvenTreeModel {
|
||||
@override
|
||||
String get URL => "part/category/";
|
||||
|
||||
@override
|
||||
List<String> get rolesRequired => ["part_category"];
|
||||
|
||||
@override
|
||||
Map<String, dynamic> formFields() {
|
||||
|
||||
@ -182,6 +185,9 @@ class InvenTreePart extends InvenTreeModel {
|
||||
@override
|
||||
String get URL => "part/";
|
||||
|
||||
@override
|
||||
List<String> get rolesRequired => ["part"];
|
||||
|
||||
@override
|
||||
Map<String, dynamic> formFields() {
|
||||
return {
|
||||
|
@ -18,6 +18,9 @@ class InvenTreePurchaseOrder extends InvenTreeModel {
|
||||
@override
|
||||
String get URL => "order/po/";
|
||||
|
||||
@override
|
||||
List<String> get rolesRequired => ["purchase_order"];
|
||||
|
||||
String get receive_url => "${url}receive/";
|
||||
|
||||
@override
|
||||
|
@ -20,6 +20,9 @@ class InvenTreeStockItemTestResult extends InvenTreeModel {
|
||||
@override
|
||||
String get URL => "stock/test/";
|
||||
|
||||
@override
|
||||
List<String> get rolesRequired => ["stock"];
|
||||
|
||||
@override
|
||||
Map<String, dynamic> formFields() {
|
||||
return {
|
||||
@ -134,6 +137,9 @@ class InvenTreeStockItem extends InvenTreeModel {
|
||||
@override
|
||||
String get URL => "stock/";
|
||||
|
||||
@override
|
||||
List<String> 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<String> get rolesRequired => ["stock_location"];
|
||||
|
||||
String get pathstring => (jsondata["pathstring"] ?? "") as String;
|
||||
|
||||
@override
|
||||
|
@ -40,7 +40,7 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
List<Widget> 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<CategoryDisplayWidget> {
|
||||
List<SpeedDialChild> actionButtons(BuildContext context) {
|
||||
List<SpeedDialChild> actions = [];
|
||||
|
||||
if (api.checkPermission("part", "add")) {
|
||||
if (InvenTreePart().canCreate) {
|
||||
actions.add(
|
||||
SpeedDialChild(
|
||||
child: FaIcon(FontAwesomeIcons.shapes),
|
||||
@ -70,7 +70,7 @@ class _CategoryDisplayState extends RefreshableState<CategoryDisplayWidget> {
|
||||
);
|
||||
}
|
||||
|
||||
if (api.checkPermission("part_category", "add")) {
|
||||
if (InvenTreePartCategory().canCreate) {
|
||||
actions.add(
|
||||
SpeedDialChild(
|
||||
child: FaIcon(FontAwesomeIcons.sitemap),
|
||||
|
@ -49,17 +49,15 @@ class _CompanyDetailState extends RefreshableState<CompanyDetailWidget> {
|
||||
List<Widget> appBarActions(BuildContext context) {
|
||||
List<Widget> 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<CompanyDetailWidget> {
|
||||
builder: (context) => AttachmentWidget(
|
||||
InvenTreeCompanyAttachment(),
|
||||
widget.company.pk,
|
||||
api.checkPermission("purchase_order", "change") || api.checkPermission("sales_order", "change")
|
||||
InvenTreeCompany().canEdit
|
||||
)
|
||||
)
|
||||
);
|
||||
|
@ -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),
|
||||
|
@ -63,7 +63,7 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
|
||||
}
|
||||
|
||||
// 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<LocationDisplayWidget> {
|
||||
|
||||
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<LocationDisplayWidget> {
|
||||
}
|
||||
|
||||
// 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<LocationDisplayWidget> {
|
||||
List<SpeedDialChild> 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<LocationDisplayWidget> {
|
||||
}
|
||||
|
||||
// Create new item
|
||||
if (location != null && api.checkPermission("stock", "add")) {
|
||||
if (location != null && InvenTreeStockItem().canCreate) {
|
||||
actions.add(
|
||||
SpeedDialChild(
|
||||
child: FaIcon(FontAwesomeIcons.boxesStacked),
|
||||
|
@ -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<PartNotesWidget> {
|
||||
/*
|
||||
* Class representing the state of the NotesWidget
|
||||
*/
|
||||
class _NotesState extends RefreshableState<NotesWidget> {
|
||||
|
||||
_PartNotesState(this.part);
|
||||
|
||||
final InvenTreePart part;
|
||||
_NotesState();
|
||||
|
||||
@override
|
||||
Future<void> request(BuildContext context) async {
|
||||
await part.reload();
|
||||
await widget.model.reload();
|
||||
}
|
||||
|
||||
@override
|
||||
String getAppBarTitle() => L10().partNotes;
|
||||
String getAppBarTitle() => L10().editNotes;
|
||||
|
||||
@override
|
||||
List<Widget> appBarActions(BuildContext context) {
|
||||
|
||||
List<Widget> 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<PartNotesWidget> {
|
||||
Widget getBody(BuildContext context) {
|
||||
return Markdown(
|
||||
selectable: false,
|
||||
data: part.notes,
|
||||
data: widget.model.notes,
|
||||
);
|
||||
}
|
||||
|
@ -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<PartDetailWidget> {
|
||||
List<Widget> appBarActions(BuildContext context) {
|
||||
List<Widget> 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<PartDetailWidget> {
|
||||
List<SpeedDialChild> barcodeButtons(BuildContext context) {
|
||||
List<SpeedDialChild> actions = [];
|
||||
|
||||
if (api.checkPermission("part", "change")) {
|
||||
if (InvenTreePart().canEdit) {
|
||||
if (api.supportModernBarcodes) {
|
||||
actions.add(
|
||||
customBarcodeAction(
|
||||
@ -109,7 +109,7 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
||||
List<SpeedDialChild> actionButtons(BuildContext context) {
|
||||
List<SpeedDialChild> actions = [];
|
||||
|
||||
if (api.checkPermission("stock", "add")) {
|
||||
if (InvenTreeStockItem().canCreate) {
|
||||
actions.add(
|
||||
SpeedDialChild(
|
||||
child: FaIcon(FontAwesomeIcons.box),
|
||||
@ -234,7 +234,7 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
||||
*/
|
||||
Future <void> _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<PartDetailWidget> {
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => PartNotesWidget(part))
|
||||
MaterialPageRoute(builder: (context) => NotesWidget(part))
|
||||
);
|
||||
},
|
||||
)
|
||||
@ -575,7 +575,8 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
||||
builder: (context) => AttachmentWidget(
|
||||
InvenTreePartAttachment(),
|
||||
part.pk,
|
||||
api.checkPermission("part", "change"))
|
||||
part.canEdit
|
||||
)
|
||||
)
|
||||
);
|
||||
},
|
||||
@ -678,10 +679,6 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
|
||||
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<PartDetailWidget> {
|
||||
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));
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ class _PartImageState extends RefreshableState<PartImageWidget> {
|
||||
|
||||
List<Widget> actions = [];
|
||||
|
||||
if (InvenTreeAPI().checkPermission("part", "change")) {
|
||||
if (part.canEdit) {
|
||||
|
||||
// File upload
|
||||
actions.add(
|
||||
|
@ -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<PurchaseOrderDetailWidg
|
||||
List<Widget> appBarActions(BuildContext context) {
|
||||
List<Widget> 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<PurchaseOrderDetailWidg
|
||||
List<SpeedDialChild> actionButtons(BuildContext context) {
|
||||
List<SpeedDialChild> actions = [];
|
||||
|
||||
if (api.checkPermission("purchase_order", "add")) {
|
||||
if (order.canCreate) {
|
||||
if (order.isPending) {
|
||||
actions.add(
|
||||
SpeedDialChild(
|
||||
@ -255,6 +256,22 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
||||
));
|
||||
}
|
||||
|
||||
// Notes tile
|
||||
tiles.add(
|
||||
ListTile(
|
||||
title: Text(L10().notes),
|
||||
leading: FaIcon(FontAwesomeIcons.noteSticky, color: COLOR_ACTION),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => NotesWidget(order)
|
||||
)
|
||||
);
|
||||
},
|
||||
)
|
||||
);
|
||||
|
||||
// Attachments
|
||||
tiles.add(
|
||||
ListTile(
|
||||
@ -263,13 +280,14 @@ class _PurchaseOrderDetailState extends RefreshableState<PurchaseOrderDetailWidg
|
||||
trailing: attachmentCount > 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
|
||||
)
|
||||
)
|
||||
);
|
||||
},
|
||||
)
|
||||
|
@ -52,7 +52,7 @@ class _PurchaseOrderListWidgetState extends RefreshableState<PurchaseOrderListWi
|
||||
List<SpeedDialChild> actionButtons(BuildContext context) {
|
||||
List<SpeedDialChild> actions = [];
|
||||
|
||||
if (api.checkPermission("purchase_order", "add")) {
|
||||
if (InvenTreePurchaseOrder().canCreate) {
|
||||
actions.add(
|
||||
SpeedDialChild(
|
||||
child: FaIcon(FontAwesomeIcons.circlePlus),
|
||||
|
@ -207,29 +207,29 @@ class _SearchDisplayState extends RefreshableState<SearchWidget> {
|
||||
};
|
||||
|
||||
// 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<SearchWidget> {
|
||||
Future<void> 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<SearchWidget> {
|
||||
}
|
||||
|
||||
// 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<SearchWidget> {
|
||||
}
|
||||
|
||||
// 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<SearchWidget> {
|
||||
}
|
||||
|
||||
// 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<SearchWidget> {
|
||||
}
|
||||
|
||||
// Search purchase orders
|
||||
if (api.checkPermission("purchase_order", "view")) {
|
||||
if (InvenTreePurchaseOrder().canView) {
|
||||
nPendingSearches++;
|
||||
InvenTreePurchaseOrder().count(
|
||||
searchQuery: term,
|
||||
|
@ -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<StockDetailWidget> {
|
||||
);
|
||||
}
|
||||
|
||||
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<StockDetailWidget> {
|
||||
|
||||
List<SpeedDialChild> 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<StockDetailWidget> {
|
||||
);
|
||||
}
|
||||
|
||||
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<StockDetailWidget> {
|
||||
List<SpeedDialChild> barcodeButtons(BuildContext context) {
|
||||
List<SpeedDialChild> 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<StockDetailWidget> {
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => StockNotesWidget(widget.item))
|
||||
MaterialPageRoute(builder: (context) => NotesWidget(widget.item))
|
||||
);
|
||||
}
|
||||
)
|
||||
@ -829,13 +829,14 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
||||
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,
|
||||
)
|
||||
)
|
||||
);
|
||||
},
|
||||
)
|
||||
|
@ -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<StockNotesWidget> {
|
||||
|
||||
_StockNotesState(this.item);
|
||||
|
||||
final InvenTreeStockItem item;
|
||||
|
||||
@override
|
||||
String getAppBarTitle() => L10().stockItemNotes;
|
||||
|
||||
@override
|
||||
Future<void> request(BuildContext context) async {
|
||||
if (item.pk > 0) {
|
||||
await item.reload();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
List<Widget> appBarActions(BuildContext context) {
|
||||
List<Widget> 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,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -57,10 +57,7 @@ class _SupplierPartDisplayState extends RefreshableState<SupplierPartDetailWidge
|
||||
List<SpeedDialChild> barcodeButtons(BuildContext context) {
|
||||
List<SpeedDialChild> 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<SupplierPartDetailWidge
|
||||
List<Widget> appBarActions(BuildContext context) {
|
||||
List<Widget> 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),
|
||||
|
Loading…
x
Reference in New Issue
Block a user