From b5f9291116135d6a39556529ceeb3c559bb68ab0 Mon Sep 17 00:00:00 2001 From: suvaysov Date: Mon, 21 Nov 2022 13:18:07 +0600 Subject: [PATCH] stocks --- .../models/but_item/buy_item_response.dart | 6 + .../models/but_item/buy_item_response.g.dart | 6 + .../buy_invoice/buy_invoice_response.dart | 2 + .../buy_invoice/buy_invoice_response.g.dart | 2 + lib/core/models/stock/stock_response.dart | 28 +++ lib/core/models/stock/stock_response.g.dart | 29 +++ lib/core/redux/actions/user_actions.dart | 9 +- lib/core/redux/reducers/user_reducer.dart | 1 + lib/core/redux/state/user_state.dart | 41 ++-- lib/core/services/api_service.dart | 25 ++- lib/core/services/buy_service.dart | 47 ++++- lib/core/services/dictionary_service.dart | 49 +++-- lib/core/services/sell_service.dart | 7 +- lib/core/services/stocks_service.dart | 36 ++++ lib/core/utils/locator.dart | 3 + lib/routes/route_names.dart | 1 + lib/routes/router.dart | 16 +- lib/views/dictionaries/goods/goods_view.dart | 4 +- .../view/inventarization_edit_view.dart | 29 ++- .../good_inventarization_list_item.dart | 6 +- lib/views/main/main_view.dart | 5 + lib/views/stocks/stocks_view.dart | 179 ++++++++++++++++++ lib/views/stocks/widget/stock_tile.dart | 72 +++++++ lib/views/work/tabs/buy/buy_add.dart | 99 ++++++++++ lib/views/work/tabs/buy/buy_edit.dart | 81 ++++++-- lib/views/work/tabs/buy/buy_view.dart | 51 +++-- .../tabs/buy/component/product_buy_tile.dart | 5 - .../tabs/component/contagent_select_bar.dart | 15 +- .../sell/component/product_sell_tile.dart | 5 +- lib/views/work/tabs/sell/sell_view.dart | 22 ++- .../contragent/select_contragent_view.dart | 100 ---------- .../add_by_barcode_view.dart | 8 +- .../companent/contragent_list_item.dart | 0 .../select_contragent_view.dart | 126 ++++++++++++ .../add_product_view.dart | 14 +- .../component/add_category_list_item.dart | 0 .../component/add_product_list_item.dart | 0 lib/widgets/drawer/app_drawer.dart | 6 +- lib/widgets/fields/line_tile.dart | 2 +- pubspec.yaml | 2 +- 40 files changed, 905 insertions(+), 234 deletions(-) create mode 100644 lib/core/models/stock/stock_response.dart create mode 100644 lib/core/models/stock/stock_response.g.dart create mode 100644 lib/core/services/stocks_service.dart create mode 100644 lib/views/stocks/stocks_view.dart create mode 100644 lib/views/stocks/widget/stock_tile.dart create mode 100644 lib/views/work/tabs/buy/buy_add.dart delete mode 100644 lib/views/work/views/contragent/select_contragent_view.dart rename lib/views/work/views/{add_by_barcode => select_by_scan}/add_by_barcode_view.dart (80%) rename lib/views/work/views/{contragent => select_contragent}/companent/contragent_list_item.dart (100%) create mode 100644 lib/views/work/views/select_contragent/select_contragent_view.dart rename lib/views/work/views/{add_product => select_product}/add_product_view.dart (91%) rename lib/views/work/views/{add_product => select_product}/component/add_category_list_item.dart (100%) rename lib/views/work/views/{add_product => select_product}/component/add_product_list_item.dart (100%) diff --git a/lib/core/models/but_item/buy_item_response.dart b/lib/core/models/but_item/buy_item_response.dart index f57a9c9..726f96d 100644 --- a/lib/core/models/but_item/buy_item_response.dart +++ b/lib/core/models/but_item/buy_item_response.dart @@ -13,8 +13,11 @@ class BuyItemResponse { required this.createdAt, required this.updatedAt, required this.unitPrice, + required this.name, this.deletedAt, this.refOkeiId, + this.ean13, + this.category }); int id; @@ -34,6 +37,9 @@ class BuyItemResponse { DateTime? deletedAt; @JsonKey(name: 'unit_price') double unitPrice; + String name; + String? ean13; + String? category; factory BuyItemResponse.fromJson(Map json) => _$BuyItemResponseFromJson(json); diff --git a/lib/core/models/but_item/buy_item_response.g.dart b/lib/core/models/but_item/buy_item_response.g.dart index fc6f221..ca473a7 100644 --- a/lib/core/models/but_item/buy_item_response.g.dart +++ b/lib/core/models/but_item/buy_item_response.g.dart @@ -16,10 +16,13 @@ BuyItemResponse _$BuyItemResponseFromJson(Map json) => createdAt: DateTime.parse(json['created_at'] as String), updatedAt: DateTime.parse(json['updated_at'] as String), unitPrice: (json['unit_price'] as num).toDouble(), + name: json['name'] as String, deletedAt: json['deleted_at'] == null ? null : DateTime.parse(json['deleted_at'] as String), refOkeiId: json['ref_okei_id'] as int?, + ean13: json['ean13'] as String?, + category: json['category'] as String?, ); Map _$BuyItemResponseToJson(BuyItemResponse instance) => @@ -34,4 +37,7 @@ Map _$BuyItemResponseToJson(BuyItemResponse instance) => 'updated_at': instance.updatedAt.toIso8601String(), 'deleted_at': instance.deletedAt?.toIso8601String(), 'unit_price': instance.unitPrice, + 'name': instance.name, + 'ean13': instance.ean13, + 'category': instance.category, }; diff --git a/lib/core/models/buy_invoice/buy_invoice_response.dart b/lib/core/models/buy_invoice/buy_invoice_response.dart index 8e0dfde..84c7305 100644 --- a/lib/core/models/buy_invoice/buy_invoice_response.dart +++ b/lib/core/models/buy_invoice/buy_invoice_response.dart @@ -22,6 +22,7 @@ class BuyInvoiceResponse { required this.eaccContragentId, required this.createdAt, required this.updatedAt, + required this.name, this.deletedAt, this.summ, }); @@ -43,6 +44,7 @@ class BuyInvoiceResponse { DateTime updatedAt; @JsonKey(name: 'deleted_at') DateTime? deletedAt; + String name; factory BuyInvoiceResponse.fromJson(Map json) => _$BuyInvoiceResponseFromJson(json); diff --git a/lib/core/models/buy_invoice/buy_invoice_response.g.dart b/lib/core/models/buy_invoice/buy_invoice_response.g.dart index 5d1ed11..0900370 100644 --- a/lib/core/models/buy_invoice/buy_invoice_response.g.dart +++ b/lib/core/models/buy_invoice/buy_invoice_response.g.dart @@ -16,6 +16,7 @@ BuyInvoiceResponse _$BuyInvoiceResponseFromJson(Map json) => eaccContragentId: json['eacc_contragent_id'] as int, createdAt: DateTime.parse(json['created_at'] as String), updatedAt: DateTime.parse(json['updated_at'] as String), + name: json['name'] as String, deletedAt: json['deleted_at'] == null ? null : DateTime.parse(json['deleted_at'] as String), @@ -34,4 +35,5 @@ Map _$BuyInvoiceResponseToJson(BuyInvoiceResponse instance) => 'created_at': instance.createdAt.toIso8601String(), 'updated_at': instance.updatedAt.toIso8601String(), 'deleted_at': instance.deletedAt?.toIso8601String(), + 'name': instance.name, }; diff --git a/lib/core/models/stock/stock_response.dart b/lib/core/models/stock/stock_response.dart new file mode 100644 index 0000000..d5bf267 --- /dev/null +++ b/lib/core/models/stock/stock_response.dart @@ -0,0 +1,28 @@ +import 'package:json_annotation/json_annotation.dart'; +part 'stock_response.g.dart'; + +@JsonSerializable() +class StockResponse { + StockResponse({ + required this.articul, + required this.ean13, + required this.eaccGoodId, + required this.name, + required this.price, + required this.cnt, + required this.katName, + }); + + int articul; + String ean13; + @JsonKey(name: 'eacc_good_id') + int eaccGoodId; + String name; + double price; + double cnt; + String katName; + +factory StockResponse.fromJson(Map json) => _$StockResponseFromJson(json); + +Map toJson() => _$StockResponseToJson(this); +} \ No newline at end of file diff --git a/lib/core/models/stock/stock_response.g.dart b/lib/core/models/stock/stock_response.g.dart new file mode 100644 index 0000000..3dc15a8 --- /dev/null +++ b/lib/core/models/stock/stock_response.g.dart @@ -0,0 +1,29 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'stock_response.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +StockResponse _$StockResponseFromJson(Map json) => + StockResponse( + articul: json['articul'] as int, + ean13: json['ean13'] as String, + eaccGoodId: json['eacc_good_id'] as int, + name: json['name'] as String, + price: (json['price'] as num).toDouble(), + cnt: (json['cnt'] as num).toDouble(), + katName: json['katName'] as String, + ); + +Map _$StockResponseToJson(StockResponse instance) => + { + 'articul': instance.articul, + 'ean13': instance.ean13, + 'eacc_good_id': instance.eaccGoodId, + 'name': instance.name, + 'price': instance.price, + 'cnt': instance.cnt, + 'katName': instance.katName, + }; diff --git a/lib/core/redux/actions/user_actions.dart b/lib/core/redux/actions/user_actions.dart index 1533724..9270ec9 100644 --- a/lib/core/redux/actions/user_actions.dart +++ b/lib/core/redux/actions/user_actions.dart @@ -2,6 +2,7 @@ import 'package:redux/redux.dart'; import 'package:meta/meta.dart'; import 'package:redux_thunk/redux_thunk.dart'; import 'package:satu/core/models/auth/auth_response.dart'; +import 'package:satu/core/models/dictionary/contragent/contragent_response_entity.dart'; import 'package:satu/core/redux/state/user_state.dart'; import 'package:satu/core/services/api_service.dart'; import 'package:satu/core/services/dialog_service.dart'; @@ -13,8 +14,8 @@ import '../store.dart'; @immutable class SetUserStateAction { - SetUserStateAction(this.userState); + final UserState userState; } @@ -47,6 +48,12 @@ ThunkAction authenticate(String email, String password) { }; } +ThunkAction setDefaultContragent(ContragentResponseEntity? entity) { + return (Store store) async { + store.dispatch(SetUserStateAction(UserState(defaultContragent: entity))); + }; +} + ThunkAction authenticateByToken(String token) { return (Store store) async { store.dispatch(SetUserStateAction(UserState(isLoading: true))); diff --git a/lib/core/redux/reducers/user_reducer.dart b/lib/core/redux/reducers/user_reducer.dart index c8dbb9b..4be2522 100644 --- a/lib/core/redux/reducers/user_reducer.dart +++ b/lib/core/redux/reducers/user_reducer.dart @@ -7,5 +7,6 @@ UserState userReducer(UserState prevState, SetUserStateAction action) { isError: payload.isError, isLoading: payload.isLoading, auth: payload.auth, + defaultContragent: payload.defaultContragent, ); } \ No newline at end of file diff --git a/lib/core/redux/state/user_state.dart b/lib/core/redux/state/user_state.dart index b440fcb..3a4bc12 100644 --- a/lib/core/redux/state/user_state.dart +++ b/lib/core/redux/state/user_state.dart @@ -1,51 +1,52 @@ - import 'package:meta/meta.dart'; import 'package:satu/core/models/auth/auth_response.dart'; - +import 'package:satu/core/models/dictionary/contragent/contragent_response_entity.dart'; @immutable class UserState { - factory UserState.initial(UserState? payload) => UserState( isLoading: false, isError: false, - auth: payload?.auth ?? (AuthResponse()..operation=false), + auth: payload?.auth ?? (AuthResponse()..operation = false), + defaultContragent: + payload?.defaultContragent ?? ContragentResponseEntity(), ); + UserState({this.isError, this.isLoading, this.auth, this.defaultContragent}); - UserState( - {this.isError, - this.isLoading, - this.auth, - }); final bool? isError; final bool? isLoading; final AuthResponse? auth; + final ContragentResponseEntity? defaultContragent; - UserState copyWith({ - required bool? isError, - required bool? isLoading, - required AuthResponse? auth - }) { + UserState copyWith( + {required bool? isError, + required bool? isLoading, + required AuthResponse? auth, + required ContragentResponseEntity? defaultContragent}) { return UserState( - isError: isError ?? this.isError, - isLoading: isLoading ?? this.isLoading, - auth: auth ?? this.auth, - ); + isError: isError ?? this.isError, + isLoading: isLoading ?? this.isLoading, + auth: auth ?? this.auth, + defaultContragent: defaultContragent ?? this.defaultContragent); } static UserState? fromJson(dynamic json) { return json != null ? UserState( auth: AuthResponse.fromMap(json['auth']), - ) + defaultContragent: json['defaultContragent'] == null + ? null + : ContragentResponseEntity.fromJson(json['defaultContragent']), + ) : null; } dynamic toJson() { return { 'auth': auth != null ? auth!.toJson() : null, + 'defaultContragent': + defaultContragent != null ? defaultContragent!.toJson() : null, }; } } - diff --git a/lib/core/services/api_service.dart b/lib/core/services/api_service.dart index c1975e7..ccf01e5 100644 --- a/lib/core/services/api_service.dart +++ b/lib/core/services/api_service.dart @@ -47,13 +47,6 @@ class ApiService extends BaseService { final response = await http.post(Uri.https(host, url), body: jsonEncode(requestBody), headers: headers); - if (requestBody != null) { - // log.i(host); - // log.i(url); - // log.i(headers); - //log.i(jsonEncode(requestBody)); - //log.i(jsonEncode(response.body)); - } return response.body; } @@ -143,6 +136,24 @@ class ApiService extends BaseService { return result; } + Future postRequestOriginal(String target, + {Map? requestBody}) async { + ResponseOriginal result; + try { + final Map headers = { + HttpHeaders.authorizationHeader: 'Bearer $token' + }; + + final String response = + await _post(target, header: headers, requestBody: requestBody); + result = ResponseOriginal.fromJson(json.decode(response)); + } catch (e, stack) { + log.e('postRequest', e, stack); + result = ResponseOriginal()..message = e.toString(); + } + return result; + } + Future dictionarySave( String target, Map? body) async { ResponseEntity response; diff --git a/lib/core/services/buy_service.dart b/lib/core/services/buy_service.dart index 9d4dfb9..e594d12 100644 --- a/lib/core/services/buy_service.dart +++ b/lib/core/services/buy_service.dart @@ -1,5 +1,7 @@ +import 'package:intl/intl.dart'; import 'package:satu/core/base/base_service.dart'; import 'package:satu/core/models/but_item/buy_item_response.dart'; +import 'package:satu/core/models/dictionary/contragent/contragent_response_entity.dart'; import 'package:satu/core/services/api_service.dart'; import 'package:satu/core/utils/locator.dart'; @@ -15,6 +17,10 @@ class BuyService extends BaseService { final Map requestBody = { 'page': page, 'perpage': perpage, + 'order' : { + 'col': 'doc_number', + 'desc': true + } }; ResponseEntity categories = await _api.postRequest('/general_purchases_get', @@ -42,8 +48,6 @@ class BuyService extends BaseService { 'perpage': perpage, 'id': id }; - log.i(requestBody); - ResponseEntity categories = await _api.postRequest('/general_purchases_get_items', requestBody: requestBody); if (categories.original.items != null && @@ -69,7 +73,6 @@ class BuyService extends BaseService { 'price': price, 'cnt': count }; - log.i(requestBody); ResponseEntity response = await _api.postRequest('/general_purchases_edit_item', requestBody: requestBody); @@ -86,7 +89,6 @@ class BuyService extends BaseService { final Map requestBody = { 'id': id, }; - log.i(requestBody); ResponseEntity response = await _api.postRequest('/general_purchases_delete_item', requestBody: requestBody); @@ -105,7 +107,6 @@ class BuyService extends BaseService { 'eacc_good_id': goodId, 'id': invoiceId, }; - log.i(requestBody); ResponseEntity response = await _api.postRequest('/general_purchases_add_item', requestBody: requestBody); @@ -116,4 +117,40 @@ class BuyService extends BaseService { } return result; } + + Future confirmInvoice(int invoiceId) async { + bool result = false; + try { + final Map requestBody = { + 'id': invoiceId, + }; + + ResponseEntity response = await _api.postRequest('/general_purchases_confirm', + requestBody: requestBody); + result = response.original.status == 'success'; + log.i(response.toJson()); + } catch (e, stack) { + log.e('getList', e, stack); + } + return result; + } + + Future createBuy(ContragentResponseEntity contragent, DateTime date) async { + bool result = false; + try { + DateFormat formatter = DateFormat('yyyy-MM-dd'); + final Map requestBody = { + 'eacc_contragent_id': contragent.id, + 'invoice_date': formatter.format(date), + }; + log.i('requestBody', requestBody); + ResponseEntity response = await _api.postRequest('/general_purchases_add', + requestBody: requestBody); + log.i('response', response.toJson()); + result = response.original.status == 'success'; + } catch (e, stack) { + log.e('getList', e, stack); + } + return result; + } } \ No newline at end of file diff --git a/lib/core/services/dictionary_service.dart b/lib/core/services/dictionary_service.dart index 2da60a0..2b2533d 100644 --- a/lib/core/services/dictionary_service.dart +++ b/lib/core/services/dictionary_service.dart @@ -30,8 +30,6 @@ class DictionaryService extends BaseService { final ApiService _api = locator(); final DbService _db = locator(); - - Category categoryResponseToCategory(CategoryResponse response) { return Category() ..id = response.id @@ -84,7 +82,7 @@ class DictionaryService extends BaseService { 'val': categoryId }; List responses = - await getGoods(page: 1, perpage: 100, filter: filter); + await getGoods(page: 1, perpage: 100, filter: [filter]); for (final GoodResponseEntity response in responses) { final Good good = goodResponseToGood(response); list.add(good); @@ -95,24 +93,38 @@ class DictionaryService extends BaseService { return list; } - Future> getGoodsByNameOrEan(String query) async { + Future> getGoodsByNameOrEan(String query, { bool onlyEan = false}) async { final List list = []; try { - final int? appCompanyId = Redux.store?.state.userState?.auth?.companyId; - String where = - '( $GoodColumnAppCompanyId = ? and $GoodColumnName like ? ) '; - final List args = [appCompanyId, '%$query%']; - if (_isNumericInt(query) && query.length >= 8) { - where += ' or $GoodColumnEan like ?'; - args.add('${int.parse(query).toString()}%'); + dynamic filter = [ + { + 'col': 'name', + 'action': 'like', + 'val': query, + }, + { + 'col': 'ean13', + 'action': 'like', + 'val': query, + } + ]; + if (onlyEan) { + filter = [ + { + 'col': 'ean13', + 'action': 'equals', + 'val': query, + } + ]; } - final List> elements = - await _db.queryRowsWithWhere(goodTableName, where, args); - for (final Map element in elements) { - list.add(Good.fromMap(element)); + List responses = + await getGoods(page: 1, perpage: 100, filter: filter); + for (final GoodResponseEntity response in responses) { + final Good good = goodResponseToGood(response); + list.add(good); } } catch (e, stack) { - log.e('getGoodsByCategoryId', e, stack); + log.e('getGoods', e, stack); } return list; } @@ -172,13 +184,14 @@ class DictionaryService extends BaseService { } Future> getGoods( - {required int page, required int perpage, dynamic filter}) async { + {required int page, required int perpage, dynamic filter,bool orGate = false}) async { List list = []; try { final Map requestBody = { 'page': page, 'perpage': perpage, - 'filter': [filter] + 'filter': filter, + 'or_gate': orGate }; ResponseEntity categories = diff --git a/lib/core/services/sell_service.dart b/lib/core/services/sell_service.dart index 92aea0b..2662d3e 100644 --- a/lib/core/services/sell_service.dart +++ b/lib/core/services/sell_service.dart @@ -12,10 +12,12 @@ import 'package:satu/core/models/flow/sell_return/sell_return_request.dart'; import 'package:satu/core/models/flow/transaction_state.dart'; import 'package:satu/core/redux/actions/sell_actions.dart'; import 'package:satu/core/redux/state/sell_state.dart'; +import 'package:satu/core/redux/state/user_state.dart'; import 'package:satu/core/redux/store.dart'; import 'package:satu/core/utils/locator.dart'; import 'package:uuid/uuid.dart'; +import '../models/dictionary/contragent/contragent_response_entity.dart'; import 'api_service.dart'; import 'db_service.dart'; import 'dialog_service.dart'; @@ -30,6 +32,7 @@ class SellService extends BaseService { {double card = 0, double nal = 0, double total = 0}) async { final SellRequest request = SellRequest(); final SellState sellState = Redux.store!.state.sellState!; + final UserState userState = Redux.store!.state.userState!; final TransactionState transactionState = sellState.transactionState!; final List items = sellState.items!; for (final ProductDao item in items) { @@ -40,10 +43,12 @@ class SellService extends BaseService { request.invoiceId = transactionState.uuid; request.section = transactionState.sectionName; request.contragent = transactionState.contragentName; + ContragentResponseEntity? contragent = userState.defaultContragent; final OperatorBean operator = OperatorBean() ..name = 'operator' ..code = 1; request.operator = operator; + request.contragent = contragent?.name; final SellResponse response = await _api.sell(request); if (response.operation == false) { _dialogService.showDialog(description: response.message); @@ -125,7 +130,6 @@ class SellService extends BaseService { data.nal = nal; data.total = total; transaction.data = jsonEncode(data.toMap()); - log.i(jsonEncode(data.toMap())); await _db.update(transactionTableName, transaction.toMap()); return transaction.id; } @@ -157,7 +161,6 @@ class SellService extends BaseService { data.nal = nal; data.total = total; transaction.data = jsonEncode(data.toMap()); - log.i(jsonEncode(data.toMap())); return await _db.insert(transactionTableName, transaction.toMap()); } diff --git a/lib/core/services/stocks_service.dart b/lib/core/services/stocks_service.dart new file mode 100644 index 0000000..0bb269a --- /dev/null +++ b/lib/core/services/stocks_service.dart @@ -0,0 +1,36 @@ +import 'package:satu/core/base/base_service.dart'; +import 'package:satu/core/models/stock/stock_response.dart'; + +import '../models/response/response_entity.dart'; +import '../utils/locator.dart'; +import 'api_service.dart'; + +class StockService extends BaseService { + final ApiService _api = locator(); + + Future> getList( + {required int page, required int perpage, String? query}) async { + List list = []; + try { + final Map requestBody = { + 'page': page, + 'perpage': perpage, + 'search': query ?? '' + }; + + ResponseOriginal categories = await _api.postRequestOriginal('/get_stock_balance', + requestBody: requestBody); + if (categories.data != null && + categories.data!.isNotEmpty) { + for (final dynamic map in categories.data!) { + final StockResponse item = + StockResponse.fromJson(map); + list.add(item); + } + } + } catch (e, stack) { + log.e('getList', e, stack); + } + return list; + } +} \ No newline at end of file diff --git a/lib/core/utils/locator.dart b/lib/core/utils/locator.dart index e2c7c88..579d097 100644 --- a/lib/core/utils/locator.dart +++ b/lib/core/utils/locator.dart @@ -10,6 +10,7 @@ import 'package:satu/core/services/dialog_service.dart'; import 'package:satu/core/services/dictionary_service.dart'; import 'package:satu/core/services/inventarization_service.dart'; import 'package:satu/core/services/navigator_service.dart'; +import 'package:satu/core/services/stocks_service.dart'; import 'logger.dart'; @@ -39,5 +40,7 @@ class LocatorInjector { _log.d('Initializing InventarizationService Service'); locator.registerLazySingleton( () => InventarizationService()); + _log.d('Initializing StockService Service'); + locator.registerLazySingleton(() => StockService()); } } diff --git a/lib/routes/route_names.dart b/lib/routes/route_names.dart index bb28804..69aff07 100644 --- a/lib/routes/route_names.dart +++ b/lib/routes/route_names.dart @@ -25,6 +25,7 @@ const String inventarizationEditRoute = 'inventarizationEditRoute'; //buy const String buyEditRoute = 'buyEditRoute'; +const String buyAddRoute = 'buyAddRoute'; // setting - ble printer const String settingPrinterBluetoothViewRoute = 'SettingPrinterBluetoothView'; diff --git a/lib/routes/router.dart b/lib/routes/router.dart index c94b6bf..dcb147f 100644 --- a/lib/routes/router.dart +++ b/lib/routes/router.dart @@ -14,15 +14,16 @@ import 'package:satu/views/settings/printer_bluetooth/printer_encoding_select.da import 'package:satu/views/settings/printer_bluetooth/printer_paper_size_select.dart'; import 'package:satu/views/settings/printer_bluetooth/printer_select.dart'; import 'package:satu/views/settings/printer_bluetooth/printer_view.dart'; +import 'package:satu/views/work/tabs/buy/buy_add.dart'; import 'package:satu/views/work/tabs/buy/buy_edit.dart'; -import 'package:satu/views/work/views/add_by_barcode/add_by_barcode_view.dart'; -import 'package:satu/views/work/views/add_product/add_product_view.dart'; -import 'package:satu/views/work/views/contragent/select_contragent_view.dart'; import 'package:satu/views/work/views/payment/payment_view.dart'; import 'package:satu/views/work/views/receipt/receipt_view.dart'; +import 'package:satu/views/work/views/select_by_scan/add_by_barcode_view.dart'; import 'package:satu/views/work/work_view.dart'; import '../core/models/inventarization/response/inventarization_response.dart'; +import '../views/work/views/select_contragent/select_contragent_view.dart'; +import '../views/work/views/select_product/add_product_view.dart'; import './route_names.dart'; Route generateRoute(RouteSettings settings) { @@ -46,12 +47,12 @@ Route generateRoute(RouteSettings settings) { case addProductViewRoute: return _getPageRoute( routeName: settings.name, - viewToShow: AddProductView(), + viewToShow: SelectProductView(), ); case addByBarcodeViewRoute: return _getPageRoute( routeName: settings.name, - viewToShow: const AddByBarcodeView(), + viewToShow: const SelectByScanView(), ); case settingPrinterBluetoothViewRoute: return _getPageRoute( @@ -137,6 +138,11 @@ Route generateRoute(RouteSettings settings) { invoice: invoice, ), ); + case buyAddRoute: + return _getPageRoute( + routeName: settings.name, + viewToShow: BuyAddView(), + ); default: return MaterialPageRoute( diff --git a/lib/views/dictionaries/goods/goods_view.dart b/lib/views/dictionaries/goods/goods_view.dart index b2550b8..47587eb 100644 --- a/lib/views/dictionaries/goods/goods_view.dart +++ b/lib/views/dictionaries/goods/goods_view.dart @@ -154,8 +154,8 @@ class _GoodsDictionaryViewState extends State { Future _fetchData(int pageKey, int perPage, String? query) async { final List newItems = await _dictionaryService.getGoods( page: pageKey, - filter: {'col': 'name', 'action': 'like', 'val': query ?? ''}, - perpage: perPage); + filter: [{'col': 'name', 'action': 'like', 'val': query ?? ''}], + perpage: perPage, orGate: true); final isLastPage = newItems.length < _pageSize; if (isLastPage) { diff --git a/lib/views/inventarization/view/inventarization_edit_view.dart b/lib/views/inventarization/view/inventarization_edit_view.dart index a8617a0..07acf32 100644 --- a/lib/views/inventarization/view/inventarization_edit_view.dart +++ b/lib/views/inventarization/view/inventarization_edit_view.dart @@ -108,6 +108,14 @@ class _InventarizationEditViewState extends State { }, pagingController: _pagingController, builderDelegate: PagedChildBuilderDelegate( + noItemsFoundIndicatorBuilder: (BuildContext context) { + return const Center( + child: Text( + 'Необходимо добавить товар', + style: textGray11Style, + ), + ); + }, itemBuilder: (BuildContext context, GoodInventarization item, int index) { return GoodInventarizationListItem( @@ -199,8 +207,25 @@ class _InventarizationEditViewState extends State { final dynamic result = await _nav.push(addByBarcodeViewRoute); if (result != null) { final List goods = await locator() - .getGoodsByNameOrEan(result as String); - if (goods.isNotEmpty) {} + .getGoodsByNameOrEan(result as String, onlyEan: true); + if (goods.isNotEmpty) { + final Good good = goods.first; + bool result = await _service + .addGoodToList(widget.item.id, good.id); + if (result) { + _pagingController.refresh(); + } else { + _dialogService.showDialog( + description: + 'Товара отсутсвует в остатке или ранее не' + ' использователся в системе', + ); + } + } else { + _dialogService.showDialog( + description: 'Товар не найден', + ); + } } }, child: Icon(Icons.qr_code_rounded, size: 30, color: whiteColor), diff --git a/lib/views/inventarization/widget/good_inventarization_list_item.dart b/lib/views/inventarization/widget/good_inventarization_list_item.dart index 1b2e954..b14208e 100644 --- a/lib/views/inventarization/widget/good_inventarization_list_item.dart +++ b/lib/views/inventarization/widget/good_inventarization_list_item.dart @@ -1,15 +1,11 @@ import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:satu/core/models/dialog_models.dart'; -import 'package:satu/core/redux/actions/sell_actions.dart'; -import 'package:satu/core/redux/store.dart'; import 'package:satu/core/services/dialog_service.dart'; import 'package:satu/core/services/inventarization_service.dart'; import 'package:satu/core/utils/locator.dart'; import 'package:satu/core/utils/utils_parse.dart'; import 'package:satu/shared/app_colors.dart'; -import 'package:satu/shared/shared_styles.dart'; -import 'package:satu/views/work/views/add_by_barcode/add_by_barcode_view.dart'; + import 'package:satu/widgets/ui/product_title_widget.dart'; class GoodInventarizationListItem extends StatefulWidget { diff --git a/lib/views/main/main_view.dart b/lib/views/main/main_view.dart index 927c3a8..cfb687e 100644 --- a/lib/views/main/main_view.dart +++ b/lib/views/main/main_view.dart @@ -13,6 +13,7 @@ import 'package:satu/views/work/work_view.dart'; import 'package:satu/widgets/drawer/app_drawer.dart'; import '../dictionaries/contragents/contragents_view.dart'; +import '../stocks/stocks_view.dart'; class MainView extends StatefulWidget { @override @@ -30,6 +31,7 @@ class _MainViewState extends State { final _contragentDictView = ContragentsDictionaryView(); final _analyticsView = const AnalyticsView(); final _inventarizationView = InventarizationView(); + final _stocksView = StocksView(); Widget _body(Type viewClass) { if(viewClass == WorkView) { @@ -53,6 +55,9 @@ class _MainViewState extends State { if(viewClass == AnalyticsView) { return _analyticsView; } + if(viewClass == StocksView) { + return _stocksView; + } return _workView; } diff --git a/lib/views/stocks/stocks_view.dart b/lib/views/stocks/stocks_view.dart new file mode 100644 index 0000000..ace27dd --- /dev/null +++ b/lib/views/stocks/stocks_view.dart @@ -0,0 +1,179 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; +import 'package:satu/core/models/inventarization/response/inventarization_response.dart'; +import 'package:satu/core/models/stock/stock_response.dart'; +import 'package:satu/core/services/dialog_service.dart'; +import 'package:satu/core/services/stocks_service.dart'; +import 'package:satu/views/inventarization/widget/good_inventarization_list_item.dart'; +import 'package:satu/views/stocks/widget/stock_tile.dart'; + +import '../../../core/entity/goods_entity.dart'; +import '../../../core/models/inventarization/good_item/good_item.dart'; +import '../../../core/services/dictionary_service.dart'; +import '../../../core/services/inventarization_service.dart'; +import '../../../core/services/navigator_service.dart'; +import '../../../core/utils/locator.dart'; +import '../../../routes/route_names.dart'; +import '../../../shared/app_colors.dart'; +import '../../../shared/shared_styles.dart'; +import '../../../shared/ui_helpers.dart'; +import '../../../widgets/bar/products_app_bar.dart'; +import '../../widgets/fields/input_field.dart'; + +class StocksView extends StatefulWidget { + const StocksView({ + Key? key, + }) : super(key: key); + + @override + State createState() => _StocksViewState(); +} + +class _StocksViewState extends State { + final StockService _service = locator(); + late TextEditingController _searchTextController; + final FocusNode _searchFocusNode = FocusNode(); + String query = ''; + Timer? _debounce; + + static const _pageSize = 20; + bool _isLastPage = false; + + final PagingController _pagingController = + PagingController(firstPageKey: 1); + + @override + void initState() { + _searchTextController = TextEditingController(); + _searchTextController.addListener(() { + setState(() { + query = _searchTextController.text; + }); + if (_debounce?.isActive ?? false) _debounce?.cancel(); + _debounce = Timer(const Duration(milliseconds: 500), () { + _pagingController.refresh(); + }); + }); + + _pagingController.addPageRequestListener((pageKey) { + _fetchData(pageKey, _pageSize, query); + }); + super.initState(); + } + + @override + void dispose() { + ; + _debounce?.cancel(); + _pagingController.dispose(); + _searchTextController.dispose(); + _searchFocusNode.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const ProductsAppBar( + title: 'Остатки', + drawerShow: true, + ), + body: Column( + children: [ + InputField( + placeholder: 'Поиск по наименованию товара или штрих-код', + search: true, + controller: _searchTextController, + fieldFocusNode: _searchFocusNode, + ), + verticalSpaceSmall, + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15.0), + child: SizedBox( + width: double.infinity, + child: Row( + children: [ + Expanded( + flex: 4, + child: Text( + 'Список товара', + style: textGray11Style, + ), + ), + Expanded( + flex: 2, + child: Text( + 'Количество', + style: textGray11Style, + ), + ), + Expanded( + flex: 1, + child: Text( + 'Сумма', + style: textGray11Style, + ), + ), + ], + ), + ), + ), + verticalSpaceSmall, + Expanded( + child: PagedListView.separated( + //physics: const BouncingScrollPhysics(), + separatorBuilder: (BuildContext context, int index) { + return const Divider( + height: 1.0, + color: disableColor, + ); + }, + pagingController: _pagingController, + builderDelegate: PagedChildBuilderDelegate( + noItemsFoundIndicatorBuilder: (BuildContext context) { + return const Center( + child: Text( + 'В данном списке нет товаров', + style: textGray11Style, + ), + ); + }, + itemBuilder: + (BuildContext context, StockResponse item, int index) { + return StockTile( + key: Key( + 'good_stock_${item.eaccGoodId}', + ), + name: item.name, + categoryName: item.katName, + count: item.cnt, + price: item.price, + ean: item.ean13, + ); + }, + ), + ), + ), + ], + ), + ); + } + + Future _fetchData(int pageKey, int perPage, String? query) async { + final List newItems = await _service.getList( + page: pageKey, + perpage: perPage, + query: query, + ); + _isLastPage = newItems.length < _pageSize; + + if (_isLastPage) { + _pagingController.appendLastPage(newItems); + } else { + final nextPageKey = pageKey + 1; + _pagingController.appendPage(newItems, nextPageKey); + } + } +} diff --git a/lib/views/stocks/widget/stock_tile.dart b/lib/views/stocks/widget/stock_tile.dart new file mode 100644 index 0000000..f5156ff --- /dev/null +++ b/lib/views/stocks/widget/stock_tile.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; +import 'package:satu/core/utils/utils_parse.dart'; +import 'package:satu/shared/app_colors.dart'; + +import 'package:satu/widgets/ui/product_title_widget.dart'; + +class StockTile extends StatefulWidget { + const StockTile({ + required Key key, + required this.price, + required this.count, + this.name = '', + this.ean, + this.categoryName, + + }) : super(key: key); + final String name; + final String? ean; + final String? categoryName; + final double price; + final double count; + + + + @override + _StockTileState createState() => + _StockTileState(); +} + +class _StockTileState + extends State { + + @override + Widget build(BuildContext context) { + return ListTile( + key: widget.key!, + contentPadding: + const EdgeInsets.symmetric(horizontal: 10.0, vertical: 4.0), + title: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + flex: 4, + child: ProductTitleWidget( + name: widget.name, + ean: widget.ean, + categoryName: widget.categoryName, + ), + ), + Expanded( + flex: 2, + child: Text( + formatDecimal(widget.count), + style: const TextStyle(fontSize: 12, color: textColor), + overflow: TextOverflow.ellipsis, + )), + Expanded( + flex: 1, + child: Text( + formatDecimal(widget.price), + style: const TextStyle(fontSize: 12, color: textColor), + overflow: TextOverflow.ellipsis, + )), + ], + ), + tileColor: whiteColor, + ); + } + + +} diff --git a/lib/views/work/tabs/buy/buy_add.dart b/lib/views/work/tabs/buy/buy_add.dart new file mode 100644 index 0000000..dc08e5c --- /dev/null +++ b/lib/views/work/tabs/buy/buy_add.dart @@ -0,0 +1,99 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:satu/core/models/dictionary/contragent/contragent_response_entity.dart'; +import 'package:satu/widgets/fields/line_tile.dart'; + +import '../../../../core/services/buy_service.dart'; +import '../../../../core/services/navigator_service.dart'; +import '../../../../core/utils/locator.dart'; +import '../../../../routes/route_names.dart'; +import '../../../../widgets/bar/products_app_bar.dart'; +import '../../../../widgets/buttons/busy_button.dart'; + +class BuyAddView extends StatefulWidget { + const BuyAddView({Key? key}) : super(key: key); + + @override + State createState() => _BuyAddViewState(); +} + +class _BuyAddViewState extends State { + ContragentResponseEntity? _contragent; + DateTime _date = DateTime.now(); + final BuyService _service = locator(); + DateFormat _dateFormat = DateFormat('dd.MM.yyyy'); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const ProductsAppBar( + title: 'Новая накладная покупки', + ), + body: Column( + children: [ + Expanded( + child: ListView( + children: [ + LineTile( + key: const Key('BuyAddView_Contragent'), + _contragent?.name ?? 'Выберите контрагента', + onTap: () async { + ContragentResponseEntity? entity = + await locator() + .push(contragentSelectViewRoute) + as ContragentResponseEntity?; + if (entity != null) { + setState(() { + _contragent = entity; + }); + } + }, + labelText: 'Контрагент', + ), + LineTile( + key: const Key('BuyAddView_Date'), + _dateFormat.format(_date), + onTap: () async { + + DateTime? pickedDate = await showDatePicker( + context: context, + initialDate: DateTime.now(), //get today's date + firstDate:DateTime(2000), //DateTime.now() - not to allow to choose before today. + lastDate: DateTime(2101) + ); + if (pickedDate != null) { + setState(() { + _date = pickedDate; + }); + } + + }, + labelText: 'Дата накладной', + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 45, vertical: 30), + child: BusyButton( + title: 'СОЗДАТЬ', + busy: false, + onPressed: () async { + + if (_contragent == null) { + return; + } + + + bool result = await _service.createBuy(_contragent!, _date); + if (result) { + locator().pop(result); + } + }, + ), + ), + ], + ), + ); + } +} diff --git a/lib/views/work/tabs/buy/buy_edit.dart b/lib/views/work/tabs/buy/buy_edit.dart index c899a76..c192605 100644 --- a/lib/views/work/tabs/buy/buy_edit.dart +++ b/lib/views/work/tabs/buy/buy_edit.dart @@ -3,10 +3,11 @@ import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:satu/core/models/but_item/buy_item_response.dart'; +import 'package:satu/core/models/dialog_models.dart'; import 'package:satu/core/services/dialog_service.dart'; +import 'package:satu/shared/shared_styles.dart'; import 'package:satu/views/work/tabs/buy/component/product_buy_tile.dart'; - import '../../../../core/entity/goods_entity.dart'; import '../../../../core/models/buy_invoice/buy_invoice_response.dart'; import '../../../../core/services/buy_service.dart'; @@ -34,10 +35,11 @@ class _BuyEditViewState extends State { static const _pageSize = 20; final PagingController _pagingController = - PagingController(firstPageKey: 1); + PagingController(firstPageKey: 1); bool editable = false; + bool itemsExist = false; @override void initState() { @@ -46,7 +48,7 @@ class _BuyEditViewState extends State { _fetchData(pageKey, _pageSize); }); log('refBuyInvoiceStatusId: ${widget.invoice.refBuyInvoiceStatusId}'); - if(widget.invoice.refBuyInvoiceStatusId == 1){ + if (widget.invoice.refBuyInvoiceStatusId == 1) { editable = true; } @@ -55,7 +57,6 @@ class _BuyEditViewState extends State { @override void dispose() { - _pagingController.dispose(); super.dispose(); } @@ -79,15 +80,23 @@ class _BuyEditViewState extends State { }, pagingController: _pagingController, builderDelegate: PagedChildBuilderDelegate( - itemBuilder: (BuildContext context, BuyItemResponse item, - int index) { + noItemsFoundIndicatorBuilder: (BuildContext context) { + return const Center( + child: Text( + 'Необходимо добавить товар', + style: textGray11Style, + ), + ); + }, + itemBuilder: + (BuildContext context, BuyItemResponse item, int index) { return ProductBuyTile( key: ValueKey(item.id), - ean: '1234567890123', - name: 'Картофель', + ean: item.ean13, + name: item.name, price: item.price, count: item.cnt, - categoryName: 'Овощи', + categoryName: item.category, editable: editable, invoiceId: widget.invoice.id, id: item.id, @@ -115,14 +124,29 @@ class _BuyEditViewState extends State { crossAxisAlignment: CrossAxisAlignment.end, children: [ Visibility( - visible: false, + visible: itemsExist, child: Padding( padding: const EdgeInsets.all(8.0), child: FloatingActionButton( mini: true, elevation: 2, backgroundColor: successColor, - onPressed: () {}, + onPressed: () async { + DialogResponse confirm = + await _dialogService.showConfirmationDialog( + title: 'Подтверждение', + description: 'Вы действительно хотите завершить покупку?', + confirmationTitle: 'Да', + cancelTitle: 'Нет', + ); + if (confirm.confirmed) { + bool result = + await _service.confirmInvoice(widget.invoice.id); + if (result) { + locator().pop(result); + } + } + }, child: Icon( Icons.check, color: whiteColor, @@ -141,13 +165,12 @@ class _BuyEditViewState extends State { .push(addProductViewRoute) as Good?; if (good != null && good.id != null) { bool result = - await _service.addItem(widget.invoice.id, good.id!); + await _service.addItem(widget.invoice.id, good.id!); if (result) { _pagingController.refresh(); } else { _dialogService.showDialog( - description: - 'Товара отсутсвует в остатке или ранее не' + description: 'Товара отсутсвует в остатке или ранее не' ' использователся в системе', ); } @@ -168,8 +191,25 @@ class _BuyEditViewState extends State { final dynamic result = await _nav.push(addByBarcodeViewRoute); if (result != null) { final List goods = await locator() - .getGoodsByNameOrEan(result as String); - if (goods.isNotEmpty) {} + .getGoodsByNameOrEan(result as String, onlyEan: true); + if (goods.isNotEmpty) { + final Good good = goods.first; + bool result = await _service.addItem( + widget.invoice.id, good.id!); + if (result) { + _pagingController.refresh(); + } else { + _dialogService.showDialog( + description: 'Товара отсутсвует в остатке или ранее не' + ' использователся в системе', + ); + } + } else { + _dialogService.showDialog( + description: 'Товара отсутсвует в остатке или ранее не' + ' использователся в системе', + ); + } } }, child: Icon(Icons.qr_code_rounded, size: 30, color: whiteColor), @@ -191,16 +231,21 @@ class _BuyEditViewState extends State { final nextPageKey = pageKey + 1; _pagingController.appendPage(newItems, nextPageKey); } + if ((_pagingController.value.itemList ?? []).isNotEmpty) { + setState(() { + itemsExist = true; + }); + } } void _editData(int id, double price, double count) { final List oldList = _pagingController.value.itemList ?? []; - oldList..firstWhere((element) => element.id == id).price = price + oldList + ..firstWhere((element) => element.id == id).price = price ..firstWhere((element) => element.id == id).cnt = count; setState(() { _pagingController.itemList = oldList; }); } - } diff --git a/lib/views/work/tabs/buy/buy_view.dart b/lib/views/work/tabs/buy/buy_view.dart index fafc00b..301f988 100644 --- a/lib/views/work/tabs/buy/buy_view.dart +++ b/lib/views/work/tabs/buy/buy_view.dart @@ -21,15 +21,13 @@ class BuyView extends StatefulWidget { } class _BuyViewState extends State { - - final BuyService _service = locator(); final NavigatorService _navigatorService = locator(); static const _pageSize = 20; final PagingController _pagingController = - PagingController(firstPageKey: 1); + PagingController(firstPageKey: 1); final DateFormat formatterDay = DateFormat('dd.MM.yyyy'); @@ -43,7 +41,6 @@ class _BuyViewState extends State { @override void dispose() { - _pagingController.dispose(); super.dispose(); } @@ -68,8 +65,8 @@ class _BuyViewState extends State { }, pagingController: _pagingController, builderDelegate: PagedChildBuilderDelegate( - itemBuilder: (BuildContext context, BuyInvoiceResponse item, - int index) { + itemBuilder: + (BuildContext context, BuyInvoiceResponse item, int index) { return DictionaryTile( key: Key('category_${item.id}'), onPress: () async { @@ -82,29 +79,41 @@ class _BuyViewState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Expanded( + flex: 2, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '${item.name}', + style: textBlack12Style, + ), + SizedBox( + height: 5.0, + ), + Text( + 'Номер: ${item.docNumber.toString()}', + style: textGray11Style, + ), + ], + )), Expanded( flex: 2, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '${item.eaccContragentId.toString()}', + '${formatterDay.format(item.invoiceDate)}', style: textBlack12Style, ), SizedBox( height: 5.0, ), Text( - 'Статус: ${item.refBuyInvoiceStatusId.toString()}', + 'Статус: ${item.refBuyInvoiceStatusId == 1 ? 'Новая' : 'Обработана'}', style: textGray11Style, ), ], - )), - Expanded( - flex: 2, - child: Text( - '${formatterDay.format(item.invoiceDate)}', - style: textBlack12Style, ), ), Expanded( @@ -127,12 +136,22 @@ class _BuyViewState extends State { ) ], ), + floatingActionButton: FloatingActionButton( + onPressed: () async { + final dynamic result = await _navigatorService.push(buyAddRoute); + if (result != null && true == (result as bool)) { + _pagingController.refresh(); + } + }, + mini: true, + child: const Icon(Icons.add), + ), ); } Future _fetchData(int pageKey, int perPage) async { - final List newItems = await _service - .getList(page: pageKey, perpage: perPage); + final List newItems = + await _service.getList(page: pageKey, perpage: perPage); final isLastPage = newItems.length < _pageSize; if (isLastPage) { _pagingController.appendLastPage(newItems); diff --git a/lib/views/work/tabs/buy/component/product_buy_tile.dart b/lib/views/work/tabs/buy/component/product_buy_tile.dart index 512f95e..40f11ac 100644 --- a/lib/views/work/tabs/buy/component/product_buy_tile.dart +++ b/lib/views/work/tabs/buy/component/product_buy_tile.dart @@ -1,15 +1,10 @@ import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:satu/core/models/dialog_models.dart'; -import 'package:satu/core/redux/actions/sell_actions.dart'; -import 'package:satu/core/redux/store.dart'; import 'package:satu/core/services/buy_service.dart'; import 'package:satu/core/services/dialog_service.dart'; import 'package:satu/core/utils/locator.dart'; import 'package:satu/core/utils/utils_parse.dart'; import 'package:satu/shared/app_colors.dart'; -import 'package:satu/shared/shared_styles.dart'; -import 'package:satu/views/work/views/add_by_barcode/add_by_barcode_view.dart'; import 'package:satu/widgets/ui/product_title_widget.dart'; class ProductBuyTile extends StatefulWidget { diff --git a/lib/views/work/tabs/component/contagent_select_bar.dart b/lib/views/work/tabs/component/contagent_select_bar.dart index 5bb2bba..1b5d9b5 100644 --- a/lib/views/work/tabs/component/contagent_select_bar.dart +++ b/lib/views/work/tabs/component/contagent_select_bar.dart @@ -1,12 +1,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:satu/core/models/dictionary/contragent/contragent_response_entity.dart'; +import 'package:satu/core/redux/actions/user_actions.dart'; +import 'package:satu/core/redux/store.dart'; import 'package:satu/core/services/navigator_service.dart'; import 'package:satu/core/utils/locator.dart'; import 'package:satu/routes/route_names.dart'; import 'package:satu/shared/app_colors.dart'; class ContragentSelectBar extends StatelessWidget { - const ContragentSelectBar({required this.value, Key? key}) : super(key: key); final String value; @@ -17,11 +19,16 @@ class ContragentSelectBar extends StatelessWidget { child: Material( type: MaterialType.transparency, child: InkWell( - onTap: () { - locator().push(contragentSelectViewRoute); + onTap: () async { + ContragentResponseEntity? entity = + await locator() + .push(contragentSelectViewRoute) as ContragentResponseEntity?; + if (entity != null) { + Redux.store?.dispatch(setDefaultContragent(entity)); + } }, child: Padding( - padding: EdgeInsets.symmetric(vertical:08.w, horizontal: 15.w), + padding: EdgeInsets.symmetric(vertical: 08.w, horizontal: 15.w), child: Row( children: [ Expanded( diff --git a/lib/views/work/tabs/sell/component/product_sell_tile.dart b/lib/views/work/tabs/sell/component/product_sell_tile.dart index e628c27..9be607f 100644 --- a/lib/views/work/tabs/sell/component/product_sell_tile.dart +++ b/lib/views/work/tabs/sell/component/product_sell_tile.dart @@ -9,9 +9,10 @@ import 'package:satu/core/utils/locator.dart'; import 'package:satu/core/utils/utils_parse.dart'; import 'package:satu/shared/app_colors.dart'; import 'package:satu/shared/shared_styles.dart'; -import 'package:satu/views/work/views/add_by_barcode/add_by_barcode_view.dart'; import 'package:satu/widgets/ui/product_title_widget.dart'; +import '../../../views/select_by_scan/add_by_barcode_view.dart'; + class ProductSellTile extends StatefulWidget { const ProductSellTile( @@ -42,7 +43,7 @@ class _ProductSellTileState extends State { void _onItemTapped(BuildContext context) { Navigator.of(context).push(MaterialPageRoute( - builder: (BuildContext context) => const AddByBarcodeView())); + builder: (BuildContext context) => const SelectByScanView())); } @override diff --git a/lib/views/work/tabs/sell/sell_view.dart b/lib/views/work/tabs/sell/sell_view.dart index 7c72bd8..9931c7f 100644 --- a/lib/views/work/tabs/sell/sell_view.dart +++ b/lib/views/work/tabs/sell/sell_view.dart @@ -4,6 +4,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:redux/src/store.dart'; import 'package:satu/core/models/flow/dao/product_dao.dart'; import 'package:satu/core/redux/state/sell_state.dart'; +import 'package:satu/core/redux/state/user_state.dart'; import 'package:satu/core/redux/store.dart'; import 'package:satu/core/services/navigator_service.dart'; import 'package:satu/core/utils/locator.dart'; @@ -34,15 +35,20 @@ class SellView extends StatelessWidget { backgroundColor: backgroundColor, childHeight: 60, child: ProductHeaderBar( - count: state.items!.length, + count: state.items!.length, sum: sumProducts(state.items!), ), ), body: Column( children: [ - const ContragentSelectBar( - value: 'Частное лицо', - ), + StoreConnector( + converter: (store) => store.state.userState!, + builder: (_, uState) { + return ContragentSelectBar( + value: (uState.defaultContragent?.name ?? + 'Выберите контрагента'), + ); + }), Expanded( child: ListView( physics: const BouncingScrollPhysics(), @@ -120,11 +126,11 @@ class SellView extends StatelessWidget { elevation: 2, mini: true, onPressed: () async { - final Good? good = await locator().push(addProductViewRoute) as Good?; - if(good !=null) { + final Good? good = await locator() + .push(addProductViewRoute) as Good?; + if (good != null) { Redux.store!.dispatch(addSellItem(good: good)); } - }, child: Icon( Icons.add_rounded, @@ -144,7 +150,7 @@ class SellView extends StatelessWidget { if (result != null) { final List goods = await locator() - .getGoodsByNameOrEan(result as String); + .getGoodsByNameOrEan(result as String, onlyEan: true); if (goods.isNotEmpty) { Redux.store ?.dispatch(addSellItem(good: goods.first)); diff --git a/lib/views/work/views/contragent/select_contragent_view.dart b/lib/views/work/views/contragent/select_contragent_view.dart deleted file mode 100644 index 8b7c814..0000000 --- a/lib/views/work/views/contragent/select_contragent_view.dart +++ /dev/null @@ -1,100 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:satu/core/entity/category_entity.dart'; -import 'package:satu/core/entity/goods_entity.dart'; -import 'package:satu/core/services/dictionary_service.dart'; -import 'package:satu/core/utils/locator.dart'; -import 'package:satu/shared/app_colors.dart'; -import 'package:satu/shared/ui_helpers.dart'; -import 'package:satu/widgets/bar/products_app_bar.dart'; -import 'package:satu/widgets/bar/products_title_bar.dart'; -import 'package:satu/widgets/fields/input_field.dart'; - -import 'companent/contragent_list_item.dart'; - -class SelectContragentView extends StatefulWidget { - @override - _SelectContragentViewState createState() => _SelectContragentViewState(); -} - -class _SelectContragentViewState extends State { - final DictionaryService _dictionaryService = locator(); - late TextEditingController _searchTextController; - final FocusNode _searchFocusNode = new FocusNode(); - - final List _contragents = []; - - @override - void initState() { - _searchTextController = TextEditingController(); - _searchTextController.addListener(() { - if (_searchTextController.text.isNotEmpty) { - searchByField(_searchTextController.text); - } else { - reset(); - } - }); - super.initState(); - } - - @override - void dispose() { - _searchTextController.dispose(); - _searchFocusNode.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: const ProductsAppBar( - title: 'Контрагент', - ), - body: Column( - children: [ - InputField( - placeholder: 'Поиск по наименованию или коду товара', - search: true, - controller: _searchTextController, - fieldFocusNode: _searchFocusNode, - ), - verticalSpaceTiny, - const ProductsTitleBarBar( - title: 'Выберите контрагента', - ), - Expanded( - child: ListView.separated( - physics: const BouncingScrollPhysics(), - itemCount: _contragents.length, - itemBuilder: (BuildContext context, int index) { - final Category category = _contragents[index]; - return ContragentListItem( - name: category.name, - key: Key('category_${category.id}'), - onPress: () => () {}, - ); - }, - separatorBuilder: (BuildContext context, int index) { - return const Divider( - height: 1.0, - color: disableColor, - ); - }, - ), - ), - ], - ), - ); - } - - void reset() { - _searchTextController.clear(); - } - - void searchByField(String query) async { - - List goods = await _dictionaryService.getGoodsByNameOrEan(query); - setState(() { - goods; - }); - } -} diff --git a/lib/views/work/views/add_by_barcode/add_by_barcode_view.dart b/lib/views/work/views/select_by_scan/add_by_barcode_view.dart similarity index 80% rename from lib/views/work/views/add_by_barcode/add_by_barcode_view.dart rename to lib/views/work/views/select_by_scan/add_by_barcode_view.dart index 458200c..0fe1cfe 100644 --- a/lib/views/work/views/add_by_barcode/add_by_barcode_view.dart +++ b/lib/views/work/views/select_by_scan/add_by_barcode_view.dart @@ -5,17 +5,17 @@ import 'package:satu/core/utils/locator.dart'; import 'package:satu/widgets/bar/products_app_bar.dart'; import 'package:satu/widgets/tools/app_barcode_scanner_widget.dart'; -class AddByBarcodeView extends StatefulWidget { +class SelectByScanView extends StatefulWidget { - const AddByBarcodeView({ + const SelectByScanView({ Key? key }) : super(key: key); @override - _AddByBarcodeViewState createState() => _AddByBarcodeViewState(); + _SelectByScanViewState createState() => _SelectByScanViewState(); } -class _AddByBarcodeViewState extends State { +class _SelectByScanViewState extends State { @override Widget build(BuildContext context) { return Scaffold( diff --git a/lib/views/work/views/contragent/companent/contragent_list_item.dart b/lib/views/work/views/select_contragent/companent/contragent_list_item.dart similarity index 100% rename from lib/views/work/views/contragent/companent/contragent_list_item.dart rename to lib/views/work/views/select_contragent/companent/contragent_list_item.dart diff --git a/lib/views/work/views/select_contragent/select_contragent_view.dart b/lib/views/work/views/select_contragent/select_contragent_view.dart new file mode 100644 index 0000000..0a00cd7 --- /dev/null +++ b/lib/views/work/views/select_contragent/select_contragent_view.dart @@ -0,0 +1,126 @@ +import 'package:flutter/material.dart'; +import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; +import 'package:satu/core/entity/category_entity.dart'; +import 'package:satu/core/entity/goods_entity.dart'; +import 'package:satu/core/services/dictionary_service.dart'; +import 'package:satu/core/utils/locator.dart'; +import 'package:satu/shared/app_colors.dart'; +import 'package:satu/shared/ui_helpers.dart'; +import 'package:satu/widgets/bar/products_app_bar.dart'; +import 'package:satu/widgets/bar/products_title_bar.dart'; +import 'package:satu/widgets/fields/input_field.dart'; + +import '../../../../core/models/dictionary/contragent/contragent_response_entity.dart'; +import '../../../../core/services/navigator_service.dart'; +import '../../../dictionaries/component/dictionary_list_tile.dart'; +import 'companent/contragent_list_item.dart'; + +class SelectContragentView extends StatefulWidget { + @override + _SelectContragentViewState createState() => _SelectContragentViewState(); +} + +class _SelectContragentViewState extends State { + final DictionaryService _dictionaryService = locator(); + final NavigatorService _navigatorService = locator(); + late TextEditingController _searchTextController; + final FocusNode _searchFocusNode = FocusNode(); + + late List items = []; + static const _pageSize = 20; + + final PagingController _pagingController = + PagingController(firstPageKey: 0); + + @override + void initState() { + _searchTextController = TextEditingController(); + _searchTextController.addListener(() { + if (_searchTextController.text.isNotEmpty) { + //searchByField(_searchTextController.text); + } else { + reset(); + } + }); + _pagingController.addPageRequestListener((pageKey) { + _fetchData(pageKey, _pageSize, null); + }); + super.initState(); + } + + Future initQuery() async { + //searchByField(''); + } + + @override + void dispose() { + _searchTextController.dispose(); + _searchFocusNode.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: ProductsAppBar( + title: 'Контрагенты', + ), + body: Column( + children: [ + InputField( + placeholder: 'Поиск по наименованию', + search: true, + controller: _searchTextController, + fieldFocusNode: _searchFocusNode, + ), + const ProductsTitleBarBar(title: 'Список контрагентов'), + Expanded( + child: PagedListView( + pagingController: _pagingController, + builderDelegate: + PagedChildBuilderDelegate( + itemBuilder: (BuildContext context, + ContragentResponseEntity entity, int index) { + return DictionaryTile( + key: Key('contragent_${entity.id}'), + onPress: () async { + _navigatorService.pop(entity); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + entity.name ?? '', + style: const TextStyle(fontSize: 12, color: textColor), + ), + Text('БИН/ИИН: ${entity.biniin}', + style: const TextStyle( + fontSize: 10, color: placeholderColor)), + ], + ), + ); + }, + ), + )) + ], + ), + ); + } + + void reset() { + _searchTextController.clear(); + } + + Future _fetchData(int pageKey, int perPage, String? query) async { + final List newItems = await _dictionaryService + .getContragents(page: pageKey, query: query, perpage: perPage); + + final isLastPage = newItems.length < _pageSize; + if (isLastPage) { + _pagingController.appendLastPage(newItems); + } else { + final nextPageKey = pageKey + newItems.length; + _pagingController.appendPage(newItems, nextPageKey); + } + } +} diff --git a/lib/views/work/views/add_product/add_product_view.dart b/lib/views/work/views/select_product/add_product_view.dart similarity index 91% rename from lib/views/work/views/add_product/add_product_view.dart rename to lib/views/work/views/select_product/add_product_view.dart index 33764ab..659c08e 100644 --- a/lib/views/work/views/add_product/add_product_view.dart +++ b/lib/views/work/views/select_product/add_product_view.dart @@ -15,12 +15,12 @@ import 'package:satu/widgets/fields/input_field.dart'; import '../../../../core/models/dictionary/good/good_response_entity.dart'; import 'component/add_product_list_item.dart'; -class AddProductView extends StatefulWidget { +class SelectProductView extends StatefulWidget { @override - _AddProductViewState createState() => _AddProductViewState(); + _SelectProductViewState createState() => _SelectProductViewState(); } -class _AddProductViewState extends State { +class _SelectProductViewState extends State { final DictionaryService _dictionaryService = locator(); final NavigatorService _navigatorService = locator(); late TextEditingController _searchTextController; @@ -121,8 +121,12 @@ class _AddProductViewState extends State { Future _fetchData(int pageKey, int perPage, String? query) async { final List newItems = await _dictionaryService.getGoods( page: pageKey, - filter: {'col': 'name', 'action': 'like', 'val': query ?? ''}, - perpage: perPage); + filter: [{'col': 'name', 'action': 'like', 'val': query ?? ''}, { + 'col': 'ean13', + 'action': 'like', + 'val': query ?? '' + }], + perpage: perPage, orGate: true); List items = newItems.map((e) => goodResponseToGood(e)).toList(); final isLastPage = newItems.length < _pageSize; diff --git a/lib/views/work/views/add_product/component/add_category_list_item.dart b/lib/views/work/views/select_product/component/add_category_list_item.dart similarity index 100% rename from lib/views/work/views/add_product/component/add_category_list_item.dart rename to lib/views/work/views/select_product/component/add_category_list_item.dart diff --git a/lib/views/work/views/add_product/component/add_product_list_item.dart b/lib/views/work/views/select_product/component/add_product_list_item.dart similarity index 100% rename from lib/views/work/views/add_product/component/add_product_list_item.dart rename to lib/views/work/views/select_product/component/add_product_list_item.dart diff --git a/lib/widgets/drawer/app_drawer.dart b/lib/widgets/drawer/app_drawer.dart index 0bc41ad..9134262 100644 --- a/lib/widgets/drawer/app_drawer.dart +++ b/lib/widgets/drawer/app_drawer.dart @@ -14,6 +14,7 @@ import 'package:satu/views/dictionaries/category/category_view.dart'; import 'package:satu/views/dictionaries/contragents/contragents_view.dart'; import 'package:satu/views/dictionaries/goods/goods_view.dart'; import 'package:satu/views/settings/setting_view.dart'; +import 'package:satu/views/stocks/stocks_view.dart'; import 'package:satu/views/work/work_view.dart'; import '../../views/inventarization/view/inventarization_view.dart'; @@ -50,9 +51,8 @@ class AppDrawer extends StatelessWidget { text: 'Остатки', onTap: () { Navigator.of(context).pop(); - Redux.store!.dispatch(navigateDrawer(AnalyticsView)); - }, - disable: true, + Redux.store!.dispatch(navigateDrawer(StocksView)); + } ), _createDrawerItem( svgFile: 'inventarization', diff --git a/lib/widgets/fields/line_tile.dart b/lib/widgets/fields/line_tile.dart index 5badfdc..a542387 100644 --- a/lib/widgets/fields/line_tile.dart +++ b/lib/widgets/fields/line_tile.dart @@ -6,7 +6,7 @@ import 'note_text.dart'; class LineTile extends StatelessWidget { const LineTile(this.text, - {required this.onTap, this.labelText, this.placeholder = 'Выберите'}); + {required this.onTap, this.labelText, this.placeholder = 'Выберите', Key? key}) : super(key: key); final String text; final String placeholder; diff --git a/pubspec.yaml b/pubspec.yaml index 42e7947..d7b1a7f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.0.0+1 +version: 1.0.1+2 environment: