Инвентаризация ++

null-safety-migration
suvaysov 2022-11-15 16:40:23 +06:00
parent 94db86ee66
commit e461fb5662
26 changed files with 737 additions and 184 deletions

View File

@ -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<String, dynamic> json) =>
_$GoodInventarizationFromJson(json);
Map<String, dynamic> toJson() => _$GoodInventarizationToJson(this);
}

View File

@ -0,0 +1,36 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'good_item.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
GoodInventarization _$GoodInventarizationFromJson(Map<String, dynamic> 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<String, dynamic> _$GoodInventarizationToJson(
GoodInventarization instance) =>
<String, dynamic>{
'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,
};

View File

@ -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<GoodInventarization> goodsList;
factory InventarizationGoodList.fromJson(Map<String, dynamic> json) => _$InventarizationGoodListFromJson(json);
Map<String, dynamic> toJson() => _$InventarizationGoodListToJson(this);
}

View File

@ -0,0 +1,21 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'invetarization_good_list.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
InventarizationGoodList _$InventarizationGoodListFromJson(
Map<String, dynamic> json) =>
InventarizationGoodList(
goodsList: (json['goods_list'] as List<dynamic>)
.map((e) => GoodInventarization.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> _$InventarizationGoodListToJson(
InventarizationGoodList instance) =>
<String, dynamic>{
'goods_list': instance.goodsList.map((e) => e.toJson()).toList(),
};

View File

@ -48,6 +48,7 @@ class ResponseOriginal {
int? page;
int? perpage;
List<dynamic>? data;
dynamic result;
Map<String, List<String>>? errors;
String? message;

View File

@ -33,6 +33,7 @@ ResponseOriginal _$ResponseOriginalFromJson(Map<String, dynamic> json) =>
..page = json['page'] as int?
..perpage = json['perpage'] as int?
..data = json['data'] as List<dynamic>?
..result = json['result']
..errors = (json['errors'] as Map<String, dynamic>?)?.map(
(k, e) =>
MapEntry(k, (e as List<dynamic>).map((e) => e as String).toList()),
@ -45,6 +46,7 @@ Map<String, dynamic> _$ResponseOriginalToJson(ResponseOriginal instance) =>
'page': instance.page,
'perpage': instance.perpage,
'data': instance.data,
'result': instance.result,
'errors': instance.errors,
'message': instance.message,
};

View File

@ -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;
}

View File

@ -54,8 +54,8 @@ class DialogService {
Future<DialogResponse> showConfirmationDialogInput({
required String title,
required String requestPrice,
required String requestCount,
String? requestPrice,
String? description,
String confirmationTitle = 'ПОДТВЕРДИТЬ',
String cancelTitle = 'Отмена',

View File

@ -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<ApiService>();
final DbService _db = locator<DbService>();
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()

View File

@ -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<ApiService>();
Future<List<InventarizationResponse>> 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;
}
}
Future<List<GoodInventarization>> getGoodByInventarizationId(
{required int page, required int perpage, required int id}) async {
List<GoodInventarization> list = [];
try {
final Map<String, dynamic> requestBody = <String, dynamic>{
'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<bool> addGoodToList(int inventoryId, int? goodId) async {
bool result = false;
try {
final Map<String, dynamic> requestBody = <String, dynamic>{
'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<bool> setCountToItem(int inventoryId, int inventoryItemId, double value) async {
bool result = false;
try {
final Map<String, dynamic> requestBody = <String, dynamic>{
'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 ;
}
}

View File

@ -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 =

View File

@ -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<dynamic> generateRoute(RouteSettings settings) {
@ -117,6 +119,14 @@ Route<dynamic> 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(

View File

@ -109,6 +109,7 @@ class _GoodsDictionaryViewState extends State<GoodsDictionaryView> {
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);

View File

@ -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<InventarizationEditView> createState() =>
_InventarizationEditViewState();
}
class _InventarizationEditViewState extends State<InventarizationEditView> {
final InventarizationService _service = locator<InventarizationService>();
final NavigatorService _navigatorService = locator<NavigatorService>();
final DialogService _dialogService = locator<DialogService>();
static const _pageSize = 20;
int _pageCurrent = 1;
bool _isLastPage = false;
final PagingController<int, GoodInventarization> _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<int, GoodInventarization>.separated(
//physics: const BouncingScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return const Divider(
height: 1.0,
color: disableColor,
);
},
pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<GoodInventarization>(
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: <Widget>[
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<NavigatorService>()
.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<NavigatorService>();
final dynamic result = await _nav.push(addByBarcodeViewRoute);
if (result != null) {
final List<Good> goods = await locator<DictionaryService>()
.getGoodsByNameOrEan(result as String);
if (goods.isNotEmpty) {
}
}
},
child: Icon(Icons.qr_code_rounded, size: 30, color: whiteColor),
),
],
)
],
),
);
}
Future<void> _fetchData(int pageKey, int perPage) async {
final List<GoodInventarization> 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<void> _refreshData(int pageKey, int perPage) async {
print('${pageKey} - ${perPage}');
final List<GoodInventarization> newItems =
await _service.getGoodByInventarizationId(
page: pageKey, perpage: perPage, id: widget.item.id);
final List<GoodInventarization> oldList = _pagingController.value.itemList ?? [];
oldList.setAll((pageKey - 1) * perPage , newItems);
setState(() {
_pagingController.itemList = oldList;
});
}
}

View File

@ -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<InventarizationView> {
final InventarizationService _service = locator<InventarizationService>();
final NavigatorService _navigatorService = locator<NavigatorService>();
static const _pageSize = 20;
final PagingController<int, InventarizationResponse> _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<InventarizationView> {
builderDelegate: PagedChildBuilderDelegate<InventarizationResponse>(
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,
),
),
],
),
);
},
),

