Skip to content

Commit ee553af

Browse files
Part requirements (#761)
* Add setting to control if part requirements are shown * Split settings for Part and Stock * Fetch part requirements information * Add "building" indicator * Show order allocation progress for part requirements * Bump release notes * Remove unused import
1 parent e38c51e commit ee553af

File tree

10 files changed

+357
-70
lines changed

10 files changed

+357
-70
lines changed

assets/release_notes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
### x.xx.x - Month Year
22
---
33

4+
- Display overall part requirements on part detail view
45
- Support display of custom status codes
56
- Fix default values for list sorting
67
- Fix bug related to null values in list filters

lib/api.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,9 @@ class InvenTreeAPI {
353353
// Supports separate search against "supplier" / "customer" / "manufacturer"
354354
bool get supportsSplitCompanySearch => apiVersion >= 315;
355355

356+
// Supports "requirements" information for specific part
357+
bool get supportsPartRequirements => apiVersion >= 350;
358+
356359
// Does the server support the "modern" (consolidated) parameter API?
357360
// Ref: https://github.com/inventree/InvenTree/pull/10699
358361
bool get supportsModernParameters => apiVersion >= 429;

lib/inventree/part.dart

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,27 @@ class InvenTreePart extends InvenTreeModel {
214214
});
215215
}
216216

