557 lines
17 KiB
Dart
557 lines
17 KiB
Dart
import 'dart:convert';
|
||
|
||
import 'package:aman_kassa_flutter/core/entity/Voucher.dart';
|
||
import 'package:aman_kassa_flutter/core/locator.dart';
|
||
import 'package:aman_kassa_flutter/core/logger.dart';
|
||
import 'package:aman_kassa_flutter/core/models/aman_dao.dart';
|
||
import 'package:aman_kassa_flutter/core/models/calc_model.dart';
|
||
import 'package:aman_kassa_flutter/core/models/card_data.dart';
|
||
import 'package:aman_kassa_flutter/core/models/check_data.dart';
|
||
import 'package:aman_kassa_flutter/core/models/product_dao.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/BankService.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/actions/user_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/bank_state.dart';
|
||
import 'package:aman_kassa_flutter/redux/state/calc_state.dart';
|
||
import 'package:aman_kassa_flutter/redux/state/kassa_state.dart';
|
||
import 'package:aman_kassa_flutter/redux/store.dart';
|
||
import 'package:aman_kassa_flutter/shared/app_colors.dart';
|
||
import 'package:aman_kassa_flutter/views/check/image_show_container.dart';
|
||
import 'package:aman_kassa_flutter/views/payment/payment_view.dart';
|
||
import 'package:aman_kassa_flutter/views/payment_nfc/widgets/action_view.dart';
|
||
import 'package:aman_kassa_flutter/views/payment_nfc/widgets/background_view.dart';
|
||
import 'package:aman_kassa_flutter/views/payment_nfc/widgets/card_view.dart';
|
||
import 'package:aman_kassa_flutter/views/payment_nfc/widgets/phone_view.dart';
|
||
import 'package:aman_kassa_flutter/views/payment_nfc/widgets/text_state.dart';
|
||
import 'package:aman_kassa_flutter/widgets/components/calculator/calculator.dart';
|
||
import 'package:aman_kassa_flutter/widgets/loader/Dialogs.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter/services.dart';
|
||
import 'package:flutter_redux/flutter_redux.dart';
|
||
import 'package:flutter_screenutil/screenutil.dart';
|
||
import 'package:logger/logger.dart';
|
||
|
||
class PaymentNfcView extends StatefulWidget {
|
||
final PaymentModel model;
|
||
const PaymentNfcView({Key key, this.model}) : super(key: key);
|
||
|
||
@override
|
||
_PaymentNfcViewState createState() => _PaymentNfcViewState();
|
||
}
|
||
|
||
class _PaymentNfcViewState extends State<PaymentNfcView> {
|
||
final GlobalKey<State> _keyLoader = new GlobalKey<State>();
|
||
BankService _bankService = locator<BankService>();
|
||
DialogService _dialogService = locator<DialogService>();
|
||
final DataService _dataService = locator<DataService>();
|
||
final NavigatorService _navigatorService = locator<NavigatorService>();
|
||
Logger log = getLogger('PaymentNfcView');
|
||
bool isBusy;
|
||
bool isPhoneScaled;
|
||
int status;
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
isBusy = false;
|
||
isPhoneScaled = false;
|
||
status = 0;
|
||
start();
|
||
}
|
||
|
||
void start() async {
|
||
|
||
|
||
//права доступа
|
||
bool success = await _bankService.permissions();
|
||
log.i(success);
|
||
if (!success) {
|
||
setState(() {
|
||
status = 4;
|
||
});
|
||
return;
|
||
}
|
||
|
||
|
||
var today = new DateTime.now();
|
||
var yesterday = today.subtract(new Duration(days: 1));
|
||
if( Redux.store.state.userState == null
|
||
|| Redux.store.state.userState.smena == null
|
||
|| Redux.store.state.userState.smena.startedAt == null
|
||
|| yesterday.isAfter(Redux.store.state.userState.smena.startedAt)) {
|
||
_dialogService.showDialog(description: 'Текущая смена открыта более 24 ч. Необходимо закрыть смену и открыть ее заново.');
|
||
_navigatorService.pop();
|
||
return;
|
||
}
|
||
|
||
//Инициализация
|
||
bool initialized = await _bankService.init();
|
||
log.i(initialized);
|
||
if (!initialized) {
|
||
setState(() {
|
||
status = 4;
|
||
});
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
//Проверка связи
|
||
bool connected = await _bankService.connect();
|
||
log.i(connected);
|
||
if (!connected) {
|
||
setState(() {
|
||
status = 5;
|
||
});
|
||
return;
|
||
}
|
||
//Авторизация
|
||
BankState bankState = Redux.store.state.bankState;
|
||
AmanDao authDao = await _bankService.auth(
|
||
login: bankState.login, password: bankState.password);
|
||
if (!authDao.success) {
|
||
setState(() {
|
||
status = 6;
|
||
});
|
||
|
||
if (authDao.msg != null) {
|
||
log.i(authDao.msg);
|
||
_dialogService.showDialog(description: authDao.msg);
|
||
}
|
||
return;
|
||
}
|
||
|
||
//валюта
|
||
bool currency = await _bankService.currency();
|
||
log.i(currency);
|
||
if (!currency) {
|
||
setState(() {
|
||
status = 4;
|
||
});
|
||
return;
|
||
}
|
||
|
||
if(widget.model.voucher == null) {
|
||
pay();
|
||
} else {
|
||
refund();
|
||
}
|
||
}
|
||
|
||
refund() async {
|
||
CardData _cardData = widget.model.cardData;
|
||
AmanDao findTransaction = await _bankService.findTransaction(transactionNumber: _cardData.transactionNumber, authorizationCode: _cardData.authorizationCode);
|
||
if(!findTransaction.success){
|
||
_dialogService.showDialog(description: findTransaction.msg);
|
||
setState(() {
|
||
status = 8;
|
||
});
|
||
return;
|
||
}
|
||
|
||
setState(() {
|
||
status = 1;
|
||
isPhoneScaled = true;
|
||
});
|
||
|
||
AmanDao refundDao = await _bankService.refund();
|
||
if (!refundDao.success) {
|
||
int _status = 7;
|
||
|
||
setState(() {
|
||
status = _status;
|
||
isPhoneScaled = false;
|
||
});
|
||
|
||
if (refundDao.msg != null) {
|
||
log.i(refundDao.msg);
|
||
_dialogService.showDialog(description: refundDao.msg);
|
||
}
|
||
return;
|
||
}
|
||
|
||
setState(() {
|
||
status = 9;
|
||
isPhoneScaled = false;
|
||
});
|
||
|
||
//check
|
||
pressRefund('card' , refundDao.data);
|
||
|
||
}
|
||
|
||
pay() async {
|
||
//Платеж
|
||
num total = 0.0;
|
||
if (widget.model.mode == SettingModeCalc) {
|
||
total = totalCalc(Redux.store.state.calcState.calcItems);
|
||
} else {
|
||
total = totalKassa(Redux.store.state.kassaState.kassaItems);
|
||
}
|
||
|
||
setState(() {
|
||
status = 1;
|
||
isPhoneScaled = true;
|
||
});
|
||
|
||
log.i('total: $total');
|
||
AmanDao payDao = await _bankService.pay(amount: total);
|
||
if (!payDao.success) {
|
||
int _status = 7;
|
||
if (payDao.data != null ) {
|
||
if("onWrongApiCalled" == payDao.data.toString()) {
|
||
cancel();
|
||
} else if("notAuthorized" == payDao.data.toString() ) {
|
||
cancel();
|
||
_status = 6;
|
||
}
|
||
}
|
||
|
||
setState(() {
|
||
status = _status;
|
||
isPhoneScaled = false;
|
||
});
|
||
|
||
if (payDao.msg != null) {
|
||
log.i(payDao.msg);
|
||
_dialogService.showDialog(description: payDao.msg);
|
||
}
|
||
return;
|
||
}
|
||
|
||
setState(() {
|
||
status = 3;
|
||
isPhoneScaled = false;
|
||
});
|
||
|
||
//check
|
||
pressPayment('card' , payDao.data);
|
||
}
|
||
|
||
cancel() async {
|
||
bool isCanceled = await _bankService.cancel();
|
||
_navigatorService.pop();
|
||
}
|
||
|
||
pressPayment(String type, dynamic cardDataDynamic) async {
|
||
setState(() {
|
||
isBusy = true;
|
||
});
|
||
Dialogs.showLoadingDialog(context, _keyLoader);
|
||
try {
|
||
AppState _state = Redux.store.state;
|
||
String _token = _state.userState.user.token;
|
||
String _tradeType = _state.settingState.tradeType;
|
||
String _mode = _state.settingState.mode;
|
||
if (_mode == SettingModeCalc) {
|
||
_tradeType = SettingTradeTypeGood;
|
||
}
|
||
CardData cardData = cardDataDynamic != null ? CardData.fromJson(cardDataDynamic) : null;
|
||
|
||
List<ProductDao> kassaItems = _state.kassaState.kassaItems;
|
||
List<CalcModel> calcItems = _state.calcState.calcItems;
|
||
Response<dynamic> response = await _dataService.sellOrReturn(
|
||
token: _token,
|
||
kassaItems: kassaItems,
|
||
paymentType: type,
|
||
operationType: widget.model.operationType,
|
||
tradeType: _tradeType,
|
||
calcItems: calcItems,
|
||
mode: _mode,
|
||
cardData: cardData
|
||
);
|
||
setState(() {
|
||
isBusy = false;
|
||
});
|
||
if (response != null) {
|
||
if (response.operation) {
|
||
String message = response.body['message'];
|
||
String check = response.body['check'];
|
||
String url = response?.body['link'];
|
||
print('url : $url');
|
||
if (_mode == SettingModeCalc) {
|
||
Redux.store.dispatch(cleanCalcItems);
|
||
} else if (_mode == SettingModeKassa) {
|
||
Redux.store.dispatch(cleanKassaItems);
|
||
}
|
||
Redux.store.dispatch(checkMoney);
|
||
Redux.store.dispatch(openSmenaPseudo);
|
||
Navigator.of(_keyLoader.currentContext, rootNavigator: true).pop();
|
||
_navigatorService.replace(HomeViewRoute);
|
||
_navigatorService.push(ImageShowRoute,
|
||
arguments: ImageShowModel(data: check, title: message, url: url));
|
||
} else if (!response.operation &&
|
||
![401, 402, 403, 412, 500].contains(response.status)) {
|
||
Navigator.of(_keyLoader.currentContext, rootNavigator: true).pop();
|
||
_dialogService.showDialog(description: response.body['message']);
|
||
} else {
|
||
Navigator.of(_keyLoader.currentContext, rootNavigator: true).pop();
|
||
}
|
||
} else {
|
||
Navigator.of(_keyLoader.currentContext, rootNavigator: true).pop();
|
||
}
|
||
} catch (e) {
|
||
print(e);
|
||
Navigator.of(_keyLoader.currentContext, rootNavigator: true).pop();
|
||
} finally {
|
||
//Navigator.of(context, rootNavigator: true).pop();
|
||
setState(() {
|
||
isBusy = false;
|
||
});
|
||
}
|
||
}
|
||
|
||
pressRefund(String type, dynamic cardDataDynamic) async {
|
||
setState(() {
|
||
isBusy = true;
|
||
});
|
||
Dialogs.showLoadingDialog(context, _keyLoader);
|
||
try {
|
||
AppState _state = Redux.store.state;
|
||
String _token = _state.userState.user.token;
|
||
CardData _cardData = cardDataDynamic != null ? CardData.fromJson(cardDataDynamic) : null;
|
||
CheckData _checkData = CheckData.fromJson(json.decode(widget.model.voucher.data));
|
||
Response<dynamic> response = await _dataService.refundM4Bank(
|
||
token: _token,
|
||
cardData: _cardData,
|
||
checkData: _checkData
|
||
);
|
||
setState(() {
|
||
isBusy = false;
|
||
});
|
||
if (response != null) {
|
||
if (response.operation) {
|
||
String message = response.body['message'];
|
||
String check = response.body['check'];
|
||
String url = response?.body['link'];
|
||
print('url : $url');
|
||
Redux.store.dispatch(checkMoney);
|
||
Redux.store.dispatch(openSmenaPseudo);
|
||
Navigator.of(_keyLoader.currentContext, rootNavigator: true).pop();
|
||
_navigatorService.replace(HomeViewRoute);
|
||
_navigatorService.push(ImageShowRoute,
|
||
arguments: ImageShowModel(data: check, title: message, url: url));
|
||
} else if (!response.operation &&
|
||
![401, 402, 403, 412, 500].contains(response.status)) {
|
||
Navigator.of(_keyLoader.currentContext, rootNavigator: true).pop();
|
||
_dialogService.showDialog(description: response.body['message']);
|
||
} else {
|
||
Navigator.of(_keyLoader.currentContext, rootNavigator: true).pop();
|
||
}
|
||
} else {
|
||
Navigator.of(_keyLoader.currentContext, rootNavigator: true).pop();
|
||
}
|
||
} catch (e) {
|
||
print(e);
|
||
Navigator.of(_keyLoader.currentContext, rootNavigator: true).pop();
|
||
} finally {
|
||
//Navigator.of(context, rootNavigator: true).pop();
|
||
setState(() {
|
||
isBusy = false;
|
||
});
|
||
}
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
_bankService.shutdown();
|
||
super.dispose();
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return WillPopScope(
|
||
onWillPop: () {
|
||
if (!isBusy) Navigator.pop(context);
|
||
return new Future(() => false);
|
||
},
|
||
child: Scaffold(
|
||
appBar: AppBar(
|
||
brightness: Brightness.light,
|
||
backgroundColor: purpleColor,
|
||
elevation: 0,
|
||
leading: IconButton(
|
||
icon: Icon(Icons.arrow_back_ios),
|
||
color: whiteColor,
|
||
onPressed: () => Navigator.pop(context),
|
||
),
|
||
title: Text(
|
||
dataTitle(),
|
||
style: TextStyle(color: whiteColor),
|
||
),
|
||
),
|
||
body: Container(
|
||
decoration: BoxDecoration(color: purpleColor),
|
||
padding: EdgeInsets.symmetric(
|
||
vertical: ScreenUtil().setSp(12.0),
|
||
horizontal: ScreenUtil().setSp(12.0)),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: <Widget>[
|
||
Text(
|
||
dataText(),
|
||
style: TextStyle(
|
||
fontWeight: FontWeight.bold,
|
||
color: whiteColor.withOpacity(0.7),
|
||
fontSize: ScreenUtil().setSp(15.0)),
|
||
),
|
||
buildStoreConnector(),
|
||
Expanded(
|
||
child: Stack(
|
||
children: <Widget>[
|
||
BackgroundView(),
|
||
TextStateView(
|
||
status: status,
|
||
),
|
||
CardView(
|
||
show: isPhoneScaled,
|
||
),
|
||
PhoneView(
|
||
scaled: isPhoneScaled,
|
||
status: status,
|
||
),
|
||
buildActionView()
|
||
],
|
||
),
|
||
)
|
||
],
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
ActionView buildActionView() {
|
||
switch (status) {
|
||
case 5:
|
||
return ActionView(
|
||
show: true,
|
||
acceptText: 'Повторить',
|
||
acceptCallback: () async {
|
||
await _bankService.shutdown();
|
||
start();
|
||
},
|
||
declineText: 'Отмена',
|
||
declineCallback: () {
|
||
cancel();
|
||
},
|
||
);
|
||
break;
|
||
case 6:
|
||
return ActionView(
|
||
show: true,
|
||
acceptText: 'Повторить',
|
||
acceptCallback: () async {
|
||
await _bankService.shutdown();
|
||
start();
|
||
},
|
||
declineText: 'Отмена',
|
||
declineCallback: () {
|
||
cancel();
|
||
},
|
||
);
|
||
break;
|
||
case 7:
|
||
return ActionView(
|
||
show: true,
|
||
acceptText: 'Повторить',
|
||
acceptCallback: () {
|
||
pay();
|
||
},
|
||
declineText: 'Отмена',
|
||
declineCallback: () {
|
||
cancel();
|
||
},
|
||
);
|
||
break;
|
||
case 8:
|
||
return ActionView(
|
||
show: true,
|
||
acceptText: 'Повторить',
|
||
acceptCallback: () {
|
||
refund();
|
||
},
|
||
declineText: 'Отмена',
|
||
declineCallback: () {
|
||
cancel();
|
||
},
|
||
);
|
||
break;
|
||
default:
|
||
}
|
||
|
||
return ActionView();
|
||
}
|
||
|
||
String dataTitle() =>
|
||
widget.model.operationType == OperationTypePay ? 'Оплата' : 'Возврат';
|
||
|
||
String dataText() => widget.model.operationType == OperationTypePay
|
||
? 'К оплате'
|
||
: 'К возврату';
|
||
|
||
StoreConnector buildStoreConnector() {
|
||
if (widget.model.mode == SettingModeCalc) {
|
||
return StoreConnector<AppState, CalcState>(
|
||
converter: (store) => store.state.calcState,
|
||
builder: (_, vm) {
|
||
return Text('${totalCalc(vm.calcItems)} тнг',
|
||
style: TextStyle(
|
||
fontWeight: FontWeight.bold,
|
||
color: whiteColor,
|
||
fontSize: 35));
|
||
});
|
||
}
|
||
if(widget.model.voucher !=null) {
|
||
return StoreConnector<AppState, KassaState>(
|
||
converter: (store) => store.state.kassaState,
|
||
builder: (_, vm) {
|
||
return Text('${widget.model.voucher.total} тнг',
|
||
style: TextStyle(
|
||
fontWeight: FontWeight.bold,
|
||
color: whiteColor,
|
||
fontSize: 35));
|
||
});
|
||
}
|
||
return StoreConnector<AppState, KassaState>(
|
||
converter: (store) => store.state.kassaState,
|
||
builder: (_, vm) {
|
||
return Text('${totalKassa(vm.kassaItems)} тнг',
|
||
style: TextStyle(
|
||
fontWeight: FontWeight.bold,
|
||
color: whiteColor,
|
||
fontSize: 35));
|
||
});
|
||
}
|
||
|
||
num totalKassa(List<ProductDao> kassaItems) {
|
||
num total = 0.0;
|
||
kassaItems.forEach((element) {
|
||
total += element.total == null ? 0.0 : element.total.toDouble();
|
||
});
|
||
return total;
|
||
}
|
||
|
||
num totalCalc(List<CalcModel> items) {
|
||
num total = 0.0;
|
||
items.forEach((element) {
|
||
if (element.operation == Calculations.MULTIPLY) {
|
||
double num1 = element.num1 == null ? 0.0 : double.parse(element.num1);
|
||
double num2 = element.num2 == null ? 0.0 : double.parse(element.num2);
|
||
total += num1 * num2;
|
||
} else {
|
||
total += element.num1 == null ? 0.0 : double.parse(element.num1);
|
||
}
|
||
});
|
||
return total;
|
||
}
|
||
}
|