387 lines
10 KiB
Dart
387 lines
10 KiB
Dart
|
|
import 'dart:io';
|
||
|
|
|
||
|
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||
|
|
import 'package:intl/intl.dart';
|
||
|
|
|
||
|
|
import 'po.dart';
|
||
|
|
import 'setting.dart';
|
||
|
|
|
||
|
|
class DOSubmission {
|
||
|
|
final dateFormatter = new DateFormat('dd MMM yyyy hh:mm a');
|
||
|
|
|
||
|
|
String id;
|
||
|
|
String poID;
|
||
|
|
String doNumber;
|
||
|
|
DateTime deliveryDate;
|
||
|
|
DateTime doDate;
|
||
|
|
String driverLicenseNumber;
|
||
|
|
String driverName;
|
||
|
|
String carNo;
|
||
|
|
String status;
|
||
|
|
String deliveryStatus;
|
||
|
|
DateTime deliveryInitiatedTime;
|
||
|
|
String comment;
|
||
|
|
String userID;
|
||
|
|
String userName;
|
||
|
|
String type;
|
||
|
|
String bizName;
|
||
|
|
String driverImgUrl;
|
||
|
|
String doReceiptUrl;
|
||
|
|
int storageCharge = 0;
|
||
|
|
String storageReceiptUrl;
|
||
|
|
String driverLicenceUrl;
|
||
|
|
int totalQty;
|
||
|
|
|
||
|
|
File driverImg;
|
||
|
|
|
||
|
|
List<DOLine> doLines = List();
|
||
|
|
List<POSubmission> pos = List();
|
||
|
|
List<DOPOLine> dopoLies = List();
|
||
|
|
|
||
|
|
get isApproved => status != null && status == "approved";
|
||
|
|
get isClosed => status != null && status == "closed";
|
||
|
|
get isPending => status == "pending";
|
||
|
|
get getDeliveryStatus =>
|
||
|
|
deliveryStatus == null ? "You can initiate delivery" : deliveryStatus;
|
||
|
|
get deliveryInitTime => deliveryInitiatedTime == null
|
||
|
|
? ""
|
||
|
|
: dateFormatter.format(deliveryInitiatedTime);
|
||
|
|
|
||
|
|
bool hasStorageCharge() {
|
||
|
|
return this.storageCharge != null && this.storageCharge > 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
DOSubmission(
|
||
|
|
{this.id,
|
||
|
|
this.doNumber,
|
||
|
|
this.deliveryDate,
|
||
|
|
this.doDate,
|
||
|
|
this.driverLicenseNumber,
|
||
|
|
this.driverName,
|
||
|
|
this.carNo,
|
||
|
|
this.status,
|
||
|
|
this.comment,
|
||
|
|
this.type,
|
||
|
|
this.userName,
|
||
|
|
this.bizName,
|
||
|
|
this.deliveryStatus,
|
||
|
|
this.driverImgUrl,
|
||
|
|
this.deliveryInitiatedTime,
|
||
|
|
this.poID,
|
||
|
|
this.doReceiptUrl,
|
||
|
|
this.storageCharge,
|
||
|
|
this.storageReceiptUrl,
|
||
|
|
this.driverLicenceUrl,
|
||
|
|
this.totalQty});
|
||
|
|
|
||
|
|
void loadPOs() {
|
||
|
|
this.doLines.clear();
|
||
|
|
var isSingle = type == "single";
|
||
|
|
pos.forEach((p) => p.poLines.forEach((l) {
|
||
|
|
var dl = DOLine(
|
||
|
|
productID: l.productID,
|
||
|
|
productName: l.productName,
|
||
|
|
qty: isSingle ? l.balanceQty : 0,
|
||
|
|
poBalQty: l.balanceQty);
|
||
|
|
if (this.doLines.contains(dl)) {
|
||
|
|
this.doLines[this.doLines.indexOf(dl)].qty += dl.qty;
|
||
|
|
this.doLines[this.doLines.indexOf(dl)].poBalQty += dl.poBalQty;
|
||
|
|
} else {
|
||
|
|
this.doLines.add(dl);
|
||
|
|
}
|
||
|
|
}));
|
||
|
|
|
||
|
|
dopoLies.clear();
|
||
|
|
pos.forEach((p) => p.poLines.forEach((l) => dopoLies.add(DOPOLine(
|
||
|
|
poID: p.id,
|
||
|
|
productID: l.productID,
|
||
|
|
productName: l.productName,
|
||
|
|
poNumber: p.poNumber,
|
||
|
|
poQty: l.qty,
|
||
|
|
poBalQty: l.balanceQty,
|
||
|
|
doQty: isSingle ? l.balanceQty : 0,
|
||
|
|
poApproveDate: p.poApproveDate,
|
||
|
|
))));
|
||
|
|
dopoLies.sort((l1, l2) => l1.poNumber.compareTo(l2.poNumber));
|
||
|
|
}
|
||
|
|
|
||
|
|
void updateDoline(String productID, int qty) {
|
||
|
|
var _doLine = this.doLines.firstWhere((l) => l.productID == productID);
|
||
|
|
|
||
|
|
if (qty < 0) {
|
||
|
|
throw Exception("invalid number, must be greater than or equal to zero");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (_doLine.poBalQty < qty) {
|
||
|
|
throw Exception(
|
||
|
|
"invalid number,must be less than or equal to PO balance qty");
|
||
|
|
}
|
||
|
|
if (_doLine != null && _doLine.poBalQty >= qty) {
|
||
|
|
_doLine.qty = qty;
|
||
|
|
|
||
|
|
var _dopoLines = dopoLies.where((l) => l.productID == productID).toList();
|
||
|
|
// clear
|
||
|
|
_dopoLines.forEach((l) => l.doQty = 0);
|
||
|
|
|
||
|
|
// add
|
||
|
|
var poBalQty = qty;
|
||
|
|
_dopoLines.forEach((l) {
|
||
|
|
if (poBalQty == 0) return;
|
||
|
|
var _qty = poBalQty > l.poBalQty ? l.poBalQty : poBalQty;
|
||
|
|
l.doQty = _qty;
|
||
|
|
poBalQty -= _qty;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void updateStorageCharge(Setting setting) {
|
||
|
|
if (deliveryDate == null) {
|
||
|
|
storageCharge = 0;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
var _deliveryDate =
|
||
|
|
DateTime(deliveryDate.year, deliveryDate.month, deliveryDate.day);
|
||
|
|
storageCharge = 0;
|
||
|
|
dopoLies.forEach((l) {
|
||
|
|
var _approveDate = DateTime(
|
||
|
|
l.poApproveDate.year, l.poApproveDate.month, l.poApproveDate.day);
|
||
|
|
int diff = _deliveryDate.difference(_approveDate).inDays;
|
||
|
|
if (diff > setting.firstStorageChargeIn) {
|
||
|
|
storageCharge += l.doQty * setting.firstStorageCharge;
|
||
|
|
}
|
||
|
|
if (diff > setting.secondStorageChargeIn) {
|
||
|
|
storageCharge += l.doQty * setting.secondStorageCharge;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
List<String> getPOs() {
|
||
|
|
return this.dopoLies.map((l) => l.poID).toList();
|
||
|
|
}
|
||
|
|
|
||
|
|
void setDOPOLineBalance(String poID, List<POLine> poLines) {
|
||
|
|
poLines.forEach((poLine) {
|
||
|
|
this.dopoLies.forEach((l) {
|
||
|
|
if (l.poID == poID && l.productID == poLine.productID) {
|
||
|
|
l.poBalQty = poLine.balanceQty;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
Map<String, dynamic> toMap() {
|
||
|
|
List lines = doLines.map((l) => l.toMap()).toList();
|
||
|
|
List doPOlines = dopoLies.map((l) => l.toMap()).toList();
|
||
|
|
return {
|
||
|
|
"id": id,
|
||
|
|
'delivery_date': deliveryDate?.toUtc()?.toIso8601String(),
|
||
|
|
'do_number': doNumber,
|
||
|
|
'po_id': poID,
|
||
|
|
'car_number': carNo,
|
||
|
|
'driver_name': driverName,
|
||
|
|
'driver_license_number': driverLicenseNumber,
|
||
|
|
'do_lines': lines,
|
||
|
|
'do_po_lines': doPOlines,
|
||
|
|
'comment': comment,
|
||
|
|
'user_id': userID,
|
||
|
|
'user_name': userName,
|
||
|
|
'type': type,
|
||
|
|
'driver_img_url': driverImgUrl,
|
||
|
|
'driver_sign_url': doReceiptUrl,
|
||
|
|
'delivery_status': deliveryStatus,
|
||
|
|
'storage_charge': storageCharge,
|
||
|
|
'storage_receipt_url': storageReceiptUrl,
|
||
|
|
'driver_license_url': driverLicenceUrl,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
factory DOSubmission.fromMap(Map<String, dynamic> map, String id) {
|
||
|
|
var ts = (map['delivery_date'] as Timestamp);
|
||
|
|
var dt = (map['delivery_initiated_time'] as Timestamp);
|
||
|
|
var dots = (map['do_date'] as Timestamp);
|
||
|
|
return DOSubmission(
|
||
|
|
id: id,
|
||
|
|
deliveryDate: ts == null ? null : ts.toDate(),
|
||
|
|
deliveryInitiatedTime: dt == null ? null : dt.toDate(),
|
||
|
|
doDate: dots == null ? null : dots.toDate(),
|
||
|
|
doNumber: map['do_number'],
|
||
|
|
status: map['status'],
|
||
|
|
comment: map["comment"],
|
||
|
|
driverName: map["driver_name"],
|
||
|
|
driverLicenseNumber: map["driver_license_number"],
|
||
|
|
driverImgUrl: map["driver_img_url"],
|
||
|
|
doReceiptUrl: map["driver_sign_url"],
|
||
|
|
carNo: map["car_number"],
|
||
|
|
type: map["type"],
|
||
|
|
userName: map['user_name'],
|
||
|
|
bizName: map['biz_name'],
|
||
|
|
deliveryStatus: map['delivery_status'],
|
||
|
|
poID: map['po_id'],
|
||
|
|
storageCharge: map['storage_charge'],
|
||
|
|
storageReceiptUrl: map['storage_receipt_url'],
|
||
|
|
driverLicenceUrl: map['driver_license_url'],
|
||
|
|
totalQty: map['total_qty']);
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
String toString() {
|
||
|
|
return 'DOSubmission{id:$id, deliveryDate:$deliveryDate,driverLicenseNumber:$driverLicenseNumber,driverName:$driverName,carNo:$carNo,status:$status,doLines:$doLines}';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
class DOLine {
|
||
|
|
String id;
|
||
|
|
String productID;
|
||
|
|
String productName;
|
||
|
|
int qty;
|
||
|
|
int amount;
|
||
|
|
int price;
|
||
|
|
String storageID;
|
||
|
|
String storageName;
|
||
|
|
String action;
|
||
|
|
int displayOrder;
|
||
|
|
|
||
|
|
int poBalQty; // only for UI
|
||
|
|
|
||
|
|
DOLine(
|
||
|
|
{this.id,
|
||
|
|
this.productID,
|
||
|
|
this.productName,
|
||
|
|
this.qty,
|
||
|
|
this.amount,
|
||
|
|
this.price,
|
||
|
|
this.storageID,
|
||
|
|
this.storageName,
|
||
|
|
this.action,
|
||
|
|
this.poBalQty,
|
||
|
|
this.displayOrder});
|
||
|
|
|
||
|
|
factory DOLine.fromMap(Map<String, dynamic> map) {
|
||
|
|
return DOLine(
|
||
|
|
id: map['id'],
|
||
|
|
productID: map['product_id'],
|
||
|
|
productName: map['product_name'],
|
||
|
|
qty: map['quantity'],
|
||
|
|
amount: map['amount'],
|
||
|
|
price: map['price'],
|
||
|
|
storageID: map['storage_id'],
|
||
|
|
storageName: map['storage_name'],
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
Map<String, dynamic> toMap() {
|
||
|
|
return {
|
||
|
|
'id': id,
|
||
|
|
'product_id': productID,
|
||
|
|
'product_name': productName,
|
||
|
|
'quantity': qty,
|
||
|
|
'price': price,
|
||
|
|
'amount': amount,
|
||
|
|
'storage_id': storageID,
|
||
|
|
'storage_name': storageName,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
bool operator ==(other) {
|
||
|
|
if (identical(this, other)) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return other.productID == this.productID;
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
int get hashCode {
|
||
|
|
int result = 17;
|
||
|
|
result = 37 * result + productID.hashCode;
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
String toString() {
|
||
|
|
return 'DOLine{productID:$productID,productName:$productName,qty:$qty,amount:$amount,price:$price,storageID:$storageID,displayOrder:$displayOrder}';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
class DOPOLine {
|
||
|
|
String id;
|
||
|
|
String poID;
|
||
|
|
String poNumber;
|
||
|
|
DateTime poApproveDate;
|
||
|
|
String productID;
|
||
|
|
String productName;
|
||
|
|
int poQty;
|
||
|
|
int poBalQty;
|
||
|
|
int doQty;
|
||
|
|
int poBalAtCreate;
|
||
|
|
|
||
|
|
DateTime poApprovedDate;
|
||
|
|
int displayOrder;
|
||
|
|
|
||
|
|
get getPoBalanceQty => poBalQty - doQty;
|
||
|
|
get getPoBalanceQtyAtCreate =>
|
||
|
|
poBalAtCreate == null ? 0 : (poBalAtCreate - doQty);
|
||
|
|
|
||
|
|
DOPOLine(
|
||
|
|
{this.id,
|
||
|
|
this.poID,
|
||
|
|
this.poNumber,
|
||
|
|
this.productID,
|
||
|
|
this.productName,
|
||
|
|
this.poQty,
|
||
|
|
this.poBalQty,
|
||
|
|
this.doQty,
|
||
|
|
this.poApproveDate,
|
||
|
|
this.displayOrder,
|
||
|
|
this.poBalAtCreate});
|
||
|
|
|
||
|
|
factory DOPOLine.fromMap(Map<String, dynamic> map) {
|
||
|
|
return DOPOLine(
|
||
|
|
id: map['id'],
|
||
|
|
poID: map['po_id'],
|
||
|
|
poNumber: map['po_number'],
|
||
|
|
productID: map['product_id'],
|
||
|
|
productName: map['product_name'],
|
||
|
|
doQty: map['do_quantity'],
|
||
|
|
poQty: map['po_quantity'],
|
||
|
|
poBalQty: map['po_bal_quantity'],
|
||
|
|
poBalAtCreate: map['po_balance_at_create']);
|
||
|
|
}
|
||
|
|
|
||
|
|
Map<String, dynamic> toMap() {
|
||
|
|
return {
|
||
|
|
'id': id,
|
||
|
|
'po_id': poID,
|
||
|
|
'po_number': poNumber,
|
||
|
|
'product_id': productID,
|
||
|
|
'product_name': productName,
|
||
|
|
'do_quantity': doQty,
|
||
|
|
'po_quantity': poQty,
|
||
|
|
'po_bal_quantity': poBalQty,
|
||
|
|
'po_balance_at_create': poBalQty,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
bool operator ==(other) {
|
||
|
|
if (identical(this, other)) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return other.productID == this.productID && other.poID == this.poID;
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
int get hashCode {
|
||
|
|
int result = 17;
|
||
|
|
result = 37 * result + poID.hashCode;
|
||
|
|
result = 37 * result + productID.hashCode;
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
String toString() {
|
||
|
|
return 'DOLine{productID:$productID,productName:$productName,doQty:$doQty}';
|
||
|
|
}
|
||
|
|
}
|