add structure

This commit is contained in:
2020-05-29 07:45:27 +06:30
parent 4c851d9971
commit bad27ba5c4
272 changed files with 36065 additions and 174 deletions

View File

@@ -0,0 +1,904 @@
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:quiver/async.dart';
import 'package:fcs/model/do_model.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/model/log_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/photo_page.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 '../document_log_page.dart';
import '../util.dart';
import 'do_files.dart';
import 'do_storage_item.dart';
class DOApproval extends StatefulWidget {
final DOSubmission doSubmission;
const DOApproval({this.doSubmission});
@override
_DOApprovalState createState() => _DOApprovalState();
}
class _DOApprovalState extends State<DOApproval> {
var dateFormatter = new DateFormat('dd MMM yyyy');
final numberFormatter = new NumberFormat("#,###");
var doDateFormatter = new DateFormat('dd MMM yyyy - hh:mm a');
bool _isLoading = false;
TextEditingController _date = new TextEditingController();
TextEditingController _doDate = new TextEditingController();
TextEditingController _number = new TextEditingController();
TextEditingController _licence = new TextEditingController();
TextEditingController _driver = new TextEditingController();
TextEditingController _carNo = new TextEditingController();
TextEditingController _type = new TextEditingController();
TextEditingController _name = new TextEditingController();
TextEditingController _bizName = new TextEditingController();
TextEditingController _storage = new TextEditingController();
TextEditingController _comment = new TextEditingController();
DOSubmission doObj = DOSubmission();
int _count;
DateTime _result;
DOFiles files = DOFiles();
List<DOLine> doLines = new List();
@override
void initState() {
super.initState();
var mainModel = Provider.of<MainModel>(context, listen: false);
var doModel = Provider.of<DOModel>(context, listen: false);
doObj = widget.doSubmission;
_date.text = doObj.deliveryDate != null
? dateFormatter.format(doObj.deliveryDate)
: "";
_doDate.text =
doObj.doDate != null ? doDateFormatter.format(doObj.doDate) : "";
_number.text = doObj.doNumber.toString();
_licence.text = doObj.driverLicenseNumber;
_driver.text = doObj.driverName;
_carNo.text = doObj.carNo;
_type.text = doObj.type;
_name.text = doObj.userName;
_bizName.text = doObj.bizName;
_storage.text = doObj.storageCharge == null
? ""
: numberFormatter.format(doObj.storageCharge);
_comment.text = doObj.comment;
if (doObj.deliveryStatus == 'initiated') {
_count = doModel.timber;
Duration diff = DateTime.now().difference(doObj.deliveryInitiatedTime);
if (diff.inMinutes < mainModel.setting.deliveryStartWaitMin) {
var time = mainModel.setting.deliveryStartWaitMin - diff.inMinutes;
new CountdownTimer(
new Duration(minutes: time), new Duration(seconds: 1))
.listen((data) {
if (mounted) {
setState(() {
_count = data.remaining.inSeconds;
doModel.addTimber(_count);
});
}
});
}
} else {
_count = 0;
}
_load();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
MainModel mainModel = Provider.of<MainModel>(context);
bool isBuyer = mainModel.user.isBuyer();
var logModel = Provider.of<LogModel>(context);
String formattedTime;
if (doObj.deliveryStatus == 'initiated') {
_result = DateTime(
doObj.deliveryInitiatedTime.year,
doObj.deliveryInitiatedTime.month,
doObj.deliveryInitiatedTime.day,
doObj.deliveryInitiatedTime.hour,
doObj.deliveryInitiatedTime.minute,
_count);
formattedTime = DateFormat.ms().format(_result);
}
final doDateBox = Container(
padding: EdgeInsets.only(left: 20, top: 15),
child: Row(
children: <Widget>[
LocalText(context, "do.do_date"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_doDate.text,
style: textStyle,
),
)
],
),
);
final dateBox = Container(
padding: EdgeInsets.only(left: 20, top: 8),
child: Row(
children: <Widget>[
LocalText(context, "do.date"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_date.text,
style: textStyle,
),
)
],
),
);
final numberBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.do_num"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_number.text,
style: textStyle,
),
)
],
),
);
final driverBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.driver"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_driver.text,
style: textStyle,
),
)
],
),
);
final carNoBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.car"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_carNo.text,
style: textStyle,
),
)
],
),
);
final licenceBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.licence"),
ImageFile(
enabled: false,
title: "Image",
initialImgUrl: doObj.driverLicenceUrl,
onFile: (file) {}),
],
),
);
final statusBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.status"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
doObj.status,
style: doObj.isPending
? textHighlightBlueStyle
: doObj.isApproved
? textHighlightGreenStyle
: textHighlightRedStyle,
),
),
],
),
);
final deliveryStatusBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.delivery.status"),
Container(
padding: EdgeInsets.only(left: 10, right: 15),
child: Text(
doObj.getDeliveryStatus,
style: textStyle,
),
),
doObj.deliveryStatus == 'initiated'
? Text(
"(can start in $formattedTime)",
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
)
: Container()
],
),
);
final typeBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.type"),
Container(
padding: EdgeInsets.only(left: 20),
child: Text(
_type.text,
style: textStyle,
),
)
],
),
);
final userNameBox = Container(
padding: EdgeInsets.only(top: 5, left: 20),
child: Row(
children: <Widget>[
LocalText(context, "do.name"),
Container(
padding: EdgeInsets.only(left: 20),
child: Text(
_name.text,
style: textStyle,
),
)
],
),
);
final bizNameBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.biz"),
Container(
padding: EdgeInsets.only(left: 20),
child: Text(
_bizName.text,
style: textStyle,
),
)
],
),
);
final driverImgUrlBox = Container(
padding: EdgeInsets.only(left: 20),
child: Row(children: <Widget>[
LocalText(context, "do.driver.image"),
Container(
padding: EdgeInsets.only(left: 10),
child: ImageFile(
enabled: !isBuyer && doObj.deliveryStatus == null,
initialImgUrl: doObj.driverImgUrl,
title: "Image",
imageSource: ImageSource.camera,
onFile: (file) {
doObj.driverImg = file;
}),
),
]));
final receiptImagebox = Container(
padding: EdgeInsets.only(left: 20, top: 0),
child: Row(children: <Widget>[
LocalText(context, "do.receipt"),
Container(
padding: EdgeInsets.only(left: 10),
child: ImageFile(
enabled: false,
initialImgUrl: doObj.doReceiptUrl,
title: "Receipt",
),
),
]));
final deliveryInitTimeBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.delivery.init.time"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
doObj.deliveryInitTime,
style: textStyle,
),
)
],
),
);
final storageBox = Container(
padding: EdgeInsets.only(left: 20),
child: Row(
children: <Widget>[
LocalText(context, "do.storage_charge"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_storage.text,
style: textStyle,
),
)
],
),
);
final storagePaymentBox = Container(
padding: EdgeInsets.only(left: 20),
child: Row(children: <Widget>[
LocalText(context, "do.storage_receipt"),
ImageFile(
enabled: mainModel.user.isBuyer() ? true : false,
title: "Receipt File",
initialImgUrl: this.doObj.storageReceiptUrl,
onFile: (file) {
this.files.setStorageChargeFile = file;
}),
]));
final commentBox = Container(
padding: EdgeInsets.only(top: 5, left: 20),
child: Row(
children: <Widget>[
LocalText(context, "do.comment"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_comment.text,
style: textStyle,
),
)
],
),
);
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>[
mainModel.showHistoryBtn()
? IconButton(
icon: Icon(Icons.history),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
DocumentLogPage(docID: doObj.id)),
);
},
)
: Container(),
isBuyer
? doObj.isPending
? PopupMenuButton(
onSelected: _selectBuyer,
itemBuilder: (context) => List<PopupMenuEntry>.from([
PopupMenuItem(
enabled: this.doObj.isPending &&
this.doObj.storageCharge > 0,
value: 1,
child: Text("Update DO"),
),
PopupMenuItem(
enabled: this.doObj.isPending,
value: 2,
child: Text("Cancel DO"),
),
]))
: Container()
: PopupMenuButton(
onSelected: _select,
itemBuilder: (context) => List<PopupMenuEntry>.from([
PopupMenuItem(
enabled: this.doObj.isPending,
value: 1,
child: Text("Approve DO"),
),
PopupMenuItem(
enabled: this.doObj.isPending,
value: 2,
child: Text("Reject DO"),
),
PopupMenuItem(
enabled:
this.doObj.isApproved && mainModel.user.isOwner(),
value: 6,
child: Text("Cancel DO"),
)
]),
),
],
),
body: Container(
padding:
EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 10),
child: Card(
elevation: 23,
child: ListView(
children: <Widget>[
Column(
children: <Widget>[
doDateBox,
Divider(),
dateBox,
Divider(),
numberBox,
Divider(),
userNameBox,
Divider(),
bizNameBox,
Divider(),
typeBox,
Divider(),
statusBox,
Divider(),
doObj.comment == null || doObj.comment == ''
? Container()
: commentBox,
doObj.comment == null || doObj.comment == ''
? Container()
: Divider(),
driverBox,
Divider(),
carNoBox,
Divider(),
licenceBox,
Divider(),
receiptImagebox,
Divider(),
doObj.hasStorageCharge() ? storageBox : Container(),
doObj.hasStorageCharge() ? Divider() : Container(),
doObj.hasStorageCharge()
? storagePaymentBox
: Container(),
doObj.isApproved || doObj.isClosed
? deliveryStatusBox
: Container(),
doObj.isApproved || doObj.isClosed
? Divider()
: Container(),
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,
child: MyDataTable(
headingRowHeight: 40,
columnSpacing: 40,
columns: [
MyDataColumn(
label: LocalText(context, "do.product"),
),
MyDataColumn(
label: LocalText(context, "do.storage"),
),
MyDataColumn(
label: LocalText(context, "do.quantity"),numeric: true
),
],
rows: getProductRow(doObj.doLines),
),
),
],
),
),
SizedBox(
height: 15,
),
getPOProductTable()
],
),
],
),
)),
));
}
List<MyDataRow> getProductRow(List<DOLine> doLines) {
MainModel mainModel = Provider.of<MainModel>(context);
ProductModel productModel = Provider.of<ProductModel>(context);
bool isBuyer = mainModel.user.isBuyer();
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 (isBuyer) return;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DOStorageItem(
doLine: d,
onSave: (storageID, storageName) {
setState(() {
d.storageID = storageID;
d.storageName = storageName;
});
},
)),
);
},
cells: [
MyDataCell(
new Text(
d.productName,
style: textStyle,
),
),
MyDataCell(
new Text(d.storageName, style: textStyle),
),
MyDataCell(NumberCell(d.qty)),
],
);
}).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, "do.po_balance_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 (doObj.dopoLies.isNotEmpty) {
doObj.dopoLies.sort((p1, p2) => p1.poNumber.compareTo(p2.poNumber));
doObj.dopoLies.forEach((d) {
productModel.products.forEach((p) {
if (p.id == d.productID) {
d.displayOrder = p.displayOrder;
} else {
return;
}
});
});
doObj.dopoLies.sort((p1, p2) {
if (p1.displayOrder != p2.displayOrder)
return p1.displayOrder.compareTo(p2.displayOrder);
return p1.poNumber.compareTo(p2.poNumber);
});
}
return doObj.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.poBalAtCreate)),
MyDataCell(NumberCell(d.getPoBalanceQtyAtCreate)),
MyDataCell(NumberCell(d.doQty)),
],
);
}).toList();
}
_select(s) {
if (s == 1) {
showConfirmDialog(context, "do.approve.confirm", () {
_approve();
});
} else if (s == 2) {
showCommentDialog(context, (comment) {
doObj.comment = comment;
_reject();
});
} else if (s == 5) {
showConfirmDialog(context, "do.end.confirm", () {
_endDelivery();
});
} else if (s == 6) {
showConfirmDialog(context, "do.cancel.confirm", () {
_cancelDelivery();
});
}
}
_selectBuyer(s) {
if (s == 1) {
showConfirmDialog(context, "do.confirm", () {
_submit();
});
} else if (s == 2) {
showConfirmDialog(context, "do.cancel.confirm", () {
_cancelDelivery();
});
}
}
Future<void> _load() async {
POSubmissionModel poModel =
Provider.of<POSubmissionModel>(context, listen: false);
DOModel doModel = Provider.of<DOModel>(context, listen: false);
var _doSub = await poModel.loadDOLines(doObj);
_doSub = await doModel.loadDOPOLines(_doSub);
// set po balance
List<String> pos = _doSub.getPOs();
for (var po in pos) {
List<POLine> poLines = await poModel.loadPOLines(po);
_doSub.setDOPOLineBalance(po, poLines);
}
if (mounted) {
setState(() {
doObj.doLines = _doSub.doLines;
doObj.dopoLies = _doSub.dopoLies;
});
}
}
_approve() async {
if (doObj.doLines.any((l) => l.storageID.isEmpty)) {
showMsgDialog(context, "Error", "Storage required for every product");
return;
}
setState(() {
_isLoading = true;
});
try {
DOModel doModel = Provider.of<DOModel>(context);
await doModel.approveDO(doObj);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_reject() async {
setState(() {
_isLoading = true;
});
try {
DOModel doModel = Provider.of<DOModel>(context);
await doModel.rejectDO(doObj);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_initDelivery() async {
if (doObj.driverImg == null) {
showMsgDialog(context, "Error", "Please attach driver image");
return;
}
setState(() {
_isLoading = true;
});
try {
DOModel doModel = Provider.of<DOModel>(context);
await doModel.initDelivery(doObj);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_startDelivery() async {
MainModel mainModel = Provider.of<MainModel>(context);
Duration diff = DateTime.now().difference(doObj.deliveryInitiatedTime);
if (diff.inMinutes < mainModel.setting.deliveryStartWaitMin) {
showMsgDialog(context, "Waiting...",
"Can not start delivery, wait for ${mainModel.setting.deliveryStartWaitMin} minutes");
return;
}
setState(() {
_isLoading = true;
});
try {
DOModel doModel = Provider.of<DOModel>(context);
await doModel.startDelivery(doObj);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_endDelivery() async {
var photo = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => PhotoPage()),
);
if (photo == null) {
return;
}
Uint8List bytesPhoto = photo.readAsBytesSync() as Uint8List;
setState(() {
_isLoading = true;
});
try {
DOModel doModel = Provider.of<DOModel>(context);
await doModel.endDelivery(doObj, bytesPhoto);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_cancelDelivery() async {
setState(() {
_isLoading = true;
});
try {
DOModel doModel = Provider.of<DOModel>(context);
await doModel.cancelDO(doObj);
Navigator.pop(context);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_submit() async {
if (doObj.hasStorageCharge()) {
if (files.storageChargeFile == null && doObj.storageReceiptUrl == '') {
showMsgDialog(context, "Error", "Please insert storage receipt");
return;
}
}
setState(() {
_isLoading = true;
});
try {
DOModel doModel = Provider.of<DOModel>(context);
await doModel.updateDO(doObj, files);
Navigator.pop<bool>(context, true);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}

View File

@@ -0,0 +1,650 @@
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;
});
}
}
}

View File

@@ -0,0 +1,423 @@
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/model/po_model.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:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:progress/progress.dart';
import 'package:fcs/widget/my_data_table.dart';
import 'package:fcs/widget/progress.dart';
import 'do_creation_form.dart';
class DoCreation extends StatefulWidget {
final POSubmission poSubmission;
const DoCreation({this.poSubmission});
@override
_DoCreationState createState() => _DoCreationState();
}
class _DoCreationState extends State<DoCreation> {
var dateFormatter = new DateFormat('dd MMM yyyy');
bool _isLoading = false;
TextEditingController _podate = new TextEditingController();
TextEditingController _ponumber = new TextEditingController();
TextEditingController _postatus = new TextEditingController();
POSubmission poSubmission = POSubmission();
@override
void initState() {
super.initState();
this.poSubmission = widget.poSubmission;
_podate.text = dateFormatter.format(this.poSubmission.poDate);
_ponumber.text = this.poSubmission.poNumber.toString();
_postatus.text = this.poSubmission.status;
_isLoading = true;
_load();
}
Future<void> _load() async {
List<POLine> poLines =
await Provider.of<POSubmissionModel>(context, listen: false)
.loadPOLines(poSubmission.id);
var _poDos = await Provider.of<POSubmissionModel>(context, listen: false)
.loadDOs(poSubmission);
setState(() {
this.poSubmission.poLines = poLines;
this.poSubmission.dos = _poDos.dos;
_isLoading = false;
});
}
Widget doDateBox(BuildContext context, DOSubmission doSubmission) {
return Container(
padding: EdgeInsets.only(left: 20, top: 0),
child: Row(
children: <Widget>[
LocalText(context, "do.date"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
dateFormatter.format(doSubmission.deliveryDate),
style: textStyle,
),
)
],
),
);
}
Widget doNumberBox(BuildContext context, DOSubmission doSubmission) {
return Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.do_num"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
doSubmission.doNumber,
style: textStyle,
),
)
],
),
);
}
Widget driverBox(BuildContext context, DOSubmission doSubmission) {
return Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.driver"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
doSubmission.driverName,
style: textStyle,
),
)
],
),
);
}
Widget carNoBox(BuildContext context, DOSubmission doSubmission) {
return Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.car"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
doSubmission.carNo,
style: textStyle,
),
)
],
),
);
}
Widget doStatusBox(BuildContext context, DOSubmission doSubmission) {
return Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.status"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
doSubmission.status,
style: textStyle,
),
)
],
),
);
}
Widget driverLicence(BuildContext context, DOSubmission doSubmission) {
return Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.licence"),
ImageFile(
enabled: false,
title: "Receipt File",
initialImgUrl: doSubmission.driverLicenceUrl,
onFile: (file) {}),
],
),
);
}
@override
Widget build(BuildContext context) {
var languageModle = Provider.of<LanguageModel>(context);
final poDateBox = Container(
padding: EdgeInsets.only(left: 20, top: 15),
child: Row(
children: <Widget>[
LocalText(context, "po.date"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_podate.text,
style: textStyle,
),
)
],
),
);
final poNumberBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "do.po_num"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_ponumber.text,
style: textStyle,
),
)
],
),
);
final poStatusBox = Container(
padding: EdgeInsets.only(left: 20, top: 5),
child: Row(
children: <Widget>[
LocalText(context, "po.status"),
Container(
padding: EdgeInsets.only(left: 10),
child: Text(
_postatus.text,
style: textStyle,
),
)
],
),
);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: Text(AppTranslations.of(context).text("do.title"),
style: languageModle.isEng
? TextStyle(fontSize: 18)
: TextStyle(fontSize: 18, fontFamily: 'MyanmarUnicode')),
),
floatingActionButton: FloatingActionButton(
backgroundColor: primaryColor,
heroTag: "btn2",
onPressed: () async {
if (poSubmission.poLines
.fold<int>(0, (p, e) => p + e.balanceQty) ==
0) {
showMsgDialog(context, "Error", "Zero PO balance qty");
return;
}
final bool successful = await Navigator.push<bool>(
context, MaterialPageRoute(builder: (context) => DOForm()));
if (successful != null && successful) _load();
},
child: Icon(Icons.add),
),
body: Container(
padding: EdgeInsets.only(top: 5),
child: ListView(
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Card(
elevation: 23,
child: Column(
children: <Widget>[
poDateBox,
poNumberBox,
poStatusBox,
Container(
padding: EdgeInsets.only(top: 10),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: MyDataTable(
headingRowHeight: 40,
columnSpacing: 15,
columns: [
MyDataColumn(
label: LocalText(context, "po.product"),
),
MyDataColumn(
label: LocalText(context, "do.po_qty"),
),
MyDataColumn(
label:
LocalText(context, "do.po_balance_qty"),
),
],
rows:
getPoProductRow(widget.poSubmission.poLines),
),
),
),
SizedBox(
height: 15,
)
],
),
),
),
Column(
children: getDos(poSubmission.dos),
),
SizedBox(
height: 10,
)
],
),
),
));
}
List<MyDataRow> getPoProductRow(List<POLine> poLines) {
return poLines.map((p) {
return MyDataRow(
cells: [
MyDataCell(
new Text(
p.productName,
style: textStyle,
),
),
MyDataCell(
new Text(p.qty.toString(), style: textStyle),
),
MyDataCell(
new Text(p.balanceQty.toString(), style: textStyle),
),
],
);
}).toList();
}
List<Widget> getDos(List<DOSubmission> dos) {
return dos.map((d) {
return Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: Card(
child: Theme(
data: ThemeData(accentColor: Colors.grey),
child: ExpansionTile(
onExpansionChanged: (e) => _onExpend(context, e, d),
title: ListTile(
title: Text(dateFormatter.format(d.deliveryDate)),
subtitle: Text(d.doNumber),
),
children: <Widget>[
Container(
child: Column(
children: <Widget>[
Align(
alignment: Alignment.topRight,
child: Container(
padding: EdgeInsets.only(right: 20),
child: InkWell(
child: Icon(Icons.edit),
onTap: () async {
final bool successful =
await Navigator.push<bool>(
context,
MaterialPageRoute(
builder: (context) => DOForm(
doSubmission: d,
)));
if (successful != null && successful) _load();
},
)),
),
doDateBox(context, d),
doNumberBox(context, d),
doStatusBox(context, d),
driverBox(context, d),
carNoBox(context, d),
driverLicence(context, d),
Container(
alignment: AlignmentDirectional.centerStart,
padding: EdgeInsets.only(top: 10),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: MyDataTable(
headingRowHeight: 40,
columnSpacing: 15,
columns: [
MyDataColumn(
label: LocalText(context, "do.product"),
),
MyDataColumn(
label: LocalText(context, "do.do_qty"),
),
],
rows: getdoProductRow(d.doLines),
),
),
),
SizedBox(
height: 15,
)
],
),
),
],
),
),
),
);
}).toList();
}
List<MyDataRow> getdoProductRow(List<DOLine> doLines) {
return doLines.map((p) {
return MyDataRow(
cells: [
MyDataCell(
new Text(
p.productName,
style: textStyle,
),
),
MyDataCell(
new Text(p.qty.toString(), style: textStyle),
),
],
);
}).toList();
}
_onExpend(BuildContext context, expended, DOSubmission doSub) async {
if (!expended) return;
POSubmissionModel poModel = Provider.of<POSubmissionModel>(context);
var _doSub = await poModel.loadDOLines(doSub);
setState(() {
doSub = _doSub;
});
}
}

View File

@@ -0,0 +1,26 @@
import 'dart:io';
class DOFiles {
File doPaymentFile, storageChargeFile, licenseFile;
bool doFileChanged = false,
storageFileChanged = false,
licenseFileChanged = false;
set setDoPaymentFile(File file) {
doPaymentFile = file;
doFileChanged = true;
}
set setStorageChargeFile(File file) {
storageChargeFile = file;
storageFileChanged = true;
}
set setlicenseFile(File file) {
licenseFile = file;
licenseFileChanged = true;
}
bool get anyChanged =>
doFileChanged || storageFileChanged || licenseFileChanged;
}

286
lib/pages/do/do_list.dart Normal file
View File

@@ -0,0 +1,286 @@
import 'package:flutter/material.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/pages/do/do_creation_form.dart';
import 'package:fcs/pages/util.dart';
import 'package:fcs/theme/theme.dart';
import 'package:fcs/vo/popup_menu.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/popupmenu.dart';
import 'package:fcs/widget/progress.dart';
import 'do_approve.dart';
class DOList extends StatefulWidget {
@override
_DOListState createState() => _DOListState();
}
class _DOListState extends State<DOList> {
var dateFormatter = new DateFormat('dd MMM yyyy');
final double dotSize = 10.0;
DateTime _selectedDate = DateTime.now();
String status;
int _selectedIndex = 0;
int _dateIndex = 0;
bool _isLoading = false;
@override
void initState() {
super.initState();
var doModel = Provider.of<DOModel>(context, listen: false);
_selectedIndex = doModel.popupMenu.index;
_dateIndex = doModel.dateIndex;
_selectedDate = doModel.selectedDate;
}
@override
void dispose() {
super.dispose();
}
Future<Null> _selectDate(BuildContext context) async {
var doModel = Provider.of<DOModel>(context);
final DateTime picked = await showDatePicker(
context: context,
initialDate: _selectedDate,
firstDate: DateTime(2015, 8),
lastDate: DateTime(2101),
builder: (BuildContext context, Widget child) {
return Theme(
data: ThemeData.light().copyWith(
primaryColor: primaryColor, //Head background
accentColor: secondaryColor, //selection color
dialogBackgroundColor: Colors.white, //Background color
),
child: child,
);
},
);
if (picked != null) {
var pickedDate = new DateTime(picked.year, picked.month, picked.day);
var currentDate = new DateTime(
DateTime.now().year, DateTime.now().month, DateTime.now().day);
this._dateIndex = pickedDate == currentDate ? 0 : 1;
setState(() {
_selectedDate = picked;
doModel.filterData(status, _selectedDate, _selectedIndex, _dateIndex);
});
}
}
@override
Widget build(BuildContext context) {
var doModel = Provider.of<DOModel>(context);
MainModel mainModel = Provider.of<MainModel>(context);
bool isBuyer = mainModel.user.isBuyer();
var languageModle = Provider.of<LanguageModel>(context);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: Text(
AppTranslations.of(context).text("do.title"),
style: languageModle.isEng
? TextStyle()
: TextStyle(fontFamily: 'MyanmarUnicode'),
),
actions: <Widget>[
InkWell(
child: Container(
padding: EdgeInsets.only(top: 15),
child: Stack(
children: <Widget>[
Image.asset(
"assets/date_filter.png",
color: Colors.white,
width: 25,
),
_dateIndex == 0
? Container()
: Positioned(
bottom: 15,
right: 10,
child: Container(
width: 10,
height: 10,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: secondaryColor,
),
),
)
],
),
),
onTap: () => _selectDate(context),
),
PopupMenuButton<PopupMenu>(
elevation: 3.2,
onSelected: (selected) {
setState(() {
_selectedIndex = selected.index;
});
if (selected.status == 'All') {
status = null;
} else {
status = selected.status;
}
doModel.filterData(
status, _selectedDate, _selectedIndex, _dateIndex);
},
icon: Container(
width: 30,
height: 30,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Icon(
Icons.filter_list,
color: primaryColor,
),
_selectedIndex != 0
? Positioned(
bottom: 0,
right: 0,
child: Container(
width: 10,
height: 10,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: secondaryColor,
),
),
)
: Container()
],
)),
itemBuilder: (BuildContext context) {
return statusMenu.map((PopupMenu choice) {
return PopupMenuItem<PopupMenu>(
value: choice,
child: Row(
children: <Widget>[
Text(choice.status),
SizedBox(
width: 10,
),
_selectedIndex != null &&
_selectedIndex == choice.index
? Icon(
Icons.check,
color: Colors.grey,
)
: Container(),
],
),
);
}).toList();
}),
],
),
floatingActionButton: mainModel.isBuyer()
? FloatingActionButton(
backgroundColor: primaryColor,
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => DOForm()),
),
child: Icon(Icons.add),
)
: null,
body: new ListView.builder(
scrollDirection: Axis.vertical,
padding: EdgeInsets.only(left: 15, right: 15, top: 15),
shrinkWrap: true,
itemCount: doModel.dos.length,
itemBuilder: (BuildContext context, int index) {
return Card(
elevation: 10,
color: Colors.white,
child: Row(
children: <Widget>[
Expanded(
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DOApproval(
doSubmission: doModel.dos[index])),
);
},
child: new Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: new Row(
children: <Widget>[
new Padding(
padding: new EdgeInsets.symmetric(
horizontal: 32.0 - dotSize / 2),
child: Padding(
padding: EdgeInsets.all(5.0),
child: Image.asset(
"assets/do.png",
width: 40,
height: 40,
color: primaryColor,
),
),
),
new Expanded(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Text(
doModel.dos[index].doNumber,
style: new TextStyle(
fontSize: 12.0, color: Colors.black),
),
new Text(
doModel.dos[index].deliveryDate == null
? ""
: dateFormatter.format(
doModel.dos[index].deliveryDate),
style: new TextStyle(
fontSize: 14.0, color: Colors.grey),
),
!isBuyer
? new Text(
doModel.dos[index].userName,
style: new TextStyle(
fontSize: 12.0,
color: Colors.grey),
)
: Container()
],
),
),
Container(
padding: EdgeInsets.only(right: 15),
child: getStatus(doModel.dos[index].status),
),
],
),
),
),
),
],
),
);
}),
),
);
}
}

View File

@@ -0,0 +1,134 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/pages/util.dart';
import 'package:fcs/theme/theme.dart';
import 'package:fcs/vo/do.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import 'package:fcs/widget/progress.dart';
class DOProductItem extends StatefulWidget {
final DOLine doLine;
const DOProductItem({Key key, this.doLine}) : super(key: key);
@override
_DOProductItemState createState() => _DOProductItemState();
}
class _DOProductItemState extends State<DOProductItem> {
final _formKey = GlobalKey<FormState>();
TextEditingController _doQty = new TextEditingController();
DOLine doLine;
bool _isLoading = false;
@override
void initState() {
super.initState();
if (widget.doLine != null) {
doLine = widget.doLine;
_doQty.text = widget.doLine.qty.toString();
}
}
@override
Widget build(BuildContext context) {
var languageModel = Provider.of<LanguageModel>(context);
return LocalProgress(
inAsyncCall: _isLoading,
child: AlertDialog(
title: Center(
child: Text(
AppTranslations.of(context).text("do_qty"),
style: TextStyle(
color: primaryColor, fontWeight: FontWeight.bold, fontSize: 20),
)),
content: Form(
key: _formKey,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Expanded(
child: new TextFormField(
keyboardType: TextInputType.number,
autofocus: true,
controller: _doQty,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
cursorColor: primaryColor,
decoration: new InputDecoration(
fillColor: primaryColor,
icon: Icon(
FontAwesomeIcons.sortNumericUpAlt,
color: primaryColor,
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey, width: 1.0)),
),
validator: (value) {
if (value.isEmpty) {
return AppTranslations.of(context).text("do.form.volume");
}
return null;
},
))
],
),
),
actions: <Widget>[
FlatButton(
child: LocalText(
context,
'do.cancel',
color: secondaryColor,
),
onPressed: () {
_doQty.clear();
Navigator.of(context).pop();
}),
FlatButton(
color: primaryColor,
child: LocalText(
context,
'do.enter',
color: Colors.white,
fontWeight: FontWeight.bold,
),
onPressed: () async {
if (!_formKey.currentState.validate()) return;
_save();
})
],
),
);
}
_save() {
setState(() {
_isLoading = true;
});
try {
var qty = int.parse(_doQty.text);
if (qty < 0)
throw Exception("invalid number, must be zero or greater than zero");
// if (qty > doLine.poLine.balanceQty)
// throw Exception(
// "invalid number, must be less than or equal to PO balance qty");
this.doLine.qty = qty;
Navigator.pop<DOLine>(context, this.doLine);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_delete() {
this.doLine.action = "delete";
this.doLine.qty = 0;
Navigator.pop<DOLine>(context, this.doLine);
}
}

View File

@@ -0,0 +1,209 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/storage_model.dart';
import 'package:fcs/theme/theme.dart';
import 'package:fcs/util.dart';
import 'package:fcs/vo/do.dart';
import 'package:fcs/vo/storage.dart';
import 'package:fcs/widget/local_text.dart';
import 'package:fcs/widget/my_data_table.dart';
import 'package:fcs/widget/number_cell.dart';
import 'package:fcs/widget/progress.dart';
typedef OnSave = void Function(String storageID, String storageName);
class DOStorageItem extends StatefulWidget {
final DOLine doLine;
final OnSave onSave;
const DOStorageItem({Key key, this.doLine, this.onSave}) : super(key: key);
@override
_DOStorageItemState createState() => _DOStorageItemState();
}
class _DOStorageItemState extends State<DOStorageItem> {
final _formKey = GlobalKey<FormState>();
bool _isLoading = false;
String currentStorageID;
TextEditingController _product = new TextEditingController();
TextEditingController _quantity = new TextEditingController();
DOLine doLine = DOLine();
@override
void initState() {
super.initState();
this.doLine = widget.doLine;
if (doLine.storageID != null && doLine.storageID.isNotEmpty) {
this.currentStorageID = doLine.storageID;
}
_product.text = this.doLine.productName;
_quantity.text = this.doLine.qty.toString();
}
Widget showStorages(BuildContext context, StorageModel storageModel) {
return Container(
padding: EdgeInsets.only(top: 10),
child: Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Image.asset(
"assets/inventory.png",
width: 30,
height: 30,
color: primaryColor,
),
SizedBox(
width: 20,
),
new Flexible(
child: Container(
width: 170.0,
child: DropdownButton<String>(
value: currentStorageID,
isExpanded: true,
hint: Text(
'Select Storage',
style: labelStyle,
),
onChanged: changedStorage,
items: storageModel
.getStorage(doLine.productID)
.map<DropdownMenuItem<String>>((Storage storage) {
return new DropdownMenuItem<String>(
value: storage.id,
child: new Text(storage.name, style: textStyle),
);
}).toList(),
),
),
),
],
),
);
}
void changedStorage(selected) {
setState(() {
currentStorageID = selected;
});
}
Widget showStorgeTable(BuildContext context, StorageModel storageModel) {
return Container(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: MyDataTable(
headingRowHeight: 40,
columnSpacing: 100,
columns: [
MyDataColumn(
label: LocalText(context, "storge"),
),
MyDataColumn(
label: LocalText(context, "storage.product.qty"),
),
],
rows: getProductRow(storageModel.getStorage(doLine.productID)),
),
),
);
}
List<MyDataRow> getProductRow(List<Storage> storages) {
return storages.map((s) {
return MyDataRow(
onSelectChanged: (bool selected) {
setState(() {
currentStorageID = s.id;
});
},
cells: [
MyDataCell(
new Text(s.name, style: textStyle),
),
MyDataCell(
NumberCell(s.products
.firstWhere((p) => p.id == doLine.productID)
.quantity),
),
],
);
}).toList();
}
@override
Widget build(BuildContext context) {
var storgeModel = Provider.of<StorageModel>(context);
final productbox = Container(
padding: EdgeInsets.only(top: 10),
child: Row(
children: <Widget>[
LocalText(context, "do.product"),
SizedBox(
width: 20,
),
Text(_product.text, style: textStyle)
],
));
final quantitybox = Container(
padding: EdgeInsets.only(top: 15),
child: Row(
children: <Widget>[
LocalText(context, "do.quantity"),
SizedBox(
width: 20,
),
Text(formatNumber(this.doLine.qty), style: textStyle)
],
),
);
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: Text("DO"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.save),
onPressed: () {
_save();
},
)
],
),
body: Form(
key: _formKey,
child: Column(
children: <Widget>[
Expanded(
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.0, right: 24.0, top: 10),
children: <Widget>[
productbox,
quantitybox,
showStorages(context, storgeModel),
showStorgeTable(context, storgeModel)
],
),
),
],
),
)),
);
}
_save() {
this.doLine.storageID = currentStorageID;
var storageName =
Provider.of<StorageModel>(context).getStorageName(currentStorageID);
this.doLine.storageName = storageName;
if (widget.onSave != null)
widget.onSave(this.doLine.storageID, storageName);
Navigator.pop<DOLine>(context, this.doLine);
}
}

View File

@@ -0,0 +1,165 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:provider/provider.dart';
import 'package:fcs/model/language_model.dart';
import 'package:fcs/theme/theme.dart';
import 'package:fcs/widget/localization/app_translations.dart';
import '../util.dart';
class PhotoPage extends StatefulWidget {
PhotoPage({Key key}) : super(key: key);
@override
_PhotoPageState createState() => _PhotoPageState();
}
class _PhotoPageState extends State<PhotoPage> {
File receiptImageFile;
@override
Widget build(BuildContext context) {
var languageModel = Provider.of<LanguageModel>(context);
final receiptImageBox = Padding(
padding: EdgeInsets.symmetric(vertical: 5.0),
child: Container(
color: Colors.grey[400],
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.only(top: 15),
child: Text(
AppTranslations.of(context).text('do.receipt'),
style:
languageModel.isEng ? photoLabelStyle : photoLabelStyleMM,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ButtonTheme.bar(
child: new ButtonBar(
alignment: MainAxisAlignment.start,
children: <Widget>[
new FlatButton(
child: Icon(
Icons.add_photo_alternate,
size: 60.0,
color: Colors.black,
),
onPressed: () {
pickImageFromGallery(ImageSource.gallery);
},
),
],
)),
ButtonTheme.bar(
child: new ButtonBar(
alignment: MainAxisAlignment.start,
children: <Widget>[
new FlatButton(
child: Icon(
Icons.add_a_photo,
size: 50.0,
color: Colors.black,
),
onPressed: () {
pickImageFromCamera(ImageSource.camera);
},
),
],
)),
],
),
receiptShowImage(),
SizedBox(
height: 10,
)
],
),
),
);
return Scaffold(
appBar: AppBar(
backgroundColor: primaryColor,
title: Text(AppTranslations.of(context).text("do.receipt.title"),
style: Provider.of<LanguageModel>(context).isEng
? TextStyle(fontSize: 18)
: TextStyle(fontSize: 18, fontFamily: 'MyanmarUnicode')),
actions: <Widget>[
IconButton(
icon: Icon(Icons.send),
onPressed: () {
if (receiptImageFile == null) {
showMsgDialog(
context, "Error", "Please insert delivery receipt file");
return;
}
Navigator.pop(context, receiptImageFile);
},
)
],
),
body: Column(
children: <Widget>[
Expanded(
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.0, right: 24.0, top: 20),
children: <Widget>[
receiptImageBox,
],
),
),
SizedBox(
height: 20,
)
],
),
);
}
Widget receiptShowImage() {
return Container(
child: receiptImageFile == null ? initialImage() : receiptEnableImage(),
);
}
Widget initialImage() {
var languageModel = Provider.of<LanguageModel>(context);
return Center(
child: Text(
AppTranslations.of(context).text('do.no.photo'),
style: languageModel.isEng ? photoLabelStyle : photoLabelStyleMM,
),
);
}
Widget receiptEnableImage() {
return Center(
child: Image.file(
receiptImageFile,
),
);
}
pickImageFromGallery(ImageSource source) async {
var tempImage = await ImagePicker.pickImage(
source: source, imageQuality: 80, maxWidth: 300);
setState(() {
receiptImageFile = tempImage;
});
}
pickImageFromCamera(ImageSource source) async {
var tempImage = await ImagePicker.pickImage(
source: source, imageQuality: 80, maxWidth: 300);
setState(() {
receiptImageFile = tempImage;
});
}
}

View File

@@ -0,0 +1,158 @@
import 'package:flutter/material.dart';
import 'package:fcs/theme/theme.dart';
import 'package:fcs/vo/po.dart';
import 'package:fcs/widget/local_text.dart';
class POSelection extends StatefulWidget {
final List<POSubmission> pos;
final List<POSubmission> selectedPOs;
POSelection({Key key, this.pos, this.selectedPOs}) : super(key: key);
@override
_POSelectionState createState() => new _POSelectionState();
static Future<void> showPOSelection(BuildContext context,
List<POSubmission> pos, List<POSubmission> selectedPOs,
{ok(List<POSubmission> pos)}) async {
List<POSubmission> _selectedPOs = [];
selectedPOs.forEach((i) => _selectedPOs.add(i));
final poselection = POSelection(
pos: pos,
selectedPOs: _selectedPOs,
);
await showDialog(
context: context,
builder: (_) {
return AlertDialog(
title: Center(
child: LocalText(context, "po.title"),
),
content: Container(
width: double.maxFinite,
child: ListView(
shrinkWrap: true,
children: <Widget>[
poselection,
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
FlatButton(
child: LocalText(context, "Cancel"),
onPressed: () {
Navigator.of(context).pop();
}),
FlatButton(
color: primaryColor,
child: LocalText(context, "Ok"),
onPressed: () async {
if (ok != null) ok(_selectedPOs);
Navigator.of(context).pop();
})
],
),
),
],
),
),
);
});
}
}
class _POSelectionState extends State<POSelection> {
List<POSubmission> pos;
@override
void initState() {
super.initState();
pos = widget.pos;
pos.sort((p1, p2) => p1.poNumber.compareTo(p2.poNumber));
}
@override
Widget build(BuildContext context) {
var width = MediaQuery.of(context).size.width * 0.8;
var height = MediaQuery.of(context).size.height * 0.5;
return Column(
children: <Widget>[
FlatButton(
child: Text("Select All"),
onPressed: () {
setState(() {
widget.selectedPOs.clear();
widget.selectedPOs.addAll(pos);
});
}),
Container(
width: width,
height: height,
child: Column(
children: pos.asMap().entries.map((p) {
return InkWell(
onTap: () {
setState(() {
if (widget.selectedPOs.contains(p.value)) {
widget.selectedPOs.remove(p.value);
} else {
widget.selectedPOs.add(p.value);
}
});
},
child: Row(
children: <Widget>[
Checkbox(
onChanged: (v) => {_update(p.key)},
value: widget.selectedPOs.contains(p.value),
),
Text(p.value.poNumber),
],
),
);
}).toList(),
),
// child: ListView.builder(
// itemCount: pos.length,
// scrollDirection: Axis.vertical,
// itemBuilder: (BuildContext ctxt, int index) {
// return InkWell(
// onTap: () {
// setState(() {
// if (widget.selectedPOs.contains(pos[index])) {
// widget.selectedPOs.remove(pos[index]);
// } else {
// widget.selectedPOs.add(pos[index]);
// }
// });
// },
// child: Row(
// children: <Widget>[
// Checkbox(
// onChanged: (v) => {_update(index)},
// value: widget.selectedPOs.contains(pos[index]),
// ),
// Text(pos[index].poNumber),
// ],
// ),
// );
// }),
),
],
);
}
_update(int index) {
setState(() {
if (widget.selectedPOs.contains(pos[index])) {
widget.selectedPOs.remove(pos[index]);
} else {
widget.selectedPOs.add(pos[index]);
}
});
}
}