mirror of
https://github.com/HendrikRauh/inventree-app.git
synced 2026-04-17 08:00:34 +00:00
commit
84e8ccc43a
18 changed files with 257 additions and 48 deletions
|
|
@ -27,7 +27,7 @@ Download and install from the [Apple App Store](https://apps.apple.com/au/app/in
|
||||||
|
|
||||||
### Direct Download (Android)
|
### Direct Download (Android)
|
||||||
|
|
||||||
We provide direct downloads for Android users - view our [download page via polar.sh](https://polar.sh/inventree/products/299bf0d5-af88-4e0f-becf-c007ad37ecf2)
|
We provide direct downloads for Android users - view our [download page via polar.sh](https://buy.polar.sh/polar_cl_UnGILJ0c7P3hQrOrJs127oyLTTDOTHKrnqfCg30XtBI)
|
||||||
|
|
||||||
## User Documentation
|
## User Documentation
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,8 @@ if (keystorePropertiesFile.exists()) {
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace "inventree.inventree_app"
|
namespace "inventree.inventree_app"
|
||||||
compileSdkVersion 35
|
ndkVersion "26.3.11579264"
|
||||||
|
compileSdkVersion 36
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_17
|
sourceCompatibility JavaVersion.VERSION_17
|
||||||
|
|
@ -56,7 +57,7 @@ android {
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "inventree.inventree_app"
|
applicationId "inventree.inventree_app"
|
||||||
minSdkVersion 21
|
minSdkVersion 23
|
||||||
targetSdkVersion 35
|
targetSdkVersion 35
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ pluginManagement {
|
||||||
plugins {
|
plugins {
|
||||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||||
id "com.android.application" version "8.6.0" apply false
|
id "com.android.application" version "8.6.0" apply false
|
||||||
id "org.jetbrains.kotlin.android" version "1.9.25" apply false
|
id "org.jetbrains.kotlin.android" version "2.3.10" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
include ":app"
|
include ":app"
|
||||||
|
|
@ -1,3 +1,39 @@
|
||||||
|
## 0.22.7 - March 2026
|
||||||
|
---
|
||||||
|
|
||||||
|
- Bug fix for loading sales order shipments
|
||||||
|
|
||||||
|
## 0.22.6 - March 2026
|
||||||
|
---
|
||||||
|
|
||||||
|
- Fix for displaying stock item test results
|
||||||
|
|
||||||
|
## 0.22.5 - March 2026
|
||||||
|
---
|
||||||
|
|
||||||
|
- Fix default value for "cascade" filter in PartCategory list view
|
||||||
|
- Fix default value for "cascade" filter in StockLocation list view
|
||||||
|
|
||||||
|
## 0.22.4 - March 2026
|
||||||
|
---
|
||||||
|
|
||||||
|
- Adds button to check server connection
|
||||||
|
- Fixes bug fetching sales order shipments
|
||||||
|
- Fix for boolean fields in API forms
|
||||||
|
|
||||||
|
## 0.22.3 - February 2026
|
||||||
|
---
|
||||||
|
|
||||||
|
- Auto-fill location data when receiving item via barcode scan
|
||||||
|
- Visual improvements for boolean form fields
|
||||||
|
- Add support for tri-state boolean form fields
|
||||||
|
- Bug fixes for refreshing list view data
|
||||||
|
|
||||||
|
## 0.22.2 - February 2026
|
||||||
|
---
|
||||||
|
|
||||||
|
- Bug fix for label printing, which used improperly formatted URL
|
||||||
|
|
||||||
## 0.22.1 - February 2026
|
## 0.22.1 - February 2026
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
1
ios/.gitignore
vendored
1
ios/.gitignore
vendored
|
|
@ -22,6 +22,7 @@ Flutter/Generated.xcconfig
|
||||||
Flutter/ephemeral/
|
Flutter/ephemeral/
|
||||||
Flutter/app.flx
|
Flutter/app.flx
|
||||||
Flutter/app.zip
|
Flutter/app.zip
|
||||||
|
Flutter/ephemeral/
|
||||||
Flutter/flutter_assets/
|
Flutter/flutter_assets/
|
||||||
Flutter/flutter_export_environment.sh
|
Flutter/flutter_export_environment.sh
|
||||||
ServiceDefinitions.json
|
ServiceDefinitions.json
|
||||||
|
|
|
||||||
14
lib/api.dart
14
lib/api.dart
|
|
@ -411,7 +411,7 @@ class InvenTreeAPI {
|
||||||
* 5. Request information on available plugins
|
* 5. Request information on available plugins
|
||||||
*/
|
*/
|
||||||
Future<bool> _connectToServer() async {
|
Future<bool> _connectToServer() async {
|
||||||
if (!await _checkServer()) {
|
if (!await checkServer()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -451,8 +451,8 @@ class InvenTreeAPI {
|
||||||
* Check that the remote server is available.
|
* Check that the remote server is available.
|
||||||
* Ping the api/ endpoint, which does not require user authentication
|
* Ping the api/ endpoint, which does not require user authentication
|
||||||
*/
|
*/
|
||||||
Future<bool> _checkServer() async {
|
Future<bool> checkServer({String? server}) async {
|
||||||
String address = profile?.server ?? "";
|
String address = server ?? profile?.server ?? "";
|
||||||
|
|
||||||
if (address.isEmpty) {
|
if (address.isEmpty) {
|
||||||
showSnackIcon(
|
showSnackIcon(
|
||||||
|
|
@ -463,8 +463,10 @@ class InvenTreeAPI {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!address.endsWith("/")) {
|
String url = _makeUrl("/api/", base: address);
|
||||||
address = address + "/";
|
|
||||||
|
if (!url.endsWith("/")) {
|
||||||
|
url = url + "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache the "strictHttps" setting, so we can use it later without async requirement
|
// Cache the "strictHttps" setting, so we can use it later without async requirement
|
||||||
|
|
@ -474,7 +476,7 @@ class InvenTreeAPI {
|
||||||
|
|
||||||
debug("Connecting to ${apiUrl}");
|
debug("Connecting to ${apiUrl}");
|
||||||
|
|
||||||
APIResponse response = await get("", expectedStatusCode: 200);
|
APIResponse response = await get(url, expectedStatusCode: 200);
|
||||||
|
|
||||||
if (!response.successful()) {
|
if (!response.successful()) {
|
||||||
debug("Server returned invalid response: ${response.statusCode}");
|
debug("Server returned invalid response: ${response.statusCode}");
|
||||||
|
|
|
||||||
|
|
@ -275,7 +275,7 @@ class APIFormField {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String url = api_url + "/" + value.toString() + "/";
|
String url = api_url + value.toString() + "/";
|
||||||
|
|
||||||
final APIResponse response = await InvenTreeAPI().get(url, params: filters);
|
final APIResponse response = await InvenTreeAPI().get(url, params: filters);
|
||||||
|
|
||||||
|
|
@ -293,6 +293,8 @@ class APIFormField {
|
||||||
return _constructString();
|
return _constructString();
|
||||||
case "boolean":
|
case "boolean":
|
||||||
return _constructBoolean();
|
return _constructBoolean();
|
||||||
|
case "boolean filter":
|
||||||
|
return _constructBooleanFilter();
|
||||||
case "related field":
|
case "related field":
|
||||||
return _constructRelatedField();
|
return _constructRelatedField();
|
||||||
case "integer":
|
case "integer":
|
||||||
|
|
@ -874,25 +876,109 @@ class APIFormField {
|
||||||
|
|
||||||
// Construct a boolean input element
|
// Construct a boolean input element
|
||||||
Widget _constructBoolean() {
|
Widget _constructBoolean() {
|
||||||
bool? initial_value;
|
bool v = false;
|
||||||
|
|
||||||
if (value is bool || value == null) {
|
if (value is bool) {
|
||||||
initial_value = value as bool?;
|
v = value as bool;
|
||||||
} else {
|
} else {
|
||||||
String vs = value.toString().toLowerCase();
|
v = false;
|
||||||
initial_value = ["1", "true", "yes"].contains(vs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return CheckBoxField(
|
return ListTile(
|
||||||
label: label,
|
title: Text(label),
|
||||||
labelStyle: _labelStyle(),
|
subtitle: Text(helpText),
|
||||||
helperText: helpText,
|
contentPadding: EdgeInsets.zero,
|
||||||
helperStyle: _helperStyle(),
|
trailing: Switch(
|
||||||
initial: initial_value,
|
value: v,
|
||||||
tristate: (getParameter("tristate") ?? false) as bool,
|
onChanged: (val) {
|
||||||
onSaved: (val) {
|
setFieldValue(val);
|
||||||
setFieldValue(val);
|
},
|
||||||
},
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct a tri-state boolean filter element
|
||||||
|
Widget _constructBooleanFilter() {
|
||||||
|
String initial_value = "null";
|
||||||
|
|
||||||
|
bool allow_null = (getParameter("tristate") ?? false) as bool;
|
||||||
|
|
||||||
|
if (value is bool) {
|
||||||
|
initial_value = value.toString().toLowerCase();
|
||||||
|
} else if (value == null) {
|
||||||
|
if (allow_null) {
|
||||||
|
initial_value = "null";
|
||||||
|
} else {
|
||||||
|
initial_value = "false";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Not a boolean value - may be a string
|
||||||
|
if (["1", "true", "yes"].contains(value.toString().toLowerCase())) {
|
||||||
|
initial_value = "true";
|
||||||
|
} else if ([
|
||||||
|
"0",
|
||||||
|
"false",
|
||||||
|
"no",
|
||||||
|
].contains(value.toString().toLowerCase())) {
|
||||||
|
initial_value = "false";
|
||||||
|
} else if (allow_null) {
|
||||||
|
initial_value = "null";
|
||||||
|
} else {
|
||||||
|
initial_value = "false";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ButtonSegment<String>> buttons = [];
|
||||||
|
|
||||||
|
if ((getParameter("tristate") ?? false) as bool) {
|
||||||
|
buttons.add(
|
||||||
|
ButtonSegment<String>(
|
||||||
|
value: "null",
|
||||||
|
icon: Icon(TablerIcons.minus, color: COLOR_GRAY_LIGHT),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
buttons.add(
|
||||||
|
ButtonSegment<String>(
|
||||||
|
value: "false",
|
||||||
|
icon: Icon(TablerIcons.x, color: COLOR_DANGER),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
buttons.add(
|
||||||
|
ButtonSegment<String>(
|
||||||
|
value: "true",
|
||||||
|
icon: Icon(TablerIcons.check, color: COLOR_SUCCESS),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
|
title: Text(label),
|
||||||
|
subtitle: Text(helpText),
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
trailing: SegmentedButton<String>(
|
||||||
|
segments: buttons,
|
||||||
|
selected: {initial_value},
|
||||||
|
showSelectedIcon: false,
|
||||||
|
multiSelectionEnabled: false,
|
||||||
|
style: SegmentedButton.styleFrom(
|
||||||
|
padding: EdgeInsets.all(0),
|
||||||
|
// minimumSize: MaterialStateProperty.all(Size(0, 0)),
|
||||||
|
// tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
),
|
||||||
|
onSelectionChanged: (Set<String> selection) {
|
||||||
|
String element = selection.first;
|
||||||
|
if (element == "null" && allow_null) {
|
||||||
|
setFieldValue(null);
|
||||||
|
} else if (element == "true") {
|
||||||
|
setFieldValue(true);
|
||||||
|
} else {
|
||||||
|
setFieldValue(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1168,7 +1254,9 @@ class APIFormWidgetState extends State<APIFormWidget> {
|
||||||
// Callback for when a field value is changed
|
// Callback for when a field value is changed
|
||||||
// Default implementation does nothing,
|
// Default implementation does nothing,
|
||||||
// but custom form implementations may override this function
|
// but custom form implementations may override this function
|
||||||
void onValueChanged(String field, dynamic value) {}
|
void onValueChanged(String field, dynamic value) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> handleSuccess(
|
Future<void> handleSuccess(
|
||||||
Map<String, dynamic> submittedData,
|
Map<String, dynamic> submittedData,
|
||||||
|
|
@ -1394,7 +1482,7 @@ class APIFormWidgetState extends State<APIFormWidget> {
|
||||||
|
|
||||||
if (field.isSimple) {
|
if (field.isSimple) {
|
||||||
// Simple top-level field data
|
// Simple top-level field data
|
||||||
data[field.name] = field.data["value"];
|
data[field.name] = field.data["value"] ?? field.defaultValue;
|
||||||
} else {
|
} else {
|
||||||
// Not so simple... (WHY DID I MAKE THE API SO COMPLEX?)
|
// Not so simple... (WHY DID I MAKE THE API SO COMPLEX?)
|
||||||
if (field.parent.isNotEmpty) {
|
if (field.parent.isNotEmpty) {
|
||||||
|
|
|
||||||
|
|
@ -154,9 +154,12 @@ class POAllocateBarcodeHandler extends BarcodeHandler {
|
||||||
return onBarcodeUnknown(data);
|
return onBarcodeUnknown(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract field data from the returned result
|
||||||
dynamic supplier_part = data["supplierpart"];
|
dynamic supplier_part = data["supplierpart"];
|
||||||
|
dynamic location = data["location"];
|
||||||
|
|
||||||
int supplier_part_pk = -1;
|
int supplier_part_pk = -1;
|
||||||
|
int location_pk = -1;
|
||||||
|
|
||||||
if (supplier_part is Map<String, dynamic>) {
|
if (supplier_part is Map<String, dynamic>) {
|
||||||
supplier_part_pk = (supplier_part["pk"] ?? -1) as int;
|
supplier_part_pk = (supplier_part["pk"] ?? -1) as int;
|
||||||
|
|
@ -164,6 +167,10 @@ class POAllocateBarcodeHandler extends BarcodeHandler {
|
||||||
return onBarcodeUnknown(data);
|
return onBarcodeUnknown(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (location is Map<String, dynamic>) {
|
||||||
|
location_pk = (location["pk"] ?? -1) as int;
|
||||||
|
}
|
||||||
|
|
||||||
// Dispose of the barcode scanner
|
// Dispose of the barcode scanner
|
||||||
if (OneContext.hasContext) {
|
if (OneContext.hasContext) {
|
||||||
OneContext().pop();
|
OneContext().pop();
|
||||||
|
|
@ -177,6 +184,10 @@ class POAllocateBarcodeHandler extends BarcodeHandler {
|
||||||
fields["part"]?["hidden"] = false;
|
fields["part"]?["hidden"] = false;
|
||||||
fields["part"]?["value"] = supplier_part_pk;
|
fields["part"]?["value"] = supplier_part_pk;
|
||||||
|
|
||||||
|
if (location_pk > 0) {
|
||||||
|
fields["location"]?["value"] = location_pk;
|
||||||
|
}
|
||||||
|
|
||||||
InvenTreePOLineItem().createForm(
|
InvenTreePOLineItem().createForm(
|
||||||
context,
|
context,
|
||||||
L10().lineItemAdd,
|
L10().lineItemAdd,
|
||||||
|
|
|
||||||
|
|
@ -270,9 +270,9 @@ class InvenTreeSalesOrderShipment extends InvenTreeModel {
|
||||||
InvenTreeSalesOrderShipment.fromJson(json);
|
InvenTreeSalesOrderShipment.fromJson(json);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get URL => "/order/so/shipment/";
|
String get URL => "order/so/shipment/";
|
||||||
|
|
||||||
String get SHIP_SHIPMENT_URL => "/order/so/shipment/${pk}/ship/";
|
String get SHIP_SHIPMENT_URL => "order/so/shipment/${pk}/ship/";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Object?> goToDetailPage(BuildContext context) async {
|
Future<Object?> goToDetailPage(BuildContext context) async {
|
||||||
|
|
@ -345,7 +345,7 @@ class InvenTreeSalesOrderAllocation extends InvenTreeModel {
|
||||||
InvenTreeSalesOrderAllocation.fromJson(json);
|
InvenTreeSalesOrderAllocation.fromJson(json);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get URL => "/order/so-allocation/";
|
String get URL => "order/so-allocation/";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<String> get rolesRequired => ["sales_order"];
|
List<String> get rolesRequired => ["sales_order"];
|
||||||
|
|
|
||||||
|
|
@ -294,6 +294,9 @@
|
||||||
"confirmScanDetail": "Confirm stock transfer details when scanning barcodes",
|
"confirmScanDetail": "Confirm stock transfer details when scanning barcodes",
|
||||||
"@confirmScanDetail": {},
|
"@confirmScanDetail": {},
|
||||||
|
|
||||||
|
"connectionCheck": "Check Connection",
|
||||||
|
"@connectionCheck": {},
|
||||||
|
|
||||||
"connectionRefused": "Connection Refused",
|
"connectionRefused": "Connection Refused",
|
||||||
"@connectionRefused": {},
|
"@connectionRefused": {},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import "package:inventree/l10.dart";
|
||||||
import "package:inventree/widget/progress.dart";
|
import "package:inventree/widget/progress.dart";
|
||||||
import "package:inventree/widget/snacks.dart";
|
import "package:inventree/widget/snacks.dart";
|
||||||
|
|
||||||
const String PRINT_LABEL_URL = "api/label/print/";
|
const String PRINT_LABEL_URL = "label/print/";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Custom form handler for label printing.
|
* Custom form handler for label printing.
|
||||||
|
|
@ -45,6 +45,8 @@ class LabelFormWidgetState extends APIFormWidgetState {
|
||||||
if (field == "plugin") {
|
if (field == "plugin") {
|
||||||
onPluginChanged(value.toString());
|
onPluginChanged(value.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
super.onValueChanged(field, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
|
|
@ -298,6 +298,9 @@ class _ProfileEditState extends State<ProfileEditWidget> {
|
||||||
String name = "";
|
String name = "";
|
||||||
String server = "";
|
String server = "";
|
||||||
|
|
||||||
|
bool? serverStatus;
|
||||||
|
bool serverChecking = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
|
@ -411,6 +414,49 @@ class _ProfileEditState extends State<ProfileEditWidget> {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
Divider(),
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: ElevatedButton.icon(
|
||||||
|
label: Text(L10().connectionCheck),
|
||||||
|
icon: serverStatus == true
|
||||||
|
? Icon(TablerIcons.circle_check, color: COLOR_SUCCESS)
|
||||||
|
: serverStatus == false
|
||||||
|
? Icon(TablerIcons.circle_x, color: COLOR_DANGER)
|
||||||
|
: Icon(TablerIcons.question_mark, color: COLOR_WARNING),
|
||||||
|
onPressed: serverChecking
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
if (serverChecking) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formKey.currentState!.validate()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
serverStatus = null;
|
||||||
|
serverChecking = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
formKey.currentState!.save();
|
||||||
|
|
||||||
|
InvenTreeAPI().checkServer(server: server).then((
|
||||||
|
result,
|
||||||
|
) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
serverStatus = result;
|
||||||
|
serverChecking = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
padding: EdgeInsets.all(16),
|
padding: EdgeInsets.all(16),
|
||||||
|
|
|
||||||
|
|
@ -92,9 +92,10 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget>
|
||||||
|
|
||||||
// Skip null values
|
// Skip null values
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
continue;
|
f[k] = "null";
|
||||||
|
} else {
|
||||||
|
f[k] = value.toString();
|
||||||
}
|
}
|
||||||
f[k] = value.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return f;
|
return f;
|
||||||
|
|
@ -206,7 +207,7 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget>
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> filter = {
|
Map<String, dynamic> filter = {
|
||||||
"type": "boolean",
|
"type": "boolean filter",
|
||||||
"display_name": label,
|
"display_name": label,
|
||||||
"label": label,
|
"label": label,
|
||||||
"help_text": help_text,
|
"help_text": help_text,
|
||||||
|
|
@ -341,7 +342,16 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget>
|
||||||
Map<String, String> f = await constructFilters();
|
Map<String, String> f = await constructFilters();
|
||||||
|
|
||||||
if (f.isNotEmpty) {
|
if (f.isNotEmpty) {
|
||||||
params.addAll(f);
|
for (String k in f.keys) {
|
||||||
|
// Remove any existing filter keys
|
||||||
|
dynamic value = f[k];
|
||||||
|
|
||||||
|
if (value == null || value == "null") {
|
||||||
|
params.remove(k);
|
||||||
|
} else {
|
||||||
|
params[k] = value.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final page = await requestPage(_pageSize, pageKey, params);
|
final page = await requestPage(_pageSize, pageKey, params);
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ class _PaginatedPartCategoryListState
|
||||||
@override
|
@override
|
||||||
Map<String, Map<String, dynamic>> get filterOptions => {
|
Map<String, Map<String, dynamic>> get filterOptions => {
|
||||||
"cascade": {
|
"cascade": {
|
||||||
"default": false,
|
"default": true,
|
||||||
"label": L10().includeSubcategories,
|
"label": L10().includeSubcategories,
|
||||||
"help_text": L10().includeSubcategoriesDetail,
|
"help_text": L10().includeSubcategoriesDetail,
|
||||||
"tristate": false,
|
"tristate": false,
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ class _PaginatedStockLocationListState
|
||||||
"label": L10().includeSublocations,
|
"label": L10().includeSublocations,
|
||||||
"help_text": L10().includeSublocationsDetail,
|
"help_text": L10().includeSublocationsDetail,
|
||||||
"tristate": false,
|
"tristate": false,
|
||||||
|
"default": true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -225,7 +225,7 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
|
||||||
// Request part information
|
// Request part information
|
||||||
part = await InvenTreePart().get(widget.item.partId) as InvenTreePart?;
|
part = await InvenTreePart().get(widget.item.partId) as InvenTreePart?;
|
||||||
|
|
||||||
stockShowTests &= part?.isTrackable ?? false;
|
stockShowTests &= part?.isTestable ?? false;
|
||||||
|
|
||||||
// Request default location
|
// Request default location
|
||||||
int? defaultLocationId = part?.defaultLocation;
|
int? defaultLocationId = part?.defaultLocation;
|
||||||
|
|
|
||||||
20
pubspec.lock
20
pubspec.lock
|
|
@ -637,6 +637,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.5"
|
||||||
|
jni:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: jni
|
||||||
|
sha256: d2c361082d554d4593c3012e26f6b188f902acd291330f13d6427641a92b3da1
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.14.2"
|
||||||
js:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -737,10 +745,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: mobile_scanner
|
name: mobile_scanner
|
||||||
sha256: "54005bdea7052d792d35b4fef0f84ec5ddc3a844b250ecd48dc192fb9b4ebc95"
|
sha256: c92c26bf2231695b6d3477c8dcf435f51e28f87b1745966b1fe4c47a286171ce
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.1"
|
version: "7.2.0"
|
||||||
node_preamble:
|
node_preamble:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -921,18 +929,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: sentry
|
name: sentry
|
||||||
sha256: "599701ca0693a74da361bc780b0752e1abc98226cf5095f6b069648116c896bb"
|
sha256: "605ad1f6f1ae5b72018cbe8fc20f490fa3bd53e58882e5579566776030d8c8c1"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.14.2"
|
version: "9.14.0"
|
||||||
sentry_flutter:
|
sentry_flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: sentry_flutter
|
name: sentry_flutter
|
||||||
sha256: "5ba2cf40646a77d113b37a07bd69f61bb3ec8a73cbabe5537b05a7c89d2656f8"
|
sha256: "7fd0fb80050c1f6a77ae185bda997a76d384326d6777cf5137a6c38952c4ac7d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.14.2"
|
version: "9.14.0"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
name: inventree
|
name: inventree
|
||||||
description: InvenTree stock management
|
description: InvenTree stock management
|
||||||
|
|
||||||
version: 0.22.1+110
|
version: 0.22.7+116
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.8.1
|
sdk: ^3.8.1
|
||||||
|
|
@ -28,19 +28,19 @@ dependencies:
|
||||||
flutter_markdown: ^0.6.19 # Rendering markdown
|
flutter_markdown: ^0.6.19 # Rendering markdown
|
||||||
flutter_overlay_loader: ^2.0.0 # Overlay screen support
|
flutter_overlay_loader: ^2.0.0 # Overlay screen support
|
||||||
flutter_speed_dial: ^6.2.0 # Speed dial / FAB implementation
|
flutter_speed_dial: ^6.2.0 # Speed dial / FAB implementation
|
||||||
flutter_tabler_icons: ^1.43.0
|
flutter_tabler_icons: ^1.43.0 # Tabler icons
|
||||||
http: ^1.4.0
|
http: ^1.4.0
|
||||||
image_picker: ^1.1.2 # Select or take photos
|
image_picker: ^1.1.2 # Select or take photos
|
||||||
infinite_scroll_pagination: ^4.0.0 # Let the server do all the work!
|
infinite_scroll_pagination: ^4.0.0 # Let the server do all the work!
|
||||||
intl: ^0.20.2
|
intl: ^0.20.2
|
||||||
mobile_scanner: ^7.0.1 # Barcode scanning support
|
mobile_scanner: ^7.2.0 # Barcode scanning support
|
||||||
one_context: ^4.0.0 # Dialogs without requiring context
|
one_context: ^4.0.0 # Dialogs without requiring context
|
||||||
open_filex: ^4.7.0 # Open local files
|
open_filex: ^4.7.0 # Open local files
|
||||||
package_info_plus: ^8.1.1 # App information introspection
|
package_info_plus: ^8.1.1 # App information introspection
|
||||||
path: ^1.9.0
|
path: ^1.9.0
|
||||||
path_provider: ^2.1.5 # Local file storage
|
path_provider: ^2.1.5 # Local file storage
|
||||||
sembast: ^3.6.0 # NoSQL data storage
|
sembast: ^3.6.0 # NoSQL data storage
|
||||||
sentry_flutter: 8.14.2 # Error reporting
|
sentry_flutter: ^9.14.0 # Error reporting
|
||||||
url_launcher: ^6.3.1 # Open link in system browser
|
url_launcher: ^6.3.1 # Open link in system browser
|
||||||
wakelock_plus: ^1.3.2 # Prevent device from sleeping
|
wakelock_plus: ^1.3.2 # Prevent device from sleeping
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue