diff --git a/lib/core/models/inventarization/good_item/good_item.dart b/lib/core/models/inventarization/good_item/good_item.dart new file mode 100644 index 0000000..f7ea3fc --- /dev/null +++ b/lib/core/models/inventarization/good_item/good_item.dart @@ -0,0 +1,38 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'good_item.g.dart'; + +@JsonSerializable(explicitToJson: true) +class GoodInventarization { + GoodInventarization({ + required this.id, + required this.eaccGoodId, + required this.name, + required this.cnt, + required this.price, + required this.cntBuh, + required this.priceBuh, + this.articul, + this.category, + this.ean13, + }); + + int id; + @JsonKey(name: 'eacc_good_id') + int eaccGoodId; + double cnt; + double price; + @JsonKey(name: 'cnt_buh') + double cntBuh; + @JsonKey(name: 'price_buh') + double priceBuh; + String name; + int? articul; + String? ean13; + String? category; + + factory GoodInventarization.fromJson(Map json) => + _$GoodInventarizationFromJson(json); + + Map toJson() => _$GoodInventarizationToJson(this); +} diff --git a/lib/core/models/inventarization/good_item/good_item.g.dart b/lib/core/models/inventarization/good_item/good_item.g.dart new file mode 100644 index 0000000..66c5cd1 --- /dev/null +++ b/lib/core/models/inventarization/good_item/good_item.g.dart @@ -0,0 +1,36 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'good_item.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +GoodInventarization _$GoodInventarizationFromJson(Map json) => + GoodInventarization( + id: json['id'] as int, + eaccGoodId: json['eacc_good_id'] as int, + name: json['name'] as String, + cnt: (json['cnt'] as num).toDouble(), + price: (json['price'] as num).toDouble(), + cntBuh: (json['cnt_buh'] as num).toDouble(), + priceBuh: (json['price_buh'] as num).toDouble(), + articul: json['articul'] as int?, + category: json['category'] as String?, + ean13: json['ean13'] as String?, + ); + +Map _$GoodInventarizationToJson( + GoodInventarization instance) => + { + 'id': instance.id, + 'eacc_good_id': instance.eaccGoodId, + 'cnt': instance.cnt, + 'price': instance.price, + 'cnt_buh': instance.cntBuh, + 'price_buh': instance.priceBuh, + 'name': instance.name, + 'articul': instance.articul, + 'ean13': instance.ean13, + 'category': instance.category, + }; diff --git a/lib/core/models/inventarization/invetarization_good_list/invetarization_good_list.dart b/lib/core/models/inventarization/invetarization_good_list/invetarization_good_list.dart new file mode 100644 index 0000000..d72a572 --- /dev/null +++ b/lib/core/models/inventarization/invetarization_good_list/invetarization_good_list.dart @@ -0,0 +1,17 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:satu/core/models/inventarization/good_item/good_item.dart'; + +part 'invetarization_good_list.g.dart'; +@JsonSerializable(explicitToJson: true) +class InventarizationGoodList { + InventarizationGoodList({ + required this.goodsList +}); + + @JsonKey(name: 'goods_list') + List goodsList; + +factory InventarizationGoodList.fromJson(Map json) => _$InventarizationGoodListFromJson(json); + +Map toJson() => _$InventarizationGoodListToJson(this); +} \ No newline at end of file diff --git a/lib/core/models/inventarization/invetarization_good_list/invetarization_good_list.g.dart b/lib/core/models/inventarization/invetarization_good_list/invetarization_good_list.g.dart new file mode 100644 index 0000000..c6cf051 --- /dev/null +++ b/lib/core/models/inventarization/invetarization_good_list/invetarization_good_list.g.dart @@ -0,0 +1,21 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'invetarization_good_list.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +InventarizationGoodList _$InventarizationGoodListFromJson( + Map json) => + InventarizationGoodList( + goodsList: (json['goods_list'] as List) + .map((e) => GoodInventarization.fromJson(e as Map)) + .toList(), + ); + +Map _$InventarizationGoodListToJson( + InventarizationGoodList instance) => + { + 'goods_list': instance.goodsList.map((e) => e.toJson()).toList(), + }; diff --git a/lib/core/models/inventarization/inventarization_response.dart b/lib/core/models/inventarization/response/inventarization_response.dart similarity index 100% rename from lib/core/models/inventarization/inventarization_response.dart rename to lib/core/models/inventarization/response/inventarization_response.dart diff --git a/lib/core/models/inventarization/inventarization_response.g.dart b/lib/core/models/inventarization/response/inventarization_response.g.dart similarity index 100% rename from lib/core/models/inventarization/inventarization_response.g.dart rename to lib/core/models/inventarization/response/inventarization_response.g.dart diff --git a/lib/core/models/response/response_entity.dart b/lib/core/models/response/response_entity.dart index db13b52..fa6f748 100644 --- a/lib/core/models/response/response_entity.dart +++ b/lib/core/models/response/response_entity.dart @@ -48,6 +48,7 @@ class ResponseOriginal { int? page; int? perpage; List? data; + dynamic result; Map>? errors; String? message; diff --git a/lib/core/models/response/response_entity.g.dart b/lib/core/models/response/response_entity.g.dart index 168f124..a14b9eb 100644 --- a/lib/core/models/response/response_entity.g.dart +++ b/lib/core/models/response/response_entity.g.dart @@ -33,6 +33,7 @@ ResponseOriginal _$ResponseOriginalFromJson(Map json) => ..page = json['page'] as int? ..perpage = json['perpage'] as int? ..data = json['data'] as List? + ..result = json['result'] ..errors = (json['errors'] as Map?)?.map( (k, e) => MapEntry(k, (e as List).map((e) => e as String).toList()), @@ -45,6 +46,7 @@ Map _$ResponseOriginalToJson(ResponseOriginal instance) => 'page': instance.page, 'perpage': instance.perpage, 'data': instance.data, + 'result': instance.result, 'errors': instance.errors, 'message': instance.message, }; diff --git a/lib/core/services/api_service.dart b/lib/core/services/api_service.dart index 2a659a7..c1975e7 100644 --- a/lib/core/services/api_service.dart +++ b/lib/core/services/api_service.dart @@ -51,8 +51,8 @@ class ApiService extends BaseService { // log.i(host); // log.i(url); // log.i(headers); - log.i(jsonEncode(requestBody)); - log.i(jsonEncode(response.body)); + //log.i(jsonEncode(requestBody)); + //log.i(jsonEncode(response.body)); } return response.body; } diff --git a/lib/core/services/dialog_service.dart b/lib/core/services/dialog_service.dart index 64f1499..406214d 100644 --- a/lib/core/services/dialog_service.dart +++ b/lib/core/services/dialog_service.dart @@ -54,8 +54,8 @@ class DialogService { Future showConfirmationDialogInput({ required String title, - required String requestPrice, required String requestCount, + String? requestPrice, String? description, String confirmationTitle = 'ПОДТВЕРДИТЬ', String cancelTitle = 'Отмена', diff --git a/lib/core/services/dictionary_service.dart b/lib/core/services/dictionary_service.dart index 5e83f93..f088b7c 100644 --- a/lib/core/services/dictionary_service.dart +++ b/lib/core/services/dictionary_service.dart @@ -11,24 +11,26 @@ import 'package:satu/core/utils/locator.dart'; import 'api_service.dart'; import 'db_service.dart'; +Good goodResponseToGood(GoodResponseEntity response) { + return Good() + ..id = response.id + ..name = response.name + ..basePrice = response.basePrice + ..optPrice = response.optPrice + ..price = response.price + ..articul = response.articul + ..categoryId = response.categoryId + ..categoryName = response.categoryName + ..divisible = response.divisible + ..ean = response.ean13 + ..appCompanyId = response.appCompanyId; +} + class DictionaryService extends BaseService { final ApiService _api = locator(); final DbService _db = locator(); - Good goodResponseToGood(GoodResponseEntity response) { - return Good() - ..id = response.id - ..name = response.name - ..basePrice = response.basePrice - ..optPrice = response.optPrice - ..price = response.price - ..articul = response.articul - ..categoryId = response.categoryId - ..categoryName = response.categoryName - ..divisible = response.divisible - ..ean = response.ean13 - ..appCompanyId = response.appCompanyId; - } + Category categoryResponseToCategory(CategoryResponse response) { return Category() diff --git a/lib/core/services/inventarization_service.dart b/lib/core/services/inventarization_service.dart index 33ec9e7..3b67525 100644 --- a/lib/core/services/inventarization_service.dart +++ b/lib/core/services/inventarization_service.dart @@ -1,11 +1,13 @@ import 'package:satu/core/base/base_service.dart'; -import 'package:satu/core/models/inventarization/inventarization_response.dart'; +import 'package:satu/core/models/inventarization/invetarization_good_list/invetarization_good_list.dart'; +import 'package:satu/core/models/inventarization/response/inventarization_response.dart'; import 'package:satu/core/utils/locator.dart'; +import '../models/inventarization/good_item/good_item.dart'; import '../models/response/response_entity.dart'; import 'api_service.dart'; -class InventarizationService extends BaseService { +class InventarizationService extends BaseService { final ApiService _api = locator(); Future> getList( @@ -17,12 +19,13 @@ class InventarizationService extends BaseService { 'perpage': perpage }; - ResponseEntity categories = await _api - .postRequest('/goods_inventory_get', requestBody: requestBody); + ResponseEntity categories = await _api.postRequest('/goods_inventory_get', + requestBody: requestBody); if (categories.original.data != null && categories.original.data!.isNotEmpty) { for (final dynamic map in categories.original.data!) { - final InventarizationResponse item = InventarizationResponse.fromJson(map); + final InventarizationResponse item = + InventarizationResponse.fromJson(map); list.add(item); } } @@ -32,4 +35,63 @@ class InventarizationService extends BaseService { return list; } -} \ No newline at end of file + Future> getGoodByInventarizationId( + {required int page, required int perpage, required int id}) async { + List list = []; + try { + final Map requestBody = { + 'page': page, + 'perpage': perpage, + 'id': id + }; + + ResponseEntity categories = await _api.postRequest( + '/goods_inventory_get_edit_form_data', + requestBody: requestBody); + if (categories.original.result != null) { + InventarizationGoodList listResponse = InventarizationGoodList.fromJson(categories.original.result); + list.addAll(listResponse.goodsList); + } + } catch (e, stack) { + log.e('getList', e, stack); + } + return list; + } + + Future addGoodToList(int inventoryId, int? goodId) async { + bool result = false; + try { + final Map requestBody = { + 'page': 1, + 'perpage': 1, + 'id': inventoryId, + 'good_id' : goodId + }; + ResponseEntity response = await _api.postRequest( + '/goods_inventory_select_item', + requestBody: requestBody); + result = response.original.result != null; + } catch (e, stack) { + log.e('getList', e, stack); + } + return result ; + } + + Future setCountToItem(int inventoryId, int inventoryItemId, double value) async { + bool result = false; + try { + final Map requestBody = { + 'id': inventoryId, + 'inventory_item_id' : inventoryItemId, + 'cnt_buh': value + }; + ResponseEntity response = await _api.postRequest( + '/goods_inventory_set_cnt_item', + requestBody: requestBody); + result = response.original.result != null; + } catch (e, stack) { + log.e('getList', e, stack); + } + return result ; + } +} diff --git a/lib/routes/route_names.dart b/lib/routes/route_names.dart index 47d8710..9a2226a 100644 --- a/lib/routes/route_names.dart +++ b/lib/routes/route_names.dart @@ -20,6 +20,9 @@ const String goodsDictionaryViewRoute = 'goodsDictionaryViewRoute'; const String contragentSelectViewRoute = 'ContragentSelectViewRoute'; const String contragentEditRoute = 'contragentEditRoute'; +// inventarization +const String inventarizationEditRoute = 'inventarizationEditRoute'; + // setting - ble printer const String settingPrinterBluetoothViewRoute = 'SettingPrinterBluetoothView'; const String settingPrinterBluetoothSelectViewRoute = diff --git a/lib/routes/router.dart b/lib/routes/router.dart index 78d61f1..3980f42 100644 --- a/lib/routes/router.dart +++ b/lib/routes/router.dart @@ -6,6 +6,7 @@ import 'package:satu/views/dictionaries/category/category_edit.dart'; import 'package:satu/views/dictionaries/category/category_select_view.dart'; import 'package:satu/views/dictionaries/contragents/contragents_edit.dart'; import 'package:satu/views/dictionaries/goods/goods_edit.dart'; +import 'package:satu/views/inventarization/view/inventarization_edit_view.dart'; import 'package:satu/views/login/login_view.dart'; import 'package:satu/views/main/main_view.dart'; import 'package:satu/views/settings/printer_bluetooth/printer_encoding_select.dart'; @@ -19,6 +20,7 @@ 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/work_view.dart'; +import '../core/models/inventarization/response/inventarization_response.dart'; import './route_names.dart'; Route generateRoute(RouteSettings settings) { @@ -117,6 +119,14 @@ Route generateRoute(RouteSettings settings) { transactionId: data, ), ); + case inventarizationEditRoute: + final InventarizationResponse inventarizationResponse = settings.arguments! as InventarizationResponse; + return _getPageRoute( + routeName: settings.name, + viewToShow: InventarizationEditView( + item: inventarizationResponse, + ), + ); default: return MaterialPageRoute( builder: (_) => Scaffold( diff --git a/lib/views/dictionaries/goods/goods_view.dart b/lib/views/dictionaries/goods/goods_view.dart index 3679aae..b2550b8 100644 --- a/lib/views/dictionaries/goods/goods_view.dart +++ b/lib/views/dictionaries/goods/goods_view.dart @@ -109,6 +109,7 @@ class _GoodsDictionaryViewState extends State { itemBuilder: (BuildContext context, GoodResponseEntity good, int index) { return DictionaryTile( + key: Key('good_${good.id}'), onPress: () async { final dynamic result = await _navigatorService .push(goodsEditRoute, arguments: good); diff --git a/lib/views/inventarization/view/inventarization_edit_view.dart b/lib/views/inventarization/view/inventarization_edit_view.dart new file mode 100644 index 0000000..18e4d62 --- /dev/null +++ b/lib/views/inventarization/view/inventarization_edit_view.dart @@ -0,0 +1,241 @@ +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/services/dialog_service.dart'; +import 'package:satu/views/inventarization/widget/good_inventarization_list_item.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'; + +class InventarizationEditView extends StatefulWidget { + const InventarizationEditView({ + required this.item, + Key? key, + }) : super(key: key); + final InventarizationResponse item; + + @override + State createState() => + _InventarizationEditViewState(); +} + +class _InventarizationEditViewState extends State { + final InventarizationService _service = locator(); + final NavigatorService _navigatorService = locator(); + final DialogService _dialogService = locator(); + + static const _pageSize = 20; + int _pageCurrent = 1; + bool _isLastPage = false; + + final PagingController _pagingController = + PagingController(firstPageKey: 1); + + @override + void initState() { + _pagingController.addPageRequestListener((pageKey) { + _pageCurrent = pageKey; + _fetchData(pageKey, _pageSize); + }); + super.initState(); + } + @override + void dispose() { + _pagingController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const ProductsAppBar( + title: 'Создание инвентаризации', + drawerShow: false, + ), + body: Column( + children: [ + 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( + itemBuilder: (BuildContext context, GoodInventarization item, + int index) { + return GoodInventarizationListItem( + key: Key( + 'good_inv_${item.id}', + ), + inventoryId: widget.item.id, + inventoryItemId: item.id, + name: item.name, + categoryName: item.category, + count: item.cntBuh, + price: item.priceBuh, + ean: item.ean13, + isOdd: index % 2 == 0, + refresh: () { + _refreshData(_pageCurrent, _pageSize); + }, + ); + }, + ), + ), + ), + ], + ), + floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, + floatingActionButton: floatingActionButtonRender(), + ); + } + + /// render floating buttons + Widget floatingActionButtonRender() { + return Padding( + padding: EdgeInsets.all(15), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Visibility( + visible: true, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: FloatingActionButton( + mini: true, + elevation: 2, + backgroundColor: successColor, + onPressed: () {}, + child: Icon( + Icons.check, + color: whiteColor, + size: 35, + ), + ), + )), + Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + FloatingActionButton( + elevation: 2, + mini: true, + onPressed: () async { + final Good? good = await locator() + .push(addProductViewRoute) as Good?; + if (good != null) { + bool result = + await _service.addGoodToList(widget.item.id, good.id); + if (result) { + _pagingController.refresh(); + } else { + _dialogService.showDialog( + description: 'Товара отсутсвует в остатке'); + } + } + }, + child: Icon( + Icons.add_rounded, + size: 40, + color: whiteColor, + ), + ), + verticalSpaceSmall, + FloatingActionButton( + elevation: 2, + mini: true, + onPressed: () async { + final NavigatorService _nav = locator(); + final dynamic result = await _nav.push(addByBarcodeViewRoute); + if (result != null) { + final List goods = await locator() + .getGoodsByNameOrEan(result as String); + if (goods.isNotEmpty) { + + } + } + }, + child: Icon(Icons.qr_code_rounded, size: 30, color: whiteColor), + ), + ], + ) + ], + ), + ); + } + + Future _fetchData(int pageKey, int perPage) async { + final List newItems = + await _service.getGoodByInventarizationId( + page: pageKey, perpage: perPage, id: widget.item.id); + _isLastPage = newItems.length < _pageSize; + _pageCurrent = pageKey; + if (_isLastPage) { + _pagingController.appendLastPage(newItems); + } else { + final nextPageKey = pageKey + 1; + _pageCurrent = nextPageKey; + _pagingController.appendPage(newItems, nextPageKey); + } + } + + Future _refreshData(int pageKey, int perPage) async { + print('${pageKey} - ${perPage}'); + final List newItems = + await _service.getGoodByInventarizationId( + page: pageKey, perpage: perPage, id: widget.item.id); + final List oldList = _pagingController.value.itemList ?? []; + oldList.setAll((pageKey - 1) * perPage , newItems); + setState(() { + _pagingController.itemList = oldList; + }); + } + + +} diff --git a/lib/views/inventarization/view/inventarization_view.dart b/lib/views/inventarization/view/inventarization_view.dart index 373b5ff..7e0b391 100644 --- a/lib/views/inventarization/view/inventarization_view.dart +++ b/lib/views/inventarization/view/inventarization_view.dart @@ -1,14 +1,18 @@ import 'package:flutter/material.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; -import 'package:satu/core/models/inventarization/inventarization_response.dart'; +import 'package:intl/intl.dart'; +import 'package:satu/core/models/inventarization/response/inventarization_response.dart'; import 'package:satu/core/services/inventarization_service.dart'; +import 'package:satu/core/services/navigator_service.dart'; import 'package:satu/core/utils/locator.dart'; import 'package:satu/shared/app_colors.dart'; import 'package:satu/shared/shared_styles.dart'; import 'package:satu/shared/ui_helpers.dart'; import 'package:satu/views/inventarization/widget/inventarization_list_tile.dart'; +import '../../../routes/route_names.dart'; import '../../../widgets/bar/products_app_bar.dart'; +import '../../dictionaries/component/dictionary_list_tile.dart'; class InventarizationView extends StatefulWidget { const InventarizationView({Key? key}) : super(key: key); @@ -20,12 +24,15 @@ class InventarizationView extends StatefulWidget { class _InventarizationViewState extends State { final InventarizationService _service = locator(); + final NavigatorService _navigatorService = locator(); static const _pageSize = 20; final PagingController _pagingController = PagingController(firstPageKey: 1); + final DateFormat formatterDay = DateFormat('dd.MM.yyyy'); + @override void initState() { _pagingController.addPageRequestListener((pageKey) { @@ -89,16 +96,37 @@ class _InventarizationViewState extends State { builderDelegate: PagedChildBuilderDelegate( itemBuilder: (BuildContext context, InventarizationResponse item, int index) { - return InventarizationListTile( + return DictionaryTile( key: Key('category_${item.id}'), - // onPress: () async { - // final dynamic result = await _navigatorService - // .push(categoryEditRoute, arguments: item); - // if (result != null && true == (result as bool)) { - // _pagingController.refresh(); - // } - // }, - item: item, + onPress: () async { + final dynamic result = await _navigatorService + .push(inventarizationEditRoute, arguments: item); + if (result != null && true == (result as bool)) { + _pagingController.refresh(); + } + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + flex: 2, + child: InventarizationCellTile( + value: item.docNumber.toString()), + ), + Expanded( + flex: 2, + child: InventarizationCellTile( + value: formatterDay.format(item.createdAt), + ), + ), + Expanded( + flex: 1, + child: InventarizationCellButton( + value: item.act, + ), + ), + ], + ), ); }, ), diff --git a/lib/views/inventarization/widget/good_inventarization_list_item.dart b/lib/views/inventarization/widget/good_inventarization_list_item.dart new file mode 100644 index 0000000..d9067b3 --- /dev/null +++ b/lib/views/inventarization/widget/good_inventarization_list_item.dart @@ -0,0 +1,140 @@ +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 { + const GoodInventarizationListItem({ + required Key key, + required this.price, + required this.count, + required this.inventoryItemId, + required this.inventoryId, + required this.refresh, + this.name = '', + this.ean, + this.categoryName, + this.isOdd = true, + this.transactionId, + + }) : super(key: key); + final int inventoryItemId; + final int inventoryId; + final String name; + final String? ean; + final String? categoryName; + final double price; + final double count; + final bool isOdd; + final int? transactionId; + + final void Function() refresh; + + @override + _GoodInventarizationListItemState createState() => + _GoodInventarizationListItemState(); +} + +class _GoodInventarizationListItemState + extends State { + final DialogService _dialogService = locator(); + final InventarizationService _service = locator(); + + @override + Widget build(BuildContext context) { + return Dismissible( + background: Container( + alignment: AlignmentDirectional.centerEnd, + color: dangerColor, + child: const Padding( + padding: EdgeInsets.all(8.0), + child: Text( + 'Удалить', + style: TextStyle(color: whiteColor, fontWeight: FontWeight.w500), + ), + ), + ), + direction: DismissDirection.endToStart, + confirmDismiss: (DismissDirection direction) async { + final DialogResponse response = + await _dialogService.showConfirmationDialog( + title: 'Внимание', + description: 'Удалить из списка товар ' + '"${widget.name}"' + ' - ${widget.count} ед. ?', + confirmationTitle: 'Удалить', + cancelTitle: 'Отмена'); + + return response.confirmed; + }, + onDismissed: (direction) {}, + key: widget.key!, + child: ListTile( + key: widget.key!, + onTap: () { + editProductModal(); + }, + 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: !widget.isOdd ? whiteColor : whiteColor, + ), + ); + } + + Future editProductModal() async { + final DialogResponse response = + await _dialogService.showConfirmationDialogInput( + title: widget.name, + requestCount: formatDecimal(widget.count), + ); + if (response.confirmed) { + if (isNumeric(response.responseCount)) { + bool result = await _service.setCountToItem(widget.inventoryId, widget.inventoryItemId, double.parse(response.responseCount!)); + if(result) { + widget.refresh(); + } else { + _dialogService.showDialog(description: 'Что то пошло не так!'); + } + } else { + _dialogService.showDialog(description: 'Не верный формат'); + } + } + } +} diff --git a/lib/views/inventarization/widget/inventarization_list_tile.dart b/lib/views/inventarization/widget/inventarization_list_tile.dart index 38566ac..1906056 100644 --- a/lib/views/inventarization/widget/inventarization_list_tile.dart +++ b/lib/views/inventarization/widget/inventarization_list_tile.dart @@ -1,52 +1,8 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; -import 'package:satu/core/models/inventarization/inventarization_response.dart'; +import 'package:satu/core/models/inventarization/response/inventarization_response.dart'; import 'package:satu/shared/app_colors.dart'; -class InventarizationListTile extends StatefulWidget { - const InventarizationListTile({required this.item, Key? key}) - : super(key: key); - - final InventarizationResponse item; - - @override - State createState() => - _InventarizationListTileState(); -} - -class _InventarizationListTileState extends State { - final DateFormat formatterDay = DateFormat('dd.MM.yyyy'); - - @override - Widget build(BuildContext context) { - return Container( - decoration: BoxDecoration(color: whiteColor), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - flex: 2, - child: InventarizationCellTile( - value: widget.item.docNumber.toString()), - ), - Expanded( - flex: 2, - child: InventarizationCellTile( - value: formatterDay.format(widget.item.createdAt), - ), - ), - Expanded( - flex: 1, - child: InventarizationCellButton( - value: formatterDay.format(widget.item.createdAt), - ), - ), - ], - ), - ); - } -} - class InventarizationCellTile extends StatelessWidget { const InventarizationCellTile({ required this.value, @@ -58,7 +14,6 @@ class InventarizationCellTile extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - padding: EdgeInsets.all(15.0), child: Text( value, style: TextStyle(fontSize: 12), @@ -73,20 +28,24 @@ class InventarizationCellButton extends StatelessWidget { Key? key, }) : super(key: key); - final String value; + final int? value; @override Widget build(BuildContext context) { + + + return Padding( padding: const EdgeInsets.only( right: 15.0), - child: OutlinedButton( - onPressed: () {}, + child: ElevatedButton( + onPressed: value == null ? () {} : () {}, + child: Text( 'Акт', style: TextStyle(fontSize: 12, color: whiteColor), ), - style: OutlinedButton.styleFrom( - backgroundColor: blueColor, + style: ElevatedButton.styleFrom( + backgroundColor: value == null ? disableColor : blueColor ), ), ); diff --git a/lib/views/work/tabs/component/product_list_item.dart b/lib/views/work/tabs/component/product_list_item.dart index c249898..7b4075e 100644 --- a/lib/views/work/tabs/component/product_list_item.dart +++ b/lib/views/work/tabs/component/product_list_item.dart @@ -78,7 +78,7 @@ class _ProductListItemState extends State { }, key: Key(widget.name), child: ListTile( - //onTap: () => _onItemTapped(context), + key: Key(widget.name), onTap: () { editProductModal(); }, diff --git a/lib/views/work/tabs/sell_view.dart b/lib/views/work/tabs/sell_view.dart index ed0ae70..406e73c 100644 --- a/lib/views/work/tabs/sell_view.dart +++ b/lib/views/work/tabs/sell_view.dart @@ -16,6 +16,9 @@ import 'package:satu/widgets/bar/products_header_bar.dart'; import 'package:satu/widgets/bar/products_title_bar.dart'; import 'package:satu/views/work/tabs/utils/product_utils.dart'; +import '../../../core/entity/goods_entity.dart'; +import '../../../core/redux/actions/sell_actions.dart'; +import '../../../core/services/dictionary_service.dart'; import 'component/contagent_select_bar.dart'; class SellView extends StatelessWidget { @@ -95,7 +98,7 @@ class SellView extends StatelessWidget { converter: (Store store) => store.state.sellState!, builder: (_, SellState snapshot) { return Padding( - padding: EdgeInsets.all(15.w), + padding: EdgeInsets.all(15), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.end, @@ -108,7 +111,7 @@ class SellView extends StatelessWidget { backgroundColor: successColor, onPressed: () => locator().push(paymentViewRoute), - child: Icon(Icons.check, color: whiteColor, size: 35.sp), + child: Icon(Icons.check, color: whiteColor, size: 35), )), Column( mainAxisAlignment: MainAxisAlignment.end, @@ -116,11 +119,16 @@ class SellView extends StatelessWidget { FloatingActionButton( elevation: 2, mini: true, - onPressed: () => - locator().push(addProductViewRoute), + onPressed: () async { + final Good? good = await locator().push(addProductViewRoute) as Good?; + if(good !=null) { + Redux.store!.dispatch(addSellItem(good: good)); + } + + }, child: Icon( Icons.add_rounded, - size: 40.sp, + size: 40, color: whiteColor, ), ), @@ -134,17 +142,17 @@ class SellView extends StatelessWidget { final dynamic result = await _nav.push(addByBarcodeViewRoute); if (result != null) { - // final List goods = - // await locator() - // .getGoodsByEan(result as String); - // if (goods.isNotEmpty) { - // Redux.store - // ?.dispatch(addSellItem(good: goods.first)); - // } + final List goods = + await locator() + .getGoodsByNameOrEan(result as String); + if (goods.isNotEmpty) { + Redux.store + ?.dispatch(addSellItem(good: goods.first)); + } } }, child: Icon(Icons.qr_code_rounded, - size: 30.sp, color: whiteColor), + size: 30, color: whiteColor), ), ], ) diff --git a/lib/views/work/views/add_by_barcode/add_by_barcode_view.dart b/lib/views/work/views/add_by_barcode/add_by_barcode_view.dart index 3a5998e..458200c 100644 --- a/lib/views/work/views/add_by_barcode/add_by_barcode_view.dart +++ b/lib/views/work/views/add_by_barcode/add_by_barcode_view.dart @@ -16,7 +16,6 @@ class AddByBarcodeView extends StatefulWidget { } class _AddByBarcodeViewState extends State { - String _code = ''; @override Widget build(BuildContext context) { return Scaffold( @@ -30,11 +29,6 @@ class _AddByBarcodeViewState extends State { resultCallback: (String code) { final NavigatorService _nav = locator(); _nav.pop(code); - //Navigator.pop(context, code); - - // setState(() { - // _code = code; - // }); }, ), ), diff --git a/lib/views/work/views/add_product/add_product_view.dart b/lib/views/work/views/add_product/add_product_view.dart index 939e6fb..33764ab 100644 --- a/lib/views/work/views/add_product/add_product_view.dart +++ b/lib/views/work/views/add_product/add_product_view.dart @@ -1,18 +1,18 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; -import 'package:satu/core/entity/category_entity.dart'; +import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:satu/core/entity/goods_entity.dart'; -import 'package:satu/core/redux/actions/sell_actions.dart'; -import 'package:satu/core/redux/store.dart'; import 'package:satu/core/services/dictionary_service.dart'; import 'package:satu/core/services/navigator_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/views/work/views/add_product/component/add_category_list_item.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/good/good_response_entity.dart'; import 'component/add_product_list_item.dart'; class AddProductView extends StatefulWidget { @@ -24,27 +24,30 @@ class _AddProductViewState extends State { final DictionaryService _dictionaryService = locator(); final NavigatorService _navigatorService = locator(); late TextEditingController _searchTextController; - final FocusNode _searchFocusNode = new FocusNode(); + final FocusNode _searchFocusNode = FocusNode(); + static const _pageSize = 20; + String query = ''; + Timer? _debounce; - List? _history; - List? _categories; - List? _goods; + final PagingController _pagingController = + PagingController(firstPageKey: 1); @override void initState() { _searchTextController = TextEditingController(); _searchTextController.addListener(() { - if (_searchTextController.text.isNotEmpty) { - searchByField(_searchTextController.text); - } else { - reset(); - } + 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); }); - _history = [Category()..id = null]; - _categories = []; - _goods = []; super.initState(); - navigateCategory(null); } @override @@ -56,8 +59,7 @@ class _AddProductViewState extends State { @override Widget build(BuildContext context) { - int catSize = _categories?.length ?? 0; - int goodSize = _goods?.length ?? 0; + return Scaffold( appBar: ProductsAppBar( title: 'Категория', @@ -72,39 +74,33 @@ class _AddProductViewState extends State { ), verticalSpaceTiny, ProductsTitleBarBar( - title: goodSize > 0 ? 'Выберите товар' : 'Выберите категорию', + title: 'Выберите товар', ), Expanded( - child: ListView.separated( - physics: BouncingScrollPhysics(), - itemCount: catSize + goodSize, - itemBuilder: (BuildContext context, int index) { - if (index < catSize) { - Category category = _categories![index]; - return AddCategoryListItem( - name: category.name, - key: Key('category_${category.id}'), - onPress: () => onCategoryPress(category), - ); - } - Good good = _goods![index - catSize]; - return AddProductListItem( - key: Key('product_${good.id}'), - ean: good.ean, - name: good.name, - price: good.price, - categoryName: _history?.last.name, - onPress: () { - onGoodPress(good); - }, - ); - }, + child: PagedListView.separated( + physics: const BouncingScrollPhysics(), separatorBuilder: (BuildContext context, int index) { - return Divider( + return const Divider( height: 1.0, color: disableColor, ); }, + pagingController: _pagingController, + builderDelegate: PagedChildBuilderDelegate( + itemBuilder: + (BuildContext context, Good good, int index) { + return AddProductListItem( + key: Key('product_${good.id}'), + ean: good.ean, + name: good.name, + price: good.price, + categoryName: good.categoryName, + onPress: () { + onGoodPress(good); + }, + ); + }, + ), ), ), ], @@ -112,39 +108,29 @@ class _AddProductViewState extends State { ); } - void onCategoryPress(Category category) { - _history!.add(category); - navigateCategory(category.id!); - } + void onGoodPress(Good good) { - Redux.store!.dispatch(addSellItem(good: good)); - _navigatorService.pop(); + _navigatorService.pop(good); } void reset() { - _history = [Category()..id = 0]; - navigateCategory(0); _searchTextController.clear(); } - void navigateCategory(int? categoryId) async { - List categories = - await _dictionaryService.getCategoryByParentId(categoryId); - List goods = - await _dictionaryService.getGoodsByCategoryId(categoryId); - setState(() { - _categories = categories; - _goods = goods; - }); - } + 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); - void searchByField(String query) async { - List categories = []; - List goods = await _dictionaryService.getGoodsByNameOrEan(query); - setState(() { - _categories = categories; - _goods = goods; - }); + List items = newItems.map((e) => goodResponseToGood(e)).toList(); + final isLastPage = newItems.length < _pageSize; + if (isLastPage) { + _pagingController.appendLastPage(items); + } else { + final nextPageKey = pageKey + 1; + _pagingController.appendPage(items, nextPageKey); + } } } diff --git a/lib/views/work/views/add_product/component/add_product_list_item.dart b/lib/views/work/views/add_product/component/add_product_list_item.dart index 4938b99..660184a 100644 --- a/lib/views/work/views/add_product/component/add_product_list_item.dart +++ b/lib/views/work/views/add_product/component/add_product_list_item.dart @@ -25,6 +25,7 @@ class AddProductListItem extends StatelessWidget { @override Widget build(BuildContext context) { return Material( + key: key, color: Colors.transparent, child: InkWell( onTap: () => onPress!(), @@ -51,7 +52,7 @@ class AddProductListItem extends StatelessWidget { Text( '$price ₸', style: TextStyle( - fontSize: ScreenUtil().setSp(20.0), + fontSize: 15, fontWeight: FontWeight.bold), ), ], diff --git a/lib/widgets/dialog/dialog_manager.dart b/lib/widgets/dialog/dialog_manager.dart index e5cc659..29d84a5 100644 --- a/lib/widgets/dialog/dialog_manager.dart +++ b/lib/widgets/dialog/dialog_manager.dart @@ -132,13 +132,16 @@ class _DialogManagerState extends State { verticalSpaceSmall, const Divider(), verticalSpaceSmall, - InputFieldRounded( - controller: _controllerPrice, - placeholder: '', - suffixText: '₸', - labelText: 'Стоимость товара', - textInputType: - const TextInputType.numberWithOptions(decimal: true), + Visibility( + visible: request.requestPrice != null, + child: InputFieldRounded( + controller: _controllerPrice, + placeholder: '', + suffixText: '₸', + labelText: 'Стоимость товара', + textInputType: + const TextInputType.numberWithOptions(decimal: true), + ), ), InputFieldRounded( textInputType: diff --git a/lib/widgets/ui/product_title_widget.dart b/lib/widgets/ui/product_title_widget.dart index 0ed812c..ae137d7 100644 --- a/lib/widgets/ui/product_title_widget.dart +++ b/lib/widgets/ui/product_title_widget.dart @@ -18,9 +18,9 @@ class ProductTitleWidget extends StatelessWidget { children: [ Text( name, - style: const TextStyle(fontSize: 14, color: textColor), + style: const TextStyle(fontSize: 12, color: textColor), overflow: TextOverflow.ellipsis, - maxLines: 2, + maxLines: 1, ), verticalSpaceTiny, if (ean != null)