651 lines
21 KiB
Dart
651 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/pages/util.dart';
|
|
import 'package:fcs/theme/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;
|
|
});
|
|
}
|
|
}
|
|
}
|