import 'package:aman_kassa_flutter/core/locator.dart'; import 'package:aman_kassa_flutter/core/models/nct_product.dart'; import 'package:aman_kassa_flutter/core/services/NctService.dart' show NctService, NctServiceException; import 'package:aman_kassa_flutter/redux/actions/kassa_actions.dart'; import 'package:aman_kassa_flutter/redux/store.dart'; import 'package:aman_kassa_flutter/shared/app_colors.dart'; import 'package:aman_kassa_flutter/shared/ui_helpers.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class ProductAddBottomSheet extends StatefulWidget { final ScrollController? scrollController; final String? initialName; final String? initialNtinCode; ProductAddBottomSheet({this.scrollController, this.initialName, this.initialNtinCode}); @override _ProductAddBottomSheetState createState() => _ProductAddBottomSheetState(); } class _ProductAddBottomSheetState extends State { final NctService _nctService = locator(); late TextEditingController ntinController; late TextEditingController nameController; late TextEditingController countController; late TextEditingController priceController; double sum = 0.0; bool _isSearching = false; String? _foundNtinCode; @override void initState() { super.initState(); ntinController = new TextEditingController(); nameController = new TextEditingController(text: widget.initialName ?? ''); countController = new TextEditingController(); priceController = new TextEditingController(); _foundNtinCode = widget.initialNtinCode; } @override void dispose() { ntinController.dispose(); nameController.dispose(); countController.dispose(); priceController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration(color: whiteColor), child: Scaffold( appBar: AppBar( iconTheme: IconThemeData(color: Colors.black), backgroundColor: fillColor, elevation: 3, title: Text( 'Добавить товар/услугу', style: TextStyle(color: Colors.black87), ), ), body: Padding( padding: EdgeInsets.only(top: 15, left: 10, right: 15, bottom: 0), child: ListView( controller: widget.scrollController, children: [ Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ Expanded( child: TextField( decoration: new InputDecoration( border: new OutlineInputBorder( borderSide: new BorderSide(color: primaryColor)), hintText: 'Введите NTIN/GTIN', labelText: 'NTIN/GTIN', prefixText: ' ', ), keyboardType: TextInputType.text, controller: ntinController, ), ), SizedBox(width: 8), SizedBox( height: 56, child: ElevatedButton( onPressed: _isSearching ? null : _searchByNtin, style: ElevatedButton.styleFrom( primary: primaryColor, padding: EdgeInsets.symmetric(horizontal: 12), ), child: _isSearching ? SizedBox( width: 20, height: 20, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2), ) : Icon(Icons.search, color: Colors.white), ), ), ], ), verticalSpaceSmall, TextField( decoration: new InputDecoration( border: new OutlineInputBorder( borderSide: new BorderSide(color: primaryColor)), hintText: 'Введите наименовение', labelText: 'Наименование', prefixText: ' ', ), controller: nameController, ), verticalSpaceSmall, TextField( decoration: new InputDecoration( border: new OutlineInputBorder( borderSide: new BorderSide(color: primaryColor)), hintText: 'Введите количество', labelText: 'Количество', prefixText: ' ', ), keyboardType: const TextInputType.numberWithOptions( decimal: true, ), inputFormatters: [ FilteringTextInputFormatter.allow(RegExp("^[0-9.]*")), ], controller: countController, onChanged: calcOnChange, ), verticalSpaceSmall, TextField( decoration: new InputDecoration( border: new OutlineInputBorder( borderSide: new BorderSide(color: primaryColor)), hintText: 'Введите цену за единицу', labelText: 'Стоимость', prefixText: ' ', ), keyboardType: const TextInputType.numberWithOptions(decimal: true), inputFormatters: [ FilteringTextInputFormatter.allow(RegExp("^[0-9.]*")), ], controller: priceController, onChanged: calcOnChange, ), const Divider( height: 1.0, ), new ListTile( leading: const Icon( Icons.account_balance_wallet, color: primaryColor, ), title: Text(sum == 0 ? '' : sum.toString()), subtitle: const Text('Стоимость'), ), verticalSpaceMedium, Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ RawMaterialButton( onPressed: submit, elevation: 2.0, fillColor: greenColor, child: Icon( Icons.done, size: 35.0, color: whiteColor, ), padding: EdgeInsets.all(15.0), shape: CircleBorder(), ), RawMaterialButton( onPressed: () { Navigator.pop(context); }, elevation: 2.0, fillColor: redColor, child: Icon( Icons.close, size: 35.0, color: whiteColor, ), padding: EdgeInsets.all(15.0), shape: CircleBorder(), ) ], ) ], ), ), ), ); } Future _searchByNtin() async { final ntin = ntinController.text.trim(); if (ntin.isEmpty) { _showErrorDialog('Введите NTIN/GTIN для поиска.'); return; } setState(() => _isSearching = true); try { List results = await _nctService.searchByNtin(ntin); if (results.isNotEmpty) { final product = results.first; setState(() { nameController.text = product.nameRu ?? product.nameKk ?? ''; _foundNtinCode = product.ntinCode; }); if (product.ntinIsDeactivated == true) { String reason = product.ntinDeactivationReason ?? ''; String duplicate = product.ntinDuplicateOfProduct != null ? '\nДубликат: ${product.ntinDuplicateOfProduct}' : ''; _showErrorDialog('Карточка деактивирована.$reason$duplicate'); } } else { _showErrorDialog('Товар с NTIN/GTIN "$ntin" не найден.'); } } on NctServiceException catch (e) { _showErrorDialog(e.message); } catch (e) { _showErrorDialog('Ошибка поиска NCT: $e'); } finally { setState(() => _isSearching = false); } } void submit() { if (nameController.text.isEmpty || countController.text.isEmpty || priceController.text.isEmpty) { _showErrorDialog('Введите наименование, количество и цену'); } else { Redux.store!.dispatch(addCustomProductToKassaItems( nameController.text, double.parse(countController.text), double.parse(priceController.text), sum, ntin: _foundNtinCode)); Navigator.pop(context); } } void calcOnChange(value) { try { setState(() { sum = 0; }); if (countController.text != '' && priceController.text != '') { double count = double.parse(countController.text); double price = double.parse(priceController.text); double result = count * price; setState(() { sum = ((result * 100).roundToDouble()) / 100; }); } } catch (e) { print(e); } } void _showErrorDialog(String message) { showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: new Text("Aman Касса"), content: new Text(message), actions: [ TextButton( child: Text( "ОК", style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold), ), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); } }