2
0
mirror of https://github.com/inventree/inventree-app.git synced 2025-05-02 07:26:50 +00:00

Merge branch 'barcode-fixes'

This commit is contained in:
Oliver 2021-07-30 11:10:47 +10:00
commit aca7b79a96
9 changed files with 138 additions and 207 deletions

View File

@ -23,7 +23,7 @@ import 'dart:io';
class BarcodeHandler {
/**
/*
* Class which "handles" a barcode, by communicating with the InvenTree server,
* and handling match / unknown / error cases.
*
@ -36,7 +36,6 @@ class BarcodeHandler {
BarcodeHandler();
QRViewController? _controller;
BuildContext? _context;
void successTone() async {
@ -58,12 +57,12 @@ class BarcodeHandler {
}
}
Future<void> onBarcodeMatched(Map<String, dynamic> data) async {
Future<void> onBarcodeMatched(BuildContext context, Map<String, dynamic> data) async {
// Called when the server "matches" a barcode
// Override this function
}
Future<void> onBarcodeUnknown(Map<String, dynamic> data) async {
Future<void> onBarcodeUnknown(BuildContext context, Map<String, dynamic> data) async {
// Called when the server does not know about a barcode
// Override this function
@ -76,7 +75,7 @@ class BarcodeHandler {
);
}
Future<void> onBarcodeUnhandled(Map<String, dynamic> data) async {
Future<void> onBarcodeUnhandled(BuildContext context, Map<String, dynamic> data) async {
failureTone();
@ -86,8 +85,7 @@ class BarcodeHandler {
_controller?.resumeCamera();
}
Future<void> processBarcode(BuildContext? context, QRViewController? _controller, String barcode, {String url = "barcode/"}) async {
this._context = context;
Future<void> processBarcode(BuildContext context, QRViewController? _controller, String barcode, {String url = "barcode/"}) async {
this._controller = _controller;
print("Scanned barcode data: ${barcode}");
@ -106,20 +104,20 @@ class BarcodeHandler {
if (response.data.containsKey('error')) {
_controller?.resumeCamera();
onBarcodeUnknown(response.data);
onBarcodeUnknown(context, response.data);
} else if (response.data.containsKey('success')) {
_controller?.resumeCamera();
onBarcodeMatched(response.data);
onBarcodeMatched(context, response.data);
} else {
_controller?.resumeCamera();
onBarcodeUnhandled(response.data);
onBarcodeUnhandled(context, response.data);
}
}
}
class BarcodeScanHandler extends BarcodeHandler {
/**
/*
* Class for general barcode scanning.
* Scan *any* barcode without context, and then redirect app to correct view
*/
@ -128,7 +126,7 @@ class BarcodeScanHandler extends BarcodeHandler {
String getOverlayText(BuildContext context) => L10().barcodeScanGeneral;
@override
Future<void> onBarcodeUnknown(Map<String, dynamic> data) async {
Future<void> onBarcodeUnknown(BuildContext context, Map<String, dynamic> data) async {
failureTone();
@ -140,13 +138,10 @@ class BarcodeScanHandler extends BarcodeHandler {
}
@override
Future<void> onBarcodeMatched(Map<String, dynamic> data) async {
Future<void> onBarcodeMatched(BuildContext context, Map<String, dynamic> data) async {
int pk = -1;
print("Handle barcode:");
print(data);
// A stocklocation has been passed?
if (data.containsKey('stocklocation')) {
@ -158,13 +153,8 @@ class BarcodeScanHandler extends BarcodeHandler {
InvenTreeStockLocation().get(pk).then((var loc) {
if (loc is InvenTreeStockLocation) {
var _ctx = _context;
if (_ctx != null) {
Navigator.of(_ctx).pop();
Navigator.push(_ctx, MaterialPageRoute(builder: (context) => LocationDisplayWidget(loc)));
}
Navigator.of(context).pop();
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(loc)));
}
});
} else {
@ -187,16 +177,12 @@ class BarcodeScanHandler extends BarcodeHandler {
InvenTreeStockItem().get(pk).then((var item) {
var _ctx = _context;
if (_ctx != null) {
// Dispose of the barcode scanner
Navigator.of(_ctx).pop();
Navigator.of(context).pop();
if (item is InvenTreeStockItem) {
Navigator.push(_ctx, MaterialPageRoute(builder: (context) => StockDetailWidget(item)));
Navigator.push(context, MaterialPageRoute(builder: (context) => StockDetailWidget(item)));
}
}
});
} else {
@ -217,16 +203,12 @@ class BarcodeScanHandler extends BarcodeHandler {
InvenTreePart().get(pk).then((var part) {
var _ctx = _context;
if (_ctx != null) {
// Dismiss the barcode scanner
Navigator.of(_ctx).pop();
Navigator.of(context).pop();
if (part is InvenTreePart) {
Navigator.push(_ctx, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
Navigator.push(context, MaterialPageRoute(builder: (context) => PartDetailWidget(part)));
}
}
});
} else {
@ -265,7 +247,7 @@ class BarcodeScanHandler extends BarcodeHandler {
class StockItemBarcodeAssignmentHandler extends BarcodeHandler {
/**
/*
* Barcode handler for assigning a new barcode to a stock item
*/
@ -277,7 +259,7 @@ class StockItemBarcodeAssignmentHandler extends BarcodeHandler {
String getOverlayText(BuildContext context) => L10().barcodeScanAssign;
@override
Future<void> onBarcodeMatched(Map<String, dynamic> data) async {
Future<void> onBarcodeMatched(BuildContext context, Map<String, dynamic> data) async {
failureTone();
@ -290,7 +272,7 @@ class StockItemBarcodeAssignmentHandler extends BarcodeHandler {
}
@override
Future<void> onBarcodeUnknown(Map<String, dynamic> data) async {
Future<void> onBarcodeUnknown(BuildContext context, Map<String, dynamic> data) async {
// If the barcode is unknown, we *can* assign it to the stock item!
if (!data.containsKey("hash")) {
@ -310,14 +292,7 @@ class StockItemBarcodeAssignmentHandler extends BarcodeHandler {
failureTone();
// Close the barcode scanner
_controller?.dispose();
var _ctx = (_context);
if (_ctx != null) {
Navigator.of(_ctx).pop();
}
Navigator.of(context).pop();
showSnackIcon(
L10().barcodeAssigned,
@ -339,12 +314,8 @@ class StockItemBarcodeAssignmentHandler extends BarcodeHandler {
}
}
class StockItemScanIntoLocationHandler extends BarcodeHandler {
/**
/*
* Barcode handler for scanning a provided StockItem into a scanned StockLocation
*/
@ -356,11 +327,20 @@ class StockItemScanIntoLocationHandler extends BarcodeHandler {
String getOverlayText(BuildContext context) => L10().barcodeScanLocation;
@override
Future<void> onBarcodeMatched(Map<String, dynamic> data) async {
Future<void> onBarcodeMatched(BuildContext context, Map<String, dynamic> data) async {
// If the barcode points to a 'stocklocation', great!
if (data.containsKey('stocklocation')) {
// Extract location information
int location = data['stocklocation']['pk'] as int;
int location = (data['stocklocation']['pk'] ?? -1) as int;
if (location == -1) {
showSnackIcon(
L10().invalidStockLocation,
success: false,
);
return;
}
// Transfer stock to specified location
final result = await item.transferStock(location);
@ -369,14 +349,7 @@ class StockItemScanIntoLocationHandler extends BarcodeHandler {
successTone();
// Close the scanner
_controller?.dispose();
var _ctx = _context;
if (_ctx != null) {
Navigator.of(_ctx).pop();
}
Navigator.of(context).pop();
showSnackIcon(
L10().barcodeScanIntoLocationSuccess,
@ -405,7 +378,7 @@ class StockItemScanIntoLocationHandler extends BarcodeHandler {
class StockLocationScanInItemsHandler extends BarcodeHandler {
/**
/*
* Barcode handler for scanning stock item(s) into the specified StockLocation
*/
@ -417,7 +390,7 @@ class StockLocationScanInItemsHandler extends BarcodeHandler {
String getOverlayText(BuildContext context) => L10().barcodeScanItem;
@override
Future<void> onBarcodeMatched(Map<String, dynamic> data) async {
Future<void> onBarcodeMatched(BuildContext context, Map<String, dynamic> data) async {
// Returned barcode must match a stock item
if (data.containsKey('stockitem')) {
@ -489,33 +462,32 @@ class InvenTreeQRView extends StatefulWidget {
class _QRViewState extends State<InvenTreeQRView> {
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
QRViewController? _controller;
final BarcodeHandler _handler;
BuildContext? _context;
// In order to get hot reload to work we need to pause the camera if the platform
// is android, or resume the camera if the platform is iOS.
@override
void reassemble() {
super.reassemble();
if (Platform.isAndroid) {
_controller?.pauseCamera();
} else if (Platform.isIOS) {
_controller?.resumeCamera();
_controller!.pauseCamera();
}
_controller!.resumeCamera();
}
_QRViewState(this._handler) : super();
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
void _onViewCreated(QRViewController controller) {
void _onViewCreated(BuildContext context, QRViewController controller) {
_controller = controller;
controller.scannedDataStream.listen((barcode) {
_controller?.pauseCamera();
_handler.processBarcode(_context, _controller, barcode.code);
_handler.processBarcode(context, _controller, barcode.code);
});
}
@ -528,9 +500,6 @@ class _QRViewState extends State<InvenTreeQRView> {
@override
Widget build(BuildContext context) {
// Save the context for later on!
this._context = context;
return Scaffold(
body: Stack(
children: <Widget>[
@ -539,7 +508,9 @@ class _QRViewState extends State<InvenTreeQRView> {
Expanded(
child: QRView(
key: qrKey,
onQRViewCreated: _onViewCreated,
onQRViewCreated: (QRViewController controller) {
_onViewCreated(context, controller);
},
overlay: QrScannerOverlayShape(
borderColor: Colors.red,
borderRadius: 10,

View File

@ -1,7 +1,7 @@
import 'dart:io';
import 'package:device_info/device_info.dart';
import 'package:package_info/package_info.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:inventree/api.dart';

View File

@ -8,6 +8,7 @@ import 'package:inventree/widget/home.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:one_context/one_context.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'dsn.dart';
@ -17,14 +18,23 @@ import 'package:sentry_flutter/sentry_flutter.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await runZonedGuarded<Future<void>>(() async {
PackageInfo info = await PackageInfo.fromPlatform();
String pkg = info.packageName;
String version = info.version;
String build = info.buildNumber;
String release = "${pkg}@${version}:${build}";
await Sentry.init((options) {
options.dsn = SENTRY_DSN_KEY;
options.release = release;
options.environment = isInDebugMode() ? "debug" : "release";
});
WidgetsFlutterBinding.ensureInitialized();
// Pass any flutter errors off to the Sentry reporting context!
FlutterError.onError = (FlutterErrorDetails details) async {

View File

@ -6,7 +6,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:package_info/package_info.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:inventree/l10.dart';

View File

@ -14,7 +14,7 @@ import 'package:url_launcher/url_launcher.dart';
import 'login.dart';
import 'package:package_info/package_info.dart';
import 'package:package_info_plus/package_info_plus.dart';
class InvenTreeSettingsWidget extends StatefulWidget {
// InvenTree settings view

View File

@ -39,14 +39,6 @@ class _CompanyDetailState extends RefreshableState<CompanyDetailWidget> {
// TODO
}
void _saveCompany(Map<String, String> values) async {
Navigator.of(context).pop();
await company.update(values: values);
refresh();
}
void editCompanyDialog() {
// Values which can be edited
@ -54,55 +46,7 @@ class _CompanyDetailState extends RefreshableState<CompanyDetailWidget> {
var _description;
var _website;
showFormDialog(L10().edit,
key: _editCompanyKey,
actions: <Widget>[
TextButton(
child: Text(L10().cancel),
onPressed: () {
Navigator.pop(context);
},
),
TextButton(
child: Text(L10().save),
onPressed: () {
if (_editCompanyKey.currentState!.validate()) {
_editCompanyKey.currentState!.save();
_saveCompany({
"name": _name,
"description": _description,
"website": _website,
});
}
},
),
],
fields: <Widget>[
StringField(
label: L10().name,
initial: company.name,
onSaved: (value) {
_name = value;
},
),
StringField(
label: L10().description,
initial: company.description,
onSaved: (value) {
_description = value;
},
),
StringField(
label: L10().website,
initial: company.website,
allowEmpty: true,
onSaved: (value) {
_website = value;
},
)
]
);
// TODO - API form
}
List<Widget> _companyTiles() {

View File

@ -184,62 +184,47 @@ Future<void> showTimeoutError() async {
await showServerError(L10().timeout, L10().noResponse);
}
void showFormDialog(String title, {String? acceptText, String? cancelText, GlobalKey<FormState>? key, List<Widget>? fields, List<Widget>? actions, Function? callback}) {
void showFormDialog(String title, {String? acceptText, String? cancelText, GlobalKey<FormState>? key, List<Widget>? fields, Function? callback}) {
BuildContext? dialogContext;
String _accept = acceptText ?? L10().save;
String _cancel = cancelText ?? L10().cancel;
// Undefined actions = OK + Cancel
if (actions == null) {
actions = <Widget>[
TextButton(
child: Text(_cancel),
onPressed: () {
// Close the form
var _ctx = dialogContext;
if (_ctx != null) {
Navigator.pop(_ctx);
}
}
),
TextButton(
child: Text(_accept),
onPressed: () {
var _key = key;
if (_key != null && _key.currentState != null) {
if (_key.currentState!.validate()) {
_key.currentState!.save();
// Close the dialog
var _ctx = dialogContext;
if (_ctx != null) {
Navigator.pop(_ctx);
}
// Callback
if (callback != null) {
callback();
}
}
}
}
)
];
}
List<Widget> _fields = fields ?? [];
OneContext().showDialog(
builder: (BuildContext context) {
dialogContext = context;
return AlertDialog(
title: Text(title),
actions: actions,
actions: <Widget>[
TextButton(
child: Text(_cancel),
onPressed: () {
// Close the form
Navigator.pop(context);
}
),
TextButton(
child: Text(_accept),
onPressed: () {
var _key = key;
if (_key != null && _key.currentState != null) {
if (_key.currentState!.validate()) {
_key.currentState!.save();
Navigator.pop(context);
// Callback
if (callback != null) {
callback();
}
}
}
}
)
],
content: Form(
key: key,
child: SingleChildScrollView(

View File

@ -113,20 +113,48 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
device_info:
device_info_plus:
dependency: "direct main"
description:
name: device_info
name: device_info_plus
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
device_info_platform_interface:
version: "2.1.0"
device_info_plus_linux:
dependency: transitive
description:
name: device_info_platform_interface
name: device_info_plus_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
version: "2.1.0"
device_info_plus_macos:
dependency: transitive
description:
name: device_info_plus_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
device_info_plus_platform_interface:
dependency: transitive
description:
name: device_info_plus_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
device_info_plus_web:
dependency: transitive
description:
name: device_info_plus_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
device_info_plus_windows:
dependency: transitive
description:
name: device_info_plus_windows
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
dropdown_search:
dependency: "direct main"
description:
@ -322,27 +350,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
package_info:
dependency: "direct main"
description:
name: package_info
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
package_info_plus:
dependency: transitive
dependency: "direct main"
description:
name: package_info_plus
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
version: "1.0.4"
package_info_plus_linux:
dependency: transitive
description:
name: package_info_plus_linux
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
version: "1.0.3"
package_info_plus_macos:
dependency: transitive
description:
@ -356,21 +377,21 @@ packages:
name: package_info_plus_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
version: "1.0.2"
package_info_plus_web:
dependency: transitive
description:
name: package_info_plus_web
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
version: "1.0.3"
package_info_plus_windows:
dependency: transitive
description:
name: package_info_plus_windows
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
version: "1.0.3"
path:
dependency: "direct main"
description:
@ -454,7 +475,7 @@ packages:
name: qr_code_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.1"
version: "0.5.2"
quiver:
dependency: transitive
description:

View File

@ -24,9 +24,9 @@ dependencies:
cupertino_icons: ^1.0.3
http: ^0.13.0
cached_network_image: ^3.0.0 # Download and cache remote images
qr_code_scanner: ^0.5.1 # Barcode scanning
package_info: ^2.0.0 # App information introspection
device_info: ^2.0.0 # Information about the device
qr_code_scanner: ^0.5.2 # Barcode scanning
package_info_plus: ^1.0.4 # App information introspection
device_info_plus: ^2.1.0 # Information about the device
font_awesome_flutter: ^9.1.0 # FontAwesome icon set
flutter_speed_dial: ^3.0.5 # FAB menu elements
sentry_flutter: 5.0.0 # Error reporting