View File

@ -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<GoodInventarizationListItem> {
final DialogService _dialogService = locator<DialogService>();
final InventarizationService _service = locator<InventarizationService>();
@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<void> 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: 'Не верный формат');
}
}
}
}

View File

@ -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<InventarizationListTile> createState() =>
_InventarizationListTileState();
}
class _InventarizationListTileState extends State<InventarizationListTile> {
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
),
),
);

View File

@ -78,7 +78,7 @@ class _ProductListItemState extends State<ProductListItem> {
},
key: Key(widget.name),
child: ListTile(
//onTap: () => _onItemTapped(context),
key: Key(widget.name),
onTap: () {
editProductModal();
},

View File

@ -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<AppState> 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<NavigatorService>().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<NavigatorService>().push(addProductViewRoute),
onPressed: () async {
final Good? good = await locator<NavigatorService>().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<Good> goods =
// await locator<DictionaryService>()
// .getGoodsByEan(result as String);
// if (goods.isNotEmpty) {
// Redux.store
// ?.dispatch(addSellItem(good: goods.first));
// }
final List<Good> goods =
await locator<DictionaryService>()
.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),
),
],
)

View File

@ -16,7 +16,6 @@ class AddByBarcodeView extends StatefulWidget {
}
class _AddByBarcodeViewState extends State<AddByBarcodeView> {
String _code = '';
@override
Widget build(BuildContext context) {
return Scaffold(
@ -30,11 +29,6 @@ class _AddByBarcodeViewState extends State<AddByBarcodeView> {
resultCallback: (String code) {
final NavigatorService _nav = locator<NavigatorService>();
_nav.pop(code);
//Navigator.pop(context, code);
// setState(() {
// _code = code;
// });
},
),
),

View File

@ -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<AddProductView> {
final DictionaryService _dictionaryService = locator<DictionaryService>();
final NavigatorService _navigatorService = locator<NavigatorService>();
late TextEditingController _searchTextController;
final FocusNode _searchFocusNode = new FocusNode();
final FocusNode _searchFocusNode = FocusNode();
static const _pageSize = 20;
String query = '';
Timer? _debounce;
List<Category>? _history;
List<Category>? _categories;
List<Good>? _goods;
final PagingController<int, Good> _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<AddProductView> {
@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<AddProductView> {
),
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<int, Good>.separated(
physics: const BouncingScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return Divider(
return const Divider(
height: 1.0,
color: disableColor,
);
},
pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<Good>(
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<AddProductView> {
);
}
void onCategoryPress(Category category) {
_history!.add(category);
navigateCategory(category.id!);
}
void onGoodPress(Good good) {
Redux.store!.dispatch(addSellItem(good: good));
_navigatorService.pop<String>();
_navigatorService.pop<Good>(good);
}
void reset() {
_history = [Category()..id = 0];
navigateCategory(0);
_searchTextController.clear();
}
void navigateCategory(int? categoryId) async {
List<Category> categories =
await _dictionaryService.getCategoryByParentId(categoryId);
List<Good> goods =
await _dictionaryService.getGoodsByCategoryId(categoryId);
setState(() {
_categories = categories;
_goods = goods;
});
}
Future<void> _fetchData(int pageKey, int perPage, String? query) async {
final List<GoodResponseEntity> newItems = await _dictionaryService.getGoods(
page: pageKey,
filter: {'col': 'name', 'action': 'like', 'val': query ?? ''},
perpage: perPage);
void searchByField(String query) async {
List<Category> categories = [];
List<Good> goods = await _dictionaryService.getGoodsByNameOrEan(query);
setState(() {
_categories = categories;
_goods = goods;
});
List<Good> 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);
}
}
}

View File

@ -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),
),
],

View File

@ -132,13 +132,16 @@ class _DialogManagerState extends State<DialogManager> {
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:

View File

@ -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)