522 lines
16 KiB
Dart
522 lines
16 KiB
Dart
|
|
import 'dart:io';
|
||
|
|
import 'dart:typed_data';
|
||
|
|
|
||
|
|
import 'package:flutter/material.dart';
|
||
|
|
import 'package:flutter/widgets.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/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/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 '../util.dart';
|
||
|
|
|
||
|
|
class DeliveryItem extends StatefulWidget {
|
||
|
|
final DOSubmission doSubmission;
|
||
|
|
const DeliveryItem({this.doSubmission});
|
||
|
|
@override
|
||
|
|
_DeliveryItemState createState() => _DeliveryItemState();
|
||
|
|
}
|
||
|
|
|
||
|
|
class _DeliveryItemState extends State<DeliveryItem> {
|
||
|
|
var dateFormatter = new DateFormat('dd MMM yyyy');
|
||
|
|
final numberFormatter = new NumberFormat("#,###");
|
||
|
|
|
||
|
|
bool _isLoading = false;
|
||
|
|
TextEditingController _date = 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;
|
||
|
|
File storageChargeFile;
|
||
|
|
File receiptImageFile;
|
||
|
|
|
||
|
|
@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)
|
||
|
|
: "";
|
||
|
|
_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();
|
||
|
|
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 dateBox = Container(
|
||
|
|
padding: EdgeInsets.only(left: 20, top: 15),
|
||
|
|
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 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: true,
|
||
|
|
initialImgUrl: doObj.doReceiptUrl,
|
||
|
|
title: "Receipt File",
|
||
|
|
onFile: (file) {
|
||
|
|
this.receiptImageFile = file;
|
||
|
|
},
|
||
|
|
),
|
||
|
|
),
|
||
|
|
]));
|
||
|
|
|
||
|
|
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: false,
|
||
|
|
title: "Receipt File",
|
||
|
|
initialImgUrl: this.doObj.storageReceiptUrl,
|
||
|
|
onFile: (file) {
|
||
|
|
this.storageChargeFile = 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("delivery"),
|
||
|
|
style: Provider.of<LanguageModel>(context).isEng
|
||
|
|
? TextStyle(fontSize: 18)
|
||
|
|
: TextStyle(fontSize: 18, fontFamily: 'MyanmarUnicode')),
|
||
|
|
actions: <Widget>[
|
||
|
|
isBuyer
|
||
|
|
? Container()
|
||
|
|
: PopupMenuButton(
|
||
|
|
onSelected: _select,
|
||
|
|
itemBuilder: (context) => List<PopupMenuEntry>.from([
|
||
|
|
PopupMenuItem(
|
||
|
|
enabled: this.doObj.isApproved,
|
||
|
|
value: 5,
|
||
|
|
child: Text("End Delivery"),
|
||
|
|
),
|
||
|
|
]),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
body: Container(
|
||
|
|
padding: EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 10),
|
||
|
|
child: Card(
|
||
|
|
elevation: 23,
|
||
|
|
child: ListView(
|
||
|
|
children: <Widget>[
|
||
|
|
Column(
|
||
|
|
children: <Widget>[
|
||
|
|
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: 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,
|
||
|
|
)
|
||
|
|
],
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
)),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
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(
|
||
|
|
cells: [
|
||
|
|
MyDataCell(
|
||
|
|
new Text(
|
||
|
|
d.productName,
|
||
|
|
style: textStyle,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
MyDataCell(
|
||
|
|
new Text(d.storageName, style: textStyle),
|
||
|
|
),
|
||
|
|
MyDataCell(
|
||
|
|
NumberCell(d.qty)
|
||
|
|
),
|
||
|
|
],
|
||
|
|
);
|
||
|
|
}).toList();
|
||
|
|
}
|
||
|
|
|
||
|
|
_select(s) {
|
||
|
|
if (s == 5) {
|
||
|
|
if (receiptImageFile == null) {
|
||
|
|
showMsgDialog(context, "Error", "Please insert delivery receipt file");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
showConfirmDialog(context, "delivery.confirm", () {
|
||
|
|
_endDelivery(receiptImageFile);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> _load() async {
|
||
|
|
POSubmissionModel poModel =
|
||
|
|
Provider.of<POSubmissionModel>(context, listen: false);
|
||
|
|
var _doSub = await poModel.loadDOLines(doObj);
|
||
|
|
setState(() {
|
||
|
|
doObj.doLines = _doSub.doLines;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
_endDelivery(dynamic receiptFile) async {
|
||
|
|
Uint8List bytesPhoto = receiptFile.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;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|