diff --git a/android/app/build.gradle b/android/app/build.gradle
index 44b44b4..849a2c2 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -47,7 +47,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.aman_kassa_flutter"
- minSdkVersion 16
+ minSdkVersion 18
targetSdkVersion 28
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index c25a2fd..10582c7 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -6,9 +6,13 @@
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
+
+
+
+
+ if (call.method == "getBatteryLevel") {
+ val batteryLevel = getBatteryLevel()
+
+ if (batteryLevel != -1) {
+ result.success(batteryLevel)
+ } else {
+ result.error("UNAVAILABLE", "Battery level not available.", null)
+ }
+ } else if (call.method == "sendMessage") {
+ val batteryLevel = sendMessage()
+
+ if (batteryLevel != -1) {
+ result.success(batteryLevel)
+ } else {
+ result.error("UNAVAILABLE", "Battery level not available.", null)
+ }
+ } else {
+ result.notImplemented()
+ }
+ }
+ }
+
+ private fun getBatteryLevel(): Int {
+ val batteryLevel: Int
+ if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
+ batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
+ } else {
+ val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
+ batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
+ }
+ println("batteryLevel");
+ println(batteryLevel);
+ return batteryLevel
+ }
+
+ private fun sendMessage(): Int {
+ val packageManager: PackageManager = context.packageManager
+ val i = Intent(Intent.ACTION_VIEW)
+ try {
+ val mobileNo: String = "77774904900" //call.argument("mobileNo")
+ val message: String = "Hello world" //call.argument("message")
+ //https://wa.me/919167370647?text=Yes%20We'll%20do%20this%20in%20frag4%20inOCW
+ println("mobileNo: $mobileNo message: $message")
+ val url = "https://wa.me/" + mobileNo.trim { it <= ' ' } + "?text=" + message.trim { it <= ' ' }
+ i.setPackage("com.whatsapp")
+ i.data = Uri.parse(url)
+ if (i.resolveActivity(packageManager) != null) {
+ context.startActivity(i)
+ }
+ println("finish method - 2")
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ return 25
}
}
diff --git a/android/build.gradle b/android/build.gradle
index 3100ad2..1eddda3 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,5 +1,5 @@
buildscript {
- ext.kotlin_version = '1.3.50'
+ ext.kotlin_version = '1.3.61'
repositories {
google()
jcenter()
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 59e3452..0adb400 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -2,6 +2,8 @@
+ NSCameraUsageDescription
+ Camera permission is required for barcode and qr-code scanning.
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleExecutable
diff --git a/lib/core/models/user.dart b/lib/core/models/user.dart
index 6ed4a42..03ea279 100644
--- a/lib/core/models/user.dart
+++ b/lib/core/models/user.dart
@@ -9,14 +9,27 @@ class User {
User({this.email, this.fullName, this.name, this.token, this.appCompanyId, this.kassaId});
- factory User.fromJson(Map json) {
- return User (
+ static User fromJson(Map json) {
+ return json != null
+ ? User (
name: json['name'],
email: json['mail'],
token: json['api_token'],
fullName: json['fullname'],
appCompanyId: json['app_company_id'] as int,
kassaId: json['kassa_id'] as int,
- );
- }
+ )
+ : null;
+ }
+
+ dynamic toJson() {
+ return {
+ "name": name,
+ "mail": email,
+ "api_token": token,
+ "fullname": fullName,
+ "app_company_id": appCompanyId,
+ "kassa_id": kassaId
+ };
+ }
}
diff --git a/lib/core/services/ApiService.dart b/lib/core/services/ApiService.dart
index 3ec151c..f7f3a98 100644
--- a/lib/core/services/ApiService.dart
+++ b/lib/core/services/ApiService.dart
@@ -2,10 +2,14 @@ import 'dart:convert';
import 'dart:io';
import 'package:aman_kassa_flutter/core/base/base_service.dart';
-import 'package:aman_kassa_flutter/core/entity/Goods.dart';
+import 'package:device_info/device_info.dart';
import 'package:aman_kassa_flutter/core/models/message.dart';
import 'package:aman_kassa_flutter/core/models/response.dart';
import 'package:aman_kassa_flutter/core/models/smena.dart';
+import 'package:aman_kassa_flutter/core/route_names.dart';
+import 'package:aman_kassa_flutter/core/services/dialog_service.dart';
+import 'package:aman_kassa_flutter/core/services/navigator_service.dart';
+import 'package:aman_kassa_flutter/core/locator.dart';
import '../models/auth_response.dart';
import 'package:http/http.dart' as http;
@@ -13,6 +17,8 @@ import 'package:http/http.dart' as http;
/// The service responsible for networking requests
class ApiService extends BaseService {
static const endpoint = 'https://kassa-test.aman.com.kz/ru/api/v2';
+ final NavigatorService _navigatorService = locator();
+ final DialogService _dialogService = locator();
var client = new http.Client();
@@ -27,16 +33,27 @@ class ApiService extends BaseService {
return aman.body;
}
+ Future authenticate_token(String token, { bool statusCheck = true}) async {
+ Map requestBody = {
+ 'token': token
+ };
+ String response = await requestFormData('/activate_token', requestBody, statusCheck: statusCheck );
+
+ AuthResponse aman = AuthResponse.fromJson(json.decode(response));
+ return aman.body;
+ }
+
Future> isActive(String token) async {
Map requestBody = {'api_token': token};
var response = await requestFormData('/test_auth', requestBody);
return Response.fromJson(json.decode(response), Message.fromJson);
}
- Future> logout(String token) async {
+ Future> logout(String token) async {
Map requestBody = {'api_token': token};
var response = await requestFormData('/logout', requestBody);
- return Response.fromJson(json.decode(response), Message.fromJson);
+ print(json.decode(response));
+ return Response.fromJsonDynamic(json.decode(response));
}
Future> money(String token) async {
@@ -102,10 +119,28 @@ class ApiService extends BaseService {
Future requestFormData(String point, Map requestBody, { bool statusCheck = true } ) async {
+ DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
+
+
Map headers = {
HttpHeaders.contentTypeHeader: "multipart/form-data",
HttpHeaders.cacheControlHeader: "no-cache"
};
+ if(Platform.isAndroid) {
+ AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
+ print(androidInfo.model);
+ headers.addAll({
+ HttpHeaders.userAgentHeader: androidInfo.model,
+ });
+ }
+
+ if(Platform.isIOS) {
+ IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
+ print(iosInfo.utsname.machine);
+ headers.addAll({
+ HttpHeaders.userAgentHeader: iosInfo.utsname.machine,
+ });
+ }
var uri = Uri.parse('$endpoint$point');
var request = http.MultipartRequest('POST', uri)
@@ -118,7 +153,8 @@ class ApiService extends BaseService {
if(statusCheck) { //Проверка на авторизованный запрос, необязательный параметр
Response check = Response.fromJsonDynamic(json.decode(body));
if (!check.operation && check.status == 401) {
- print('object');
+ _dialogService.showDialog(description: 'Необходимо пройти повторную авторизацию');
+ _navigatorService.replace(LoginViewRoute);
}
}
return body;
diff --git a/lib/core/services/DataService.dart b/lib/core/services/DataService.dart
index 2b64e2c..6d173a5 100644
--- a/lib/core/services/DataService.dart
+++ b/lib/core/services/DataService.dart
@@ -5,6 +5,7 @@ import 'package:aman_kassa_flutter/core/entity/Category.dart';
import 'package:aman_kassa_flutter/core/entity/Goods.dart';
import 'package:aman_kassa_flutter/core/entity/Service.dart';
import 'package:aman_kassa_flutter/core/locator.dart';
+import 'package:aman_kassa_flutter/core/models/calc_model.dart';
import 'package:aman_kassa_flutter/core/models/check_data.dart';
import 'package:aman_kassa_flutter/core/models/check_item.dart';
import 'package:aman_kassa_flutter/core/models/product_dao.dart';
@@ -12,6 +13,7 @@ import 'package:aman_kassa_flutter/core/models/response.dart';
import 'package:aman_kassa_flutter/core/models/user.dart';
import 'package:aman_kassa_flutter/core/services/DbService.dart';
import 'package:aman_kassa_flutter/redux/constants/operation_const.dart';
+import 'package:aman_kassa_flutter/redux/constants/setting_const.dart';
import 'package:aman_kassa_flutter/redux/store.dart';
import 'ApiService.dart';
@@ -38,9 +40,7 @@ class DataService extends BaseService {
}
CheckData _transformProductsToCheckData(
- {String paymentType,
- String tradeType,
- List items}) {
+ {String paymentType, String tradeType, List items}) {
List itemsList = [];
int iterator = 1;
num summ = 0.0;
@@ -67,16 +67,52 @@ class DataService extends BaseService {
return checkData;
}
+ CheckData _transformCalcModelToCheckData(
+ {String paymentType, String tradeType, List items}) {
+ List itemsList = [];
+ int iterator = 1;
+ num summ = 0.0;
+ items.forEach((el) {
+ int articul = iterator;
+ CheckItem item = CheckItem(
+ name: 'Позиция $iterator',
+ cnt: el.num2 != null ? double.parse(el.num2) : 1.0,
+ price: double.parse(el.num1) ,
+ articul: articul);
+
+ summ += item.cnt * item.price;
+ itemsList.add(item);
+ iterator++;
+ });
+ CheckData checkData = CheckData(type: tradeType, items: itemsList);
+ if ((paymentType ?? 'cash') == 'card') {
+ checkData.card = summ;
+ }
+ print(checkData);
+ return checkData;
+ }
+
Future> sellOrReturn(
{String paymentType,
String tradeType,
String token,
- List items,
- String operationType}) async {
+ List kassaItems,
+ List calcItems,
+ String operationType,
+ String mode}) async {
try {
- CheckData checkData = _transformProductsToCheckData(
- paymentType: paymentType, tradeType: tradeType, items: items);
- String data = jsonEncode(checkData.toJson());
+ String data;
+ if(mode == SettingModeKassa) {
+ CheckData checkData = _transformProductsToCheckData(
+ paymentType: paymentType, tradeType: tradeType, items: kassaItems);
+ data = jsonEncode(checkData.toJson());
+ } else if(mode == SettingModeCalc) {
+ CheckData checkData = _transformCalcModelToCheckData(
+ paymentType: paymentType, tradeType: tradeType, items: calcItems);
+ data = jsonEncode(checkData.toJson());
+ }
+
+
log.i('token: $token');
log.i('data: $data');
Response response = await (operationType == OperationTypePay
diff --git a/lib/redux/actions/calc_actions.dart b/lib/redux/actions/calc_actions.dart
index 5e8c351..05e745a 100644
--- a/lib/redux/actions/calc_actions.dart
+++ b/lib/redux/actions/calc_actions.dart
@@ -32,6 +32,10 @@ Future setEqual(Store store) async {
store.dispatch(SetCalcStateAction(CalcState(isEqual: true)));
}
+Future cleanCalcItems(Store store) async {
+ store.dispatch(SetCalcStateAction(CalcState(calcItems: [])));
+}
+
ThunkAction onTapAction(String value) {
return (Store store) async {
diff --git a/lib/redux/actions/kassa_actions.dart b/lib/redux/actions/kassa_actions.dart
index 7f4e621..61ad660 100644
--- a/lib/redux/actions/kassa_actions.dart
+++ b/lib/redux/actions/kassa_actions.dart
@@ -38,6 +38,10 @@ Future backBottomElement(Store store) async {
}
}
+Future cleanKassaItems(Store store) async {
+ store.dispatch(SetKassaStateAction(KassaState(kassaItems: [])));
+}
+
ThunkAction addCustomProductToKassaItems(String name, int count, double price, double total) {
return (Store store) async {
List items = store.state.kassaState.kassaItems;
diff --git a/lib/redux/actions/user_actions.dart b/lib/redux/actions/user_actions.dart
index ef9ecdc..af8fb01 100644
--- a/lib/redux/actions/user_actions.dart
+++ b/lib/redux/actions/user_actions.dart
@@ -10,6 +10,7 @@ import 'package:aman_kassa_flutter/core/route_names.dart';
import 'package:aman_kassa_flutter/core/services/ApiService.dart';
import 'package:aman_kassa_flutter/core/services/dialog_service.dart';
import 'package:aman_kassa_flutter/core/services/navigator_service.dart';
+import 'package:aman_kassa_flutter/redux/constants/auth_type_const.dart';
import 'package:aman_kassa_flutter/redux/state/user_state.dart';
import 'package:redux/redux.dart';
import 'package:meta/meta.dart';
@@ -28,8 +29,14 @@ final DialogService _dialogService = locator();
Future checkUserAction(Store store) async {
store.dispatch(SetUserStateAction(UserState(isLoading: true)));
try {
- Response session = await _api.isActive('test');
- bool isAuthenticated = "OK" == session.body.message;
+ String token = store.state.userState.user?.token;
+ bool isAuthenticated = false;
+ if(token!=null) {
+ Response session = await _api.isActive(token);
+ isAuthenticated = "OK" == session.body.message;
+ } else {
+ await Future.delayed(Duration(milliseconds: 100));
+ }
store.dispatch(
SetUserStateAction(
UserState(
@@ -41,14 +48,59 @@ Future checkUserAction(Store store) async {
if(!isAuthenticated){
_navigation.replace(LoginViewRoute);
+ } else {
+ _navigation.replace(HomeViewRoute);
}
+ } catch (error) {
+ print(error);
+ store.dispatch(SetUserStateAction(UserState(isLoading: false)));
+ }
+}
+
+
+Future logoutAction(Store store) async {
+ try {
+ store.dispatch(
+ SetUserStateAction(
+ UserState(
+ isLoading: false,
+ isAuthenticated: false,
+ user: User(),
+ ),
+ ),
+ );
+ _navigation.replace(LoginViewRoute);
} catch (error) {
store.dispatch(SetUserStateAction(UserState(isLoading: false)));
}
}
+ThunkAction authenticateToken(String token) {
+ return (Store store) async {
+ store.dispatch(SetUserStateAction(UserState(isLoading: true)));
+ try {
+ AuthBody result = await _api.authenticate_token(token, statusCheck: false);
+ store.dispatch(SetUserStateAction(UserState(
+ isLoading: false,
+ loginFormMessage: LoginFormMessage(email: result.email?.join(","), password: result.password?.join(","), message: result.message),
+ user: result.user,
+ authenticateType: AuthenticateTypeQr,
+ isAuthenticated: result.user != null,
+ )));
+ if(result.user == null && result.message!=null){
+ _dialogService.showDialog(title: 'Warning', buttonTitle: 'Ok', description: result.message);
+ }
+ if(result.user!=null) {
+ _navigation.replace(HomeViewRoute);
+ }
+ } catch(e) {
+ print(e);
+ store.dispatch(SetUserStateAction(UserState(isLoading: false)));
+ }
+ };
+}
ThunkAction authenticate(String email, String password) {
return (Store store) async {
@@ -58,7 +110,11 @@ ThunkAction authenticate(String email, String password) {
store.dispatch(SetUserStateAction(UserState(
isLoading: false,
loginFormMessage: LoginFormMessage(email: result.email?.join(","), password: result.password?.join(","), message: result.message),
- user: result.user
+ user: result.user,
+ login: email,
+ password: password,
+ authenticateType: AuthenticateTypeLogin,
+ isAuthenticated: result.user != null,
)));
if(result.user == null && result.message!=null){
_dialogService.showDialog(title: 'Warning', buttonTitle: 'Ok', description: result.message);
diff --git a/lib/redux/constants/auth_type_const.dart b/lib/redux/constants/auth_type_const.dart
new file mode 100644
index 0000000..2227403
--- /dev/null
+++ b/lib/redux/constants/auth_type_const.dart
@@ -0,0 +1,2 @@
+const String AuthenticateTypeQr = 'AuthenticateTypeQr';
+const String AuthenticateTypeLogin = 'AuthenticateTypeLogin';
\ No newline at end of file
diff --git a/lib/redux/state/setting_state.dart b/lib/redux/state/setting_state.dart
index 3443d09..33797bd 100644
--- a/lib/redux/state/setting_state.dart
+++ b/lib/redux/state/setting_state.dart
@@ -8,8 +8,14 @@ class SettingState {
SettingState({this.mode, this.tradeType});
- factory SettingState.initial() => SettingState(mode: SettingModeCalc, tradeType: SettingTradeTypeGood);
+ //read hive
+ factory SettingState.initial(SettingState payload) {
+ return SettingState(
+ mode: payload?.mode ?? SettingModeCalc,
+ tradeType: payload?.tradeType ?? SettingTradeTypeGood);
+ }
+ //write hive
SettingState copyWith({
@required mode,
@required tradeType,
@@ -19,4 +25,17 @@ class SettingState {
tradeType: tradeType ?? this.tradeType,
);
}
+
+ static SettingState fromJson(dynamic json) {
+ return json != null
+ ? SettingState(
+ tradeType: json['tradeType'],
+ mode: json['mode'],
+ )
+ : null;
+ }
+
+ dynamic toJson() {
+ return {"tradeType": tradeType, "mode": mode};
+ }
}
diff --git a/lib/redux/state/user_state.dart b/lib/redux/state/user_state.dart
index eb32786..19401eb 100644
--- a/lib/redux/state/user_state.dart
+++ b/lib/redux/state/user_state.dart
@@ -7,49 +7,85 @@ class UserState {
final bool isError;
final bool isLoading;
final bool isAuthenticated;
+ final String authenticateType;
+ final String login;
+ final String password;
final LoginFormMessage loginFormMessage;
final User user;
final Smena smena;
- UserState({
- this.isError,
- this.isLoading,
- this.isAuthenticated,
- this.user,
- this.loginFormMessage,
- this.smena
- });
+ UserState(
+ {this.isError,
+ this.isLoading,
+ this.isAuthenticated,
+ this.authenticateType,
+ this.login,
+ this.password,
+ this.user,
+ this.loginFormMessage,
+ this.smena});
- factory UserState.initial() => UserState(
- isLoading: false,
- isError: false,
- isAuthenticated: false,
- loginFormMessage: LoginFormMessage(),
- smena: Smena(),
- );
+ factory UserState.initial(UserState payload) => UserState(
+ isLoading: false,
+ isError: false,
+ isAuthenticated: false,
+ loginFormMessage: LoginFormMessage(),
+ smena: Smena(),
+ user: payload?.user ?? User(),
+ authenticateType: payload?.authenticateType ?? null,
+ login: payload?.login ?? null,
+ password: payload?.password ?? null,
+ );
- UserState copyWith({
- @required bool isError,
- @required bool isLoading,
- @required User user,
- @required bool isAuthenticated,
- @required LoginFormMessage loginFormMessage,
- @required Smena smena,
- }) {
+ UserState copyWith(
+ {@required bool isError,
+ @required bool isLoading,
+ @required User user,
+ @required bool isAuthenticated,
+ @required LoginFormMessage loginFormMessage,
+ @required Smena smena,
+ @required String authenticateType,
+ @required String login,
+ @required String password,
+ }) {
return UserState(
- isError: isError ?? this.isError,
- isLoading: isLoading ?? this.isLoading,
- isAuthenticated: isAuthenticated ?? this.isAuthenticated,
- user: user ?? this.user,
- loginFormMessage: loginFormMessage ?? this.loginFormMessage,
- smena: smena ?? this.smena,
+ isError: isError ?? this.isError,
+ isLoading: isLoading ?? this.isLoading,
+ isAuthenticated: isAuthenticated ?? this.isAuthenticated,
+ user: user ?? this.user,
+ loginFormMessage: loginFormMessage ?? this.loginFormMessage,
+ smena: smena ?? this.smena,
+ authenticateType: authenticateType ?? this.authenticateType,
+ login: login ?? this.login,
+ password: password ?? this.password,
);
}
+
+ static UserState fromJson(dynamic json) {
+ return json != null
+ ? UserState(
+ user: User.fromJson(json['user']),
+ authenticateType: json['authenticateType'],
+ login: json['login'],
+ password: json['password']
+ )
+ : null;
+ }
+
+ dynamic toJson() {
+ return {
+ "user": user != null ? user.toJson() : null,
+ "authenticateType": authenticateType,
+ "login": login,
+ "password": password,
+ };
+ }
}
class LoginFormMessage {
final String email;
final String password;
final String message;
+
LoginFormMessage({this.email, this.password, this.message});
-}
\ No newline at end of file
+}
diff --git a/lib/redux/store.dart b/lib/redux/store.dart
index 2b6b906..26c5e8d 100644
--- a/lib/redux/store.dart
+++ b/lib/redux/store.dart
@@ -11,7 +11,10 @@ import 'package:aman_kassa_flutter/redux/state/setting_state.dart';
import 'package:aman_kassa_flutter/redux/state/user_state.dart';
import 'package:meta/meta.dart';
import 'package:redux/redux.dart';
+import 'package:redux_persist_flutter/redux_persist_flutter.dart';
import 'package:redux_thunk/redux_thunk.dart';
+import 'package:redux_persist/redux_persist.dart';
+import 'dart:io';
import 'actions/calc_actions.dart';
@@ -46,10 +49,10 @@ class AppState {
final CalcState calcState;
AppState({
- @required this.userState,
- @required this.kassaState,
- @required this.settingState,
- @required this.calcState,
+ this.userState,
+ this.kassaState,
+ this.settingState,
+ this.calcState,
});
//stable work
@@ -66,6 +69,23 @@ class AppState {
calcState: calcState ?? this.calcState,
);
}
+
+ static AppState fromJson(dynamic json){
+ print(json);
+ return json !=null
+ ? AppState(
+ settingState: SettingState.fromJson(json['settingState']),
+ userState: UserState.fromJson(json['userState']),
+ )
+ : null;
+ }
+
+ dynamic toJson() {
+ return {
+ "settingState": settingState.toJson(),
+ "userState" : userState.toJson(),
+ };
+ }
}
class Redux {
@@ -81,14 +101,22 @@ class Redux {
//initial context
static Future init() async {
- final userStateInitial = UserState.initial();
+ // Create Persistor
+ final persist = Persistor(
+ storage: FlutterStorage(), // Or use other engines
+ serializer: JsonSerializer(AppState.fromJson), // Or use other serializers
+ );
+
+ final initialState = await persist.load();
+
+ final userStateInitial = UserState.initial(initialState?.userState);
final kassaStateInitial = KassaState.initial();
- final settingStateInitial = SettingState.initial();
+ final settingStateInitial = SettingState.initial(initialState?.settingState);
final calcStateInitial = CalcState.initial();
_store = Store(
appReducer,
- middleware: [thunkMiddleware],
+ middleware: [thunkMiddleware, persist.createMiddleware() ],
initialState: AppState(
userState: userStateInitial,
kassaState: kassaStateInitial,
diff --git a/lib/views/check/image_show_container.dart b/lib/views/check/image_show_container.dart
index c51a1eb..ac7dfc4 100644
--- a/lib/views/check/image_show_container.dart
+++ b/lib/views/check/image_show_container.dart
@@ -1,10 +1,16 @@
import 'dart:convert';
import 'package:aman_kassa_flutter/shared/app_colors.dart';
+import 'package:aman_kassa_flutter/shared/ui_helpers.dart';
+import 'package:aman_kassa_flutter/widgets/fields/busy_button.dart';
import 'package:flutter/material.dart';
+import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
+//import 'package:flutter/services.dart';
class ImageShowContainer extends StatelessWidget {
final ImageShowModel data;
+
ImageShowContainer(this.data);
+
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -13,12 +19,12 @@ class ImageShowContainer extends StatelessWidget {
title: Text(data.title),
),
body: ListView(
- children: [
- imageFromBase64String(data.data)
- ],
+ children: [imageFromBase64String(data.data)],
),
+ floatingActionButton: MyFloatingActionButton(),
);
}
+
}
Padding imageFromBase64String(String base64String) {
@@ -28,9 +34,99 @@ Padding imageFromBase64String(String base64String) {
);
}
-
class ImageShowModel {
final String data;
final String title;
+
ImageShowModel(this.data, this.title);
+}
+
+class MyFloatingActionButton extends StatefulWidget {
+ @override
+ _MyFloatingActionButtonState createState() => _MyFloatingActionButtonState();
+}
+
+class _MyFloatingActionButtonState extends State {
+ bool showFab = true;
+// String _batteryLevel = 'Unknown battery level.';
+// static const platform = const MethodChannel('samples.flutter.dev/battery');
+//
+// Future _getBatteryLevel() async {
+// String batteryLevel;
+// try {
+// final int result = await platform.invokeMethod('sendMessage');
+// print(result);
+// batteryLevel = 'Battery level at $result % .';
+// } on PlatformException catch (e) {
+// batteryLevel = "Failed to get battery level: '${e.message}'.";
+// }
+//
+// setState(() {
+// _batteryLevel = batteryLevel;
+// });
+// }
+
+ @override
+ Widget build(BuildContext context) {
+ return showFab
+ ? FloatingActionButton(
+ child: Icon(Icons.share),
+ onPressed: () {
+ var bottomSheetController = showBottomSheet(
+ context: context,
+ builder: (context) => Container(
+ padding: const EdgeInsets.symmetric(horizontal: 10.0 ),
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.all(Radius.circular(15)),
+ boxShadow: [
+ BoxShadow(
+ blurRadius: 10, color: Colors.grey[300], spreadRadius: 5)
+ ]),
+ //color: Colors.grey[900],
+ height: 280,
+ child: Column(
+ children: [
+ verticalSpaceSmall,
+ BusyButton(title: 'Электронная почта', onPressed: () {} , mainColor: primaryColor, icon: Icons.mail, ),
+ verticalSpaceSmall,
+ BusyButton(title: 'WhatsApp', onPressed: () {} , mainColor: greenColor, icon: MdiIcons.whatsapp, ),
+ verticalSpaceSmall,
+ BusyButton(title: '', onPressed: () {} , mainColor: yellowColor, icon: Icons.share,),
+ ],
+ )
+ ));
+ showFoatingActionButton(false);
+ bottomSheetController.closed.then((value) {
+ showFoatingActionButton(true);
+ });
+ },
+ ): Container() ;
+ }
+ void showFoatingActionButton(bool value) {
+ setState(() {
+ showFab = value;
+ });
+ }
+}
+
+
+class DecoratedTextField extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ alignment: Alignment.centerLeft,
+ padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
+ margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
+ decoration: BoxDecoration(
+ //color: fillColor, borderRadius: BorderRadius.circular(10)
+ ),
+ child: TextField(
+ decoration: InputDecoration(
+ prefixIcon: Icon(Icons.mail_outline, color: primaryColor,),
+ labelStyle: TextStyle(color: primaryColor),
+ labelText: 'Отправить на Email',
+ ),
+ ));
+ }
}
\ No newline at end of file
diff --git a/lib/views/home/home_view.dart b/lib/views/home/home_view.dart
index ab2804f..e8411ad 100644
--- a/lib/views/home/home_view.dart
+++ b/lib/views/home/home_view.dart
@@ -1,7 +1,12 @@
import 'package:aman_kassa_flutter/core/locator.dart';
import 'package:aman_kassa_flutter/core/logger.dart';
import 'package:aman_kassa_flutter/core/models/choice.dart';
+import 'package:aman_kassa_flutter/core/models/message.dart';
+import 'package:aman_kassa_flutter/core/models/response.dart';
+import 'package:aman_kassa_flutter/core/route_names.dart';
+import 'package:aman_kassa_flutter/core/services/ApiService.dart';
import 'package:aman_kassa_flutter/core/services/DataService.dart';
+import 'package:aman_kassa_flutter/core/services/navigator_service.dart';
import 'package:aman_kassa_flutter/redux/actions/user_actions.dart';
import 'package:aman_kassa_flutter/redux/constants/setting_const.dart';
import 'package:aman_kassa_flutter/redux/state/setting_state.dart';
@@ -40,6 +45,8 @@ class _HomeViewState extends State {
PageController pageController;
int selectedTabIndex;
DataService _dataService = locator();
+ ApiService _api = locator();
+ NavigatorService _navigatorService = locator();
final GlobalKey _keyLoader = new GlobalKey();
@override
@@ -58,7 +65,12 @@ class _HomeViewState extends State {
void _onSelectChoice(Choice choice) async {
if (choice.command == 'exit') {
-
+ Dialogs.showLoadingDialog(context, _keyLoader);
+ Response result = await _api.logout(Redux.store.state.userState.user.token);
+ if(result.operation && result.status == 200) {
+ Redux.store.dispatch(logoutAction);
+ }
+ Navigator.of(_keyLoader.currentContext, rootNavigator: true).pop();
} else if (choice.command == 'update') {
Dialogs.showLoadingDialog(context, _keyLoader);
bool result = await _dataService.getDataFromServer(Redux.store.state.userState.user);
diff --git a/lib/views/home/tabs/CalculatorTab.dart b/lib/views/home/tabs/CalculatorTab.dart
index 4e071ba..0be063f 100644
--- a/lib/views/home/tabs/CalculatorTab.dart
+++ b/lib/views/home/tabs/CalculatorTab.dart
@@ -40,12 +40,11 @@ class CalculatorTab extends StatelessWidget {
Row(
children: [
Expanded(
-
child: RaisedButton(
- padding: EdgeInsets.all(10.0),
+ padding: EdgeInsets.all(8.0),
color: redColor,
child: Text(
- "Возврат",
+ "возврат",
style: buttonBigTitleTextStyle,
),
onPressed: () {
@@ -55,11 +54,11 @@ class CalculatorTab extends StatelessWidget {
),
Expanded(
child: RaisedButton(
- padding: EdgeInsets.all(10.0),
+ padding: EdgeInsets.all(8.0),
color: greenColor,
child: Text(
- "Оплата",
+ "оплата",
style: buttonBigTitleTextStyle,
),
onPressed: () {
diff --git a/lib/views/home/tabs/KassaTab.dart b/lib/views/home/tabs/KassaTab.dart
index 8f31f8f..62cd58a 100644
--- a/lib/views/home/tabs/KassaTab.dart
+++ b/lib/views/home/tabs/KassaTab.dart
@@ -107,7 +107,7 @@ class KassaTab extends StatelessWidget {
padding: EdgeInsets.all(8),
color: redColor,
child: Text(
- "Возврат",
+ "возврат",
style: buttonBigTitleTextStyle,
),
onPressed: () {
@@ -123,7 +123,7 @@ class KassaTab extends StatelessWidget {
padding: EdgeInsets.all(8),
color: greenColor,
child: Text(
- "Оплата",
+ "оплата",
style: buttonBigTitleTextStyle,
),
onPressed: () {
diff --git a/lib/views/login/login_view.dart b/lib/views/login/login_view.dart
index 3bf0345..8c14fe8 100644
--- a/lib/views/login/login_view.dart
+++ b/lib/views/login/login_view.dart
@@ -1,3 +1,7 @@
+import 'dart:ui';
+
+import 'package:aman_kassa_flutter/core/locator.dart';
+import 'package:aman_kassa_flutter/core/services/dialog_service.dart';
import 'package:aman_kassa_flutter/redux/actions/user_actions.dart';
import 'package:aman_kassa_flutter/redux/state/user_state.dart';
import 'package:aman_kassa_flutter/redux/store.dart';
@@ -5,9 +9,14 @@ import 'package:aman_kassa_flutter/shared/app_colors.dart';
import 'package:aman_kassa_flutter/shared/ui_helpers.dart';
import 'package:aman_kassa_flutter/widgets/fields/busy_button.dart';
import 'package:aman_kassa_flutter/widgets/fields/input_field.dart';
-import 'package:aman_kassa_flutter/widgets/fields/text_link.dart';
+import 'package:barcode_scan/gen/protos/protos.pb.dart';
+import 'package:barcode_scan/gen/protos/protos.pbenum.dart';
+import 'package:barcode_scan/model/scan_options.dart';
+import 'package:barcode_scan/platform_wrapper.dart';
+import 'package:flutter/services.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:flutter/material.dart';
+import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
class LoginView extends StatelessWidget {
final emailController = TextEditingController(text: 'test@kkm-kassa.kz');
@@ -15,10 +24,7 @@ class LoginView extends StatelessWidget {
final FocusNode passwordNode = new FocusNode();
final GlobalKey _scaffoldKey = new GlobalKey();
-
- _pressBtnEnter() async {
- Redux.store.dispatch(authenticate(emailController.text, passwordController.text));
- }
+ final DialogService _dialogService = locator();
@override
Widget build(BuildContext context) {
@@ -35,11 +41,17 @@ class LoginView extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
- SizedBox(
- height: 150,
- child: Image.asset('assets/images/logo.png'),
- //child: FlutterLogo(size: 120,),
+ Stack(
+ alignment: Alignment.bottomLeft,
+ children: [
+ SizedBox(
+ height: 150,
+ child: Image.asset('assets/images/logo.png'),
+ ),
+ Positioned(child: Text('онлайн касса', style: TextStyle(fontWeight: FontWeight.bold),), bottom: 23.0,left: 25.0,),
+ ],
),
+
InputField(
placeholder: 'Электронная почта',
controller: emailController,
@@ -68,9 +80,15 @@ class LoginView extends StatelessWidget {
],
),
verticalSpaceLarge,
- TextLink(
- 'Регистрация',
- onPressed: () {},
+// TextLink(
+// 'Регистрация',
+// onPressed: () {},
+// ),
+ IconButton(
+ icon: Icon(MdiIcons.qrcodeScan),
+ iconSize: 40,
+ tooltip: "Scan",
+ onPressed: scan,
)
],
),
@@ -78,5 +96,42 @@ class LoginView extends StatelessWidget {
});
}
+ _pressBtnEnter() async {
+ Redux.store
+ .dispatch(authenticate(emailController.text, passwordController.text));
+ }
+ Future scan() async {
+ try {
+ var options = ScanOptions(strings: {
+ "cancel": 'Отмена',
+ "flash_on": 'Вкл фонарик',
+ "flash_off": 'Выкл фонарик',
+ });
+ var result = await BarcodeScanner.scan(options: options);
+ print(result.type); // The result type (barcode, cancelled, failed)
+ print(result.rawContent); // The barcode content
+ print(result.format); // The barcode format (as enum)
+ print(result
+ .formatNote); // If a unknown format was scanned this field contains a note
+ if (result.type == ResultType.Barcode &&
+ result.rawContent?.length == 60) {
+ Redux.store.dispatch(authenticateToken(result.rawContent));
+ } else {
+ _dialogService.showDialog(description: 'Не верный формат QR кода');
+ }
+ } on PlatformException catch (e) {
+ var result = ScanResult.create();
+ result.type = ResultType.Error;
+ result.format = BarcodeFormat.unknown;
+ if (e.code == BarcodeScanner.cameraAccessDenied) {
+ result.rawContent = 'The user did not grant the camera permission!';
+ _dialogService.showDialog(
+ description: 'Нет доступа до камеры устройства');
+ } else {
+ result.rawContent = 'Unknown error: $e';
+ _dialogService.showDialog(description: 'Неизвестная ошибка: $e');
+ }
+ }
+ }
}
diff --git a/lib/views/payment/payment_view.dart b/lib/views/payment/payment_view.dart
index b5dd9fb..137ad3b 100644
--- a/lib/views/payment/payment_view.dart
+++ b/lib/views/payment/payment_view.dart
@@ -6,6 +6,8 @@ import 'package:aman_kassa_flutter/core/route_names.dart';
import 'package:aman_kassa_flutter/core/services/DataService.dart';
import 'package:aman_kassa_flutter/core/services/dialog_service.dart';
import 'package:aman_kassa_flutter/core/services/navigator_service.dart';
+import 'package:aman_kassa_flutter/redux/actions/calc_actions.dart';
+import 'package:aman_kassa_flutter/redux/actions/kassa_actions.dart';
import 'package:aman_kassa_flutter/redux/constants/operation_const.dart';
import 'package:aman_kassa_flutter/redux/constants/setting_const.dart';
import 'package:aman_kassa_flutter/redux/state/calc_state.dart';
@@ -82,7 +84,7 @@ class _PaymentViewState extends State {
if(widget.model.mode == SettingModeCalc) {
return StoreConnector(
converter: (store) => store.state.calcState,
- builder: (context, vm) {
+ builder: (_, vm) {
return Text('${totalCalc(vm.calcItems)} тнг', style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black87,
@@ -92,7 +94,7 @@ class _PaymentViewState extends State {
}
return StoreConnector(
converter: (store) => store.state.kassaState,
- builder: (context, vm) {
+ builder: (_, vm) {
return Text('${totalKassa(vm.kassaItems)} тнг', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.black87, fontSize: 35 ));
}
);
@@ -128,13 +130,24 @@ class _PaymentViewState extends State {
AppState _state = Redux.store.state;
String _token = _state.userState.user.token;
String _tradeType = _state.settingState.tradeType;
- List items = _state.kassaState.kassaItems;
- Response response = await _dataService.sellOrReturn(token: _token, items: items, paymentType: type, operationType: widget.model.operationType, tradeType: _tradeType );
+ String _mode = _state.settingState.mode;
+ if(_mode == SettingModeCalc){
+ _tradeType = SettingTradeTypeGood;
+ }
+ List kassaItems = _state.kassaState.kassaItems;
+ List calcItems = _state.calcState.calcItems;
+ Response response = await _dataService.sellOrReturn(token: _token, kassaItems: kassaItems, paymentType: type, operationType: widget.model.operationType, tradeType: _tradeType, calcItems: calcItems, mode: _mode );
Navigator.of(context, rootNavigator: true).pop();
setState(() { isBusy = false; });
if(response.operation){
String message = response.body['message'];
String check = response.body['check'];
+
+// if(_mode == SettingModeCalc){
+// Redux.store.dispatch(cleanCalcItems);
+// } else if(_mode == SettingModeKassa) {
+// Redux.store.dispatch(cleanKassaItems);
+// }
_navigatorService.pop();
_navigatorService.push(ImageShowRoute, arguments: ImageShowModel(check, message));
} else if(!response.operation && response.status !=500) {
diff --git a/lib/widgets/fields/busy_button.dart b/lib/widgets/fields/busy_button.dart
index c891e18..d5328d8 100644
--- a/lib/widgets/fields/busy_button.dart
+++ b/lib/widgets/fields/busy_button.dart
@@ -1,5 +1,6 @@
import 'package:aman_kassa_flutter/shared/app_colors.dart';
import 'package:aman_kassa_flutter/shared/shared_styles.dart';
+import 'package:aman_kassa_flutter/shared/ui_helpers.dart';
import 'package:flutter/material.dart';
/// A button that shows a busy indicator in place of title
@@ -9,13 +10,15 @@ class BusyButton extends StatefulWidget {
final Function onPressed;
final bool enabled;
final Color mainColor;
+ final IconData icon;
const BusyButton(
{
@required this.title,
this.busy = false,
@required this.onPressed,
this.enabled = true,
- this.mainColor
+ this.mainColor,
+ this.icon
});
@override
@@ -46,14 +49,20 @@ class _BusyButtonState extends State {
margin: EdgeInsets.symmetric(
horizontal: widget.busy ? 10 : 25,
vertical: widget.busy ? 10 : 15),
- child: !widget.busy
- ? Text(
- widget.title,
- style: buttonTitleTextStyle,
- )
- : CircularProgressIndicator(
- strokeWidth: 2,
- valueColor: AlwaysStoppedAnimation(Colors.white)),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ widget.icon!=null ? Container(child: (Icon(widget.icon, color: whiteColor,)), margin: const EdgeInsets.only(right: 10.0 ),) : (Container()),
+ !widget.busy
+ ? Text(
+ widget.title,
+ style: buttonTitleTextStyle,
+ )
+ : CircularProgressIndicator(
+ strokeWidth: 2,
+ valueColor: AlwaysStoppedAnimation(Colors.white)),
+ ],
+ ),
),
),
),
diff --git a/pubspec.lock b/pubspec.lock
index 883b5ca..a4b9a29 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -22,6 +22,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.1"
+ barcode_scan:
+ dependency: "direct main"
+ description:
+ name: barcode_scan
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.0.1"
boolean_selector:
dependency: transitive
description:
@@ -64,6 +71,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
+ device_info:
+ dependency: "direct main"
+ description:
+ name: device_info
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.4.2+4"
equatable:
dependency: "direct main"
description:
@@ -71,18 +85,25 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
+ esys_flutter_share:
+ dependency: "direct main"
+ description:
+ name: esys_flutter_share
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.2"
+ fixnum:
+ dependency: transitive
+ description:
+ name: fixnum
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.10.11"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
- flutter_boom_menu:
- dependency: "direct main"
- description:
- name: flutter_boom_menu
- url: "https://pub.dartlang.org"
- source: hosted
- version: "1.0.2"
flutter_redux:
dependency: "direct main"
description:
@@ -95,6 +116,11 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
+ flutter_web_plugins:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
get_it:
dependency: "direct main"
description:
@@ -172,13 +198,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.4"
- observable_ish:
- dependency: transitive
- description:
- name: observable_ish
- url: "https://pub.dartlang.org"
- source: hosted
- version: "2.1.4"
path:
dependency: transitive
description:
@@ -235,6 +254,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
+ protobuf:
+ dependency: transitive
+ description:
+ name: protobuf
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.1"
provider:
dependency: "direct main"
description:
@@ -242,13 +268,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.1.2"
- provider_architecture:
- dependency: "direct main"
- description:
- name: provider_architecture
- url: "https://pub.dartlang.org"
- source: hosted
- version: "1.1.1+1"
quiver:
dependency: transitive
description:
@@ -263,6 +282,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
+ redux_persist:
+ dependency: "direct main"
+ description:
+ name: redux_persist
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.8.4"
+ redux_persist_flutter:
+ dependency: "direct main"
+ description:
+ name: redux_persist_flutter
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.8.2"
redux_thunk:
dependency: "direct main"
description:
@@ -277,18 +310,39 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.9"
+ shared_preferences:
+ dependency: transitive
+ description:
+ name: shared_preferences
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.5.7+3"
+ shared_preferences_macos:
+ dependency: transitive
+ description:
+ name: shared_preferences_macos
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.0.1+10"
+ shared_preferences_platform_interface:
+ dependency: transitive
+ description:
+ name: shared_preferences_platform_interface
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.4"
+ shared_preferences_web:
+ dependency: transitive
+ description:
+ name: shared_preferences_web
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.1.2+7"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
- sliding_up_panel:
- dependency: "direct main"
- description:
- name: sliding_up_panel
- url: "https://pub.dartlang.org"
- source: hosted
- version: "1.0.2"
source_span:
dependency: transitive
description:
@@ -317,13 +371,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.9.3"
- stacked:
- dependency: "direct main"
- description:
- name: stacked
- url: "https://pub.dartlang.org"
- source: hosted
- version: "1.5.2"
stream_channel:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 889e10f..92d2438 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -10,8 +10,8 @@ dependencies:
redux: ^4.0.0
flutter_redux: ^0.6.0
redux_thunk: ^0.3.0
- stacked : ^1.5.2
- provider_architecture: ^1.0.3
+ redux_persist: ^0.8.4
+ redux_persist_flutter: ^0.8.2
responsive_builder: ^0.1.4
provider: ^4.1.2
logger: ^0.9.1
@@ -21,10 +21,11 @@ dependencies:
sqflite: ^1.3.0
path_provider: ^1.6.9
google_fonts: ^1.1.0
- flutter_boom_menu: ^1.0.2
- sliding_up_panel: ^1.0.2
material_design_icons_flutter: ^4.0.5345
intl: ^0.16.1
+ barcode_scan: ^3.0.1
+ device_info: ^0.4.2+4
+ esys_flutter_share: ^1.0.2
dev_dependencies:
flutter_test:
sdk: flutter