217+
// Request requirements information for this part
218+
Future<InvenTreePartRequirements?> getRequirements() async {
219+
try {
220+
final response = await InvenTreeAPI().get(
221+
"/api/part/${pk}/requirements/",
222+
);
223+
if (response.isValid()) {
224+
final requirementsData = response.data;
225+
226+
if (requirementsData is Map<String, dynamic>) {
227+
return InvenTreePartRequirements.fromJson(requirementsData);
228+
}
229+
}
230+
} catch (e, stackTrace) {
231+
print("Exception while fetching requirements data for part $pk: $e");
232+
sentryReportError("getRequirements", e, stackTrace);
233+
}
234+
235+
return null;
236+
}
237+
217238
// Request pricing data for this part
218239
Future<InvenTreePartPricing?> getPricing() async {
219240
try {
@@ -437,6 +458,43 @@ class InvenTreePart extends InvenTreeModel {
437458
InvenTreePart.fromJson(json);
438459
}
439460

461+
/*
462+
* Class representing requirements information for a given Part instance.
463+
*/
464+
class InvenTreePartRequirements extends InvenTreeModel {
465+
InvenTreePartRequirements() : super();
466+
467+
InvenTreePartRequirements.fromJson(Map<String, dynamic> json)
468+
: super.fromJson(json);
469+
470+
@override
471+
List<String> get rolesRequired => ["part"];
472+
473+
@override
474+
InvenTreeModel createFromJson(Map<String, dynamic> json) =>
475+
InvenTreePartRequirements.fromJson(json);
476+
477+
// Data accessors
478+
double get canBuild => getDouble("can_build");
479+
480+
double get ordering => getDouble("ordering");
481+
482+
double get building => getDouble("building");
483+
484+
double get scheduledToBuild => getDouble("scheduled_to_build");
485+
486+
double get requiredForBuildOrders => getDouble("required_for_build_orders");
487+
488+
double get allocatedToBuildOrders => getDouble("allocated_to_build_orders");
489+
490+
double get requiredForSalesOrders => getDouble("required_for_sales_orders");
491+
492+
double get allocatedToSalesOrders => getDouble("allocated_to_sales_orders");
493+
}
494+
495+
/*
496+
* Class representing pricing information for a given Part instance.
497+
*/
440498
class InvenTreePartPricing extends InvenTreeModel {
441499
InvenTreePartPricing() : super();
442500

lib/l10n/app_en.arb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@
6868
"allocatedStock": "Allocated Stock",
6969
"@allocatedStock": {},
7070

71+
"allocatedToBuildOrders": "Allocated to Build Orders",
72+
"@allocatedToBuildOrders": {},
73+
74+
"allocatedToSalesOrders": "Allocated to Sales Orders",
75+
"@allocatedToSalesOrders": {},
76+
7177
"appReleaseNotes": "Display app release notes",
7278
"@appReleaseNotes": {},
7379

@@ -226,6 +232,12 @@
226232
"cameraInternalDetail": "Use internal camera to read barcodes",
227233
"@cameraInternalDetail": {},
228234

235+
"canBuild": "Can Build",
236+
"@canBuild": {},
237+
238+
"canBuildDetail": "Can be produced with current stock",
239+
"@canBuildDetail": {},
240+
229241
"cancel": "Cancel",
230242
"@cancel": {
231243
"description": "Cancel"
@@ -936,6 +948,12 @@
936948
"partPricingSettingDetail": "Display part pricing information",
937949
"@pricingSettingDetail": {},
938950

951+
"partRequirements": "Part Requirements",
952+
"@partRequirements": {},
953+
954+
"partRequirementsSettingDetail": "Display part requirements",
955+
"@partRequirementsSettingDetail": {},
956+
939957
"partSettings": "Part Settings",
940958
"@partSettings": {},
941959

@@ -1535,6 +1553,9 @@
15351553
"stockLocations": "Stock Locations",
15361554
"@stockLocations": {},
15371555

1556+
"stockSettings": "Stock Settings",
1557+
"@stockSettings": {},
1558+
15381559
"stockTopLevel": "Top level stock location",
15391560
"@stockTopLevel": {},
15401561

lib/preferences.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const String INV_LABEL_DEFAULT_PLUGIN = "defaultLabelPlugin";
3434
// Part settings
3535
const String INV_PART_SHOW_BOM = "partShowBom";
3636
const String INV_PART_SHOW_PRICING = "partShowPricing";
37+
const String INV_PART_SHOW_REQUIREMENTS = "partShowRequirements";
3738

3839
// Stock settings
3940
const String INV_STOCK_SHOW_HISTORY = "stockShowHistory";

lib/settings/part_settings.dart

Lines changed: 9 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ class _InvenTreePartSettingsState extends State<InvenTreePartSettingsWidget> {
1515

1616
bool partShowBom = true;
1717
bool partShowPricing = true;
18-
bool stockShowHistory = false;
19-
bool stockShowTests = false;
20-
bool stockConfirmScan = false;
18+
bool partShowRequirements = false;
2119

2220
@override
2321
void initState() {
@@ -35,16 +33,8 @@ class _InvenTreePartSettingsState extends State<InvenTreePartSettingsWidget> {
3533
INV_PART_SHOW_PRICING,
3634
true,
3735
);
38-
stockShowHistory = await InvenTreeSettingsManager().getBool(
39-
INV_STOCK_SHOW_HISTORY,
40-
false,
41-
);
42-
stockShowTests = await InvenTreeSettingsManager().getBool(
43-
INV_STOCK_SHOW_TESTS,
44-
true,
45-
);
46-
stockConfirmScan = await InvenTreeSettingsManager().getBool(
47-
INV_STOCK_CONFIRM_SCAN,
36+
partShowRequirements = await InvenTreeSettingsManager().getBool(
37+
INV_PART_SHOW_REQUIREMENTS,
4838
false,
4939
);
5040

@@ -94,54 +84,19 @@ class _InvenTreePartSettingsState extends State<InvenTreePartSettingsWidget> {
9484
},
9585
),
9686
),
97-
Divider(),
9887
ListTile(
99-
title: Text(L10().stockItemHistory),
100-
subtitle: Text(L10().stockItemHistoryDetail),
101-
leading: Icon(TablerIcons.history),
102-
trailing: Switch(
103-
value: stockShowHistory,
104-
onChanged: (bool value) {
105-
InvenTreeSettingsManager().setValue(
106-
INV_STOCK_SHOW_HISTORY,
107-
value,
108-
);
109-
setState(() {
110-
stockShowHistory = value;
111-
});
112-
},
113-
),
114-
),
115-
ListTile(
116-
title: Text(L10().testResults),
117-
subtitle: Text(L10().testResultsDetail),
118-
leading: Icon(TablerIcons.test_pipe),
119-
trailing: Switch(
120-
value: stockShowTests,
121-
onChanged: (bool value) {
122-
InvenTreeSettingsManager().setValue(
123-
INV_STOCK_SHOW_TESTS,
124-
value,
125-
);
126-
setState(() {
127-
stockShowTests = value;
128-
});
129-
},
130-
),
131-
),
132-
ListTile(
133-
title: Text(L10().confirmScan),
134-
subtitle: Text(L10().confirmScanDetail),
135-
leading: Icon(TablerIcons.qrcode),
88+
title: Text(L10().partRequirements),
89+
subtitle: Text(L10().partRequirementsSettingDetail),
90+
leading: Icon(TablerIcons.list),
13691
trailing: Switch(
137-
value: stockConfirmScan,
92+
value: partShowRequirements,
13893
onChanged: (bool value) {
13994
InvenTreeSettingsManager().setValue(
140-
INV_STOCK_CONFIRM_SCAN,
95+
INV_PART_SHOW_REQUIREMENTS,
14196
value,
14297
);
14398
setState(() {
144-
stockConfirmScan = value;
99+
partShowRequirements = value;
145100
});
146101
},
147102
),

lib/settings/settings.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import "package:flutter/material.dart";
22
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
3+
import "package:inventree/settings/stock_settings.dart";
34
import "package:package_info_plus/package_info_plus.dart";
45

56
import "package:inventree/app_colors.dart";
@@ -119,6 +120,20 @@ class _InvenTreeSettingsState extends State<InvenTreeSettingsWidget> {
119120
);
120121
},
121122
),
123+
ListTile(
124+
title: Text(L10().stock),
125+
subtitle: Text(L10().stockSettings),
126+
leading: Icon(TablerIcons.packages, color: COLOR_ACTION),
127+
trailing: LinkIcon(),
128+
onTap: () {
129+
Navigator.push(
130+
context,
131+
MaterialPageRoute(
132+
builder: (context) => InvenTreeStockSettingsWidget(),
133+
),
134+
);
135+
},
136+
),
122137
ListTile(
123138
title: Text(L10().purchaseOrder),
124139
subtitle: Text(L10().purchaseOrderSettings),

lib/settings/stock_settings.dart

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import "package:flutter/material.dart";
2+
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
3+
import "package:inventree/app_colors.dart";
4+
import "package:inventree/l10.dart";
5+
import "package:inventree/preferences.dart";
6+
7+
class InvenTreeStockSettingsWidget extends StatefulWidget {
8+
@override
9+
_InvenTreeStockSettingsState createState() => _InvenTreeStockSettingsState();
10+
}
11+
12+
class _InvenTreeStockSettingsState extends State<InvenTreeStockSettingsWidget> {
13+
_InvenTreeStockSettingsState();
14+
15+
bool stockShowHistory = false;
16+
bool stockShowTests = false;
17+
bool stockConfirmScan = false;
18+
19+
@override
20+
void initState() {
21+
super.initState();
22+
loadSettings();
23+
}
24+
25+
Future<void> loadSettings() async {
26+
stockShowHistory = await InvenTreeSettingsManager().getBool(
27+
INV_STOCK_SHOW_HISTORY,
28+
false,
29+
);
30+
stockShowTests = await InvenTreeSettingsManager().getBool(
31+
INV_STOCK_SHOW_TESTS,
32+
true,
33+
);
34+
stockConfirmScan = await InvenTreeSettingsManager().getBool(
35+
INV_STOCK_CONFIRM_SCAN,
36+
false,
37+
);
38+
39+
if (mounted) {
40+
setState(() {});
41+
}
42+
}
43+
44+
@override
45+
Widget build(BuildContext context) {
46+
return Scaffold(
47+
appBar: AppBar(
48+
title: Text(L10().stockSettings),
49+
backgroundColor: COLOR_APP_BAR,
50+
),
51+
body: Container(
52+
child: ListView(
53+
children: [
54+
ListTile(
55+
title: Text(L10().stockItemHistory),
56+
subtitle: Text(L10().stockItemHistoryDetail),
57+
leading: Icon(TablerIcons.history),
58+
trailing: Switch(
59+
value: stockShowHistory,
60+
onChanged: (bool value) {
61+
InvenTreeSettingsManager().setValue(
62+
INV_STOCK_SHOW_HISTORY,
63+
value,
64+
);
65+
setState(() {
66+
stockShowHistory = value;
67+
});
68+
},
69+
),
70+
),
71+
ListTile(
72+
title: Text(L10().testResults),
73+
subtitle: Text(L10().testResultsDetail),
74+
leading: Icon(TablerIcons.test_pipe),
75+
trailing: Switch(
76+
value: stockShowTests,
77+
onChanged: (bool value) {
78+
InvenTreeSettingsManager().setValue(
79+
INV_STOCK_SHOW_TESTS,
80+
value,
81+
);
82+
setState(() {
83+
stockShowTests = value;
84+
});
85+
},
86+
),
87+
),
88+
ListTile(
89+
title: Text(L10().confirmScan),
90+
subtitle: Text(L10().confirmScanDetail),
91+
leading: Icon(TablerIcons.qrcode),
92+
trailing: Switch(
93+
value: stockConfirmScan,
94+
onChanged: (bool value) {
95+
InvenTreeSettingsManager().setValue(
96+
INV_STOCK_CONFIRM_SCAN,
97+
value,
98+
);
99+
setState(() {
100+
stockConfirmScan = value;
101+
});
102+
},
103+
),
104+
),
105+
],
106+
),
107+
),
108+
);
109+
}
110+
}

0 commit comments

Comments
 (0)