Files
fcs/lib/pages/do/do_creation_form.dart
2020-09-04 15:30:10 +06:30

645 lines
21 KiB
Dart

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<DOForm> {
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<FormState>();
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<LanguageModel>(context);
var mainModel = Provider.of<MainModel>(context);
var poModel = Provider.of<POSubmissionModel>(context);
bool isBuyer = mainModel.user.isBuyer();
final doNumberBox = Container(
padding: EdgeInsets.only(left: 20, top: 10),
child: Row(
children: <Widget>[
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: <Widget>[
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: <Widget>[
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: <Widget>[
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: <Widget>[
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: <Widget>[
LocalText(context, "po.title"),
IconButton(
onPressed: () async {
POSelection.showPOSelection(
context, poModel.approvedPOs, doSubmission.pos,
ok: (List<POSubmission> 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<LanguageModel>(context).isEng
? TextStyle(fontSize: 18)
: TextStyle(fontSize: 18, fontFamily: 'MyanmarUnicode')),
actions: <Widget>[
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: <Widget>[
Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
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<MainModel>(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: <Widget>[
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<MyDataRow> getProductRow(List<DOLine> doLines) {
ProductModel productModel = Provider.of<ProductModel>(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: <Widget>[
new Text(d.qty == null ? "0" : d.qty.toString(),
style: textStyle),
],
)),
),
],
);
}).toList();
}
Widget getPOProductTable() {
return Container(
padding: EdgeInsets.only(top: 10),
child: Column(
children: <Widget>[
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<MyDataRow> getPOProductRow() {
ProductModel productModel = Provider.of<ProductModel>(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: <Widget>[
new Text(d.doQty == null ? "0" : d.doQty.toString(),
style: textStyle),
],
)),
),
],
);
}).toList();
}
_updateQty(DOLine doLine) {
if (doLine == null) return;
try {
var mainModel = Provider.of<MainModel>(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<DOModel>(context);
if (_isNew) {
await doModel.createDO(doSubmission, files);
} else {
await doModel.updateDO(doSubmission, files);
}
Navigator.pop<bool>(context, true);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}