add_product_view.dart

null-safety-migration
suvaissov 2021-05-05 23:06:35 +06:00
parent ec521ef27b
commit 1d31dce7be
30 changed files with 660 additions and 321 deletions

View File

@ -1,23 +1,26 @@
const String Category_tableName = 'goods_category';
const String Category_columnId = 'id';
const String Category_columnParentIn = 'parent_id';
const String Category_columnName = 'name';
const String Category_columnAppCompanyId = 'app_company_id';
const String CategoryTableName = 'goods_category';
const String CategoryColumnId = 'id';
const String CategoryColumnParentIn = 'parent_id';
const String CategoryColumnName = 'name';
const String CategoryColumnAppCompanyId = 'app_company_id';
const String CategoryColumnUpdatedAt = 'updated_at';
class Category {
int id;
int parentIn;
int parentId;
String name;
int appCompanyId;
String updatedAt;
Map<String, dynamic> toMap() {
var map = <String, dynamic>{
Category_columnParentIn: parentIn,
Category_columnName: name,
Category_columnAppCompanyId: appCompanyId
CategoryColumnParentIn: parentId,
CategoryColumnName: name,
CategoryColumnAppCompanyId: appCompanyId,
CategoryColumnUpdatedAt: updatedAt,
};
if (id != null) {
map[Category_columnId] = id;
map[CategoryColumnId] = id;
}
return map;
}
@ -25,18 +28,11 @@ class Category {
Category();
Category.fromMap(Map<String, dynamic> map) {
id = map[Category_columnId];
parentIn = map[Category_columnParentIn];
name = map[Category_columnName];
appCompanyId = map[Category_columnAppCompanyId];
id = map[CategoryColumnId];
parentId = map[CategoryColumnParentIn];
name = map[CategoryColumnName];
appCompanyId = map[CategoryColumnAppCompanyId];
updatedAt = map[CategoryColumnUpdatedAt];
}
Category.fromJson(Map<String, dynamic> map) {
id = map[Category_columnId];
parentIn = map[Category_columnParentIn];
name = map[Category_columnName];
appCompanyId = map[Category_columnAppCompanyId];
}
}

View File

@ -1,45 +1,44 @@
const String Goog_tableName = 'goods';
const String Goog_columnId = 'id';
const String Goog_columnArticul = 'articul';
const String Goog_columnName = 'name';
const String Goog_columnPrice = 'price';
const String Goog_columnCategoryId = 'category_id';
const String Goog_columnEan = 'ean';
const String Goog_columnAppCompanyId = 'app_company_id';
const String Goog_columnDescription = 'description';
const String Goog_columnShowPrice = 'show_price';
const String Goog_columnOkei = 'okei';
const String Goog_columnDiscount = 'discount';
const String GoodTableName = 'goods';
const String GoodColumnId = 'id';
const String GoodColumnCategoryId = 'category_id';
const String GoodColumnName = 'name';
const String GoodColumnEan = 'ean';
const String GoodColumnArticul = 'articul';
const String GoodColumnPrice = 'price';
const String GoodColumnOptPrice = 'opt_price';
const String GoodColumnBasePrice = 'base_price';
const String GoodColumnDivisible = 'divisible';
const String GoodColumnUpdatedAt = 'updated_at';
const String GoodColumnAppCompanyId = 'app_company_id';
class Good {
int id;
int articul;
String name;
double price;
int categoryId;
String name;
String ean;
int articul;
num price;
num optPrice;
num basePrice;
int divisible;
String updatedAt;
int appCompanyId;
String description;
double showPrice;
int okei;
double discount;
Map<String, dynamic> toMap() {
var map = <String, dynamic>{
Goog_columnArticul: articul,
Goog_columnName: name,
Goog_columnPrice: price,
Goog_columnCategoryId: categoryId,
Goog_columnEan: ean,
Goog_columnAppCompanyId: appCompanyId,
Goog_columnDescription: description,
Goog_columnShowPrice: showPrice,
Goog_columnOkei: okei,
Goog_columnDiscount: discount
GoodColumnArticul: articul,
GoodColumnName: name,
GoodColumnPrice: price,
GoodColumnCategoryId: categoryId,
GoodColumnEan: ean,
GoodColumnAppCompanyId: appCompanyId,
GoodColumnOptPrice: optPrice,
GoodColumnBasePrice: basePrice,
GoodColumnDivisible: divisible,
GoodColumnUpdatedAt: updatedAt,
};
if (id != null) {
map[Goog_columnId] = id;
map[GoodColumnId] = id;
}
return map;
}
@ -47,32 +46,16 @@ class Good {
Good();
Good.fromMap(Map<String, dynamic> map) {
id = map[Goog_columnId];
articul = map[Goog_columnArticul];
name = map[Goog_columnName];
price = map[Goog_columnPrice]?.toDouble();
categoryId = map[Goog_columnCategoryId];
ean = map[Goog_columnEan];
appCompanyId= map[Goog_columnAppCompanyId];
description = map[Goog_columnDescription];
showPrice = map[Goog_columnShowPrice]?.toDouble();
okei = map[Goog_columnOkei];
discount = map[Goog_columnDiscount]?.toDouble();
id = map[GoodColumnId];
articul = map[GoodColumnArticul];
name = map[GoodColumnName];
price = map[GoodColumnPrice]?.toDouble();
categoryId = map[GoodColumnCategoryId];
ean = map[GoodColumnEan];
appCompanyId = map[GoodColumnAppCompanyId];
optPrice = map[GoodColumnOptPrice];
basePrice = map[GoodColumnBasePrice];
divisible = map[GoodColumnDivisible];
updatedAt = map[GoodColumnUpdatedAt];
}
Good.fromJson(Map<String, dynamic> map) {
id = map[Goog_columnId];
articul = map[Goog_columnArticul];
name = map[Goog_columnName];
price = double.parse(map[Goog_columnPrice]);
categoryId = map[Goog_columnCategoryId];
ean = map[Goog_columnEan];
appCompanyId = map[Goog_columnAppCompanyId];
description = map[Goog_columnDescription];
showPrice = map[Goog_columnShowPrice]?.toDouble();
okei = map[Goog_columnOkei];
discount = map[Goog_columnDiscount]?.toDouble();
}
}

View File

@ -1,72 +0,0 @@
const String Service_tableName = 'services';
const String Service_columnId = 'id';
const String Service_columnArticul = 'articul';
const String Service_columnName = 'name';
const String Service_columnPrice = 'price';
const String Service_columnCategoryId = 'category_id';
const String Service_columnEan = 'ean';
const String Service_columnAppCompanyId = 'app_company_id';
const String Service_columnDescription = 'description';
const String Service_columnShowPrice = 'show_price';
const String Service_columnOkei = 'okei';
const String Service_columnDiscount = 'discount';
class Service {
int id;
int articul;
String name;
double price;
String ean;
int appCompanyId;
String description;
double showPrice;
String okei;
double discount;
Map<String, dynamic> toMap() {
var map = <String, dynamic>{
Service_columnArticul: articul,
Service_columnName: name,
Service_columnPrice: price,
Service_columnAppCompanyId: appCompanyId,
Service_columnDescription: description,
Service_columnShowPrice: showPrice,
Service_columnOkei: okei,
Service_columnDiscount: discount
};
if (id != null) {
map[Service_columnId] = id;
}
return map;
}
Service();
Service.fromMap(Map<String, dynamic> map) {
id = map[Service_columnId];
articul = map[Service_columnArticul];
name = map[Service_columnName];
price = map[Service_columnPrice]?.toDouble();
appCompanyId = map[Service_columnAppCompanyId];
description = map[Service_columnDescription];
showPrice = map[Service_columnShowPrice]?.toDouble();
okei = map[Service_columnOkei];
discount = map[Service_columnDiscount]?.toDouble();
}
Service.fromJson(Map<String, dynamic> map) {
id = map[Service_columnId];
articul = map[Service_columnArticul];
name = map[Service_columnName];
price = map[Service_columnPrice]?.toDouble();
appCompanyId = map[Service_columnAppCompanyId];
description = map[Service_columnDescription];
showPrice = map[Service_columnShowPrice]?.toDouble();
okei = map[Service_columnOkei];
discount = map[Service_columnDiscount]?.toDouble();
}
}

View File

@ -0,0 +1,49 @@
const String TransactionTableName = 'transactions';
const String TransactionColumnId = 'id';
const String TransactionColumnUuid = 'uuid';
const String TransactionColumnType = 'type';
const String TransactionColumnStatus = 'status';
const String TransactionColumnData = 'data';
const String TransactionColumnCreatedAt = 'createdAt';
const int TransactionTypeSell = 1;
const int TransactionTypeBuy = 2;
const int TransactionStatusPrepare = 0;
class Transaction {
int id;
String uuid;
int type;
int status;
String data;
String createdAt;
Map<String, dynamic> toMap() {
var map = <String, dynamic>{
TransactionColumnUuid: uuid,
TransactionColumnType: type,
TransactionColumnStatus: status,
TransactionColumnData: data,
TransactionColumnCreatedAt: createdAt,
};
if (id != null) {
map[TransactionColumnId] = id;
}
return map;
}
Transaction();
Transaction.fromMap(Map<String, dynamic> map) {
id = map[TransactionColumnId];
uuid = map[TransactionColumnUuid];
type = map[TransactionColumnType];
status = map[TransactionColumnStatus];
data = map[TransactionColumnData];
createdAt = map[TransactionColumnCreatedAt];
}
}

View File

@ -1,64 +0,0 @@
const String Voucher_tableName = 'vouches';
const String Voucher_columnId = 'id';
const String Voucher_columnName = 'name';
const String Voucher_columnTotal = 'total';
const String Voucher_columnBase64Data = 'base64Data';
const String Voucher_columnData = 'data';
const String Voucher_columnDateTime = 'dateTime';
const String Voucher_columnAppCompanyId = 'app_company_id';
const String Voucher_columnKassaId = 'kassaId';
const String Voucher_columnType = 'type';
const String Voucher_columnUrl = 'url';
const String VoucherTypePayment = 'payment';
const String VoucherTypeReturnPay = 'returnPay';
const String VoucherTypeReport = 'report';
class Voucher {
int id;
String name;
double total;
String data;
String base64Data;
DateTime dateTime;
int appCompanyId;
int kassaId;
String type;
String url;
Voucher();
Map<String, dynamic> toMap() {
var map = <String, dynamic>{
Voucher_columnName: name,
Voucher_columnTotal: total,
Voucher_columnData: data,
Voucher_columnBase64Data: base64Data,
Voucher_columnDateTime: dateTime.toIso8601String(),
Voucher_columnKassaId: kassaId,
Voucher_columnAppCompanyId: appCompanyId,
Voucher_columnType: type,
Voucher_columnUrl: url,
};
if (id != null) {
map[Voucher_columnId] = id;
}
return map;
}
Voucher.fromMap(Map<String, dynamic> map) {
id = map[Voucher_columnId];
name = map[Voucher_columnName];
total = map[Voucher_columnTotal]?.toDouble();
data = map[Voucher_columnData];
base64Data = map[Voucher_columnBase64Data];
dateTime = DateTime.parse(map[Voucher_columnDateTime]);
appCompanyId= map[Voucher_columnAppCompanyId];
kassaId = map[Voucher_columnKassaId];
type = map[Voucher_columnType];
url = map[Voucher_columnUrl];
}
}

View File

@ -0,0 +1,28 @@
/// id : 4
/// parent_id : 0
/// name : ""
/// updated_at : "2021-01-06 14:20:47"
class CategoryResponse {
int id;
int parentId;
String name;
String updatedAt;
static CategoryResponse fromMap(Map<String, dynamic> map) {
if (map == null) return null;
CategoryResponse categoryResponseBean = CategoryResponse();
categoryResponseBean.id = map['id'];
categoryResponseBean.parentId = map['parent_id'];
categoryResponseBean.name = map['name'];
categoryResponseBean.updatedAt = map['updated_at'];
return categoryResponseBean;
}
Map toJson() => {
"id": id,
"parent_id": parentId,
"name": name,
"updated_at": updatedAt,
};
}

View File

@ -0,0 +1,52 @@
/// id : 6
/// category_id : 4
/// name : "KENT SILVER"
/// ean : "46187413"
/// articul : 1
/// price : 500
/// opt_price : 500
/// base_price : 500
/// divisible : 0
/// updated_at : "2021-02-03 11:37:34"
class GoodResponse {
int id;
int categoryId;
String name;
String ean;
int articul;
int price;
int optPrice;
int basePrice;
int divisible;
String updatedAt;
static GoodResponse fromMap(Map<String, dynamic> map) {
if (map == null) return null;
GoodResponse goodResponseBean = GoodResponse();
goodResponseBean.id = map['id'];
goodResponseBean.categoryId = map['category_id'];
goodResponseBean.name = map['name'];
goodResponseBean.ean = map['ean'];
goodResponseBean.articul = map['articul'];
goodResponseBean.price = map['price'];
goodResponseBean.optPrice = map['opt_price'];
goodResponseBean.basePrice = map['base_price'];
goodResponseBean.divisible = map['divisible'];
goodResponseBean.updatedAt = map['updated_at'];
return goodResponseBean;
}
Map toJson() => {
"id": id,
"category_id": categoryId,
"name": name,
"ean": ean,
"articul": articul,
"price": price,
"opt_price": optPrice,
"base_price": basePrice,
"divisible": divisible,
"updated_at": updatedAt,
};
}

View File

@ -0,0 +1,11 @@
class ProductDao {
int id;
int categoryId;
num count;
num price;
String productName;
String categoryName;
String eanCode;
int article;
String excise;
}

View File

@ -0,0 +1,32 @@
/// list : []
/// message : ""
/// operation : true
class Response<T> {
List<T> list;
String message;
bool operation;
Response();
factory Response.fromMapList(Map<String, dynamic> map, Function parser) {
if (map == null) return null;
List<T> list = [];
if (map['list'] != null) {
(map['list'] as List).forEach((element) {
if(parser == null)
list.add(element);
else
list.add(parser(element));
});
}
Response responseBean = Response();
responseBean.list = list;
responseBean.message = map['message'];
responseBean.operation = map['operation'];
return responseBean;
}
}

View File

@ -0,0 +1,44 @@
import 'package:meta/meta.dart';
import 'package:redux/redux.dart';
import 'package:redux_thunk/redux_thunk.dart';
import 'package:satu/core/entity/Category.dart';
import 'package:satu/core/entity/Goods.dart';
import 'package:satu/core/models/flow/product_dao.dart';
import 'package:satu/core/redux/state/sell_state.dart';
import 'package:satu/core/services/db_service.dart';
import 'package:satu/core/services/dialog_service.dart';
import 'package:satu/core/utils/locator.dart';
import '../store.dart';
@immutable
class SetSellStateAction {
final SellState sellState;
SetSellStateAction(this.sellState);
}
final DbService _dbService = locator<DbService>();
ThunkAction<AppState> addSellItem({Good good, String excise}) {
return (Store<AppState> store) async {
ProductDao item = new ProductDao()
..id = good.id
..price = good.price
..article = good.articul
..count = 1.0
..eanCode = good.ean
..productName = good.name
..excise = excise;
//category add logic
if (good.categoryId != null) {
List<Map<String, dynamic>> set =
await _dbService.queryRowsWithWhere(CategoryTableName, 'id = ?', [good.categoryId]);
if (set.isNotEmpty) {
Category category = Category.fromMap(set.first);
item.categoryId = category.id;
}
}
store.dispatch(SetSellStateAction(SellState()));
};
}

View File

@ -2,10 +2,10 @@ import 'package:redux/redux.dart';
import 'package:meta/meta.dart';
import 'package:redux_thunk/redux_thunk.dart';
import 'package:satu/core/models/auth/auth_response.dart';
import 'package:satu/core/redux/constants/auth_type_const.dart';
import 'package:satu/core/redux/state/user_state.dart';
import 'package:satu/core/services/api_service.dart';
import 'package:satu/core/services/dialog_service.dart';
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/routes/route_names.dart';
@ -21,6 +21,8 @@ final ApiService _api = locator<ApiService>();
final NavigatorService _navigation = locator<NavigatorService>();
final DialogService _dialogService = locator<DialogService>();
final DictionaryService _dictionaryService = locator<DictionaryService>();
ThunkAction<AppState> authenticate(String email, String password) {
return (Store<AppState> store) async {
@ -31,6 +33,7 @@ ThunkAction<AppState> authenticate(String email, String password) {
_api.token = result.token;
store.dispatch(SetUserStateAction(UserState(isLoading: false, auth: result)));
_navigation.replace(MainViewRoute);
_afterAuth(store);
} else {
_dialogService.showDialog(title: 'Внимание', buttonTitle: 'Ok', description: result.message);
}
@ -42,6 +45,29 @@ ThunkAction<AppState> authenticate(String email, String password) {
};
}
Future<void> auth(Store<AppState> store) async {
store.dispatch(SetUserStateAction(UserState(isLoading: true)));
try {
UserState state = store.state.userState;
if(state.auth.operation == false) {
_navigation.replace(LoginViewRoute);
} else {
AuthResponse response = await _api.auth(state.auth.token);
if(response.operation){
_api.token = response.token;
_navigation.replace(MainViewRoute);
_afterAuth(store);
} else {
_navigation.replace(LoginViewRoute);
}
}
} catch (e) {
print(e);
} finally {
store.dispatch(SetUserStateAction(UserState(isLoading: false)));
}
}
Future<void> logout(Store<AppState> store) async {
store.dispatch(SetUserStateAction(UserState(isLoading: true)));
try {
@ -60,3 +86,7 @@ Future<void> logout(Store<AppState> store) async {
}
}
Future<void> _afterAuth(Store<AppState> store) async {
await _dictionaryService.refreshFull();
}

View File

@ -1,2 +0,0 @@
const String AuthenticateTypeQr = 'AuthenticateTypeQr';
const String AuthenticateTypeLogin = 'AuthenticateTypeLogin';

View File

@ -1,2 +0,0 @@
const String OperationTypePay = 'OperationTypePay';
const String OperationTypeReturn = 'OperationTypeReturn';

View File

@ -1,6 +0,0 @@
const String SettingModeKassa = 'kassaMode';
const String SettingModeCalc = 'calcMode';
const String SettingTradeTypeGood = 'g';
const String SettingTradeTypeService = 's';

View File

@ -0,0 +1,11 @@
import 'package:satu/core/redux/actions/sell_actions.dart';
import 'package:satu/core/redux/actions/user_actions.dart';
import 'package:satu/core/redux/state/sell_state.dart';
import 'package:satu/core/redux/state/user_state.dart';
sellReducer(SellState prevState, SetSellStateAction action) {
final payload = action.sellState;
return prevState.copyWith(
items: payload.items
);
}

View File

@ -0,0 +1,20 @@
import 'package:meta/meta.dart';
import 'package:satu/core/models/auth/auth_response.dart';
import 'package:satu/core/models/flow/product_dao.dart';
@immutable
class SellState {
final List<ProductDao> items;
SellState({
this.items,
});
factory SellState.initial() => SellState(
items: [],
);
SellState copyWith({@required List<ProductDao> items}) {
return SellState(items: items ?? this.items);
}
}

View File

@ -19,7 +19,7 @@ class UserState {
factory UserState.initial(UserState payload) => UserState(
isLoading: false,
isError: false,
auth: payload?.auth ?? AuthResponse(),
auth: payload?.auth ?? (AuthResponse()..operation=false),
);
UserState copyWith({

View File

@ -8,6 +8,7 @@ import 'package:satu/core/redux/actions/nav_actions.dart';
import 'package:satu/core/redux/reducers/nav_reducer.dart';
import 'package:satu/core/redux/reducers/user_reducer.dart';
import 'package:satu/core/redux/state/nav_state.dart';
import 'package:satu/core/redux/state/sell_state.dart';
import 'package:satu/core/redux/state/user_state.dart';
import 'actions/user_actions.dart';
@ -32,21 +33,25 @@ AppState appReducer(AppState state, dynamic action) {
class AppState {
final UserState userState;
final NavState navState;
final SellState sellState;
AppState({
this.userState,
this.navState,
this.sellState
});
//stable work
AppState copyWith({
UserState userState,
NavState navState,
SellState sellState,
}) {
return AppState(
userState: userState ?? this.userState,
navState: navState ?? this.navState,
sellState: sellState ?? this.sellState
);
}
@ -85,9 +90,9 @@ class Redux {
);
final initialState = await persist.load();
final userStateInitial = UserState.initial(initialState?.userState);
final navStateInitial = NavState.initial();
final sellStateInitial = SellState.initial();
_store = Store<AppState>(
appReducer,
@ -95,6 +100,7 @@ class Redux {
initialState: AppState(
userState: userStateInitial,
navState: navStateInitial,
sellState: sellStateInitial,
)
);
}

View File

@ -4,6 +4,7 @@ import 'dart:io';
import 'package:satu/core/base/base_service.dart';
import 'package:http/http.dart' as http;
import 'package:satu/core/models/auth/auth_response.dart';
import 'package:satu/core/models/response.dart';
/// The service responsible for networking requests
class ApiService extends BaseService {
@ -41,7 +42,9 @@ class ApiService extends BaseService {
headers.addAll(header);
}
String url = '$endpoint$point';
print(jsonEncode(requestBody));
if(requestBody!=null) {
log.i(jsonEncode(requestBody));
}
final response = await http.post(Uri.https(host, url), body: jsonEncode(requestBody), headers: headers);
return response.body;
@ -106,4 +109,17 @@ class ApiService extends BaseService {
}
return result;
}
Future<Response> dictionaries(String target) async {
Response result;
try {
Map<String, String> headers = <String, String>{HttpHeaders.authorizationHeader: 'Bearer $token'};
String response = await _post(target, header: headers);
result = Response.fromMapList(json.decode(response), null);
} catch (e, stack) {
log.e("dictionaries", e, stack);
result = new Response()..operation=false..list=[];
}
return result;
}
}

View File

@ -11,4 +11,6 @@ class DataService extends BaseService {
final ApiService _api = locator<ApiService>();
final DbService _db = locator<DbService>();
}

View File

@ -3,14 +3,13 @@ import 'dart:io';
import 'package:satu/core/base/base_service.dart';
import 'package:satu/core/entity/Category.dart';
import 'package:satu/core/entity/Goods.dart';
import 'package:satu/core/entity/Service.dart';
import 'package:satu/core/entity/Voucher.dart';
import 'package:satu/core/entity/Transaction.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
class DbService extends BaseService {
static final _databaseName = "AmanSatuFlutterDb.db";
static final _databaseName = "AmanSatuDb.db";
static final _databaseVersion = 1;
// make this a singleton class
@ -39,15 +38,10 @@ class DbService extends BaseService {
Future _onUpdate(Database db, int oldVersion, int newVersion) async {
log.i('update from $oldVersion to $newVersion');
//Goods table
await db.execute('DROP TABLE IF EXISTS $Goog_tableName;');
await db.execute('DROP TABLE IF EXISTS $Category_tableName;');
await db.execute('DROP TABLE IF EXISTS $Service_tableName;');
await db.execute('DROP TABLE IF EXISTS $GoodTableName;');
await db.execute('DROP TABLE IF EXISTS $CategoryTableName;');
//await db.execute('DROP TABLE IF EXISTS $Voucher_tableName;');
log.i('dropped tables');
if (newVersion > 16) {
//Не удалять таблицу с чеками
}
_onCreate(db, newVersion);
}
@ -55,55 +49,36 @@ class DbService extends BaseService {
log.i('create tables');
//Goods table
await db.execute('''
CREATE TABLE IF NOT EXISTS $Goog_tableName (
$Goog_columnId integer primary key unique,
$Goog_columnArticul integer not null,
$Goog_columnName text not null,
$Goog_columnPrice real not null,
$Goog_columnCategoryId integer not null,
$Goog_columnEan text,
$Goog_columnAppCompanyId integer,
$Goog_columnDescription text,
$Goog_columnShowPrice real,
$Goog_columnOkei integer,
$Goog_columnDiscount real
CREATE TABLE IF NOT EXISTS $GoodTableName (
$GoodColumnId integer primary key unique,
$GoodColumnArticul integer not null,
$GoodColumnName text not null,
$GoodColumnPrice real not null,
$GoodColumnCategoryId integer not null,
$GoodColumnEan text,
$GoodColumnAppCompanyId integer,
$GoodColumnOptPrice real,
$GoodColumnBasePrice real,
$GoodColumnDivisible integer,
$GoodColumnUpdatedAt text not null
);
''');
await db.execute('''
CREATE TABLE IF NOT EXISTS $Category_tableName (
$Category_columnId integer primary key unique,
$Category_columnName text not null,
$Category_columnParentIn integer,
$Category_columnAppCompanyId integer
CREATE TABLE IF NOT EXISTS $CategoryTableName (
$CategoryColumnId integer primary key unique,
$CategoryColumnName text not null,
$CategoryColumnParentIn integer,
$CategoryColumnAppCompanyId integer,
$CategoryColumnUpdatedAt text not null
);
''');
//Service
await db.execute('''
CREATE TABLE IF NOT EXISTS $Service_tableName (
$Service_columnId integer primary key unique,
$Service_columnArticul integer not null,
$Service_columnName text not null,
$Service_columnPrice real not null,
$Service_columnAppCompanyId integer,
$Service_columnDescription text,
$Service_columnShowPrice real,
$Service_columnOkei text,
$Service_columnDiscount real
);
''');
//Voucher
await db.execute('''
CREATE TABLE IF NOT EXISTS $Voucher_tableName (
$Voucher_columnId integer primary key AUTOINCREMENT,
$Voucher_columnName text not null,
$Voucher_columnTotal real,
$Voucher_columnData text,
$Voucher_columnBase64Data text,
$Voucher_columnDateTime text not null,
$Service_columnAppCompanyId integer,
$Voucher_columnKassaId integer,
$Voucher_columnType text not null,
$Voucher_columnUrl text
CREATE TABLE IF NOT EXISTS $TransactionTableName (
$TransactionColumnId integer primary key autoincrement,
$TransactionColumnUuid text,
$TransactionColumnType integer not null,
$TransactionColumnData text,
$TransactionColumnCreatedAt text not null
);
''');
}

View File

@ -0,0 +1,129 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:satu/core/base/base_service.dart';
import 'package:satu/core/entity/Category.dart';
import 'package:satu/core/entity/Goods.dart';
import 'package:satu/core/models/dictionary/category_response.dart';
import 'package:satu/core/models/dictionary/good_response.dart';
import 'package:satu/core/models/response.dart';
import 'package:satu/core/redux/store.dart';
import 'package:satu/core/utils/locator.dart';
import 'api_service.dart';
import 'db_service.dart';
class DictionaryService extends BaseService {
final ApiService _api = locator<ApiService>();
final DbService _db = locator<DbService>();
Future<void> refreshFull() async {
_db.deleteAll(CategoryTableName);
await refreshCategory();
_db.deleteAll(GoodTableName);
await refreshGood();
}
Future<void> refreshCategory() async {
try {
int appCompanyId = Redux.store.state.userState.auth.companyId;
Response categories = await _api.dictionaries('/categories');
if (categories.operation && categories.list.isNotEmpty) {
for (dynamic map in categories.list) {
CategoryResponse cat = CategoryResponse.fromMap(map);
Category entity = new Category()
..id = cat.id
..name = cat.name
..updatedAt = cat.updatedAt
..parentId = cat.parentId
..appCompanyId = appCompanyId;
_db.insert(CategoryTableName, entity.toMap());
}
}
} catch (e, stack) {
log.e("categories", e, stack);
}
}
Future<List<Category>> getCategoryByParentId(int parentId ) async {
List<Category> list = [];
try {
int appCompanyId = Redux.store.state.userState.auth.companyId;
List<Map<String, dynamic>> elements = await _db.queryRowsWithWhere(CategoryTableName, '$CategoryColumnParentIn = ?', [parentId]);
elements.forEach((element) {
list.add(Category.fromMap(element));
});
} catch (e, stack) {
log.e("getCategoryByParentId", e, stack);
}
return list;
}
Future<List<Good>> getGoodsByCategoryId(int categoryId ) async {
List<Good> list = [];
try {
int appCompanyId = Redux.store.state.userState.auth.companyId;
List<Map<String, dynamic>> elements = await _db.queryRowsWithWhere(GoodTableName, '$GoodColumnAppCompanyId = ? and $GoodColumnCategoryId = ?', [appCompanyId, categoryId]);
elements.forEach((element) {
list.add(Good.fromMap(element));
});
} catch (e, stack) {
log.e("getGoodsByCategoryId", e, stack);
}
return list;
}
Future<List<Good>> getGoodsByNameOrEan( String query ) async {
List<Good> list = [];
try {
int appCompanyId = Redux.store.state.userState.auth.companyId;
String where = '( $GoodColumnAppCompanyId = ? and $GoodColumnName like ? ) ';
List args = [appCompanyId, '%$query%'];
if(_isNumericInt(query)){
where += ' or $GoodColumnEan like ?';
args.add('${int.parse(query).toString()}%');
}
List<Map<String, dynamic>> elements = await _db.queryRowsWithWhere(GoodTableName, where, args);
elements.forEach((element) {
list.add(Good.fromMap(element));
});
} catch (e, stack) {
log.e("getGoodsByCategoryId", e, stack);
}
return list;
}
bool _isNumericInt(String str) {
if(str == null) {
return false;
}
return int.tryParse(str) != null;
}
Future<void> refreshGood() async {
try {
int appCompanyId = Redux.store.state.userState.auth.companyId;
Response categories = await _api.dictionaries('/goods');
if (categories.operation && categories.list.isNotEmpty) {
for (dynamic map in categories.list) {
GoodResponse good = GoodResponse.fromMap(map);
Good entity = new Good()
..id = good.id
..name = good.name
..categoryId = good.categoryId
..ean = good.ean
..articul = good.articul
..price = good.price
..optPrice = good.optPrice
..basePrice = good.basePrice
..divisible = good.divisible
..updatedAt = good.updatedAt
..appCompanyId = appCompanyId;
_db.insert(GoodTableName, entity.toMap());
}
}
} catch (e, stack) {
log.e("goods", e, stack);
}
}
}

View File

@ -7,20 +7,20 @@ class NavigatorService extends BaseService {
final GlobalKey<ScaffoldState> scaffoldDrawerKey = GlobalKey<ScaffoldState>();
Future<dynamic> push(String routeName, {dynamic arguments}) {
log.i('routeName: $routeName');
log.d('routeName: $routeName');
return navigatorKey.currentState
.pushNamed(routeName, arguments: arguments);
}
Future<dynamic> replace(String routeName, {dynamic arguments}) {
log.i('routeName: $routeName');
log.d('routeName: $routeName');
return navigatorKey.currentState
.pushNamedAndRemoveUntil(routeName, (Route<dynamic> route) => false, arguments: arguments);
}
Future<T> navigateToPage<T>(MaterialPageRoute<T> pageRoute) async {
log.i('navigateToPage: pageRoute: ${pageRoute.settings.name}');
log.d('navigateToPage: pageRoute: ${pageRoute.settings.name}');
if (navigatorKey.currentState == null) {
log.e('navigateToPage: Navigator State is null');
return null;
@ -30,7 +30,7 @@ class NavigatorService extends BaseService {
Future<T> navigateToPageWithReplacement<T>(
MaterialPageRoute<T> pageRoute) async {
log.i('navigateToPageWithReplacement: '
log.d('navigateToPageWithReplacement: '
'pageRoute: ${pageRoute.settings.name}');
if (navigatorKey.currentState == null) {
log.e('navigateToPageWithReplacement: Navigator State is null');
@ -40,7 +40,7 @@ class NavigatorService extends BaseService {
}
void pop<T>([T result]) {
log.i('goBack:');
log.d('goBack:');
if (navigatorKey.currentState == null) {
log.e('goBack: Navigator State is null');
return;

View File

@ -4,6 +4,7 @@ import 'package:satu/core/services/api_service.dart';
import 'package:satu/core/services/data_service.dart';
import 'package:satu/core/services/db_service.dart';
import 'package:satu/core/services/dialog_service.dart';
import 'package:satu/core/services/dictionary_service.dart';
import 'package:satu/core/services/navigator_service.dart';
import 'logger.dart';
@ -29,5 +30,7 @@ class LocatorInjector {
_log.d('Initializing DataService Service');
locator.registerLazySingleton<DataService>(() => DataService());
_log.d('Initializing DictionaryService Service');
locator.registerLazySingleton<DictionaryService>(() => DictionaryService());
}
}

View File

@ -18,7 +18,7 @@ class SimpleLogPrinter extends LogPrinter {
if (event.stackTrace == null) {
stack = formatStackTrace(StackTrace.current, 2);
} else {
stack = formatStackTrace(event.stackTrace, 2);
stack = formatStackTrace(event.stackTrace, 1);
}
print(color(' $emoji $message $error -> $stack '));
return [];

View File

@ -1,4 +1,11 @@
import 'package:flutter/material.dart';
import 'package:satu/core/entity/Category.dart';
import 'package:satu/core/entity/Goods.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/add_product/component/add_category_list_item.dart';
@ -13,14 +20,34 @@ class AddProductView extends StatefulWidget {
}
class _AddProductViewState extends State<AddProductView> {
DictionaryService _dictionaryService = locator<DictionaryService>();
NavigatorService _navigatorService = locator<NavigatorService>();
TextEditingController _searchTextController;
final FocusNode _searchFocusNode = new FocusNode();
List<Category> _history;
List<Category> _categories;
List<Good> _goods;
@override
void initState() {
_searchTextController = TextEditingController();
_searchTextController.addListener(() {
if(_searchTextController.text.isNotEmpty){
searchByField(_searchTextController.text);
} else {
reset();
}
});
_history = [Category()
..id = 0
];
_categories = [];
_goods = [];
super.initState();
navigateCategory(0);
}
@override
@ -32,34 +59,42 @@ class _AddProductViewState extends State<AddProductView> {
@override
Widget build(BuildContext context) {
int catSize = _categories?.length ?? 0;
int goodSize = _goods?.length ?? 0;
return Scaffold(
appBar: AddProductAppBar(title: 'Товар', actions: actions(),),
body: Column(
children: [
InputField(placeholder: 'Поиск по наименованию и коду товара', search: true, controller: _searchTextController, fieldFocusNode: _searchFocusNode,),
InputField(placeholder: 'Поиск по наименованию и коду товара',
search: true,
controller: _searchTextController,
fieldFocusNode: _searchFocusNode,),
verticalSpaceTiny,
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0 ),
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: ListView.builder(
itemCount: 50,
physics: BouncingScrollPhysics(),
itemCount: catSize + goodSize,
itemBuilder: (BuildContext context, int index) {
if(index < 5) {
if (index < catSize) {
Category category = _categories[index];
return AddCategoryListItem(
name: 'Категория',
isOdd: index % 2 ==0,
key: Key('category_${index}'),
name: category.name,
isOdd: index % 2 == 0,
key: Key('category_${category.id}'),
onPress: () => onCategoryPress(category),
);
}
Good good = _goods[index - catSize];
return AddProductListItem(
key: Key('product_${index}'),
ean: '1234567890123',
isOdd: index % 2 ==0,
name: 'Хлеб ржаной который необходимо покупать каждый раз когда придет мысль об этом - ${index +1}. ',
price: 75,
count: 15,
categoryName: 'Хлебобулочные изделия',
key: Key('product_${good.id}'),
ean: good.ean,
isOdd: index % 2 == 0,
name: good.name,
price: good.price,
categoryName: _history.last?.name,
onPress: () => onGoodPress(good),
);
},
),
@ -71,10 +106,52 @@ class _AddProductViewState extends State<AddProductView> {
);
}
onCategoryPress(Category category) {
_history.add(category);
navigateCategory(category.id);
}
onGoodPress(Good good) {
Redux.store.dispatch(addSellItem(good: good));
_navigatorService.pop();
}
List<Widget> actions() {
return [
FlatButton(onPressed: () {}, child: Text('Очистить', style: TextStyle( color: Colors.black ),) ,)
if(_history.length > 1)
FlatButton(onPressed: () {
_history.removeLast();
navigateCategory(_history.last.id);
}, child: Text('Назад', style: TextStyle(color: Colors.black),),),
FlatButton(onPressed: reset, child: Text('Сбросить', style: TextStyle(color: Colors.black),),)
];
}
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;
});
}
void searchByField(String query) async {
List<Category> categories = [];
List<Good> goods = await _dictionaryService.getGoodsByNameOrEan(query);
setState(() {
_categories = categories;
_goods = goods;
});
}
}

View File

@ -7,13 +7,15 @@ import 'package:satu/shared/ui_helpers.dart';
class AddCategoryListItem extends StatelessWidget {
final String name;
final bool isOdd;
final Function onPress;
const AddCategoryListItem({Key key, this.name, this.isOdd}) : super(key: key);
const AddCategoryListItem({Key key, this.name, this.isOdd, this.onPress }) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
child: ListTile(
onTap: onPress,
contentPadding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
title: Padding(
padding: const EdgeInsets.only(top: 4.0),

View File

@ -11,13 +11,15 @@ class AddProductListItem extends StatelessWidget {
final num price;
final num count;
final bool isOdd;
final Function onPress;
const AddProductListItem({Key key, this.name, this.ean, this.categoryName, this.price, this.count, this.isOdd}) : super(key: key);
const AddProductListItem({Key key, this.name, this.ean, this.categoryName, this.price, this.count, this.isOdd, this.onPress}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
child: ListTile(
onTap: onPress,
contentPadding: const EdgeInsets.symmetric( horizontal: 8.0 ,vertical: 4.0 ),
title: Padding(
padding: const EdgeInsets.all(4.0),

View File

@ -1,9 +1,11 @@
import 'package:flutter_redux/flutter_redux.dart';
import 'package:flutter/material.dart';
import 'package:satu/core/models/auth/auth_response.dart';
import 'package:satu/core/redux/actions/user_actions.dart';
import 'package:satu/core/redux/state/user_state.dart';
import 'package:satu/core/redux/store.dart';
import 'package:satu/core/services/api_service.dart';
import 'package:satu/core/services/navigator_service.dart';
import 'package:satu/core/utils/locator.dart';
import 'package:satu/routes/route_names.dart';
@ -16,6 +18,7 @@ class StartUpView extends StatefulWidget {
class _StartUpViewState extends State<StartUpView> {
final NavigatorService _navigation = locator<NavigatorService>();
final ApiService _api = locator<ApiService>();
@override
void initState() {
@ -52,7 +55,7 @@ class _StartUpViewState extends State<StartUpView> {
}
void redirect() async {
await Future.delayed(Duration(seconds: 3));
_navigation.replace(LoginViewRoute);
await Future.delayed(Duration(milliseconds: 100));
Redux.store.dispatch(auth);
}
}

View File

@ -8,23 +8,33 @@ import 'package:satu/views/work/tabs/component/products_app_bar.dart';
import 'package:satu/views/work/tabs/component/products_header_bar.dart';
class SellView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: ProductsAppBar(title: 'Продажа', actions: actions(), elevation: 2.0, child: ProductHeaderBar( count: 14, sum: 25000,), backgroundColor: backgroundColor, childHeight: 80,),
appBar: ProductsAppBar(
title: 'Продажа',
actions: actions(),
elevation: 2.0,
child: ProductHeaderBar(
count: 14,
sum: 25000,
),
backgroundColor: backgroundColor,
childHeight: 80,
),
body: Column(
children: [
//ProductHeaderBar(count: 14, sum: 25000,),
Expanded(
child: ListView.builder(
physics: BouncingScrollPhysics(),
itemCount: 50,
itemCount: 5,
itemBuilder: (BuildContext context, int index) {
return ProductListItem(
ean: '1234567890123',
isOdd: index % 2 ==0,
name: 'Хлеб ржаной который необходимо покупать каждый раз когда придет мысль об этом - ${index +1}. ',
isOdd: index % 2 == 0,
name:
'Хлеб ржаной который необходимо покупать каждый раз когда придет мысль об этом - ${index + 1}. ',
price: 75,
count: 15,
categoryName: 'Хлебобулочные изделия',
@ -41,13 +51,17 @@ class SellView extends StatelessWidget {
return [
Padding(
padding: const EdgeInsets.all(8.0),
child: IconButton(icon: Icon(Icons.add_box, size: 30.0, color: yellowColor), onPressed: () {
child: IconButton(
icon: Icon(Icons.add_box, size: 30.0, color: yellowColor),
onPressed: () {
locator<NavigatorService>().push(AddProductViewRoute);
}),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: IconButton(icon: Icon(Icons.camera_enhance_rounded, size: 30.0, color: yellowColor), onPressed: () {
child: IconButton(
icon: Icon(Icons.camera_enhance_rounded, size: 30.0, color: yellowColor),
onPressed: () {
locator<NavigatorService>().push(AddByBarcodeViewRoute);
}),
)