import 'package:flutter/material.dart'; import 'package:flutter_datetime_picker/flutter_datetime_picker.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import 'package:fcs/model/do_model.dart'; import 'package:fcs/model/language_model.dart'; import 'package:fcs/model/main_model.dart'; import 'package:fcs/model/po_model.dart'; import 'package:fcs/model/product_model.dart'; import 'package:fcs/pages/do/do_product_item.dart'; import 'package:fcs/fcs/common/pages/util.dart'; import 'package:fcs/fcs/common/helpers/theme.dart'; import 'package:fcs/vo/do.dart'; import 'package:fcs/vo/po.dart'; import 'package:fcs/widget/img_file.dart'; import 'package:fcs/widget/local_text.dart'; import 'package:fcs/widget/localization/app_translations.dart'; import 'package:fcs/widget/my_data_table.dart'; import 'package:fcs/widget/number_cell.dart'; import 'package:fcs/widget/progress.dart'; import 'do_files.dart'; import 'po_selection.dart'; class DOForm extends StatefulWidget { final DOSubmission doSubmission; const DOForm({this.doSubmission}); @override _DOFormState createState() => _DOFormState(); } class _DOFormState extends State { var dateFormatter = new DateFormat('dd MMM yyyy'); final numberFormatter = new NumberFormat("#,###"); TextEditingController _deliveryDate = new TextEditingController(); TextEditingController _licence = new TextEditingController(); TextEditingController _driver = new TextEditingController(); TextEditingController _carNo = new TextEditingController(); TextEditingController _doStatus = new TextEditingController(); TextEditingController _storage = new TextEditingController(); TextEditingController _doNumber = new TextEditingController(); DOSubmission doSubmission = DOSubmission(); bool _isLoading = false; bool _isNew = true; final _formKey = GlobalKey(); int doTypeValue = 0; DOLine doLine = new DOLine(); DOFiles files = DOFiles(); @override void initState() { super.initState(); if (widget.doSubmission != null) { this.doSubmission = widget.doSubmission; this._isNew = false; _deliveryDate.text = dateFormatter.format(widget.doSubmission.deliveryDate); _licence.text = widget.doSubmission.driverLicenseNumber; _driver.text = widget.doSubmission.driverName; _carNo.text = widget.doSubmission.carNo; _doStatus.text = widget.doSubmission.status; _storage.text = widget.doSubmission.storageCharge == null ? "" : numberFormatter.format(widget.doSubmission.storageCharge); _doNumber.text = widget.doSubmission.doNumber; if (widget.doSubmission.type == 'multiple') { doTypeValue = 1; } else { doTypeValue = 0; } this.doLine.action = 'update'; } else { this.doLine.action = 'create'; doSubmission.type = 'single'; _storage.text = "0"; } } @override Widget build(BuildContext context) { var languageModel = Provider.of(context); var mainModel = Provider.of(context); var poModel = Provider.of(context); bool isBuyer = mainModel.user.isBuyer(); final doNumberBox = Container( padding: EdgeInsets.only(left: 20, top: 10), child: Row( children: [ LocalText(context, "do.do_num"), Container( padding: EdgeInsets.only(left: 10), child: Text( _doNumber.text, style: textStyle, ), ) ], ), ); final _doTypeValueBox = Container( padding: EdgeInsets.only(left: 20, top: 0), child: Row( children: [ LocalText(context, "do.type"), Container( width: 130, child: RadioListTile( dense: true, title: LocalText(context, "do.single"), value: 0, groupValue: doTypeValue, onChanged: handleRadioValueChanged, activeColor: primaryColor, ), ), Container( width: 136, child: RadioListTile( dense: true, title: LocalText(context, 'do.multiple'), value: 1, groupValue: doTypeValue, onChanged: handleRadioValueChanged, activeColor: primaryColor, )) ], ), ); final deliveryDateBox = Container( padding: EdgeInsets.only(left: 20, right: 15), child: InkWell( onTap: () { DatePicker.showDatePicker(context, showTitleActions: true, currentTime: _deliveryDate.text == "" ? null : dateFormatter.parse(_deliveryDate.text), minTime: DateTime.now(), maxTime: DateTime(2030, 12, 31), onConfirm: (date) { setState(() { _deliveryDate.text = dateFormatter.format(date); doSubmission.deliveryDate = dateFormatter.parse(_deliveryDate.text); doSubmission.updateStorageCharge(mainModel.setting); }); }, locale: LocaleType.en); }, child: TextFormField( controller: _deliveryDate, autofocus: false, cursorColor: primaryColor, style: textStyle, enabled: false, keyboardType: TextInputType.datetime, decoration: new InputDecoration( border: InputBorder.none, focusedBorder: InputBorder.none, labelText: AppTranslations.of(context).text("do.date"), labelStyle: languageModel.isEng ? labelStyle : labelStyleMM, contentPadding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 0.0), icon: Icon( Icons.date_range, color: primaryColor, )), validator: (value) { if (value.isEmpty) { return AppTranslations.of(context).text("do.form.date"); } return null; }, ), )); final driverBox = Container( padding: EdgeInsets.only(left: 20, right: 15), child: TextFormField( controller: _driver, autofocus: false, style: textStyle, cursorColor: primaryColor, decoration: new InputDecoration( labelText: AppTranslations.of(context).text("do.driver"), labelStyle: languageModel.isEng ? labelStyle : labelStyleMM, enabledBorder: UnderlineInputBorder( borderSide: BorderSide(color: Colors.grey, width: 1.0)), focusedBorder: UnderlineInputBorder( borderSide: BorderSide(color: Colors.grey, width: 1.0)), icon: Icon( Icons.account_box, color: primaryColor, )), validator: (value) { if (value.isEmpty) { return AppTranslations.of(context).text("do.form.driver"); } return null; }, )); final carNoBox = Container( padding: EdgeInsets.only(left: 20, right: 15), child: TextFormField( controller: _carNo, autofocus: false, style: textStyle, cursorColor: primaryColor, decoration: new InputDecoration( labelText: AppTranslations.of(context).text("do.car"), labelStyle: languageModel.isEng ? labelStyle : labelStyleMM, enabledBorder: UnderlineInputBorder( borderSide: BorderSide(color: Colors.grey, width: 1.0)), focusedBorder: UnderlineInputBorder( borderSide: BorderSide(color: Colors.grey, width: 1.0)), icon: Icon( Icons.directions_car, color: primaryColor, )), validator: (value) { if (value.isEmpty) { return AppTranslations.of(context).text("do.form.car"); } return null; }, )); final doStatusBox = Container( padding: EdgeInsets.only(left: 20, right: 15), child: TextFormField( controller: _doStatus, autofocus: false, style: textStyle, readOnly: true, decoration: new InputDecoration( border: InputBorder.none, focusedBorder: InputBorder.none, icon: Image.asset("assets/status.png", width: 25, color: primaryColor)), validator: (value) { if (value.isEmpty) { return "Please enter DO Status"; } return null; }, )); final storageBox = Container( padding: EdgeInsets.only(left: 20, top: 10), child: Row( children: [ LocalText(context, "do.storage_charge"), Container( padding: EdgeInsets.only(left: 10), child: Text( doSubmission.storageCharge == null ? "" : numberFormatter.format(doSubmission.storageCharge), style: textStyle, ), ) ], ), ); final storagePaymentBox = Container( padding: EdgeInsets.only(left: 20, top: 5), child: Row(children: [ LocalText(context, "do.storage_receipt"), ImageFile( enabled: isBuyer, title: "Receipt File", initialImgUrl: this.doSubmission.storageReceiptUrl, onFile: (file) { this.files.setStorageChargeFile = file; }), ])); final licesebox = Container( padding: EdgeInsets.only(left: 20, top: 5), child: Row(children: [ LocalText(context, "do.licence"), ImageFile( enabled: isBuyer, title: "Image", initialImgUrl: this.doSubmission.driverLicenceUrl, onFile: (file) { this.files.setlicenseFile = file; }), ])); final poButtun = Container( padding: EdgeInsets.only(left: 20, top: 5), child: Row(children: [ LocalText(context, "po.title"), IconButton( onPressed: () async { POSelection.showPOSelection( context, poModel.approvedPOs, doSubmission.pos, ok: (List pos) async { for (var po in pos) { po.poLines = await poModel.loadPOLines(po.id); } setState(() { doSubmission.pos = pos; doSubmission.loadPOs(); doSubmission.updateStorageCharge(mainModel.setting); }); }); }, icon: Icon(Icons.edit)), ])); return LocalProgress( inAsyncCall: _isLoading, child: Scaffold( appBar: AppBar( backgroundColor: primaryColor, title: Text(AppTranslations.of(context).text("do"), style: Provider.of(context).isEng ? TextStyle(fontSize: 18) : TextStyle(fontSize: 18, fontFamily: 'MyanmarUnicode')), actions: [ IconButton( icon: Icon(Icons.send), onPressed: () { if (!_formKey.currentState.validate()) return; showConfirmDialog(context, "do.confirm", () { _submit(mainModel); }); }, ) ], ), body: Form( key: _formKey, child: Container( child: ListView( children: [ Container( child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ this.doSubmission.doNumber != null ? doNumberBox : Container(), this.doSubmission.hasStorageCharge() ? storageBox : Container(), this.doSubmission.hasStorageCharge() ? storagePaymentBox : Container(), _doTypeValueBox, deliveryDateBox, driverBox, licesebox, carNoBox, poButtun, widget.doSubmission == null ? Container() : doStatusBox, getProductTable(), getPOProductTable(), ], ), ), ], ), ), )), ); } void handleRadioValueChanged(int value) { var mainModel = Provider.of(context, listen: false); setState(() { doSubmission.type = value == 0 ? 'single' : 'multiple'; doTypeValue = value; doSubmission.loadPOs(); doSubmission.updateStorageCharge(mainModel.setting); switch (doTypeValue) { case 0: break; case 1: break; } }); } Widget getProductTable() { return Container( padding: EdgeInsets.only(top: 10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Center( child: LocalText( context, 'do.products', fontSize: 14, fontWeight: FontWeight.bold, underline: true, color: secondaryColor, ), ), SingleChildScrollView( scrollDirection: Axis.horizontal, padding: EdgeInsets.only(top: 10), child: MyDataTable( headingRowHeight: 40, columnSpacing: 20, columns: [ MyDataColumn(label: LocalText(context, "do.product")), MyDataColumn( label: LocalText(context, "po.avail.qty"), numeric: true), MyDataColumn( label: LocalText(context, "do.do_qty"), numeric: true, ), ], rows: getProductRow(doSubmission.doLines), ), ), ], ), ); } List getProductRow(List doLines) { ProductModel productModel = Provider.of(context); if (doLines.isNotEmpty) { doLines.forEach((d) { productModel.products.forEach((p) { if (p.id == d.productID) { d.displayOrder = p.displayOrder; } else { return; } }); }); doLines.sort((p1, p2) => p1.displayOrder.compareTo(p2.displayOrder)); } return doLines.map((d) { return MyDataRow( onSelectChanged: (bool selected) async { if (doTypeValue == 0) return; var doLine = await showDialog( context: context, builder: (_) => DOProductItem( doLine: DOLine(productID: d.productID, qty: d.qty), )); _updateQty(doLine); }, cells: [ MyDataCell( new Text( d.productName, style: textStyle, ), ), MyDataCell(NumberCell(d.poBalQty)), MyDataCell( Container( color: Colors.cyan, child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ new Text(d.qty == null ? "0" : d.qty.toString(), style: textStyle), ], )), ), ], ); }).toList(); } Widget getPOProductTable() { return Container( padding: EdgeInsets.only(top: 10), child: Column( children: [ Center( child: LocalText(context, 'po.info', fontSize: 14, fontWeight: FontWeight.bold, underline: true, color: secondaryColor), ), SingleChildScrollView( scrollDirection: Axis.horizontal, child: MyDataTable( headingRowHeight: 40, columnSpacing: 20, columns: [ MyDataColumn(label: LocalText(context, "po.number")), MyDataColumn(label: LocalText(context, "po.product")), MyDataColumn( label: LocalText(context, "do.po_qty"), numeric: true, ), MyDataColumn( label: LocalText(context, "po.avail.qty"), numeric: true, ), MyDataColumn( label: LocalText(context, "po.retrieved.amount"), numeric: true, ), MyDataColumn( label: LocalText(context, "do.do_qty"), numeric: true, ), ], rows: getPOProductRow(), ), ), ], ), ); } List getPOProductRow() { ProductModel productModel = Provider.of(context); if (doSubmission.dopoLies.isNotEmpty) { doSubmission.dopoLies .sort((p1, p2) => p1.poNumber.compareTo(p2.poNumber)); doSubmission.dopoLies.forEach((d) { productModel.products.forEach((p) { if (p.id == d.productID) { d.displayOrder = p.displayOrder; } else { return; } }); }); doSubmission.dopoLies.sort((p1, p2) { if (p1.displayOrder != p2.displayOrder) return p1.displayOrder.compareTo(p2.displayOrder); return p1.poNumber.compareTo(p2.poNumber); }); } return doSubmission.dopoLies.map((d) { return MyDataRow( cells: [ MyDataCell( new Text( d.poNumber, style: textStyle, ), ), MyDataCell( new Text( d.productName, style: textStyle, ), ), MyDataCell(NumberCell(d.poQty)), MyDataCell(NumberCell(d.poBalQty)), MyDataCell(NumberCell(d.getPoBalanceQty)), MyDataCell( Container( color: Colors.grey, child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ new Text(d.doQty == null ? "0" : d.doQty.toString(), style: textStyle), ], )), ), ], ); }).toList(); } _updateQty(DOLine doLine) { if (doLine == null) return; try { var mainModel = Provider.of(context); setState(() { doSubmission.updateDoline(doLine.productID, doLine.qty); doSubmission.updateStorageCharge(mainModel.setting); }); } catch (e) { showMsgDialog(context, "Error", e.toString()); } } _submit(MainModel mainModel) async { if (doSubmission.doLines.length == 0) { showMsgDialog(context, "Error", "No product line"); return; } if (files.licenseFile == null) { showMsgDialog(context, "Error", "Please insert driver licence"); return; } int total = 0; doSubmission.doLines.forEach((doLine) { total += doLine.qty; }); if (total <= 0) { showMsgDialog(context, "Error", "must be greater than zero"); return; } doSubmission.carNo = _carNo.text; doSubmission.driverName = _driver.text; doSubmission.driverLicenseNumber = _licence.text; doSubmission.deliveryDate = dateFormatter.parse(_deliveryDate.text); doSubmission.type = doTypeValue == 0 ? 'single' : 'multiple'; doSubmission.doLines.removeWhere((d) => d.qty == 0); doSubmission.dopoLies.removeWhere((d) => d.doQty == 0); setState(() { _isLoading = true; }); try { DOModel doModel = Provider.of(context); if (_isNew) { await doModel.createDO(doSubmission, files); } else { await doModel.updateDO(doSubmission, files); } Navigator.pop(context, true); } catch (e) { showMsgDialog(context, "Error", e.toString()); } finally { setState(() { _isLoading = false; }); } } }