diff --git a/lib/core/entity/Transaction.dart b/lib/core/entity/Transaction.dart index 9ae76d4..8c6966a 100644 --- a/lib/core/entity/Transaction.dart +++ b/lib/core/entity/Transaction.dart @@ -4,6 +4,7 @@ const String TransactionColumnUuid = 'uuid'; const String TransactionColumnType = 'type'; const String TransactionColumnStatus = 'status'; const String TransactionColumnData = 'data'; +const String TransactionColumnAppCompanyId = 'appCompanyId'; const String TransactionColumnCreatedAt = 'createdAt'; const int TransactionTypeSell = 1; @@ -18,6 +19,7 @@ class Transaction { int type; int status; String data; + int appCompanyId; String createdAt; Map toMap() { @@ -26,6 +28,7 @@ class Transaction { TransactionColumnType: type, TransactionColumnStatus: status, TransactionColumnData: data, + TransactionColumnAppCompanyId: appCompanyId, TransactionColumnCreatedAt: createdAt, }; if (id != null) { @@ -41,6 +44,7 @@ class Transaction { uuid = map[TransactionColumnUuid]; type = map[TransactionColumnType]; status = map[TransactionColumnStatus]; + appCompanyId = map[TransactionColumnAppCompanyId]; data = map[TransactionColumnData]; createdAt = map[TransactionColumnCreatedAt]; } diff --git a/lib/core/models/flow/product_dao.dart b/lib/core/models/flow/product_dao.dart index 1e9fb5d..122f7a6 100644 --- a/lib/core/models/flow/product_dao.dart +++ b/lib/core/models/flow/product_dao.dart @@ -8,4 +8,35 @@ class ProductDao { String eanCode; int article; String excise; + + + Map toMap() { + var map = { + 'id': id, + 'categoryId': categoryId, + 'count': count, + 'price': price, + 'productName': productName, + 'categoryName': categoryName, + 'eanCode': eanCode, + 'article': article, + 'excise': excise, + }; + return map; + } + + ProductDao(); + + ProductDao.fromMap(Map map) { + id = map['id']; + categoryId = map['categoryId']; + count = map['count']; + price = map['price']; + productName = map['productName']; + categoryName = map['categoryName']; + eanCode = map['eanCode']; + article = map['article']; + excise = map['excise']; + } + } \ No newline at end of file diff --git a/lib/core/models/flow/transaction_state.dart b/lib/core/models/flow/transaction_state.dart new file mode 100644 index 0000000..cba2a48 --- /dev/null +++ b/lib/core/models/flow/transaction_state.dart @@ -0,0 +1,3 @@ +class TransactionState { + String uuid; +} \ No newline at end of file diff --git a/lib/core/redux/actions/sell_actions.dart b/lib/core/redux/actions/sell_actions.dart index ddd0fcd..18e5d1e 100644 --- a/lib/core/redux/actions/sell_actions.dart +++ b/lib/core/redux/actions/sell_actions.dart @@ -1,13 +1,19 @@ +import 'dart:convert'; + +import 'package:logger/logger.dart'; 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/entity/Transaction.dart'; import 'package:satu/core/models/flow/product_dao.dart'; +import 'package:satu/core/models/flow/transaction_state.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 'package:satu/core/utils/logger.dart'; +import 'package:uuid/uuid.dart'; import '../store.dart'; @@ -18,27 +24,97 @@ class SetSellStateAction { SetSellStateAction(this.sellState); } +final Logger log = getLogger('SetSellStateAction'); + final DbService _dbService = locator(); ThunkAction addSellItem({Good good, String excise}) { return (Store 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> set = - await _dbService.queryRowsWithWhere(CategoryTableName, 'id = ?', [good.categoryId]); + log.i('addSellItem'); + int appCompanyId = store.state.userState.auth.companyId; + String uuid = store.state.sellState.transactionState.uuid; + + Transaction transaction; + + if (uuid != null && good !=null) { + List> set = await _dbService.queryRowsWithWhere( + TransactionTableName, + '$TransactionColumnAppCompanyId = ? and $TransactionColumnStatus = ? and ${TransactionColumnType} = ?', + [appCompanyId, TransactionStatusPrepare, TransactionTypeSell], + orderBy: '$TransactionColumnCreatedAt desc'); if (set.isNotEmpty) { - Category category = Category.fromMap(set.first); - item.categoryId = category.id; + for(Map map in set) { + Transaction _transaction = Transaction.fromMap(map); + ProductDao _product = ProductDao.fromMap(jsonDecode(_transaction.data)); + if(_product.id == good.id) { + transaction = _transaction; + break; + } + } } } - store.dispatch(SetSellStateAction(SellState())); + + if(transaction !=null) { + ProductDao item = ProductDao.fromMap(jsonDecode(transaction.data)); + item..count = item.count + 1; + transaction.data = jsonEncode(item.toMap()); + transaction.createdAt = DateTime.now().toIso8601String(); + _dbService.update(TransactionTableName, transaction.toMap()); + } else { + 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> set = + await _dbService.queryRowsWithWhere(CategoryTableName, 'id = ?', [good.categoryId]); + if (set.isNotEmpty) { + Category category = Category.fromMap(set.first); + item.categoryId = category.id; + } + } + + if (uuid == null) { + var uuidTool = Uuid(); + ; + uuid = uuidTool.v4(); + } + + transaction = new Transaction() + ..uuid = uuid + ..status = TransactionStatusPrepare + ..appCompanyId = appCompanyId + ..type = TransactionTypeSell + ..createdAt = DateTime.now().toIso8601String() + ..data = jsonEncode(item.toMap()); + await _dbService.insert(TransactionTableName, transaction.toMap()); + } + await loadSellData(store); }; } + +Future loadSellData(Store store) async { + try { + int appCompanyId = store.state.userState.auth.companyId; + List> set = await _dbService.queryRowsWithWhere( + TransactionTableName, + '$TransactionColumnAppCompanyId = ? and $TransactionColumnStatus = ? and ${TransactionColumnType} = ?', + [appCompanyId, TransactionStatusPrepare, TransactionTypeSell], + orderBy: '$TransactionColumnCreatedAt desc'); + List list = []; + String uuid; + for (Map map in set) { + Transaction transaction = Transaction.fromMap(map); + uuid = transaction.uuid; + list.add(ProductDao.fromMap(jsonDecode(transaction.data))); + } + store.dispatch(SetSellStateAction(SellState(items: list, transactionState: TransactionState()..uuid = uuid))); + } catch (e, stack) { + log.e('loadSellData', e, stack); + } +} diff --git a/lib/core/redux/reducers/sell_reducer.dart b/lib/core/redux/reducers/sell_reducer.dart index e7fe923..e1f940b 100644 --- a/lib/core/redux/reducers/sell_reducer.dart +++ b/lib/core/redux/reducers/sell_reducer.dart @@ -6,6 +6,7 @@ import 'package:satu/core/redux/state/user_state.dart'; sellReducer(SellState prevState, SetSellStateAction action) { final payload = action.sellState; return prevState.copyWith( - items: payload.items + items: payload.items, + transactionState: payload.transactionState ); } \ No newline at end of file diff --git a/lib/core/redux/state/sell_state.dart b/lib/core/redux/state/sell_state.dart index d23f862..eaf7794 100644 --- a/lib/core/redux/state/sell_state.dart +++ b/lib/core/redux/state/sell_state.dart @@ -1,20 +1,23 @@ import 'package:meta/meta.dart'; import 'package:satu/core/models/auth/auth_response.dart'; import 'package:satu/core/models/flow/product_dao.dart'; +import 'package:satu/core/models/flow/transaction_state.dart'; @immutable class SellState { final List items; + final TransactionState transactionState; - SellState({ - this.items, - }); + SellState({this.items, this.transactionState}); factory SellState.initial() => SellState( items: [], + transactionState: TransactionState(), ); - SellState copyWith({@required List items}) { - return SellState(items: items ?? this.items); + SellState copyWith({@required List items, @required TransactionState transactionState}) { + return SellState(items: items ?? this.items, transactionState: transactionState ?? this.transactionState); } } + + diff --git a/lib/core/redux/store.dart b/lib/core/redux/store.dart index 728032b..68cf624 100644 --- a/lib/core/redux/store.dart +++ b/lib/core/redux/store.dart @@ -5,7 +5,9 @@ import 'package:redux_persist_flutter/redux_persist_flutter.dart'; import 'package:redux_thunk/redux_thunk.dart'; import 'package:redux_persist/redux_persist.dart'; import 'package:satu/core/redux/actions/nav_actions.dart'; +import 'package:satu/core/redux/actions/sell_actions.dart'; import 'package:satu/core/redux/reducers/nav_reducer.dart'; +import 'package:satu/core/redux/reducers/sell_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'; @@ -24,6 +26,10 @@ AppState appReducer(AppState state, dynamic action) { /** NavAction **/ final nextState = navReducer(state.navState, action); return state.copyWith(navState: nextState); + } else if (action is SetSellStateAction) { + /** NavAction **/ + final nextState = sellReducer(state.sellState, action); + return state.copyWith(sellState: nextState); } return state; } diff --git a/lib/core/services/db_service.dart b/lib/core/services/db_service.dart index 61fe492..37ae2ec 100644 --- a/lib/core/services/db_service.dart +++ b/lib/core/services/db_service.dart @@ -77,6 +77,8 @@ class DbService extends BaseService { $TransactionColumnId integer primary key autoincrement, $TransactionColumnUuid text, $TransactionColumnType integer not null, + $TransactionColumnAppCompanyId integer not null, + $TransactionColumnStatus integer not null, $TransactionColumnData text, $TransactionColumnCreatedAt text not null ); @@ -98,15 +100,20 @@ class DbService extends BaseService { return await db.query(table); } + Future>> queryRaw(String sql, List args) async { + Database db = await instance.database; + return await db.rawQuery(sql, args ); + } + Future>> queryAllRowsOrderBy(String table, String orderBy) async { Database db = await instance.database; return await db.query(table, orderBy: orderBy); } Future>> queryRowsWithWhere( - String table, String where, List args) async { + String table, String where, List args, { String orderBy }) async { Database db = await instance.database; - return await db.query(table, where: where, whereArgs: args); + return await db.query(table, where: where, whereArgs: args, orderBy: orderBy); } // All of the methods (insert, query, update, delete) can also be done using diff --git a/lib/core/services/dictionary_service.dart b/lib/core/services/dictionary_service.dart index e5ccef2..7880c6f 100644 --- a/lib/core/services/dictionary_service.dart +++ b/lib/core/services/dictionary_service.dart @@ -1,6 +1,4 @@ -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'; @@ -49,7 +47,7 @@ class DictionaryService extends BaseService { List list = []; try { int appCompanyId = Redux.store.state.userState.auth.companyId; - List> elements = await _db.queryRowsWithWhere(CategoryTableName, '$CategoryColumnParentIn = ?', [parentId]); + List> elements = await _db.queryRowsWithWhere(CategoryTableName, '$CategoryColumnAppCompanyId = ? and $CategoryColumnParentIn = ?', [appCompanyId, parentId]); elements.forEach((element) { list.add(Category.fromMap(element)); }); diff --git a/lib/routes/router.dart b/lib/routes/router.dart index 825c703..2a79d80 100644 --- a/lib/routes/router.dart +++ b/lib/routes/router.dart @@ -40,7 +40,7 @@ Route generateRoute(RouteSettings settings) { case SettingPrinterBluetoothViewRoute: return _getPageRoute( routeName: settings.name, - viewToShow: PrinterSelectView(title: 'Принтер печати чеков',), + //viewToShow: PrinterSelectView(title: 'Принтер печати чеков',), ); // case ImageShowRoute: // ImageShowModel data = settings.arguments as ImageShowModel; diff --git a/lib/views/login/login_view.dart b/lib/views/login/login_view.dart index a011c0e..6cf0562 100644 --- a/lib/views/login/login_view.dart +++ b/lib/views/login/login_view.dart @@ -9,10 +9,8 @@ import 'package:flutter_redux/flutter_redux.dart'; import 'package:flutter/material.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:satu/core/redux/actions/user_actions.dart'; -import 'package:satu/core/redux/constants/auth_type_const.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/dialog_service.dart'; import 'package:satu/core/utils/locator.dart'; import 'package:satu/shared/app_colors.dart'; diff --git a/lib/views/settings/printer_bluetooth/PrinterSelect.dart b/lib/views/settings/printer_bluetooth/PrinterSelect.dart index 558ed7a..a7a61e2 100644 --- a/lib/views/settings/printer_bluetooth/PrinterSelect.dart +++ b/lib/views/settings/printer_bluetooth/PrinterSelect.dart @@ -1,366 +1,366 @@ -import 'dart:convert'; -import 'dart:typed_data'; - -import 'package:charset_converter/charset_converter.dart'; -import 'package:esc_pos_bluetooth/esc_pos_bluetooth.dart'; -import 'package:esc_pos_utils/esc_pos_utils.dart'; -import 'package:flutter/material.dart' hide Image; -import 'package:image/image.dart' as Im; -import 'package:flutter/services.dart'; -import 'package:intl/intl.dart'; - -class PrinterSelectView extends StatefulWidget { - PrinterSelectView({Key key, this.title}) : super(key: key); - final String title; - - @override - _PrinterSelectViewState createState() => _PrinterSelectViewState(); -} - -class _PrinterSelectViewState extends State { - PrinterBluetoothManager printerManager = PrinterBluetoothManager(); - List _devices = []; - - @override - void initState() { - super.initState(); - - printerManager.scanResults.listen((devices) async { - // print('UI: Devices found ${devices.length}'); - setState(() { - _devices = devices; - }); - }); - } - - void _startScanDevices() { - setState(() { - _devices = []; - }); - printerManager.startScan(Duration(seconds: 4)); - } - - void _stopScanDevices() { - printerManager.stopScan(); - } - - Future demoReceipt(PaperSize paper) async { - final Ticket ticket = Ticket(paper, ); - - // Print image - final ByteData data = await rootBundle.load('assets/images/aman_kassa_check.png'); - final Uint8List bytes = data.buffer.asUint8List(); - final Im.Image image = Im.decodeImage(bytes); - Im.Image thumbnail = Im.copyResize(image, width: 270); - // ticket.image(thumbnail, align: PosAlign.center); - - //ticket.imageRaster(image, align: PosAlign.center); - - ticket.text('AMAN-SATU', - styles: PosStyles( - align: PosAlign.center, - height: PosTextSize.size2, - width: PosTextSize.size2, - ), - linesAfter: 1); - - ticket.text('889 Watson Lane', styles: PosStyles(align: PosAlign.center)); - ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, codeTable: PosCodeTable.westEur), containsChinese: true); - ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, fontType: PosFontType.fontA), containsChinese: true); - ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, fontType: PosFontType.fontB), containsChinese: true); - ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, height: PosTextSize.size1), containsChinese: true); - ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, width: PosTextSize.size2), containsChinese: true); - ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, width: PosTextSize.size3), containsChinese: true); - ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, width: PosTextSize.size4), containsChinese: true); - ticket.text('Tel: 830-221-1234', styles: PosStyles(align: PosAlign.center)); - ticket.text('Web: www.example.com', - styles: PosStyles(align: PosAlign.center), linesAfter: 1); - - ticket.hr(); - ticket.row([ - PosColumn(text: 'Qty', width: 1), - PosColumn(text: 'Item', width: 7), - PosColumn( - text: 'Price', width: 2, styles: PosStyles(align: PosAlign.right)), - PosColumn( - text: 'Total', width: 2, styles: PosStyles(align: PosAlign.right)), - ]); - - ticket.row([ - PosColumn(text: '2', width: 1), - PosColumn(text: 'ONION RINGS', width: 7), - PosColumn( - text: '0.99', width: 2, styles: PosStyles(align: PosAlign.right)), - PosColumn( - text: '1.98', width: 2, styles: PosStyles(align: PosAlign.right)), - ]); - ticket.row([ - PosColumn(text: '1', width: 1), - PosColumn(text: 'PIZZA', width: 7), - PosColumn( - text: '3.45', width: 2, styles: PosStyles(align: PosAlign.right)), - PosColumn( - text: '3.45', width: 2, styles: PosStyles(align: PosAlign.right)), - ]); - ticket.row([ - PosColumn(text: '1', width: 1), - PosColumn(text: 'SPRING ROLLS', width: 7), - PosColumn( - text: '2.99', width: 2, styles: PosStyles(align: PosAlign.right)), - PosColumn( - text: '2.99', width: 2, styles: PosStyles(align: PosAlign.right)), - ]); - ticket.row([ - PosColumn(text: '3', width: 1), - PosColumn(text: 'CRUNCHY STICKS', width: 7), - PosColumn( - text: '0.85', width: 2, styles: PosStyles(align: PosAlign.right)), - PosColumn( - text: '2.55', width: 2, styles: PosStyles(align: PosAlign.right)), - ]); - ticket.hr(); - - ticket.row([ - PosColumn( - text: 'TOTAL', - width: 6, - styles: PosStyles( - height: PosTextSize.size2, - width: PosTextSize.size2, - )), - PosColumn( - text: '\$10.97', - width: 6, - styles: PosStyles( - align: PosAlign.right, - height: PosTextSize.size2, - width: PosTextSize.size2, - )), - ]); - - ticket.hr(ch: '=', linesAfter: 1); - - ticket.row([ - PosColumn( - text: 'Cash', - width: 7, - styles: PosStyles(align: PosAlign.right, width: PosTextSize.size2)), - PosColumn( - text: '\$15.00', - width: 5, - styles: PosStyles(align: PosAlign.right, width: PosTextSize.size2)), - ]); - ticket.row([ - PosColumn( - text: 'Change', - width: 7, - styles: PosStyles(align: PosAlign.right, width: PosTextSize.size2)), - PosColumn( - text: '\$4.03', - width: 5, - styles: PosStyles(align: PosAlign.right, width: PosTextSize.size2)), - ]); - - ticket.feed(2); - ticket.text('Thank you!', - styles: PosStyles(align: PosAlign.center, bold: true)); - - final now = DateTime.now(); - final formatter = DateFormat('MM/dd/yyyy H:m'); - final String timestamp = formatter.format(now); - ticket.text(timestamp, - styles: PosStyles(align: PosAlign.center), linesAfter: 2); - - // Print QR Code from image - // try { - // const String qrData = 'example.com'; - // const double qrSize = 200; - // final uiImg = await QrPainter( - // data: qrData, - // version: QrVersions.auto, - // gapless: false, - // ).toImageData(qrSize); - // final dir = await getTemporaryDirectory(); - // final pathName = '${dir.path}/qr_tmp.png'; - // final qrFile = File(pathName); - // final imgFile = await qrFile.writeAsBytes(uiImg.buffer.asUint8List()); - // final img = decodeImage(imgFile.readAsBytesSync()); - - // ticket.image(img); - // } catch (e) { - // print(e); - // } - - // Print QR Code using native function - // ticket.qrcode('example.com'); - - ticket.feed(2); - ticket.cut(); - return ticket; - } - - Future testTicket(PaperSize paper) async { - final Ticket ticket = Ticket(paper); - Uint8List encTxt4 = - await CharsetConverter.encode("cp866", "Russian: Привет Мир!"); - ticket.textEncoded(encTxt4, - styles: PosStyles(codeTable: PosCodeTable.pc866_2)); - - ticket.text('Kazakh: Сәлем Әлем!', - styles: PosStyles(codeTable: PosCodeTable.westEur), containsChinese: true); - - // Uint8List encTxt1 = - // await CharsetConverter.encode("utf-8", "Kazakh: Сәлем Әлем!"); - // ticket.textEncoded(encTxt1, - // styles: PosStyles(codeTable: PosCodeTable.pc866_2)); - ticket.text( - 'Regular: aA bB cC dD eE fF gG hH iI jJ kK lL mM nN oO pP qQ rR sS tT uU vV wW xX yY zZ'); - ticket.text('Special 1: àÀ èÈ éÉ ûÛ üÜ çÇ ôÔ', - styles: PosStyles(codeTable: PosCodeTable.westEur)); - ticket.text('Special 2: blåbærgrød', - styles: PosStyles(codeTable: PosCodeTable.westEur)); - - ticket.text('Bold text', styles: PosStyles(bold: true)); - ticket.text('Reverse text', styles: PosStyles(reverse: true)); - ticket.text('Underlined text', - styles: PosStyles(underline: true), linesAfter: 1); - ticket.text('Align left', styles: PosStyles(align: PosAlign.left)); - ticket.text('Align center', styles: PosStyles(align: PosAlign.center)); - ticket.text('Align right', - styles: PosStyles(align: PosAlign.right), linesAfter: 1); - - ticket.row([ - PosColumn( - text: 'col3', - width: 3, - styles: PosStyles(align: PosAlign.center, underline: true), - ), - PosColumn( - text: 'col6', - width: 6, - styles: PosStyles(align: PosAlign.center, underline: true), - ), - PosColumn( - text: 'col3', - width: 3, - styles: PosStyles(align: PosAlign.center, underline: true), - ), - ]); - - ticket.text('Text size 200%', - styles: PosStyles( - height: PosTextSize.size2, - width: PosTextSize.size2, - )); - - // Print image - //final ByteData data = await rootBundle.load('assets/images/logo.png'); - //final Uint8List bytes = data.buffer.asUint8List(); - // Print image using alternative commands - // ticket.imageRaster(image); - // ticket.imageRaster(image, imageFn: PosImageFn.graphics); - - // Print barcode - final List barData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 4]; - ticket.barcode(Barcode.upcA(barData)); - - // Print mixed (chinese + latin) text. Only for printers supporting Kanji mode - ticket.text( - 'hello ! 中文字 # world @ éphémère &', - styles: PosStyles(codeTable: PosCodeTable.westEur), - containsChinese: true, - ); - - ticket.text( - 'hello ! Мир # world @ éphémère &', - styles: PosStyles(codeTable: PosCodeTable.westEur), - containsChinese: true, - ); - - ticket.feed(2); - - ticket.cut(); - return ticket; - } - - void _testPrint(PrinterBluetooth printer) async { - printerManager.selectPrinter(printer); - - // TODO Don't forget to choose printer's paper - const PaperSize paper = PaperSize.mm58; - - // TEST PRINT - // final PosPrintResult res = - // await printerManager.printTicket(await testTicket(paper)); - - // DEMO RECEIPT - final PosPrintResult res = - await printerManager.printTicket(await testTicket(paper) , queueSleepTimeMs: 50); - - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), - body: ListView.builder( - itemCount: _devices.length, - itemBuilder: (BuildContext context, int index) { - return InkWell( - onTap: () => _testPrint(_devices[index]), - child: Column( - children: [ - Container( - height: 60, - padding: EdgeInsets.only(left: 10), - alignment: Alignment.centerLeft, - child: Row( - children: [ - Icon(Icons.print), - SizedBox(width: 10), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(_devices[index].name ?? ''), - Text(_devices[index].address), - Text( - 'Click to print a test receipt', - style: TextStyle(color: Colors.grey[700]), - ), - ], - ), - ) - ], - ), - ), - Divider(), - ], - ), - ); - }), - floatingActionButton: StreamBuilder( - stream: printerManager.isScanningStream, - initialData: false, - builder: (c, snapshot) { - if (snapshot.data) { - return FloatingActionButton( - child: Icon(Icons.stop), - onPressed: _stopScanDevices, - backgroundColor: Colors.red, - ); - } else { - return FloatingActionButton( - child: Icon(Icons.search), - onPressed: _startScanDevices, - ); - } - }, - ), - ); - } - -} \ No newline at end of file +// import 'dart:convert'; +// import 'dart:typed_data'; +// +// import 'package:charset_converter/charset_converter.dart'; +// import 'package:esc_pos_bluetooth/esc_pos_bluetooth.dart'; +// import 'package:esc_pos_utils/esc_pos_utils.dart'; +// import 'package:flutter/material.dart' hide Image; +// import 'package:image/image.dart' as Im; +// import 'package:flutter/services.dart'; +// import 'package:intl/intl.dart'; +// +// class PrinterSelectView extends StatefulWidget { +// PrinterSelectView({Key key, this.title}) : super(key: key); +// final String title; +// +// @override +// _PrinterSelectViewState createState() => _PrinterSelectViewState(); +// } +// +// class _PrinterSelectViewState extends State { +// // PrinterBluetoothManager printerManager = PrinterBluetoothManager(); +// // List _devices = []; +// +// @override +// void initState() { +// super.initState(); +// +// printerManager.scanResults.listen((devices) async { +// // print('UI: Devices found ${devices.length}'); +// setState(() { +// _devices = devices; +// }); +// }); +// } +// +// void _startScanDevices() { +// setState(() { +// _devices = []; +// }); +// printerManager.startScan(Duration(seconds: 4)); +// } +// +// void _stopScanDevices() { +// printerManager.stopScan(); +// } +// +// Future demoReceipt(PaperSize paper) async { +// final Ticket ticket = Ticket(paper, ); +// +// // Print image +// final ByteData data = await rootBundle.load('assets/images/aman_kassa_check.png'); +// final Uint8List bytes = data.buffer.asUint8List(); +// final Im.Image image = Im.decodeImage(bytes); +// Im.Image thumbnail = Im.copyResize(image, width: 270); +// // ticket.image(thumbnail, align: PosAlign.center); +// +// //ticket.imageRaster(image, align: PosAlign.center); +// +// ticket.text('AMAN-SATU', +// styles: PosStyles( +// align: PosAlign.center, +// height: PosTextSize.size2, +// width: PosTextSize.size2, +// ), +// linesAfter: 1); +// +// ticket.text('889 Watson Lane', styles: PosStyles(align: PosAlign.center)); +// ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, codeTable: PosCodeTable.westEur), containsChinese: true); +// ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, fontType: PosFontType.fontA), containsChinese: true); +// ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, fontType: PosFontType.fontB), containsChinese: true); +// ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, height: PosTextSize.size1), containsChinese: true); +// ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, width: PosTextSize.size2), containsChinese: true); +// ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, width: PosTextSize.size3), containsChinese: true); +// ticket.text('Русский язык', styles: PosStyles(align: PosAlign.center, width: PosTextSize.size4), containsChinese: true); +// ticket.text('Tel: 830-221-1234', styles: PosStyles(align: PosAlign.center)); +// ticket.text('Web: www.example.com', +// styles: PosStyles(align: PosAlign.center), linesAfter: 1); +// +// ticket.hr(); +// ticket.row([ +// PosColumn(text: 'Qty', width: 1), +// PosColumn(text: 'Item', width: 7), +// PosColumn( +// text: 'Price', width: 2, styles: PosStyles(align: PosAlign.right)), +// PosColumn( +// text: 'Total', width: 2, styles: PosStyles(align: PosAlign.right)), +// ]); +// +// ticket.row([ +// PosColumn(text: '2', width: 1), +// PosColumn(text: 'ONION RINGS', width: 7), +// PosColumn( +// text: '0.99', width: 2, styles: PosStyles(align: PosAlign.right)), +// PosColumn( +// text: '1.98', width: 2, styles: PosStyles(align: PosAlign.right)), +// ]); +// ticket.row([ +// PosColumn(text: '1', width: 1), +// PosColumn(text: 'PIZZA', width: 7), +// PosColumn( +// text: '3.45', width: 2, styles: PosStyles(align: PosAlign.right)), +// PosColumn( +// text: '3.45', width: 2, styles: PosStyles(align: PosAlign.right)), +// ]); +// ticket.row([ +// PosColumn(text: '1', width: 1), +// PosColumn(text: 'SPRING ROLLS', width: 7), +// PosColumn( +// text: '2.99', width: 2, styles: PosStyles(align: PosAlign.right)), +// PosColumn( +// text: '2.99', width: 2, styles: PosStyles(align: PosAlign.right)), +// ]); +// ticket.row([ +// PosColumn(text: '3', width: 1), +// PosColumn(text: 'CRUNCHY STICKS', width: 7), +// PosColumn( +// text: '0.85', width: 2, styles: PosStyles(align: PosAlign.right)), +// PosColumn( +// text: '2.55', width: 2, styles: PosStyles(align: PosAlign.right)), +// ]); +// ticket.hr(); +// +// ticket.row([ +// PosColumn( +// text: 'TOTAL', +// width: 6, +// styles: PosStyles( +// height: PosTextSize.size2, +// width: PosTextSize.size2, +// )), +// PosColumn( +// text: '\$10.97', +// width: 6, +// styles: PosStyles( +// align: PosAlign.right, +// height: PosTextSize.size2, +// width: PosTextSize.size2, +// )), +// ]); +// +// ticket.hr(ch: '=', linesAfter: 1); +// +// ticket.row([ +// PosColumn( +// text: 'Cash', +// width: 7, +// styles: PosStyles(align: PosAlign.right, width: PosTextSize.size2)), +// PosColumn( +// text: '\$15.00', +// width: 5, +// styles: PosStyles(align: PosAlign.right, width: PosTextSize.size2)), +// ]); +// ticket.row([ +// PosColumn( +// text: 'Change', +// width: 7, +// styles: PosStyles(align: PosAlign.right, width: PosTextSize.size2)), +// PosColumn( +// text: '\$4.03', +// width: 5, +// styles: PosStyles(align: PosAlign.right, width: PosTextSize.size2)), +// ]); +// +// ticket.feed(2); +// ticket.text('Thank you!', +// styles: PosStyles(align: PosAlign.center, bold: true)); +// +// final now = DateTime.now(); +// final formatter = DateFormat('MM/dd/yyyy H:m'); +// final String timestamp = formatter.format(now); +// ticket.text(timestamp, +// styles: PosStyles(align: PosAlign.center), linesAfter: 2); +// +// // Print QR Code from image +// // try { +// // const String qrData = 'example.com'; +// // const double qrSize = 200; +// // final uiImg = await QrPainter( +// // data: qrData, +// // version: QrVersions.auto, +// // gapless: false, +// // ).toImageData(qrSize); +// // final dir = await getTemporaryDirectory(); +// // final pathName = '${dir.path}/qr_tmp.png'; +// // final qrFile = File(pathName); +// // final imgFile = await qrFile.writeAsBytes(uiImg.buffer.asUint8List()); +// // final img = decodeImage(imgFile.readAsBytesSync()); +// +// // ticket.image(img); +// // } catch (e) { +// // print(e); +// // } +// +// // Print QR Code using native function +// // ticket.qrcode('example.com'); +// +// ticket.feed(2); +// ticket.cut(); +// return ticket; +// } +// +// Future testTicket(PaperSize paper) async { +// final Ticket ticket = Ticket(paper); +// Uint8List encTxt4 = +// await CharsetConverter.encode("cp866", "Russian: Привет Мир!"); +// ticket.textEncoded(encTxt4, +// styles: PosStyles(codeTable: PosCodeTable.pc866_2)); +// +// ticket.text('Kazakh: Сәлем Әлем!', +// styles: PosStyles(codeTable: PosCodeTable.westEur), containsChinese: true); +// +// // Uint8List encTxt1 = +// // await CharsetConverter.encode("utf-8", "Kazakh: Сәлем Әлем!"); +// // ticket.textEncoded(encTxt1, +// // styles: PosStyles(codeTable: PosCodeTable.pc866_2)); +// ticket.text( +// 'Regular: aA bB cC dD eE fF gG hH iI jJ kK lL mM nN oO pP qQ rR sS tT uU vV wW xX yY zZ'); +// ticket.text('Special 1: àÀ èÈ éÉ ûÛ üÜ çÇ ôÔ', +// styles: PosStyles(codeTable: PosCodeTable.westEur)); +// ticket.text('Special 2: blåbærgrød', +// styles: PosStyles(codeTable: PosCodeTable.westEur)); +// +// ticket.text('Bold text', styles: PosStyles(bold: true)); +// ticket.text('Reverse text', styles: PosStyles(reverse: true)); +// ticket.text('Underlined text', +// styles: PosStyles(underline: true), linesAfter: 1); +// ticket.text('Align left', styles: PosStyles(align: PosAlign.left)); +// ticket.text('Align center', styles: PosStyles(align: PosAlign.center)); +// ticket.text('Align right', +// styles: PosStyles(align: PosAlign.right), linesAfter: 1); +// +// ticket.row([ +// PosColumn( +// text: 'col3', +// width: 3, +// styles: PosStyles(align: PosAlign.center, underline: true), +// ), +// PosColumn( +// text: 'col6', +// width: 6, +// styles: PosStyles(align: PosAlign.center, underline: true), +// ), +// PosColumn( +// text: 'col3', +// width: 3, +// styles: PosStyles(align: PosAlign.center, underline: true), +// ), +// ]); +// +// ticket.text('Text size 200%', +// styles: PosStyles( +// height: PosTextSize.size2, +// width: PosTextSize.size2, +// )); +// +// // Print image +// //final ByteData data = await rootBundle.load('assets/images/logo.png'); +// //final Uint8List bytes = data.buffer.asUint8List(); +// // Print image using alternative commands +// // ticket.imageRaster(image); +// // ticket.imageRaster(image, imageFn: PosImageFn.graphics); +// +// // Print barcode +// final List barData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 4]; +// ticket.barcode(Barcode.upcA(barData)); +// +// // Print mixed (chinese + latin) text. Only for printers supporting Kanji mode +// ticket.text( +// 'hello ! 中文字 # world @ éphémère &', +// styles: PosStyles(codeTable: PosCodeTable.westEur), +// containsChinese: true, +// ); +// +// ticket.text( +// 'hello ! Мир # world @ éphémère &', +// styles: PosStyles(codeTable: PosCodeTable.westEur), +// containsChinese: true, +// ); +// +// ticket.feed(2); +// +// ticket.cut(); +// return ticket; +// } +// +// void _testPrint(PrinterBluetooth printer) async { +// printerManager.selectPrinter(printer); +// +// // TODO Don't forget to choose printer's paper +// const PaperSize paper = PaperSize.mm58; +// +// // TEST PRINT +// // final PosPrintResult res = +// // await printerManager.printTicket(await testTicket(paper)); +// +// // DEMO RECEIPT +// final PosPrintResult res = +// await printerManager.printTicket(await testTicket(paper) , queueSleepTimeMs: 50); +// +// } +// +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// appBar: AppBar( +// title: Text(widget.title), +// ), +// body: ListView.builder( +// itemCount: _devices.length, +// itemBuilder: (BuildContext context, int index) { +// return InkWell( +// onTap: () => _testPrint(_devices[index]), +// child: Column( +// children: [ +// Container( +// height: 60, +// padding: EdgeInsets.only(left: 10), +// alignment: Alignment.centerLeft, +// child: Row( +// children: [ +// Icon(Icons.print), +// SizedBox(width: 10), +// Expanded( +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// mainAxisAlignment: MainAxisAlignment.center, +// children: [ +// Text(_devices[index].name ?? ''), +// Text(_devices[index].address), +// Text( +// 'Click to print a test receipt', +// style: TextStyle(color: Colors.grey[700]), +// ), +// ], +// ), +// ) +// ], +// ), +// ), +// Divider(), +// ], +// ), +// ); +// }), +// floatingActionButton: StreamBuilder( +// stream: printerManager.isScanningStream, +// initialData: false, +// builder: (c, snapshot) { +// if (snapshot.data) { +// return FloatingActionButton( +// child: Icon(Icons.stop), +// onPressed: _stopScanDevices, +// backgroundColor: Colors.red, +// ); +// } else { +// return FloatingActionButton( +// child: Icon(Icons.search), +// onPressed: _startScanDevices, +// ); +// } +// }, +// ), +// ); +// } +// +// } \ No newline at end of file diff --git a/lib/views/work/tabs/sell_view.dart b/lib/views/work/tabs/sell_view.dart index bbef1da..04c7837 100644 --- a/lib/views/work/tabs/sell_view.dart +++ b/lib/views/work/tabs/sell_view.dart @@ -1,4 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:flutter_redux/flutter_redux.dart'; +import 'package:satu/core/models/flow/product_dao.dart'; +import 'package:satu/core/redux/state/sell_state.dart'; +import 'package:satu/core/redux/store.dart'; import 'package:satu/core/services/navigator_service.dart'; import 'package:satu/core/utils/locator.dart'; import 'package:satu/routes/route_names.dart'; @@ -6,44 +10,50 @@ import 'package:satu/shared/app_colors.dart'; import 'package:satu/views/work/tabs/component/product_list_item.dart'; import 'package:satu/views/work/tabs/component/products_app_bar.dart'; import 'package:satu/views/work/tabs/component/products_header_bar.dart'; +import 'package:satu/views/work/tabs/utils/ProductUtils.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, - ), - body: Column( - children: [ - //ProductHeaderBar(count: 14, sum: 25000,), - Expanded( - child: ListView.builder( - physics: BouncingScrollPhysics(), - itemCount: 5, - itemBuilder: (BuildContext context, int index) { - return ProductListItem( - ean: '1234567890123', - isOdd: index % 2 == 0, - name: - 'Хлеб ржаной который необходимо покупать каждый раз когда придет мысль об этом - ${index + 1}. ', - price: 75, - count: 15, - categoryName: 'Хлебобулочные изделия', - ); - }, + return StoreConnector( + converter: (store) => store.state.sellState, + builder: (_, state) { + return Scaffold( + appBar: ProductsAppBar( + title: 'Продажа', + actions: actions(), + elevation: 2.0, + child: ProductHeaderBar( + count: state.items.length, + sum: sumProducts(state.items), ), + backgroundColor: backgroundColor, + childHeight: 80, ), - ], - ), + body: Column( + children: [ + //ProductHeaderBar(count: 14, sum: 25000,), + Expanded( + child: ListView.builder( + physics: BouncingScrollPhysics(), + itemCount: state.items.length, + itemBuilder: (BuildContext context, int index) { + ProductDao product = state.items.elementAt(index); + return ProductListItem( + ean: product.eanCode, + isOdd: index % 2 == 0, + name: product.productName, + price: product.price, + count: product.count, + categoryName: product.productName, + ); + }, + ), + ), + ], + ), + ); + } ); } diff --git a/lib/views/work/tabs/utils/ProductUtils.dart b/lib/views/work/tabs/utils/ProductUtils.dart new file mode 100644 index 0000000..1ea28c7 --- /dev/null +++ b/lib/views/work/tabs/utils/ProductUtils.dart @@ -0,0 +1,12 @@ +import 'package:satu/core/models/flow/product_dao.dart'; + +num sumProducts(List list) { + num result = 0.0; + if (list.isNotEmpty) { + list.forEach((product) { + result += (product.price * product.count); + }); + } + + return result; +} diff --git a/pubspec.lock b/pubspec.lock index c42e891..079616b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -127,20 +127,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.0" - esc_pos_bluetooth: - dependency: "direct main" - description: - name: esc_pos_bluetooth - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.8" esc_pos_utils: dependency: "direct main" description: name: esc_pos_utils url: "https://pub.dartlang.org" source: hosted - version: "0.3.6" + version: "1.0.0" fake_async: dependency: transitive description: @@ -174,13 +167,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_bluetooth_basic: - dependency: transitive - description: - name: flutter_bluetooth_basic - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.5" flutter_redux: dependency: "direct main" description: @@ -218,7 +204,7 @@ packages: name: get_it url: "https://pub.dartlang.org" source: hosted - version: "6.1.1" + version: "7.1.2" hex: dependency: transitive description: @@ -239,7 +225,7 @@ packages: name: http url: "https://pub.dartlang.org" source: hosted - version: "0.13.2" + version: "0.13.3" http_parser: dependency: transitive description: @@ -275,13 +261,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.6.3" - json_annotation: - dependency: transitive - description: - name: json_annotation - url: "https://pub.dartlang.org" - source: hosted - version: "3.1.1" logger: dependency: "direct main" description: @@ -471,13 +450,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.1" - rxdart: - dependency: transitive - description: - name: rxdart - url: "https://pub.dartlang.org" - source: hosted - version: "0.23.1" searchable_dropdown: dependency: "direct main" description: @@ -644,6 +616,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.0.1+3" + uuid: + dependency: "direct main" + description: + name: uuid + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.2" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 9094888..b0b06d1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,16 +36,15 @@ dependencies: responsive_builder: ^0.4.1 provider: ^5.0.0 logger: ^1.0.0 - get_it: ^6.1.1 + get_it: ^7.1.2 equatable: ^2.0.0 - http: ^0.13.2 + http: ^0.13.3 sqflite: ^2.0.0+3 path_provider: ^2.0.1 material_design_icons_flutter: ^4.0.5955 intl: ^0.17.0 barcode_scan: ^3.0.1 device_info: ^2.0.0 - #esys_flutter_share: ^1.0.2 auto_size_text: ^2.1.0 url_launcher: ^5.7.10 qr_flutter: ^4.0.0 @@ -55,8 +54,8 @@ dependencies: searchable_dropdown: ^1.1.3 material_floating_search_bar: ^0.3.4 implicitly_animated_reorderable_list: ^0.4.0 - esc_pos_bluetooth: ^0.2.8 - esc_pos_utils: ^0.3.6 # for esc_pos_bluetooth: ^0.2.8 + esc_pos_utils: ^1.0.0 + uuid: ^2.2.2 #for esc_pos_utils: ^1.0.0 charset_converter: ^2.0.0 dev_dependencies: flutter_test: