null-safety-migration
suvaysov 2022-11-21 13:18:07 +06:00
parent 01d6e2d808
commit b5f9291116
40 changed files with 905 additions and 234 deletions

View File

@ -13,8 +13,11 @@ class BuyItemResponse {
required this.createdAt, required this.createdAt,
required this.updatedAt, required this.updatedAt,
required this.unitPrice, required this.unitPrice,
required this.name,
this.deletedAt, this.deletedAt,
this.refOkeiId, this.refOkeiId,
this.ean13,
this.category
}); });
int id; int id;
@ -34,6 +37,9 @@ class BuyItemResponse {
DateTime? deletedAt; DateTime? deletedAt;
@JsonKey(name: 'unit_price') @JsonKey(name: 'unit_price')
double unitPrice; double unitPrice;
String name;
String? ean13;
String? category;
factory BuyItemResponse.fromJson(Map<String, dynamic> json) => _$BuyItemResponseFromJson(json); factory BuyItemResponse.fromJson(Map<String, dynamic> json) => _$BuyItemResponseFromJson(json);

View File

@ -16,10 +16,13 @@ BuyItemResponse _$BuyItemResponseFromJson(Map<String, dynamic> json) =>
createdAt: DateTime.parse(json['created_at'] as String), createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String), updatedAt: DateTime.parse(json['updated_at'] as String),
unitPrice: (json['unit_price'] as num).toDouble(), unitPrice: (json['unit_price'] as num).toDouble(),
name: json['name'] as String,
deletedAt: json['deleted_at'] == null deletedAt: json['deleted_at'] == null
? null ? null
: DateTime.parse(json['deleted_at'] as String), : DateTime.parse(json['deleted_at'] as String),
refOkeiId: json['ref_okei_id'] as int?, refOkeiId: json['ref_okei_id'] as int?,
ean13: json['ean13'] as String?,
category: json['category'] as String?,
); );
Map<String, dynamic> _$BuyItemResponseToJson(BuyItemResponse instance) => Map<String, dynamic> _$BuyItemResponseToJson(BuyItemResponse instance) =>
@ -34,4 +37,7 @@ Map<String, dynamic> _$BuyItemResponseToJson(BuyItemResponse instance) =>
'updated_at': instance.updatedAt.toIso8601String(), 'updated_at': instance.updatedAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(), 'deleted_at': instance.deletedAt?.toIso8601String(),
'unit_price': instance.unitPrice, 'unit_price': instance.unitPrice,
'name': instance.name,
'ean13': instance.ean13,
'category': instance.category,
}; };

View File

@ -22,6 +22,7 @@ class BuyInvoiceResponse {
required this.eaccContragentId, required this.eaccContragentId,
required this.createdAt, required this.createdAt,
required this.updatedAt, required this.updatedAt,
required this.name,
this.deletedAt, this.deletedAt,
this.summ, this.summ,
}); });
@ -43,6 +44,7 @@ class BuyInvoiceResponse {
DateTime updatedAt; DateTime updatedAt;
@JsonKey(name: 'deleted_at') @JsonKey(name: 'deleted_at')
DateTime? deletedAt; DateTime? deletedAt;
String name;
factory BuyInvoiceResponse.fromJson(Map<String, dynamic> json) => _$BuyInvoiceResponseFromJson(json); factory BuyInvoiceResponse.fromJson(Map<String, dynamic> json) => _$BuyInvoiceResponseFromJson(json);

View File

@ -16,6 +16,7 @@ BuyInvoiceResponse _$BuyInvoiceResponseFromJson(Map<String, dynamic> json) =>
eaccContragentId: json['eacc_contragent_id'] as int, eaccContragentId: json['eacc_contragent_id'] as int,
createdAt: DateTime.parse(json['created_at'] as String), createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String), updatedAt: DateTime.parse(json['updated_at'] as String),
name: json['name'] as String,
deletedAt: json['deleted_at'] == null deletedAt: json['deleted_at'] == null
? null ? null
: DateTime.parse(json['deleted_at'] as String), : DateTime.parse(json['deleted_at'] as String),
@ -34,4 +35,5 @@ Map<String, dynamic> _$BuyInvoiceResponseToJson(BuyInvoiceResponse instance) =>
'created_at': instance.createdAt.toIso8601String(), 'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(), 'updated_at': instance.updatedAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(), 'deleted_at': instance.deletedAt?.toIso8601String(),
'name': instance.name,
}; };

View File

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

View File

@ -0,0 +1,29 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'stock_response.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
StockResponse _$StockResponseFromJson(Map<String, dynamic> 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<String, dynamic> _$StockResponseToJson(StockResponse instance) =>
<String, dynamic>{
'articul': instance.articul,
'ean13': instance.ean13,
'eacc_good_id': instance.eaccGoodId,
'name': instance.name,
'price': instance.price,
'cnt': instance.cnt,
'katName': instance.katName,
};

View File

@ -2,6 +2,7 @@ import 'package:redux/redux.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:redux_thunk/redux_thunk.dart'; import 'package:redux_thunk/redux_thunk.dart';
import 'package:satu/core/models/auth/auth_response.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/redux/state/user_state.dart';
import 'package:satu/core/services/api_service.dart'; import 'package:satu/core/services/api_service.dart';
import 'package:satu/core/services/dialog_service.dart'; import 'package:satu/core/services/dialog_service.dart';
@ -13,8 +14,8 @@ import '../store.dart';
@immutable @immutable
class SetUserStateAction { class SetUserStateAction {
SetUserStateAction(this.userState); SetUserStateAction(this.userState);
final UserState userState; final UserState userState;
} }
@ -47,6 +48,12 @@ ThunkAction<AppState> authenticate(String email, String password) {
}; };
} }
ThunkAction<AppState> setDefaultContragent(ContragentResponseEntity? entity) {
return (Store<AppState> store) async {
store.dispatch(SetUserStateAction(UserState(defaultContragent: entity)));
};
}
ThunkAction<AppState> authenticateByToken(String token) { ThunkAction<AppState> authenticateByToken(String token) {
return (Store<AppState> store) async { return (Store<AppState> store) async {
store.dispatch(SetUserStateAction(UserState(isLoading: true))); store.dispatch(SetUserStateAction(UserState(isLoading: true)));

View File

@ -7,5 +7,6 @@ UserState userReducer(UserState prevState, SetUserStateAction action) {
isError: payload.isError, isError: payload.isError,
isLoading: payload.isLoading, isLoading: payload.isLoading,
auth: payload.auth, auth: payload.auth,
defaultContragent: payload.defaultContragent,
); );
} }

View File

@ -1,43 +1,43 @@
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:satu/core/models/auth/auth_response.dart'; import 'package:satu/core/models/auth/auth_response.dart';
import 'package:satu/core/models/dictionary/contragent/contragent_response_entity.dart';
@immutable @immutable
class UserState { class UserState {
factory UserState.initial(UserState? payload) => UserState( factory UserState.initial(UserState? payload) => UserState(
isLoading: false, isLoading: false,
isError: 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? isError;
final bool? isLoading; final bool? isLoading;
final AuthResponse? auth; final AuthResponse? auth;
final ContragentResponseEntity? defaultContragent;
UserState copyWith({ UserState copyWith(
required bool? isError, {required bool? isError,
required bool? isLoading, required bool? isLoading,
required AuthResponse? auth required AuthResponse? auth,
}) { required ContragentResponseEntity? defaultContragent}) {
return UserState( return UserState(
isError: isError ?? this.isError, isError: isError ?? this.isError,
isLoading: isLoading ?? this.isLoading, isLoading: isLoading ?? this.isLoading,
auth: auth ?? this.auth, auth: auth ?? this.auth,
); defaultContragent: defaultContragent ?? this.defaultContragent);
} }
static UserState? fromJson(dynamic json) { static UserState? fromJson(dynamic json) {
return json != null return json != null
? UserState( ? UserState(
auth: AuthResponse.fromMap(json['auth']), auth: AuthResponse.fromMap(json['auth']),
defaultContragent: json['defaultContragent'] == null
? null
: ContragentResponseEntity.fromJson(json['defaultContragent']),
) )
: null; : null;
} }
@ -45,7 +45,8 @@ class UserState {
dynamic toJson() { dynamic toJson() {
return { return {
'auth': auth != null ? auth!.toJson() : null, 'auth': auth != null ? auth!.toJson() : null,
'defaultContragent':
defaultContragent != null ? defaultContragent!.toJson() : null,
}; };
} }
} }

View File

@ -47,13 +47,6 @@ class ApiService extends BaseService {
final response = await http.post(Uri.https(host, url), final response = await http.post(Uri.https(host, url),
body: jsonEncode(requestBody), headers: headers); 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; return response.body;
} }
@ -143,6 +136,24 @@ class ApiService extends BaseService {
return result; return result;
} }
Future<ResponseOriginal> postRequestOriginal(String target,
{Map<String, dynamic>? requestBody}) async {
ResponseOriginal result;
try {
final Map<String, String> headers = <String, String>{
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<ResponseEntity> dictionarySave( Future<ResponseEntity> dictionarySave(
String target, Map<String, dynamic>? body) async { String target, Map<String, dynamic>? body) async {
ResponseEntity response; ResponseEntity response;

View File

@ -1,5 +1,7 @@
import 'package:intl/intl.dart';
import 'package:satu/core/base/base_service.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/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/services/api_service.dart';
import 'package:satu/core/utils/locator.dart'; import 'package:satu/core/utils/locator.dart';
@ -15,6 +17,10 @@ class BuyService extends BaseService {
final Map<String, dynamic> requestBody = <String, dynamic>{ final Map<String, dynamic> requestBody = <String, dynamic>{
'page': page, 'page': page,
'perpage': perpage, 'perpage': perpage,
'order' : {
'col': 'doc_number',
'desc': true
}
}; };
ResponseEntity categories = await _api.postRequest('/general_purchases_get', ResponseEntity categories = await _api.postRequest('/general_purchases_get',
@ -42,8 +48,6 @@ class BuyService extends BaseService {
'perpage': perpage, 'perpage': perpage,
'id': id 'id': id
}; };
log.i(requestBody);
ResponseEntity categories = await _api.postRequest('/general_purchases_get_items', ResponseEntity categories = await _api.postRequest('/general_purchases_get_items',
requestBody: requestBody); requestBody: requestBody);
if (categories.original.items != null && if (categories.original.items != null &&
@ -69,7 +73,6 @@ class BuyService extends BaseService {
'price': price, 'price': price,
'cnt': count 'cnt': count
}; };
log.i(requestBody);
ResponseEntity response = await _api.postRequest('/general_purchases_edit_item', ResponseEntity response = await _api.postRequest('/general_purchases_edit_item',
requestBody: requestBody); requestBody: requestBody);
@ -86,7 +89,6 @@ class BuyService extends BaseService {
final Map<String, dynamic> requestBody = <String, dynamic>{ final Map<String, dynamic> requestBody = <String, dynamic>{
'id': id, 'id': id,
}; };
log.i(requestBody);
ResponseEntity response = await _api.postRequest('/general_purchases_delete_item', ResponseEntity response = await _api.postRequest('/general_purchases_delete_item',
requestBody: requestBody); requestBody: requestBody);
@ -105,7 +107,6 @@ class BuyService extends BaseService {
'eacc_good_id': goodId, 'eacc_good_id': goodId,
'id': invoiceId, 'id': invoiceId,
}; };
log.i(requestBody);
ResponseEntity response = await _api.postRequest('/general_purchases_add_item', ResponseEntity response = await _api.postRequest('/general_purchases_add_item',
requestBody: requestBody); requestBody: requestBody);
@ -116,4 +117,40 @@ class BuyService extends BaseService {
} }
return result; return result;
} }
Future<bool> confirmInvoice(int invoiceId) async {
bool result = false;
try {
final Map<String, dynamic> requestBody = <String, dynamic>{
'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<bool> createBuy(ContragentResponseEntity contragent, DateTime date) async {
bool result = false;
try {
DateFormat formatter = DateFormat('yyyy-MM-dd');
final Map<String, dynamic> requestBody = <String, dynamic>{
'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;
}
} }

View File

@ -30,8 +30,6 @@ class DictionaryService extends BaseService {
final ApiService _api = locator<ApiService>(); final ApiService _api = locator<ApiService>();
final DbService _db = locator<DbService>(); final DbService _db = locator<DbService>();
Category categoryResponseToCategory(CategoryResponse response) { Category categoryResponseToCategory(CategoryResponse response) {
return Category() return Category()
..id = response.id ..id = response.id
@ -84,7 +82,7 @@ class DictionaryService extends BaseService {
'val': categoryId 'val': categoryId
}; };
List<GoodResponseEntity> responses = List<GoodResponseEntity> responses =
await getGoods(page: 1, perpage: 100, filter: filter); await getGoods(page: 1, perpage: 100, filter: [filter]);
for (final GoodResponseEntity response in responses) { for (final GoodResponseEntity response in responses) {
final Good good = goodResponseToGood(response); final Good good = goodResponseToGood(response);
list.add(good); list.add(good);
@ -95,24 +93,38 @@ class DictionaryService extends BaseService {
return list; return list;
} }
Future<List<Good>> getGoodsByNameOrEan(String query) async { Future<List<Good>> getGoodsByNameOrEan(String query, { bool onlyEan = false}) async {
final List<Good> list = []; final List<Good> list = [];
try { try {
final int? appCompanyId = Redux.store?.state.userState?.auth?.companyId; dynamic filter = [
String where = {
'( $GoodColumnAppCompanyId = ? and $GoodColumnName like ? ) '; 'col': 'name',
final List args = [appCompanyId, '%$query%']; 'action': 'like',
if (_isNumericInt(query) && query.length >= 8) { 'val': query,
where += ' or $GoodColumnEan like ?'; },
args.add('${int.parse(query).toString()}%'); {
'col': 'ean13',
'action': 'like',
'val': query,
} }
final List<Map<String, dynamic>> elements = ];
await _db.queryRowsWithWhere(goodTableName, where, args); if (onlyEan) {
for (final Map<String, dynamic> element in elements) { filter = [
list.add(Good.fromMap(element)); {
'col': 'ean13',
'action': 'equals',
'val': query,
}
];
}
List<GoodResponseEntity> 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) { } catch (e, stack) {
log.e('getGoodsByCategoryId', e, stack); log.e('getGoods', e, stack);
} }
return list; return list;
} }
@ -172,13 +184,14 @@ class DictionaryService extends BaseService {
} }
Future<List<GoodResponseEntity>> getGoods( Future<List<GoodResponseEntity>> getGoods(
{required int page, required int perpage, dynamic filter}) async { {required int page, required int perpage, dynamic filter,bool orGate = false}) async {
List<GoodResponseEntity> list = []; List<GoodResponseEntity> list = [];
try { try {
final Map<String, dynamic> requestBody = <String, dynamic>{ final Map<String, dynamic> requestBody = <String, dynamic>{
'page': page, 'page': page,
'perpage': perpage, 'perpage': perpage,
'filter': [filter] 'filter': filter,
'or_gate': orGate
}; };
ResponseEntity categories = ResponseEntity categories =

View File

@ -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/models/flow/transaction_state.dart';
import 'package:satu/core/redux/actions/sell_actions.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/sell_state.dart';
import 'package:satu/core/redux/state/user_state.dart';
import 'package:satu/core/redux/store.dart'; import 'package:satu/core/redux/store.dart';
import 'package:satu/core/utils/locator.dart'; import 'package:satu/core/utils/locator.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
import '../models/dictionary/contragent/contragent_response_entity.dart';
import 'api_service.dart'; import 'api_service.dart';
import 'db_service.dart'; import 'db_service.dart';
import 'dialog_service.dart'; import 'dialog_service.dart';
@ -30,6 +32,7 @@ class SellService extends BaseService {
{double card = 0, double nal = 0, double total = 0}) async { {double card = 0, double nal = 0, double total = 0}) async {
final SellRequest request = SellRequest(); final SellRequest request = SellRequest();
final SellState sellState = Redux.store!.state.sellState!; final SellState sellState = Redux.store!.state.sellState!;
final UserState userState = Redux.store!.state.userState!;
final TransactionState transactionState = sellState.transactionState!; final TransactionState transactionState = sellState.transactionState!;
final List<ProductDao> items = sellState.items!; final List<ProductDao> items = sellState.items!;
for (final ProductDao item in items) { for (final ProductDao item in items) {
@ -40,10 +43,12 @@ class SellService extends BaseService {
request.invoiceId = transactionState.uuid; request.invoiceId = transactionState.uuid;
request.section = transactionState.sectionName; request.section = transactionState.sectionName;
request.contragent = transactionState.contragentName; request.contragent = transactionState.contragentName;
ContragentResponseEntity? contragent = userState.defaultContragent;
final OperatorBean operator = OperatorBean() final OperatorBean operator = OperatorBean()
..name = 'operator' ..name = 'operator'
..code = 1; ..code = 1;
request.operator = operator; request.operator = operator;
request.contragent = contragent?.name;
final SellResponse response = await _api.sell(request); final SellResponse response = await _api.sell(request);
if (response.operation == false) { if (response.operation == false) {
_dialogService.showDialog(description: response.message); _dialogService.showDialog(description: response.message);
@ -125,7 +130,6 @@ class SellService extends BaseService {
data.nal = nal; data.nal = nal;
data.total = total; data.total = total;
transaction.data = jsonEncode(data.toMap()); transaction.data = jsonEncode(data.toMap());
log.i(jsonEncode(data.toMap()));
await _db.update(transactionTableName, transaction.toMap()); await _db.update(transactionTableName, transaction.toMap());
return transaction.id; return transaction.id;
} }
@ -157,7 +161,6 @@ class SellService extends BaseService {
data.nal = nal; data.nal = nal;
data.total = total; data.total = total;
transaction.data = jsonEncode(data.toMap()); transaction.data = jsonEncode(data.toMap());
log.i(jsonEncode(data.toMap()));
return await _db.insert(transactionTableName, transaction.toMap()); return await _db.insert(transactionTableName, transaction.toMap());
} }

View File

@ -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<ApiService>();
Future<List<StockResponse>> getList(
{required int page, required int perpage, String? query}) async {
List<StockResponse> list = [];
try {
final Map<String, dynamic> requestBody = <String, dynamic>{
'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;
}
}

View File

@ -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/dictionary_service.dart';
import 'package:satu/core/services/inventarization_service.dart'; import 'package:satu/core/services/inventarization_service.dart';
import 'package:satu/core/services/navigator_service.dart'; import 'package:satu/core/services/navigator_service.dart';
import 'package:satu/core/services/stocks_service.dart';
import 'logger.dart'; import 'logger.dart';
@ -39,5 +40,7 @@ class LocatorInjector {
_log.d('Initializing InventarizationService Service'); _log.d('Initializing InventarizationService Service');
locator.registerLazySingleton<InventarizationService>( locator.registerLazySingleton<InventarizationService>(
() => InventarizationService()); () => InventarizationService());
_log.d('Initializing StockService Service');
locator.registerLazySingleton<StockService>(() => StockService());
} }
} }

View File

@ -25,6 +25,7 @@ const String inventarizationEditRoute = 'inventarizationEditRoute';
//buy //buy
const String buyEditRoute = 'buyEditRoute'; const String buyEditRoute = 'buyEditRoute';
const String buyAddRoute = 'buyAddRoute';
// setting - ble printer // setting - ble printer
const String settingPrinterBluetoothViewRoute = 'SettingPrinterBluetoothView'; const String settingPrinterBluetoothViewRoute = 'SettingPrinterBluetoothView';

View File

@ -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_paper_size_select.dart';
import 'package:satu/views/settings/printer_bluetooth/printer_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/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/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/payment/payment_view.dart';
import 'package:satu/views/work/views/receipt/receipt_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 'package:satu/views/work/work_view.dart';
import '../core/models/inventarization/response/inventarization_response.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'; import './route_names.dart';
Route<dynamic> generateRoute(RouteSettings settings) { Route<dynamic> generateRoute(RouteSettings settings) {
@ -46,12 +47,12 @@ Route<dynamic> generateRoute(RouteSettings settings) {
case addProductViewRoute: case addProductViewRoute:
return _getPageRoute( return _getPageRoute(
routeName: settings.name, routeName: settings.name,
viewToShow: AddProductView(), viewToShow: SelectProductView(),
); );
case addByBarcodeViewRoute: case addByBarcodeViewRoute:
return _getPageRoute( return _getPageRoute(
routeName: settings.name, routeName: settings.name,
viewToShow: const AddByBarcodeView(), viewToShow: const SelectByScanView(),
); );
case settingPrinterBluetoothViewRoute: case settingPrinterBluetoothViewRoute:
return _getPageRoute( return _getPageRoute(
@ -137,6 +138,11 @@ Route<dynamic> generateRoute(RouteSettings settings) {
invoice: invoice, invoice: invoice,
), ),
); );
case buyAddRoute:
return _getPageRoute(
routeName: settings.name,
viewToShow: BuyAddView(),
);
default: default:
return MaterialPageRoute( return MaterialPageRoute(

View File

@ -154,8 +154,8 @@ class _GoodsDictionaryViewState extends State<GoodsDictionaryView> {
Future<void> _fetchData(int pageKey, int perPage, String? query) async { Future<void> _fetchData(int pageKey, int perPage, String? query) async {
final List<GoodResponseEntity> newItems = await _dictionaryService.getGoods( final List<GoodResponseEntity> newItems = await _dictionaryService.getGoods(
page: pageKey, page: pageKey,
filter: {'col': 'name', 'action': 'like', 'val': query ?? ''}, filter: [{'col': 'name', 'action': 'like', 'val': query ?? ''}],
perpage: perPage); perpage: perPage, orGate: true);
final isLastPage = newItems.length < _pageSize; final isLastPage = newItems.length < _pageSize;
if (isLastPage) { if (isLastPage) {

View File

@ -108,6 +108,14 @@ class _InventarizationEditViewState extends State<InventarizationEditView> {
}, },
pagingController: _pagingController, pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<GoodInventarization>( builderDelegate: PagedChildBuilderDelegate<GoodInventarization>(
noItemsFoundIndicatorBuilder: (BuildContext context) {
return const Center(
child: Text(
'Необходимо добавить товар',
style: textGray11Style,
),
);
},
itemBuilder: (BuildContext context, GoodInventarization item, itemBuilder: (BuildContext context, GoodInventarization item,
int index) { int index) {
return GoodInventarizationListItem( return GoodInventarizationListItem(
@ -199,8 +207,25 @@ class _InventarizationEditViewState extends State<InventarizationEditView> {
final dynamic result = await _nav.push(addByBarcodeViewRoute); final dynamic result = await _nav.push(addByBarcodeViewRoute);
if (result != null) { if (result != null) {
final List<Good> goods = await locator<DictionaryService>() final List<Good> goods = await locator<DictionaryService>()
.getGoodsByNameOrEan(result as String); .getGoodsByNameOrEan(result as String, onlyEan: true);
if (goods.isNotEmpty) {} 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), child: Icon(Icons.qr_code_rounded, size: 30, color: whiteColor),

View File

@ -1,15 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:satu/core/models/dialog_models.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/dialog_service.dart';
import 'package:satu/core/services/inventarization_service.dart'; import 'package:satu/core/services/inventarization_service.dart';
import 'package:satu/core/utils/locator.dart'; import 'package:satu/core/utils/locator.dart';
import 'package:satu/core/utils/utils_parse.dart'; import 'package:satu/core/utils/utils_parse.dart';
import 'package:satu/shared/app_colors.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 'package:satu/widgets/ui/product_title_widget.dart';
class GoodInventarizationListItem extends StatefulWidget { class GoodInventarizationListItem extends StatefulWidget {

View File

@ -13,6 +13,7 @@ import 'package:satu/views/work/work_view.dart';
import 'package:satu/widgets/drawer/app_drawer.dart'; import 'package:satu/widgets/drawer/app_drawer.dart';
import '../dictionaries/contragents/contragents_view.dart'; import '../dictionaries/contragents/contragents_view.dart';
import '../stocks/stocks_view.dart';
class MainView extends StatefulWidget { class MainView extends StatefulWidget {
@override @override
@ -30,6 +31,7 @@ class _MainViewState extends State<MainView> {
final _contragentDictView = ContragentsDictionaryView(); final _contragentDictView = ContragentsDictionaryView();
final _analyticsView = const AnalyticsView(); final _analyticsView = const AnalyticsView();
final _inventarizationView = InventarizationView(); final _inventarizationView = InventarizationView();
final _stocksView = StocksView();
Widget _body(Type viewClass) { Widget _body(Type viewClass) {
if(viewClass == WorkView) { if(viewClass == WorkView) {
@ -53,6 +55,9 @@ class _MainViewState extends State<MainView> {
if(viewClass == AnalyticsView) { if(viewClass == AnalyticsView) {
return _analyticsView; return _analyticsView;
} }
if(viewClass == StocksView) {
return _stocksView;
}
return _workView; return _workView;
} }

View File

@ -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<StocksView> createState() => _StocksViewState();
}
class _StocksViewState extends State<StocksView> {
final StockService _service = locator<StockService>();
late TextEditingController _searchTextController;
final FocusNode _searchFocusNode = FocusNode();
String query = '';
Timer? _debounce;
static const _pageSize = 20;
bool _isLastPage = false;
final PagingController<int, StockResponse> _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<int, StockResponse>.separated(
//physics: const BouncingScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return const Divider(
height: 1.0,
color: disableColor,
);
},
pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<StockResponse>(
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<void> _fetchData(int pageKey, int perPage, String? query) async {
final List<StockResponse> 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);
}
}
}

View File

@ -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<StockTile> {
@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,
);
}
}

View File

@ -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<BuyAddView> createState() => _BuyAddViewState();
}
class _BuyAddViewState extends State<BuyAddView> {
ContragentResponseEntity? _contragent;
DateTime _date = DateTime.now();
final BuyService _service = locator<BuyService>();
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<NavigatorService>()
.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<NavigatorService>().pop(result);
}
},
),
),
],
),
);
}
}

View File

@ -3,10 +3,11 @@ import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.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/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/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 'package:satu/views/work/tabs/buy/component/product_buy_tile.dart';
import '../../../../core/entity/goods_entity.dart'; import '../../../../core/entity/goods_entity.dart';
import '../../../../core/models/buy_invoice/buy_invoice_response.dart'; import '../../../../core/models/buy_invoice/buy_invoice_response.dart';
import '../../../../core/services/buy_service.dart'; import '../../../../core/services/buy_service.dart';
@ -38,6 +39,7 @@ class _BuyEditViewState extends State<BuyEditView> {
bool editable = false; bool editable = false;
bool itemsExist = false;
@override @override
void initState() { void initState() {
@ -55,7 +57,6 @@ class _BuyEditViewState extends State<BuyEditView> {
@override @override
void dispose() { void dispose() {
_pagingController.dispose(); _pagingController.dispose();
super.dispose(); super.dispose();
} }
@ -79,15 +80,23 @@ class _BuyEditViewState extends State<BuyEditView> {
}, },
pagingController: _pagingController, pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<BuyItemResponse>( builderDelegate: PagedChildBuilderDelegate<BuyItemResponse>(
itemBuilder: (BuildContext context, BuyItemResponse item, noItemsFoundIndicatorBuilder: (BuildContext context) {
int index) { return const Center(
child: Text(
'Необходимо добавить товар',
style: textGray11Style,
),
);
},
itemBuilder:
(BuildContext context, BuyItemResponse item, int index) {
return ProductBuyTile( return ProductBuyTile(
key: ValueKey(item.id), key: ValueKey(item.id),
ean: '1234567890123', ean: item.ean13,
name: 'Картофель', name: item.name,
price: item.price, price: item.price,
count: item.cnt, count: item.cnt,
categoryName: 'Овощи', categoryName: item.category,
editable: editable, editable: editable,
invoiceId: widget.invoice.id, invoiceId: widget.invoice.id,
id: item.id, id: item.id,
@ -115,14 +124,29 @@ class _BuyEditViewState extends State<BuyEditView> {
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[ children: <Widget>[
Visibility( Visibility(
visible: false, visible: itemsExist,
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: FloatingActionButton( child: FloatingActionButton(
mini: true, mini: true,
elevation: 2, elevation: 2,
backgroundColor: successColor, 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<NavigatorService>().pop(result);
}
}
},
child: Icon( child: Icon(
Icons.check, Icons.check,
color: whiteColor, color: whiteColor,
@ -146,8 +170,7 @@ class _BuyEditViewState extends State<BuyEditView> {
_pagingController.refresh(); _pagingController.refresh();
} else { } else {
_dialogService.showDialog( _dialogService.showDialog(
description: description: 'Товара отсутсвует в остатке или ранее не'
'Товара отсутсвует в остатке или ранее не'
' использователся в системе', ' использователся в системе',
); );
} }
@ -168,8 +191,25 @@ class _BuyEditViewState extends State<BuyEditView> {
final dynamic result = await _nav.push(addByBarcodeViewRoute); final dynamic result = await _nav.push(addByBarcodeViewRoute);
if (result != null) { if (result != null) {
final List<Good> goods = await locator<DictionaryService>() final List<Good> goods = await locator<DictionaryService>()
.getGoodsByNameOrEan(result as String); .getGoodsByNameOrEan(result as String, onlyEan: true);
if (goods.isNotEmpty) {} 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), child: Icon(Icons.qr_code_rounded, size: 30, color: whiteColor),
@ -191,16 +231,21 @@ class _BuyEditViewState extends State<BuyEditView> {
final nextPageKey = pageKey + 1; final nextPageKey = pageKey + 1;
_pagingController.appendPage(newItems, nextPageKey); _pagingController.appendPage(newItems, nextPageKey);
} }
if ((_pagingController.value.itemList ?? []).isNotEmpty) {
setState(() {
itemsExist = true;
});
}
} }
void _editData(int id, double price, double count) { void _editData(int id, double price, double count) {
final List<BuyItemResponse> oldList = final List<BuyItemResponse> oldList =
_pagingController.value.itemList ?? []; _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; ..firstWhere((element) => element.id == id).cnt = count;
setState(() { setState(() {
_pagingController.itemList = oldList; _pagingController.itemList = oldList;
}); });
} }
} }

View File

@ -21,8 +21,6 @@ class BuyView extends StatefulWidget {
} }
class _BuyViewState extends State<BuyView> { class _BuyViewState extends State<BuyView> {
final BuyService _service = locator<BuyService>(); final BuyService _service = locator<BuyService>();
final NavigatorService _navigatorService = locator<NavigatorService>(); final NavigatorService _navigatorService = locator<NavigatorService>();
@ -43,7 +41,6 @@ class _BuyViewState extends State<BuyView> {
@override @override
void dispose() { void dispose() {
_pagingController.dispose(); _pagingController.dispose();
super.dispose(); super.dispose();
} }
@ -68,8 +65,8 @@ class _BuyViewState extends State<BuyView> {
}, },
pagingController: _pagingController, pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<BuyInvoiceResponse>( builderDelegate: PagedChildBuilderDelegate<BuyInvoiceResponse>(
itemBuilder: (BuildContext context, BuyInvoiceResponse item, itemBuilder:
int index) { (BuildContext context, BuyInvoiceResponse item, int index) {
return DictionaryTile( return DictionaryTile(
key: Key('category_${item.id}'), key: Key('category_${item.id}'),
onPress: () async { onPress: () async {
@ -88,24 +85,36 @@ class _BuyViewState extends State<BuyView> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
'${item.eaccContragentId.toString()}', '${item.name}',
style: textBlack12Style, style: textBlack12Style,
), ),
SizedBox( SizedBox(
height: 5.0, height: 5.0,
), ),
Text( Text(
'Статус: ${item.refBuyInvoiceStatusId.toString()}', 'Номер: ${item.docNumber.toString()}',
style: textGray11Style, style: textGray11Style,
), ),
], ],
)), )),
Expanded( Expanded(
flex: 2, flex: 2,
child: Text( child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${formatterDay.format(item.invoiceDate)}', '${formatterDay.format(item.invoiceDate)}',
style: textBlack12Style, style: textBlack12Style,
), ),
SizedBox(
height: 5.0,
),
Text(
'Статус: ${item.refBuyInvoiceStatusId == 1 ? 'Новая' : 'Обработана'}',
style: textGray11Style,
),
],
),
), ),
Expanded( Expanded(
flex: 1, flex: 1,
@ -127,12 +136,22 @@ class _BuyViewState extends State<BuyView> {
) )
], ],
), ),
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<void> _fetchData(int pageKey, int perPage) async { Future<void> _fetchData(int pageKey, int perPage) async {
final List<BuyInvoiceResponse> newItems = await _service final List<BuyInvoiceResponse> newItems =
.getList(page: pageKey, perpage: perPage); await _service.getList(page: pageKey, perpage: perPage);
final isLastPage = newItems.length < _pageSize; final isLastPage = newItems.length < _pageSize;
if (isLastPage) { if (isLastPage) {
_pagingController.appendLastPage(newItems); _pagingController.appendLastPage(newItems);

View File

@ -1,15 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:satu/core/models/dialog_models.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/buy_service.dart';
import 'package:satu/core/services/dialog_service.dart'; import 'package:satu/core/services/dialog_service.dart';
import 'package:satu/core/utils/locator.dart'; import 'package:satu/core/utils/locator.dart';
import 'package:satu/core/utils/utils_parse.dart'; import 'package:satu/core/utils/utils_parse.dart';
import 'package:satu/shared/app_colors.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 'package:satu/widgets/ui/product_title_widget.dart';
class ProductBuyTile extends StatefulWidget { class ProductBuyTile extends StatefulWidget {

View File

@ -1,12 +1,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.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/services/navigator_service.dart';
import 'package:satu/core/utils/locator.dart'; import 'package:satu/core/utils/locator.dart';
import 'package:satu/routes/route_names.dart'; import 'package:satu/routes/route_names.dart';
import 'package:satu/shared/app_colors.dart'; import 'package:satu/shared/app_colors.dart';
class ContragentSelectBar extends StatelessWidget { class ContragentSelectBar extends StatelessWidget {
const ContragentSelectBar({required this.value, Key? key}) : super(key: key); const ContragentSelectBar({required this.value, Key? key}) : super(key: key);
final String value; final String value;
@ -17,8 +19,13 @@ class ContragentSelectBar extends StatelessWidget {
child: Material( child: Material(
type: MaterialType.transparency, type: MaterialType.transparency,
child: InkWell( child: InkWell(
onTap: () { onTap: () async {
locator<NavigatorService>().push(contragentSelectViewRoute); ContragentResponseEntity? entity =
await locator<NavigatorService>()
.push(contragentSelectViewRoute) as ContragentResponseEntity?;
if (entity != null) {
Redux.store?.dispatch(setDefaultContragent(entity));
}
}, },
child: Padding( child: Padding(
padding: EdgeInsets.symmetric(vertical: 08.w, horizontal: 15.w), padding: EdgeInsets.symmetric(vertical: 08.w, horizontal: 15.w),

View File

@ -9,9 +9,10 @@ import 'package:satu/core/utils/locator.dart';
import 'package:satu/core/utils/utils_parse.dart'; import 'package:satu/core/utils/utils_parse.dart';
import 'package:satu/shared/app_colors.dart'; import 'package:satu/shared/app_colors.dart';
import 'package:satu/shared/shared_styles.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 'package:satu/widgets/ui/product_title_widget.dart';
import '../../../views/select_by_scan/add_by_barcode_view.dart';
class ProductSellTile extends StatefulWidget { class ProductSellTile extends StatefulWidget {
const ProductSellTile( const ProductSellTile(
@ -42,7 +43,7 @@ class _ProductSellTileState extends State<ProductSellTile> {
void _onItemTapped(BuildContext context) { void _onItemTapped(BuildContext context) {
Navigator.of(context).push(MaterialPageRoute( Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => const AddByBarcodeView())); builder: (BuildContext context) => const SelectByScanView()));
} }
@override @override

View File

@ -4,6 +4,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:redux/src/store.dart'; import 'package:redux/src/store.dart';
import 'package:satu/core/models/flow/dao/product_dao.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/sell_state.dart';
import 'package:satu/core/redux/state/user_state.dart';
import 'package:satu/core/redux/store.dart'; import 'package:satu/core/redux/store.dart';
import 'package:satu/core/services/navigator_service.dart'; import 'package:satu/core/services/navigator_service.dart';
import 'package:satu/core/utils/locator.dart'; import 'package:satu/core/utils/locator.dart';
@ -40,9 +41,14 @@ class SellView extends StatelessWidget {
), ),
body: Column( body: Column(
children: [ children: [
const ContragentSelectBar( StoreConnector<AppState, UserState>(
value: 'Частное лицо', converter: (store) => store.state.userState!,
), builder: (_, uState) {
return ContragentSelectBar(
value: (uState.defaultContragent?.name ??
'Выберите контрагента'),
);
}),
Expanded( Expanded(
child: ListView( child: ListView(
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
@ -120,11 +126,11 @@ class SellView extends StatelessWidget {
elevation: 2, elevation: 2,
mini: true, mini: true,
onPressed: () async { onPressed: () async {
final Good? good = await locator<NavigatorService>().push(addProductViewRoute) as Good?; final Good? good = await locator<NavigatorService>()
.push(addProductViewRoute) as Good?;
if (good != null) { if (good != null) {
Redux.store!.dispatch(addSellItem(good: good)); Redux.store!.dispatch(addSellItem(good: good));
} }
}, },
child: Icon( child: Icon(
Icons.add_rounded, Icons.add_rounded,
@ -144,7 +150,7 @@ class SellView extends StatelessWidget {
if (result != null) { if (result != null) {
final List<Good> goods = final List<Good> goods =
await locator<DictionaryService>() await locator<DictionaryService>()
.getGoodsByNameOrEan(result as String); .getGoodsByNameOrEan(result as String, onlyEan: true);
if (goods.isNotEmpty) { if (goods.isNotEmpty) {
Redux.store Redux.store
?.dispatch(addSellItem(good: goods.first)); ?.dispatch(addSellItem(good: goods.first));

View File

@ -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<SelectContragentView> {
final DictionaryService _dictionaryService = locator<DictionaryService>();
late TextEditingController _searchTextController;
final FocusNode _searchFocusNode = new FocusNode();
final List<Category> _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<Good> goods = await _dictionaryService.getGoodsByNameOrEan(query);
setState(() {
goods;
});
}
}

View File

@ -5,17 +5,17 @@ import 'package:satu/core/utils/locator.dart';
import 'package:satu/widgets/bar/products_app_bar.dart'; import 'package:satu/widgets/bar/products_app_bar.dart';
import 'package:satu/widgets/tools/app_barcode_scanner_widget.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 Key? key
}) : super(key: key); }) : super(key: key);
@override @override
_AddByBarcodeViewState createState() => _AddByBarcodeViewState(); _SelectByScanViewState createState() => _SelectByScanViewState();
} }
class _AddByBarcodeViewState extends State<AddByBarcodeView> { class _SelectByScanViewState extends State<SelectByScanView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(

View File

@ -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<SelectContragentView> {
final DictionaryService _dictionaryService = locator<DictionaryService>();
final NavigatorService _navigatorService = locator<NavigatorService>();
late TextEditingController _searchTextController;
final FocusNode _searchFocusNode = FocusNode();
late List<ContragentResponseEntity> items = [];
static const _pageSize = 20;
final PagingController<int, ContragentResponseEntity> _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<void> 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<int, ContragentResponseEntity>(
pagingController: _pagingController,
builderDelegate:
PagedChildBuilderDelegate<ContragentResponseEntity>(
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<void> _fetchData(int pageKey, int perPage, String? query) async {
final List<ContragentResponseEntity> 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);
}
}
}

View File

@ -15,12 +15,12 @@ import 'package:satu/widgets/fields/input_field.dart';
import '../../../../core/models/dictionary/good/good_response_entity.dart'; import '../../../../core/models/dictionary/good/good_response_entity.dart';
import 'component/add_product_list_item.dart'; import 'component/add_product_list_item.dart';
class AddProductView extends StatefulWidget { class SelectProductView extends StatefulWidget {
@override @override
_AddProductViewState createState() => _AddProductViewState(); _SelectProductViewState createState() => _SelectProductViewState();
} }
class _AddProductViewState extends State<AddProductView> { class _SelectProductViewState extends State<SelectProductView> {
final DictionaryService _dictionaryService = locator<DictionaryService>(); final DictionaryService _dictionaryService = locator<DictionaryService>();
final NavigatorService _navigatorService = locator<NavigatorService>(); final NavigatorService _navigatorService = locator<NavigatorService>();
late TextEditingController _searchTextController; late TextEditingController _searchTextController;
@ -121,8 +121,12 @@ class _AddProductViewState extends State<AddProductView> {
Future<void> _fetchData(int pageKey, int perPage, String? query) async { Future<void> _fetchData(int pageKey, int perPage, String? query) async {
final List<GoodResponseEntity> newItems = await _dictionaryService.getGoods( final List<GoodResponseEntity> newItems = await _dictionaryService.getGoods(
page: pageKey, page: pageKey,
filter: {'col': 'name', 'action': 'like', 'val': query ?? ''}, filter: [{'col': 'name', 'action': 'like', 'val': query ?? ''}, {
perpage: perPage); 'col': 'ean13',
'action': 'like',
'val': query ?? ''
}],
perpage: perPage, orGate: true);
List<Good> items = newItems.map((e) => goodResponseToGood(e)).toList(); List<Good> items = newItems.map((e) => goodResponseToGood(e)).toList();
final isLastPage = newItems.length < _pageSize; final isLastPage = newItems.length < _pageSize;

View File

@ -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/contragents/contragents_view.dart';
import 'package:satu/views/dictionaries/goods/goods_view.dart'; import 'package:satu/views/dictionaries/goods/goods_view.dart';
import 'package:satu/views/settings/setting_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 'package:satu/views/work/work_view.dart';
import '../../views/inventarization/view/inventarization_view.dart'; import '../../views/inventarization/view/inventarization_view.dart';
@ -50,9 +51,8 @@ class AppDrawer extends StatelessWidget {
text: 'Остатки', text: 'Остатки',
onTap: () { onTap: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
Redux.store!.dispatch(navigateDrawer(AnalyticsView)); Redux.store!.dispatch(navigateDrawer(StocksView));
}, }
disable: true,
), ),
_createDrawerItem( _createDrawerItem(
svgFile: 'inventarization', svgFile: 'inventarization',

View File

@ -6,7 +6,7 @@ import 'note_text.dart';
class LineTile extends StatelessWidget { class LineTile extends StatelessWidget {
const LineTile(this.text, 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 text;
final String placeholder; final String placeholder;

View File

@ -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. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1 version: 1.0.1+2
environment: environment: