Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -393,13 +393,13 @@
|
|||||||
"rate.delivery_fee":"Delivery fees",
|
"rate.delivery_fee":"Delivery fees",
|
||||||
"rate.total_estimated_amount":"Total estimated amount",
|
"rate.total_estimated_amount":"Total estimated amount",
|
||||||
"rate.volumetric_ratio":"Volumetric ratio",
|
"rate.volumetric_ratio":"Volumetric ratio",
|
||||||
"rate.custom.form.title":"CUSTOM",
|
"rate.custom.form.title":"Custom",
|
||||||
"rate.cutom.product_type":"Product type",
|
"rate.cutom.product_type":"Product type",
|
||||||
"rate.custom.fee":"Fee",
|
"rate.custom.fee":"Fee",
|
||||||
"rate.discount.weight":"Weight",
|
"rate.discount.weight":"Weight",
|
||||||
"rate.discount.rate":"Discount rate",
|
"rate.discount.rate":"Discount rate",
|
||||||
"rate.custom_duty.title":"Custom Duties",
|
"rate.custom_duty.title":"Custom Fee",
|
||||||
"rate.custom_duty":"Custom Duty",
|
"rate.custom_duty":"Custom Fee",
|
||||||
"rate.cargo.type":"Cargo Types",
|
"rate.cargo.type":"Cargo Types",
|
||||||
"rate.discount_by_weight":"Discounts by weight",
|
"rate.discount_by_weight":"Discounts by weight",
|
||||||
"rate.discount_by_weight.edit.delete.confirm":"Delete this discount by weight?",
|
"rate.discount_by_weight.edit.delete.confirm":"Delete this discount by weight?",
|
||||||
@@ -428,6 +428,8 @@
|
|||||||
"invoice.customer_name":"Customer name",
|
"invoice.customer_name":"Customer name",
|
||||||
"invoice.status":"Status",
|
"invoice.status":"Status",
|
||||||
"invoice.amount":"Amount",
|
"invoice.amount":"Amount",
|
||||||
|
"invoice.weight":"Weight(lb)",
|
||||||
|
"invoice.rate":"Rate(USD)",
|
||||||
"invoice.total":"Total amount",
|
"invoice.total":"Total amount",
|
||||||
"invoice.balance":"Balance",
|
"invoice.balance":"Balance",
|
||||||
"invoice.handling_fee":"Handling fee",
|
"invoice.handling_fee":"Handling fee",
|
||||||
@@ -435,15 +437,16 @@
|
|||||||
"invoice.custom_fee_desc":"Custom fee description",
|
"invoice.custom_fee_desc":"Custom fee description",
|
||||||
"invoice.discount":"Discount code",
|
"invoice.discount":"Discount code",
|
||||||
"invoice.payment_method":"Payment method",
|
"invoice.payment_method":"Payment method",
|
||||||
"invoice.delivery_fee":"Delivery fee : ",
|
"invoice.delivery_fee":"Delivery fee",
|
||||||
"invoice.payment_attachment":"Payment attachment",
|
"invoice.payment_attachment":"Payment attachment",
|
||||||
"invoice.box_info":"Carton information",
|
"invoice.box_info":"Cartons",
|
||||||
"invoice.cargo_table":"Cargo table",
|
"invoice.cargo_table":"Cargo table",
|
||||||
"invoice.btn_create":"Create invoice",
|
"invoice.issue.btn":"Issue invoice",
|
||||||
"invoice.btn_save":"Save invoice",
|
"invoice.btn_save":"Save invoice",
|
||||||
"invoice.btn_payment_receipt":"Attachment payment receipt",
|
"invoice.btn_payment_receipt":"Attachment payment receipt",
|
||||||
"invoice.description": "Description",
|
"invoice.description": "Description",
|
||||||
"invoice.box.cargo_type": "Cargo types",
|
"invoice.box.cargo_type": "Cargo types",
|
||||||
|
"invoice.box.desc": "Description",
|
||||||
"invoice.cargo_type":"Cargo types",
|
"invoice.cargo_type":"Cargo types",
|
||||||
"invoice.box.number":"Carton number",
|
"invoice.box.number":"Carton number",
|
||||||
"invoice.box.length":"L",
|
"invoice.box.length":"L",
|
||||||
@@ -455,10 +458,21 @@
|
|||||||
"invoice.pdf": "Invoice PDF",
|
"invoice.pdf": "Invoice PDF",
|
||||||
"invoice.total_custom_fee":"Total custom fee",
|
"invoice.total_custom_fee":"Total custom fee",
|
||||||
"invoice.shipment_weight":"Shipment weight",
|
"invoice.shipment_weight":"Shipment weight",
|
||||||
"invoice.popupmenu.pending":"Pending",
|
"invoice.popupmenu.issused":"Issused",
|
||||||
"invoice.popupmenu.paid":"Paid",
|
"invoice.popupmenu.paid":"Paid",
|
||||||
|
"invoice.popupmenu.cancel":"Canceled",
|
||||||
"invoice.shipment.title":"Select FCS shipment",
|
"invoice.shipment.title":"Select FCS shipment",
|
||||||
"invoice.customer.title":"Select Customer",
|
"invoice.customer.title":"Select Customer",
|
||||||
|
"invoice.add.custom.fee.menu":"Custom fee",
|
||||||
|
"invoice.add.handling.fee.menu":"Handling fee",
|
||||||
|
"invoice.add.discount.menu":"Discount",
|
||||||
|
"invoice.shipment.handling.fee.title":"Select shipment",
|
||||||
|
"invoice.shipment.number":"Shipment number",
|
||||||
|
"invoice.shipment.discount.title":"Select discount",
|
||||||
|
"invoice.cancel.btn":"Cancel",
|
||||||
|
"invoice.cancel.confirm":"Cancel invoice?",
|
||||||
|
"invoice.payment.confirm.confirm":"Confirm payment?",
|
||||||
|
"invoice.payment.cancel.confirm":"Cancel payment?",
|
||||||
"Invoices End ================================================================":"",
|
"Invoices End ================================================================":"",
|
||||||
|
|
||||||
"Discount Start ================================================================":"",
|
"Discount Start ================================================================":"",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"btn.approve":"အတည်ပြုရန်",
|
"btn.approve":"အတည်ပြုရန်",
|
||||||
"btn.delete":"ဖျက်ရန်",
|
"btn.delete":"ဖျက်ရန်",
|
||||||
"btn.select":"ရွေးချယ်ပါ",
|
"btn.select":"ရွေးချယ်ပါ",
|
||||||
"btn.cancel":"ဖျက်သိမ်းမည်",
|
"btn.cancel":"မလုပ်နဲ့",
|
||||||
"btn.ok": "အိုကေ",
|
"btn.ok": "အိုကေ",
|
||||||
"feet":"ပေ",
|
"feet":"ပေ",
|
||||||
"inch":"လက်မ",
|
"inch":"လက်မ",
|
||||||
@@ -428,6 +428,8 @@
|
|||||||
"invoice.customer_name":"ဝယ်ယူသူနာမည်",
|
"invoice.customer_name":"ဝယ်ယူသူနာမည်",
|
||||||
"invoice.status":"အခြေအနေ",
|
"invoice.status":"အခြေအနေ",
|
||||||
"invoice.amount":"ပမာဏ",
|
"invoice.amount":"ပမာဏ",
|
||||||
|
"invoice.weight":"Weight(lb)",
|
||||||
|
"invoice.rate":"Rate(USD)",
|
||||||
"invoice.total":"စုစုပေါင်းပမာဏ",
|
"invoice.total":"စုစုပေါင်းပမာဏ",
|
||||||
"invoice.balance":"ပေးချေရန်ကျန်ရှိငွေ",
|
"invoice.balance":"ပေးချေရန်ကျန်ရှိငွေ",
|
||||||
"invoice.handling_fee":"ထိန်းသိမ်းခ",
|
"invoice.handling_fee":"ထိန်းသိမ်းခ",
|
||||||
@@ -437,14 +439,16 @@
|
|||||||
"invoice.payment_method":"ငွေပေးချေစနစ်",
|
"invoice.payment_method":"ငွေပေးချေစနစ်",
|
||||||
"invoice.delivery_fee":"ပို့ဆောင်ခ",
|
"invoice.delivery_fee":"ပို့ဆောင်ခ",
|
||||||
"invoice.payment_attachment":"ပေးချေပြီးဖိုင်များ",
|
"invoice.payment_attachment":"ပေးချေပြီးဖိုင်များ",
|
||||||
"invoice.box_info":"Box အချက်အလက်",
|
"invoice.box_info":"Cartons",
|
||||||
"invoice.cargo_table":"ကုန်ပစ္စည်းဇယား",
|
"invoice.cargo_table":"ကုန်ပစ္စည်းဇယား",
|
||||||
"invoice.btn_create":"ငွေတောင်းခံလွှာ ပြုလုပ်ရန်",
|
"invoice.issue.btn":"ငွေတောင်းခံလွှာ ထုတ်မည်",
|
||||||
|
|
||||||
"invoice.btn_save":"ငွေတောင်းခံလွှာ သိမ်းရန်",
|
"invoice.btn_save":"ငွေတောင်းခံလွှာ သိမ်းရန်",
|
||||||
"invoice.btn_payment_receipt":"ငွေလက်ခံဖြတ်ပိုင်း ထည့်ရန်",
|
"invoice.btn_payment_receipt":"ငွေလက်ခံဖြတ်ပိုင်း ထည့်ရန်",
|
||||||
"invoice.box.cargo_type": "ကုန်ပစ္စည်းအမျိုးအစားများ",
|
"invoice.box.cargo_type": "ကုန်ပစ္စည်းအမျိုးအစားများ",
|
||||||
"invoice.description": "ငွေတောင်းခံလွှာအကြောင်းအရာ",
|
"invoice.description": "ငွေတောင်းခံလွှာအကြောင်းအရာ",
|
||||||
"invoice.cargo_type":"Cargo Types",
|
"invoice.cargo_type":"Cargo Types",
|
||||||
|
"invoice.box.desc": "Description",
|
||||||
"invoice.box.number":"Box နံပါတ်",
|
"invoice.box.number":"Box နံပါတ်",
|
||||||
"invoice.box.length":"L",
|
"invoice.box.length":"L",
|
||||||
"invoice.box.width":"W",
|
"invoice.box.width":"W",
|
||||||
@@ -455,10 +459,21 @@
|
|||||||
"invoice.pdf": "Invoice PDF",
|
"invoice.pdf": "Invoice PDF",
|
||||||
"invoice.total_custom_fee":"အခွန်စုစုပေါင်း",
|
"invoice.total_custom_fee":"အခွန်စုစုပေါင်း",
|
||||||
"invoice.shipment_weight":"Shipment weight",
|
"invoice.shipment_weight":"Shipment weight",
|
||||||
"invoice.popupmenu.pending":"Pending",
|
"invoice.popupmenu.issused":"Issused",
|
||||||
"invoice.popupmenu.paid":"Paid",
|
"invoice.popupmenu.paid":"Paid",
|
||||||
|
"invoice.popupmenu.cancel":"Canceled",
|
||||||
"invoice.shipment.title":"Select FCS shipment",
|
"invoice.shipment.title":"Select FCS shipment",
|
||||||
"invoice.customer.title":"Select Customer",
|
"invoice.customer.title":"Select Customer",
|
||||||
|
"invoice.add.custom.fee.menu":"Custom fee",
|
||||||
|
"invoice.add.handling.fee.menu":"Handling fee",
|
||||||
|
"invoice.add.discount.menu":"Discount",
|
||||||
|
"invoice.shipment.number":"Shipment number",
|
||||||
|
"invoice.shipment.handling.fee.title":"Select shipment",
|
||||||
|
"invoice.shipment.discount.title":"Select discount",
|
||||||
|
"invoice.cancel.btn":"Cancel",
|
||||||
|
"invoice.cancel.confirm":"Cancel invoice?",
|
||||||
|
"invoice.payment.confirm.confirm":"Confirm payment?",
|
||||||
|
"invoice.payment.cancel.confirm":"Cancel payment?",
|
||||||
"Invoices End ================================================================":"",
|
"Invoices End ================================================================":"",
|
||||||
|
|
||||||
"Discount Start ================================================================":"",
|
"Discount Start ================================================================":"",
|
||||||
|
|||||||
@@ -25,4 +25,10 @@ class FcsShipmentDataProvider {
|
|||||||
return await requestAPI("/fcs_shipments/ship", "PUT",
|
return await requestAPI("/fcs_shipments/ship", "PUT",
|
||||||
payload: fcsShipment.toMap(), token: await getToken());
|
payload: fcsShipment.toMap(), token: await getToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<String> reportFcsShipment(FcsShipment fcsShipment) async {
|
||||||
|
dynamic data = await requestAPI("/fcs_shipments/report", "POST",
|
||||||
|
payload: fcsShipment.toMap(), token: await getToken());
|
||||||
|
return data["url"];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
36
lib/data/provider/invoice_data_provider.dart
Normal file
36
lib/data/provider/invoice_data_provider.dart
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import 'package:fcs/domain/entities/invoice.dart';
|
||||||
|
import 'package:fcs/domain/entities/payment.dart';
|
||||||
|
import 'package:fcs/helpers/api_helper.dart';
|
||||||
|
import 'package:fcs/helpers/firebase_helper.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
|
class InvoiceDataProvider {
|
||||||
|
final log = Logger('InvoiceDataProvider');
|
||||||
|
static final InvoiceDataProvider instance = InvoiceDataProvider._();
|
||||||
|
InvoiceDataProvider._();
|
||||||
|
|
||||||
|
Future<void> createInvoice(Invoice invoice) async {
|
||||||
|
return await requestAPI("/invoices", "POST",
|
||||||
|
payload: invoice.toMap(), token: await getToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateInvoice(Invoice invoice) async {
|
||||||
|
return await requestAPI("/invoices", "PUT",
|
||||||
|
payload: invoice.toMap(), token: await getToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> cancelInvoice(Invoice invoice) async {
|
||||||
|
return await requestAPI("/invoices/cancel", "PUT",
|
||||||
|
payload: {"id": invoice.id}, token: await getToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> pay(Payment payment) async {
|
||||||
|
return await requestAPI("/invoices/pay", "PUT",
|
||||||
|
payload: payment.toMap(), token: await getToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updatPaymentStatus(Payment payment) async {
|
||||||
|
return await requestAPI("/invoices/pay/status", "PUT",
|
||||||
|
payload: payment.toMap(), token: await getToken());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,4 +33,9 @@ class FcsShipmentServiceImp implements FcsShipmentService {
|
|||||||
Future<void> ship(FcsShipment fcsShipment) {
|
Future<void> ship(FcsShipment fcsShipment) {
|
||||||
return shipmentDataProvider.ship(fcsShipment);
|
return shipmentDataProvider.ship(fcsShipment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String> report(FcsShipment fcsShipment) {
|
||||||
|
return shipmentDataProvider.reportFcsShipment(fcsShipment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ abstract class FcsShipmentService {
|
|||||||
Future<void> updateFcsShipment(FcsShipment fcsShipment);
|
Future<void> updateFcsShipment(FcsShipment fcsShipment);
|
||||||
Future<void> deleteFcsShipment(FcsShipment fcsShipment);
|
Future<void> deleteFcsShipment(FcsShipment fcsShipment);
|
||||||
Future<void> ship(FcsShipment fcsShipment);
|
Future<void> ship(FcsShipment fcsShipment);
|
||||||
|
Future<String> report(FcsShipment fcsShipment);
|
||||||
}
|
}
|
||||||
|
|||||||
45
lib/data/services/invoice_imp.dart
Normal file
45
lib/data/services/invoice_imp.dart
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import 'package:fcs/data/provider/invoice_data_provider.dart';
|
||||||
|
import 'package:fcs/data/provider/shipment_data_provider.dart';
|
||||||
|
import 'package:fcs/data/services/shipment_service.dart';
|
||||||
|
import 'package:fcs/domain/entities/connectivity.dart';
|
||||||
|
import 'package:fcs/domain/entities/invoice.dart';
|
||||||
|
import 'package:fcs/domain/entities/payment.dart';
|
||||||
|
import 'package:fcs/domain/entities/shipment.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'invoice_service.dart';
|
||||||
|
|
||||||
|
class InvoiceServiceImp implements InvoiceService {
|
||||||
|
InvoiceServiceImp({
|
||||||
|
@required this.invoiceDataProvider,
|
||||||
|
@required this.connectivity,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Connectivity connectivity;
|
||||||
|
final InvoiceDataProvider invoiceDataProvider;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> cancelInvoice(Invoice invoice) {
|
||||||
|
return invoiceDataProvider.cancelInvoice(invoice);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> createInvoice(Invoice invoice) {
|
||||||
|
return invoiceDataProvider.createInvoice(invoice);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updateInvoice(Invoice invoice) {
|
||||||
|
return invoiceDataProvider.updateInvoice(invoice);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> pay(Payment payment) {
|
||||||
|
return invoiceDataProvider.pay(payment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updatPaymentStatus(Payment payment) {
|
||||||
|
return invoiceDataProvider.updatPaymentStatus(payment);
|
||||||
|
}
|
||||||
|
}
|
||||||
10
lib/data/services/invoice_service.dart
Normal file
10
lib/data/services/invoice_service.dart
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import 'package:fcs/domain/entities/invoice.dart';
|
||||||
|
import 'package:fcs/domain/entities/payment.dart';
|
||||||
|
|
||||||
|
abstract class InvoiceService {
|
||||||
|
Future<void> createInvoice(Invoice invoice);
|
||||||
|
Future<void> updateInvoice(Invoice invoice);
|
||||||
|
Future<void> cancelInvoice(Invoice invoice);
|
||||||
|
Future<void> pay(Payment payment);
|
||||||
|
Future<void> updatPaymentStatus(Payment payment);
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import 'package:fcs/data/provider/carton_data_provider.dart';
|
|||||||
import 'package:fcs/data/provider/common_data_provider.dart';
|
import 'package:fcs/data/provider/common_data_provider.dart';
|
||||||
import 'package:fcs/data/provider/delivery_address_data_provider.dart';
|
import 'package:fcs/data/provider/delivery_address_data_provider.dart';
|
||||||
import 'package:fcs/data/provider/fcs_shipment_data_provider.dart';
|
import 'package:fcs/data/provider/fcs_shipment_data_provider.dart';
|
||||||
|
import 'package:fcs/data/provider/invoice_data_provider.dart';
|
||||||
import 'package:fcs/data/provider/package_data_provider.dart';
|
import 'package:fcs/data/provider/package_data_provider.dart';
|
||||||
import 'package:fcs/data/provider/rate_data_provider.dart';
|
import 'package:fcs/data/provider/rate_data_provider.dart';
|
||||||
import 'package:fcs/data/provider/shipment_data_provider.dart';
|
import 'package:fcs/data/provider/shipment_data_provider.dart';
|
||||||
@@ -13,6 +14,8 @@ import 'package:fcs/data/services/delivery_address_imp.dart';
|
|||||||
import 'package:fcs/data/services/delivery_address_service.dart';
|
import 'package:fcs/data/services/delivery_address_service.dart';
|
||||||
import 'package:fcs/data/services/fcs_shipment_imp.dart';
|
import 'package:fcs/data/services/fcs_shipment_imp.dart';
|
||||||
import 'package:fcs/data/services/fcs_shipment_service.dart';
|
import 'package:fcs/data/services/fcs_shipment_service.dart';
|
||||||
|
import 'package:fcs/data/services/invoice_imp.dart';
|
||||||
|
import 'package:fcs/data/services/invoice_service.dart';
|
||||||
import 'package:fcs/data/services/rate_imp.dart';
|
import 'package:fcs/data/services/rate_imp.dart';
|
||||||
import 'package:fcs/data/services/rate_service.dart';
|
import 'package:fcs/data/services/rate_service.dart';
|
||||||
import 'package:fcs/data/services/shipment_imp.dart';
|
import 'package:fcs/data/services/shipment_imp.dart';
|
||||||
@@ -42,6 +45,7 @@ class Services {
|
|||||||
RateService _rateService;
|
RateService _rateService;
|
||||||
ShipmentService _shipmentService;
|
ShipmentService _shipmentService;
|
||||||
CartonService _cartonService;
|
CartonService _cartonService;
|
||||||
|
InvoiceService _invoiceService;
|
||||||
Services._() {
|
Services._() {
|
||||||
_authService = AuthServiceImp(
|
_authService = AuthServiceImp(
|
||||||
authFb: AuthFb.instance,
|
authFb: AuthFb.instance,
|
||||||
@@ -65,6 +69,8 @@ class Services {
|
|||||||
connectivity: null);
|
connectivity: null);
|
||||||
_cartonService = CartonServiceImp(
|
_cartonService = CartonServiceImp(
|
||||||
cartonDataProvider: CartonDataProvider.instance, connectivity: null);
|
cartonDataProvider: CartonDataProvider.instance, connectivity: null);
|
||||||
|
_invoiceService = InvoiceServiceImp(
|
||||||
|
invoiceDataProvider: InvoiceDataProvider.instance, connectivity: null);
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthService get authService => _authService;
|
AuthService get authService => _authService;
|
||||||
@@ -77,4 +83,5 @@ class Services {
|
|||||||
RateService get rateService => _rateService;
|
RateService get rateService => _rateService;
|
||||||
ShipmentService get shipmentService => _shipmentService;
|
ShipmentService get shipmentService => _shipmentService;
|
||||||
CartonService get cartonService => _cartonService;
|
CartonService get cartonService => _cartonService;
|
||||||
|
InvoiceService get invoiceService => _invoiceService;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const user_joined_status = "joined";
|
|||||||
|
|
||||||
const pkg_files_path = "/packages";
|
const pkg_files_path = "/packages";
|
||||||
const shipment_labels_files_path = "/shipment_labels";
|
const shipment_labels_files_path = "/shipment_labels";
|
||||||
|
const receipt_labels_files_path = "/receipts";
|
||||||
|
|
||||||
// Link page
|
// Link page
|
||||||
const page_payment_methods = "payment_methods";
|
const page_payment_methods = "payment_methods";
|
||||||
@@ -91,3 +92,14 @@ const shipment_pickuped_status = "pickuped";
|
|||||||
const shipment_packed_status = "packed";
|
const shipment_packed_status = "packed";
|
||||||
const shipment_shipped_status = "shipped";
|
const shipment_shipped_status = "shipped";
|
||||||
const shipment_delivered_status = "delivered";
|
const shipment_delivered_status = "delivered";
|
||||||
|
|
||||||
|
// invoice status
|
||||||
|
const invoice_issued_status = "issued";
|
||||||
|
const invoice_saved_status = "saved";
|
||||||
|
const invoice_cancel_status = "canceled";
|
||||||
|
const invoice_paid_status = "paid";
|
||||||
|
|
||||||
|
// payment status
|
||||||
|
const payment_pending_status = "pending";
|
||||||
|
const payment_confirmed_status = "confirmed";
|
||||||
|
const payment_canceled_status = "canceled";
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ class CargoType {
|
|||||||
double rate;
|
double rate;
|
||||||
double weight;
|
double weight;
|
||||||
|
|
||||||
int shipmentWeight;
|
double get calAmount => (calRate ?? 0) * (calWeight ?? 0);
|
||||||
double amount;
|
|
||||||
|
|
||||||
double calRate;
|
double calRate;
|
||||||
double calWeight;
|
double calWeight;
|
||||||
@@ -16,9 +15,17 @@ class CargoType {
|
|||||||
name: map['name'],
|
name: map['name'],
|
||||||
rate: map['rate']?.toDouble() ?? 0,
|
rate: map['rate']?.toDouble() ?? 0,
|
||||||
weight: map['weight']?.toDouble() ?? 0,
|
weight: map['weight']?.toDouble() ?? 0,
|
||||||
|
calWeight: map['cal_weight']?.toDouble() ?? 0,
|
||||||
|
calRate: map['cal_rate']?.toDouble() ?? 0,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
CargoType({this.id, this.name, this.rate, this.weight});
|
CargoType(
|
||||||
|
{this.id,
|
||||||
|
this.name,
|
||||||
|
this.rate,
|
||||||
|
this.weight,
|
||||||
|
this.calWeight,
|
||||||
|
this.calRate});
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
Map<String, dynamic> toMap() {
|
||||||
return {
|
return {
|
||||||
@@ -26,6 +33,8 @@ class CargoType {
|
|||||||
'name': name,
|
'name': name,
|
||||||
'rate': rate,
|
'rate': rate,
|
||||||
'weight': weight,
|
'weight': weight,
|
||||||
|
'cal_weight': calWeight,
|
||||||
|
'cal_rate': calRate,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
import 'package:fcs/domain/entities/discount_by_weight.dart';
|
import 'package:fcs/domain/entities/discount_by_weight.dart';
|
||||||
import 'package:fcs/domain/entities/rate.dart';
|
import 'package:fcs/domain/entities/rate.dart';
|
||||||
|
import 'package:fcs/domain/entities/shipment.dart';
|
||||||
import 'package:fcs/domain/vo/shipment_status.dart';
|
import 'package:fcs/domain/vo/shipment_status.dart';
|
||||||
import 'package:fcs/domain/vo/delivery_address.dart';
|
import 'package:fcs/domain/vo/delivery_address.dart';
|
||||||
|
|
||||||
@@ -9,6 +10,7 @@ import 'package.dart';
|
|||||||
|
|
||||||
class Carton {
|
class Carton {
|
||||||
String id;
|
String id;
|
||||||
|
String shipmentID;
|
||||||
String shipmentNumber;
|
String shipmentNumber;
|
||||||
String senderFCSID;
|
String senderFCSID;
|
||||||
String senderName;
|
String senderName;
|
||||||
@@ -47,9 +49,9 @@ class Carton {
|
|||||||
List<String> packageIDs;
|
List<String> packageIDs;
|
||||||
List<Package> packages;
|
List<Package> packages;
|
||||||
List<CargoType> cargoTypes;
|
List<CargoType> cargoTypes;
|
||||||
List<Carton> cartons;
|
|
||||||
|
|
||||||
DeliveryAddress deliveryAddress;
|
DeliveryAddress deliveryAddress;
|
||||||
|
Shipment shipment;
|
||||||
|
|
||||||
int get amount => rate != null && weight != null ? rate * weight : 0;
|
int get amount => rate != null && weight != null ? rate * weight : 0;
|
||||||
|
|
||||||
@@ -73,6 +75,23 @@ class Carton {
|
|||||||
return (length * width * height) / volumetricRatio;
|
return (length * width * height) / volumetricRatio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// getCargoTypeForCalWeight returns carton with shipment weight
|
||||||
|
List<CargoType> getCargoTypeForCalWeight(double volumetricRatio) {
|
||||||
|
// get shipment weight
|
||||||
|
double volume = (length ?? 0) * (width ?? 0) * (height ?? 0);
|
||||||
|
double sw = volume / volumetricRatio ?? 0;
|
||||||
|
|
||||||
|
// get actual weight
|
||||||
|
double aw = cargoTypes.fold(0.0, (p, c) => p + c.weight);
|
||||||
|
if (aw == 0 || sw == 0) return [];
|
||||||
|
|
||||||
|
cargoTypes.forEach((e) {
|
||||||
|
double calWeight = aw > sw ? e.weight : e.weight / aw * sw;
|
||||||
|
e.calWeight = calWeight;
|
||||||
|
});
|
||||||
|
return cargoTypes;
|
||||||
|
}
|
||||||
|
|
||||||
/// calAmount returns total amount
|
/// calAmount returns total amount
|
||||||
double calAmount(Rate rate) {
|
double calAmount(Rate rate) {
|
||||||
// get shipment weight
|
// get shipment weight
|
||||||
@@ -101,6 +120,7 @@ class Carton {
|
|||||||
|
|
||||||
Carton(
|
Carton(
|
||||||
{this.id,
|
{this.id,
|
||||||
|
this.shipmentID,
|
||||||
this.shipmentNumber,
|
this.shipmentNumber,
|
||||||
this.senderFCSID,
|
this.senderFCSID,
|
||||||
this.senderName,
|
this.senderName,
|
||||||
@@ -133,7 +153,6 @@ class Carton {
|
|||||||
this.cartonNumber,
|
this.cartonNumber,
|
||||||
this.fcsShipmentID,
|
this.fcsShipmentID,
|
||||||
this.fcsShipmentNumber,
|
this.fcsShipmentNumber,
|
||||||
this.cartons,
|
|
||||||
this.packageIDs,
|
this.packageIDs,
|
||||||
this.mixCartonID,
|
this.mixCartonID,
|
||||||
this.mixCartonNumber,
|
this.mixCartonNumber,
|
||||||
@@ -143,7 +162,6 @@ class Carton {
|
|||||||
Map<String, dynamic> toMap() {
|
Map<String, dynamic> toMap() {
|
||||||
List _cargoTypes = cargoTypes.map((c) => c.toMap()).toList();
|
List _cargoTypes = cargoTypes.map((c) => c.toMap()).toList();
|
||||||
List _packages = packages?.map((c) => c.toJson())?.toList();
|
List _packages = packages?.map((c) => c.toJson())?.toList();
|
||||||
List _cartons = cartons?.map((c) => c.toMap())?.toList() ?? [];
|
|
||||||
return {
|
return {
|
||||||
"id": id,
|
"id": id,
|
||||||
'fcs_shipment_id': fcsShipmentID,
|
'fcs_shipment_id': fcsShipmentID,
|
||||||
@@ -153,10 +171,9 @@ class Carton {
|
|||||||
'length': length,
|
'length': length,
|
||||||
'width': width,
|
'width': width,
|
||||||
'height': height,
|
'height': height,
|
||||||
'delivery_address': deliveryAddress.toMap(),
|
'delivery_address': deliveryAddress?.toMap(),
|
||||||
'carton_type': cartonType,
|
'carton_type': cartonType,
|
||||||
'cartons': _cartons,
|
'mix_carton_id': mixCartonID,
|
||||||
'mix_carton_id': mixCartonID
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,6 +188,7 @@ class Carton {
|
|||||||
return Carton(
|
return Carton(
|
||||||
id: docID,
|
id: docID,
|
||||||
arrivedDate: _arrivedDate != null ? _arrivedDate.toDate() : null,
|
arrivedDate: _arrivedDate != null ? _arrivedDate.toDate() : null,
|
||||||
|
shipmentID: map['shipment_id'],
|
||||||
shipmentNumber: map['shipment_number'],
|
shipmentNumber: map['shipment_number'],
|
||||||
receiverNumber: map['receiver_number'],
|
receiverNumber: map['receiver_number'],
|
||||||
boxNumber: map['box_number'],
|
boxNumber: map['box_number'],
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ class FcsShipment {
|
|||||||
String port;
|
String port;
|
||||||
String destination;
|
String destination;
|
||||||
String status;
|
String status;
|
||||||
|
String reportName;
|
||||||
FcsShipment({
|
FcsShipment({
|
||||||
this.id,
|
this.id,
|
||||||
this.shipmentNumber,
|
this.shipmentNumber,
|
||||||
@@ -24,6 +25,7 @@ class FcsShipment {
|
|||||||
this.consignee,
|
this.consignee,
|
||||||
this.port,
|
this.port,
|
||||||
this.destination,
|
this.destination,
|
||||||
|
this.reportName,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory FcsShipment.fromMap(Map<String, dynamic> map, String docID) {
|
factory FcsShipment.fromMap(Map<String, dynamic> map, String docID) {
|
||||||
@@ -57,6 +59,7 @@ class FcsShipment {
|
|||||||
'port': port,
|
'port': port,
|
||||||
'destination': destination,
|
'destination': destination,
|
||||||
'status': status,
|
'status': status,
|
||||||
|
'report_name': reportName,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,79 +1,82 @@
|
|||||||
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
import 'package:fcs/domain/entities/cargo_type.dart';
|
import 'package:fcs/domain/entities/cargo_type.dart';
|
||||||
import 'package:fcs/domain/entities/carton.dart';
|
import 'package:fcs/domain/entities/carton.dart';
|
||||||
import 'package:fcs/domain/entities/custom_duty.dart';
|
import 'package:fcs/domain/entities/custom_duty.dart';
|
||||||
import 'package:fcs/domain/entities/discount.dart';
|
import 'package:fcs/domain/entities/discount.dart';
|
||||||
import 'package:fcs/domain/entities/discount_by_weight.dart';
|
import 'package:fcs/domain/entities/discount_by_weight.dart';
|
||||||
|
import 'package:fcs/domain/entities/payment.dart';
|
||||||
|
import 'package:fcs/domain/entities/payment_method.dart';
|
||||||
import 'package:fcs/domain/entities/rate.dart';
|
import 'package:fcs/domain/entities/rate.dart';
|
||||||
|
import 'package:fcs/domain/entities/shipment.dart';
|
||||||
import 'package.dart';
|
|
||||||
import 'receipt.dart';
|
|
||||||
|
|
||||||
class Invoice {
|
class Invoice {
|
||||||
String id;
|
String id;
|
||||||
String invoiceNumber;
|
String invoiceNumber;
|
||||||
DateTime invoiceDate;
|
DateTime invoiceDate;
|
||||||
String customerName;
|
String fcsShipmentID;
|
||||||
String customerPhoneNumber;
|
String userID;
|
||||||
double amount;
|
String fcsID;
|
||||||
|
String userName;
|
||||||
|
String phoneNumber;
|
||||||
String status;
|
String status;
|
||||||
String paymentAttachment;
|
|
||||||
double handlingFee;
|
double handlingFee;
|
||||||
double deliveryFee;
|
double deliveryFee;
|
||||||
double paidAmount;
|
double paidAmount;
|
||||||
|
double amount;
|
||||||
|
|
||||||
List<Package> packages;
|
|
||||||
List<Receipt> receipts;
|
|
||||||
List<String> receiptPhotos;
|
|
||||||
List<CustomDuty> customDuties;
|
List<CustomDuty> customDuties;
|
||||||
List<Carton> cartons;
|
List<Carton> cartons;
|
||||||
|
List<CargoType> cargoTypes;
|
||||||
|
List<Shipment> shipments;
|
||||||
|
List<Payment> payments;
|
||||||
Discount discount;
|
Discount discount;
|
||||||
|
PaymentMethod paymentMethod;
|
||||||
|
String invoiceURL;
|
||||||
|
|
||||||
List<CargoType> getCargoTypes(Rate rate) {
|
List<CargoType> getCargoTypes(Rate rate) {
|
||||||
List<CargoType> cargoTypes = [];
|
if (cargoTypes != null) return cargoTypes;
|
||||||
double actualWeight = 0;
|
|
||||||
double shipmentWeight = 0;
|
List<CargoType> _cargoTypes = [];
|
||||||
cartons.forEach((c) {
|
double totalCalWeight = 0;
|
||||||
c.cargoTypes.forEach((tc) {
|
cartons.forEach((carton) {
|
||||||
if (cargoTypes.contains(tc)) {
|
if (carton.isChecked) {
|
||||||
CargoType existing = cargoTypes.firstWhere((wc) => wc.id == tc.id);
|
var _cartonsTypes =
|
||||||
existing.weight += tc.weight;
|
carton.getCargoTypeForCalWeight(rate.volumetricRatio);
|
||||||
|
_cartonsTypes.forEach((ct) {
|
||||||
|
if (_cargoTypes.contains(ct)) {
|
||||||
|
CargoType existing = _cargoTypes.firstWhere((wc) => wc.id == ct.id);
|
||||||
|
existing.calWeight += ct.calWeight;
|
||||||
} else {
|
} else {
|
||||||
cargoTypes.add(tc.clone());
|
_cargoTypes.add(ct.clone());
|
||||||
}
|
}
|
||||||
actualWeight += tc.weight;
|
totalCalWeight += ct.calWeight;
|
||||||
});
|
});
|
||||||
double volume = (c.length ?? 0) * (c.width ?? 0) * (c.height ?? 0);
|
}
|
||||||
double sw = volume / rate.volumetricRatio ?? 0;
|
|
||||||
shipmentWeight += sw;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
DiscountByWeight discountByWeight = rate.getDiscountByWeight(
|
DiscountByWeight discountByWeight =
|
||||||
shipmentWeight > actualWeight ? shipmentWeight : actualWeight);
|
rate.getDiscountByWeight(totalCalWeight);
|
||||||
|
|
||||||
cargoTypes.forEach((e) {
|
_cargoTypes.forEach((e) {
|
||||||
print(actualWeight > shipmentWeight);
|
|
||||||
double cargoWeight = actualWeight > shipmentWeight
|
|
||||||
? e.weight
|
|
||||||
: e.weight / actualWeight * shipmentWeight;
|
|
||||||
double r =
|
double r =
|
||||||
e.rate - (discountByWeight != null ? discountByWeight.discount : 0);
|
e.rate - (discountByWeight != null ? discountByWeight.discount : 0);
|
||||||
double amount = cargoWeight * r;
|
|
||||||
e.calRate = r;
|
e.calRate = r;
|
||||||
e.calWeight = cargoWeight;
|
|
||||||
e.amount = amount;
|
|
||||||
});
|
});
|
||||||
return cargoTypes;
|
return _cargoTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
double getTotal(Rate rate) {
|
double getTotal(Rate rate) {
|
||||||
List<CargoType> cargoTypes = getCargoTypes(rate);
|
List<CargoType> cargoTypes = getCargoTypes(rate);
|
||||||
var total = cargoTypes.fold(0.0, (p, c) => c.amount + p);
|
var total = cargoTypes.fold(0.0, (p, c) => c.calAmount + p);
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double get balance => (amount ?? 0) - (paidAmount ?? 0);
|
||||||
|
|
||||||
double getNetAmount(Rate rate) {
|
double getNetAmount(Rate rate) {
|
||||||
List<CargoType> cargoTypes = getCargoTypes(rate);
|
List<CargoType> cargoTypes = getCargoTypes(rate);
|
||||||
var total = cargoTypes.fold(0.0, (p, c) => c.amount + p);
|
var total = cargoTypes.fold(0.0, (p, c) => c.calAmount + p);
|
||||||
total += getCustomFee();
|
total += getCustomFee();
|
||||||
total += getDeliveryFee();
|
total += getDeliveryFee();
|
||||||
total += getHandlingFee();
|
total += getHandlingFee();
|
||||||
@@ -81,6 +84,12 @@ class Invoice {
|
|||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double getHandlingFee() {
|
||||||
|
return shipments?.where((sh) => sh.isSelected ?? false)?.fold(0, (p, s) {
|
||||||
|
return p + (s?.handlingFee ?? 0) - (s?.paidHandlingFee ?? 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
double getTotalBalance(Rate rate) {
|
double getTotalBalance(Rate rate) {
|
||||||
return getNetAmount(rate) - (paidAmount ?? 0);
|
return getNetAmount(rate) - (paidAmount ?? 0);
|
||||||
}
|
}
|
||||||
@@ -89,10 +98,6 @@ class Invoice {
|
|||||||
return customDuties == null ? 0 : customDuties.fold(0, (p, d) => p + d.fee);
|
return customDuties == null ? 0 : customDuties.fold(0, (p, d) => p + d.fee);
|
||||||
}
|
}
|
||||||
|
|
||||||
double getHandlingFee() {
|
|
||||||
return handlingFee == null ? 0 : handlingFee;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getDeliveryFee() {
|
double getDeliveryFee() {
|
||||||
return deliveryFee == null ? 0 : deliveryFee;
|
return deliveryFee == null ? 0 : deliveryFee;
|
||||||
}
|
}
|
||||||
@@ -103,35 +108,92 @@ class Invoice {
|
|||||||
{this.id,
|
{this.id,
|
||||||
this.invoiceNumber,
|
this.invoiceNumber,
|
||||||
this.invoiceDate,
|
this.invoiceDate,
|
||||||
this.customerName,
|
this.fcsID,
|
||||||
this.customerPhoneNumber,
|
this.userName,
|
||||||
|
this.phoneNumber,
|
||||||
this.amount,
|
this.amount,
|
||||||
|
this.paidAmount,
|
||||||
this.discount,
|
this.discount,
|
||||||
this.status,
|
this.status,
|
||||||
this.paymentAttachment,
|
|
||||||
this.packages,
|
|
||||||
this.receiptPhotos,
|
|
||||||
this.customDuties,
|
this.customDuties,
|
||||||
this.cartons,
|
this.cartons,
|
||||||
|
this.cargoTypes,
|
||||||
this.handlingFee,
|
this.handlingFee,
|
||||||
this.receipts});
|
this.deliveryFee,
|
||||||
|
this.fcsShipmentID,
|
||||||
double get getAmount => packages.fold(0, (p, e) => (e.rate * e.weight) + p);
|
this.shipments,
|
||||||
|
this.invoiceURL,
|
||||||
|
this.payments,
|
||||||
|
this.paymentMethod});
|
||||||
|
|
||||||
factory Invoice.fromMap(Map<String, dynamic> map, String docID) {
|
factory Invoice.fromMap(Map<String, dynamic> map, String docID) {
|
||||||
|
var invd = (map['invoice_date'] as Timestamp);
|
||||||
|
var cargoTypesMaps =
|
||||||
|
List<Map<String, dynamic>>.from(map['cargo_types'] ?? []);
|
||||||
|
var cargoTypes =
|
||||||
|
cargoTypesMaps.map((e) => CargoType.fromMap(e, e["id"])).toList();
|
||||||
|
var customDutiesMap =
|
||||||
|
List<Map<String, dynamic>>.from(map['custom_duties'] ?? []);
|
||||||
|
var customDuties =
|
||||||
|
customDutiesMap.map((e) => CustomDuty.fromMap(e, e["id"])).toList();
|
||||||
|
var handlingShipmentsMap =
|
||||||
|
List<Map<String, dynamic>>.from(map['handling_fee_shipments'] ?? []);
|
||||||
|
var handingShipments =
|
||||||
|
handlingShipmentsMap.map((e) => Shipment.fromMap(e, e["id"])).toList();
|
||||||
|
var cartonsMap = List<Map<String, dynamic>>.from(map['cartons'] ?? []);
|
||||||
|
var cartons = cartonsMap.map((e) => Carton.fromMap(e, e["id"])).toList();
|
||||||
|
var paymentMethodMap = map['payment_method'];
|
||||||
|
var paymentMethod = paymentMethodMap != null
|
||||||
|
? PaymentMethod.fromMap(paymentMethodMap, paymentMethodMap['id'])
|
||||||
|
: null;
|
||||||
|
var discountMap = map['discount'];
|
||||||
|
var discount = Discount.fromMap(discountMap, discountMap['id']);
|
||||||
|
var paymentMaps = List<Map<String, dynamic>>.from(map['payments'] ?? []);
|
||||||
|
var payments = paymentMaps.map((e) => Payment.fromMap(e, e["id"])).toList();
|
||||||
return Invoice(
|
return Invoice(
|
||||||
id: docID,
|
id: docID,
|
||||||
invoiceNumber: map['invoice_number'],
|
invoiceNumber: map['invoice_number'],
|
||||||
invoiceDate: map['invoice_date'],
|
invoiceDate: invd?.toDate(),
|
||||||
customerName: map['customer_name'],
|
userName: map['user_name'],
|
||||||
customerPhoneNumber: map['phone_number'],
|
fcsID: map['fcs_id'],
|
||||||
|
phoneNumber: map['phone_number'],
|
||||||
amount: map['amount'],
|
amount: map['amount'],
|
||||||
|
paidAmount: double.tryParse(map['paid_amount'].toString()) ?? 0,
|
||||||
status: map['status'],
|
status: map['status'],
|
||||||
discount: map['discount'],
|
cartons: cartons,
|
||||||
paymentAttachment: map['payment_attachment'],
|
cargoTypes: cargoTypes,
|
||||||
packages: map['packages'],
|
shipments: handingShipments,
|
||||||
receiptPhotos: map['receiptPhotos'],
|
customDuties: customDuties,
|
||||||
receipts: map['receipts'],
|
deliveryFee: map['delivery_fee'],
|
||||||
|
invoiceURL: map['invoice_url'],
|
||||||
|
paymentMethod: paymentMethod,
|
||||||
|
discount: discount,
|
||||||
|
payments: payments,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
List _cargoTypes = cargoTypes.map((c) => c.toMap()).toList();
|
||||||
|
List _customDuties = customDuties?.map((c) => c.toMap())?.toList();
|
||||||
|
List _cartons = cartons?.map((c) => c.toMap())?.toList() ?? [];
|
||||||
|
List _shipments = shipments?.map((s) => s.toMap())?.toList() ?? [];
|
||||||
|
return {
|
||||||
|
"id": id,
|
||||||
|
"invoice_date": invoiceDate?.toUtc()?.toIso8601String(),
|
||||||
|
"user_id": userID,
|
||||||
|
"user_name": userName,
|
||||||
|
"invoice_number": invoiceNumber,
|
||||||
|
'fcs_shipment_id': fcsShipmentID,
|
||||||
|
'cargo_types': _cargoTypes,
|
||||||
|
'custom_duties': _customDuties,
|
||||||
|
'handling_fee_shipments': _shipments,
|
||||||
|
'cartons': _cartons,
|
||||||
|
'discount': discount?.toMap(),
|
||||||
|
'amount': amount,
|
||||||
|
'handling_fee': handlingFee,
|
||||||
|
'delivery_fee': deliveryFee,
|
||||||
|
'invoice_url': invoiceURL,
|
||||||
|
'payment_method': paymentMethod?.toMap(),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
50
lib/domain/entities/payment.dart
Normal file
50
lib/domain/entities/payment.dart
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
|
|
||||||
|
class Payment {
|
||||||
|
String id;
|
||||||
|
String invoiceID;
|
||||||
|
DateTime paymentDate;
|
||||||
|
String paymentReceiptURL;
|
||||||
|
String status;
|
||||||
|
double amount;
|
||||||
|
|
||||||
|
Payment(
|
||||||
|
{this.id,
|
||||||
|
this.invoiceID,
|
||||||
|
this.paymentDate,
|
||||||
|
this.paymentReceiptURL,
|
||||||
|
this.status,
|
||||||
|
this.amount});
|
||||||
|
|
||||||
|
factory Payment.fromMap(Map<String, dynamic> map, String id) {
|
||||||
|
var _paymentDate = (map['payment_date'] as Timestamp);
|
||||||
|
return Payment(
|
||||||
|
id: id,
|
||||||
|
paymentDate: _paymentDate?.toDate(),
|
||||||
|
paymentReceiptURL: map['payment_receipt_url'],
|
||||||
|
status: map['status'],
|
||||||
|
amount: map['amount']?.toDouble() ?? 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
"id": id,
|
||||||
|
"invoice_id": invoiceID,
|
||||||
|
'payment_date': paymentDate?.toUtc()?.toIso8601String(),
|
||||||
|
'payment_receipt_url': paymentReceiptURL,
|
||||||
|
'status': status,
|
||||||
|
'amount': amount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Payment clone() {
|
||||||
|
return Payment.fromMap(toMap(), this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) => other is Payment && other.id == id;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => id.hashCode;
|
||||||
|
}
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
|
||||||
|
|
||||||
class Receipt {
|
class Receipt {
|
||||||
String id;
|
String id;
|
||||||
int amount;
|
int amount;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class Shipment {
|
|||||||
int numberOfPackage;
|
int numberOfPackage;
|
||||||
int weight;
|
int weight;
|
||||||
double handlingFee;
|
double handlingFee;
|
||||||
|
double paidHandlingFee;
|
||||||
String address;
|
String address;
|
||||||
String status;
|
String status;
|
||||||
bool isCourier;
|
bool isCourier;
|
||||||
@@ -31,6 +32,7 @@ class Shipment {
|
|||||||
String fcsShipmentID;
|
String fcsShipmentID;
|
||||||
String fcsShipmentNumber;
|
String fcsShipmentNumber;
|
||||||
String shipmentLabelUrl;
|
String shipmentLabelUrl;
|
||||||
|
bool isSelected;
|
||||||
|
|
||||||
Shipment(
|
Shipment(
|
||||||
{this.id,
|
{this.id,
|
||||||
@@ -44,6 +46,7 @@ class Shipment {
|
|||||||
this.numberOfPackage,
|
this.numberOfPackage,
|
||||||
this.weight,
|
this.weight,
|
||||||
this.handlingFee,
|
this.handlingFee,
|
||||||
|
this.paidHandlingFee,
|
||||||
this.address,
|
this.address,
|
||||||
this.status,
|
this.status,
|
||||||
this.pickupDate,
|
this.pickupDate,
|
||||||
@@ -89,6 +92,7 @@ class Shipment {
|
|||||||
pickupUserName: map['pickup_user_name'],
|
pickupUserName: map['pickup_user_name'],
|
||||||
pickupUserPhoneNumber: map['pickup_user_phone_number'],
|
pickupUserPhoneNumber: map['pickup_user_phone_number'],
|
||||||
handlingFee: map['handling_fee'],
|
handlingFee: map['handling_fee'],
|
||||||
|
paidHandlingFee: map['paid_handling_fee'],
|
||||||
fcsShipmentID: map['fcs_shipment_id'],
|
fcsShipmentID: map['fcs_shipment_id'],
|
||||||
fcsShipmentNumber: map['fcs_shipment_number'],
|
fcsShipmentNumber: map['fcs_shipment_number'],
|
||||||
shipmentLabelUrl: map['shipment_label_url'],
|
shipmentLabelUrl: map['shipment_label_url'],
|
||||||
@@ -96,7 +100,7 @@ class Shipment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
Map<String, dynamic> toMap() {
|
||||||
List _boxes = boxes.map((l) => l.toMap()).toList();
|
List _boxes = boxes?.map((l) => l.toMap())?.toList() ?? [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"id": id,
|
"id": id,
|
||||||
@@ -116,6 +120,12 @@ class Shipment {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) => other is Shipment && other.id == id;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => id.hashCode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'PickUp{id:$id, userName:$userName,phoneNumber:$phoneNumber,numberOfPackage:$numberOfPackage,weight:$weight,status:$status}';
|
return 'PickUp{id:$id, userName:$userName,phoneNumber:$phoneNumber,numberOfPackage:$numberOfPackage,weight:$weight,status:$status}';
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
@@ -5,9 +6,11 @@ import 'package:device_info/device_info.dart';
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:fcs/domain/vo/status.dart';
|
import 'package:fcs/domain/vo/status.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
|
||||||
import '../config.dart';
|
import '../config.dart';
|
||||||
import 'dev_info.dart';
|
import 'dev_info.dart';
|
||||||
|
import 'firebase_helper.dart';
|
||||||
|
|
||||||
final log = Logger('requestAPI');
|
final log = Logger('requestAPI');
|
||||||
|
|
||||||
@@ -38,7 +41,7 @@ Future<dynamic> requestAPI(
|
|||||||
method: method,
|
method: method,
|
||||||
baseUrl: url == null ? Config.instance.apiURL : url,
|
baseUrl: url == null ? Config.instance.apiURL : url,
|
||||||
connectTimeout: 10000,
|
connectTimeout: 10000,
|
||||||
receiveTimeout: 10000,
|
receiveTimeout: 60000,
|
||||||
headers: headers,
|
headers: headers,
|
||||||
);
|
);
|
||||||
log.info("baseUrl:${options.baseUrl}, path:$path");
|
log.info("baseUrl:${options.baseUrl}, path:$path");
|
||||||
@@ -146,3 +149,92 @@ Future<dynamic> requestDownloadPDFAPI(String path, method,
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef OnDownloadDone(File file);
|
||||||
|
// request makes http request
|
||||||
|
// if token is null
|
||||||
|
Future<dynamic> requestDownload(String path, method,
|
||||||
|
{dynamic payload,
|
||||||
|
String token,
|
||||||
|
String url,
|
||||||
|
String filePath,
|
||||||
|
OnDownloadDone onDownloadDone}) async {
|
||||||
|
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
||||||
|
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
||||||
|
String deviceName = "${androidInfo.model}(${androidInfo.id})";
|
||||||
|
log.info("device:${androidInfo.androidId},deviceName:$deviceName");
|
||||||
|
|
||||||
|
var bytes = utf8.encode(payload);
|
||||||
|
var base64Str = base64.encode(bytes);
|
||||||
|
String escapePayload = HtmlEscape().convert(base64Str);
|
||||||
|
|
||||||
|
try {
|
||||||
|
String baseUrl = url == null ? Config.instance.apiURL : url;
|
||||||
|
log.info("Path:$baseUrl$path");
|
||||||
|
HttpClient client = new HttpClient();
|
||||||
|
// var _downloadData = StringBuffer();
|
||||||
|
var request = await client.postUrl(Uri.parse("$baseUrl$path"));
|
||||||
|
request.headers.set("Project-ID", Config.instance.reportProjectID);
|
||||||
|
if (token != null) {
|
||||||
|
request.headers.set("Token", token);
|
||||||
|
}
|
||||||
|
if (androidInfo.androidId != null) {
|
||||||
|
request.headers.set("Device", androidInfo.androidId + ":" + deviceName);
|
||||||
|
}
|
||||||
|
request.headers
|
||||||
|
.set(HttpHeaders.contentTypeHeader, 'application/json; charset=utf-8');
|
||||||
|
request.headers.set("payload", escapePayload);
|
||||||
|
request.write(payload);
|
||||||
|
// request.write(escapePayload);
|
||||||
|
var response = await request.close();
|
||||||
|
print("headers:${response.headers}");
|
||||||
|
var _downloadData = List<int>();
|
||||||
|
var cd = response.headers.value("content-disposition");
|
||||||
|
String fileName = "download.csv";
|
||||||
|
if (cd != null && cd.contains("filename=")) {
|
||||||
|
fileName = cd.substring(cd.indexOf("=") + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var file = filePath + "/" + fileName;
|
||||||
|
var fileSave = new File(filePath + "/" + fileName);
|
||||||
|
response.listen((d) => _downloadData.addAll(d), onDone: () async {
|
||||||
|
await fileSave.writeAsBytes(_downloadData);
|
||||||
|
if (onDownloadDone != null) {
|
||||||
|
onDownloadDone(fileSave);
|
||||||
|
}
|
||||||
|
|
||||||
|
// final message = await OpenFile.open(file);
|
||||||
|
// log.info("Open file result:${message.message}");
|
||||||
|
// if (message.message != "done") {
|
||||||
|
// throw Exception(message.message);
|
||||||
|
// }
|
||||||
|
// await Share.file(fileName, fileName, _downloadData,
|
||||||
|
// downloadType == DownloadType.CSV ? "text/csv" : "application/pdf");
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
e.toString();
|
||||||
|
log.warning("path:$path, api:$e");
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<File> downloadPDF(
|
||||||
|
String templateName, Map<String, dynamic> values) async {
|
||||||
|
final completer = Completer<File>();
|
||||||
|
|
||||||
|
final directory = await getApplicationSupportDirectory();
|
||||||
|
String path = ('${directory.path}');
|
||||||
|
log.info("download file path:$path");
|
||||||
|
|
||||||
|
var data = {"template_name": templateName, "data": values};
|
||||||
|
print("payload:$data");
|
||||||
|
|
||||||
|
await requestDownload("/api/pdf", "POST",
|
||||||
|
filePath: path,
|
||||||
|
url: Config.instance.reportURL,
|
||||||
|
token: await getToken(),
|
||||||
|
payload: jsonEncode(data), onDownloadDone: (f) async {
|
||||||
|
completer.complete(f);
|
||||||
|
});
|
||||||
|
return completer.future;
|
||||||
|
}
|
||||||
|
|||||||
12
lib/helpers/cache_mgr.dart
Normal file
12
lib/helpers/cache_mgr.dart
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||||
|
|
||||||
|
class PdfCacheMgr {
|
||||||
|
static const key = 'pdfs';
|
||||||
|
static CacheManager pdfs = CacheManager(
|
||||||
|
Config(
|
||||||
|
key,
|
||||||
|
stalePeriod: const Duration(days: 7),
|
||||||
|
maxNrOfCacheObjects: 20,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -89,14 +89,16 @@ class _CartonEditorState extends State<CartonEditor> {
|
|||||||
_user = User(fcsID: _carton.fcsID, name: _carton.userName);
|
_user = User(fcsID: _carton.fcsID, name: _carton.userName);
|
||||||
_loadPackages();
|
_loadPackages();
|
||||||
} else {
|
} else {
|
||||||
_carton = Carton(cargoTypes: [], packages: [], cartons: []);
|
_carton = Carton(
|
||||||
|
cargoTypes: [],
|
||||||
|
packages: [],
|
||||||
|
);
|
||||||
_lengthController.text = "12";
|
_lengthController.text = "12";
|
||||||
_widthController.text = "12";
|
_widthController.text = "12";
|
||||||
_heightController.text = "12";
|
_heightController.text = "12";
|
||||||
_isNew = true;
|
_isNew = true;
|
||||||
_selectedCartonType = carton_from_packages;
|
_selectedCartonType = carton_from_packages;
|
||||||
_loadFcsShipments();
|
_loadFcsShipments();
|
||||||
_loadMixCartons();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,16 +114,6 @@ class _CartonEditorState extends State<CartonEditor> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_loadMixCartons() async {
|
|
||||||
if (_fcsShipment == null) return;
|
|
||||||
CartonModel cartonModel = Provider.of<CartonModel>(context, listen: false);
|
|
||||||
var mixCartons =
|
|
||||||
await cartonModel.getMixCartonsByFcsShipment(_fcsShipment.id);
|
|
||||||
setState(() {
|
|
||||||
_mixCartons = mixCartons;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_loadPackages() async {
|
_loadPackages() async {
|
||||||
if (_user == null) return;
|
if (_user == null) return;
|
||||||
PackageModel packageModel =
|
PackageModel packageModel =
|
||||||
@@ -159,19 +151,6 @@ class _CartonEditorState extends State<CartonEditor> {
|
|||||||
_populateDeliveryAddress();
|
_populateDeliveryAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
_loadCartons() async {
|
|
||||||
if (_fcsShipment == null) return;
|
|
||||||
CartonModel cartonModel = Provider.of<CartonModel>(context, listen: false);
|
|
||||||
List<Carton> cartons =
|
|
||||||
await cartonModel.getCartonsByFcsShipment(_fcsShipment.id);
|
|
||||||
cartons.forEach((c) {
|
|
||||||
c.isChecked = true;
|
|
||||||
});
|
|
||||||
setState(() {
|
|
||||||
_carton.cartons = cartons;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_populateDeliveryAddress() {
|
_populateDeliveryAddress() {
|
||||||
if (_carton.packages == null) return;
|
if (_carton.packages == null) return;
|
||||||
var d = _carton.packages
|
var d = _carton.packages
|
||||||
@@ -183,6 +162,18 @@ class _CartonEditorState extends State<CartonEditor> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_loadMixCartons() async {
|
||||||
|
if (_fcsShipment == null || _fcsShipment.id == null) return;
|
||||||
|
if (_selectedCartonType != carton_small_bag) return;
|
||||||
|
|
||||||
|
CartonModel cartonModel = Provider.of<CartonModel>(context, listen: false);
|
||||||
|
List<Carton> cartons =
|
||||||
|
await cartonModel.getMixCartonsByFcsShipment(_fcsShipment.id);
|
||||||
|
setState(() {
|
||||||
|
_mixCartons = cartons;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_calShipmentWeight() {
|
_calShipmentWeight() {
|
||||||
double l = double.parse(_lengthController.text, (s) => 0);
|
double l = double.parse(_lengthController.text, (s) => 0);
|
||||||
double w = double.parse(_widthController.text, (s) => 0);
|
double w = double.parse(_widthController.text, (s) => 0);
|
||||||
@@ -217,12 +208,7 @@ class _CartonEditorState extends State<CartonEditor> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_fcsShipment = v;
|
_fcsShipment = v;
|
||||||
});
|
});
|
||||||
if (_selectedCartonType == carton_mix_box) {
|
|
||||||
_loadCartons();
|
|
||||||
}
|
|
||||||
if (_selectedCartonType == carton_small_bag) {
|
|
||||||
_loadMixCartons();
|
_loadMixCartons();
|
||||||
}
|
|
||||||
},
|
},
|
||||||
labelKey: "shipment.pack.fcs.shipment",
|
labelKey: "shipment.pack.fcs.shipment",
|
||||||
iconData: Ionicons.ios_airplane,
|
iconData: Ionicons.ios_airplane,
|
||||||
@@ -318,13 +304,9 @@ class _CartonEditorState extends State<CartonEditor> {
|
|||||||
values: boxModel.cartonTypes,
|
values: boxModel.cartonTypes,
|
||||||
selectedValue: _selectedCartonType,
|
selectedValue: _selectedCartonType,
|
||||||
callback: (v) {
|
callback: (v) {
|
||||||
print(v);
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedCartonType = v;
|
_selectedCartonType = v;
|
||||||
});
|
});
|
||||||
if (_selectedCartonType == carton_mix_box) {
|
|
||||||
_loadCartons();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final cargoTableTitleBox = LocalTitle(
|
final cargoTableTitleBox = LocalTitle(
|
||||||
@@ -507,9 +489,6 @@ class _CartonEditorState extends State<CartonEditor> {
|
|||||||
carton.width = w;
|
carton.width = w;
|
||||||
carton.height = h;
|
carton.height = h;
|
||||||
carton.deliveryAddress = _deliveryAddress;
|
carton.deliveryAddress = _deliveryAddress;
|
||||||
carton.cartons = _carton.cartons == null
|
|
||||||
? []
|
|
||||||
: _carton.cartons.where((c) => c.isChecked).toList();
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:fcs/domain/constants.dart';
|
import 'package:fcs/domain/constants.dart';
|
||||||
import 'package:fcs/domain/entities/cargo_type.dart';
|
|
||||||
import 'package:fcs/domain/entities/carton.dart';
|
import 'package:fcs/domain/entities/carton.dart';
|
||||||
import 'package:fcs/domain/entities/package.dart';
|
import 'package:fcs/domain/entities/package.dart';
|
||||||
import 'package:fcs/domain/vo/delivery_address.dart';
|
import 'package:fcs/domain/vo/delivery_address.dart';
|
||||||
@@ -23,7 +22,6 @@ import 'package:provider/provider.dart';
|
|||||||
|
|
||||||
import 'carton_cargo_table.dart';
|
import 'carton_cargo_table.dart';
|
||||||
import 'carton_editor.dart';
|
import 'carton_editor.dart';
|
||||||
import 'carton_mix_table.dart';
|
|
||||||
import 'carton_package_table.dart';
|
import 'carton_package_table.dart';
|
||||||
import 'model/carton_model.dart';
|
import 'model/carton_model.dart';
|
||||||
import 'widgets.dart';
|
import 'widgets.dart';
|
||||||
@@ -41,11 +39,6 @@ class CartonInfo extends StatefulWidget {
|
|||||||
class _CartonInfoState extends State<CartonInfo> {
|
class _CartonInfoState extends State<CartonInfo> {
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
Carton _box;
|
Carton _box;
|
||||||
String _selectedCartonType;
|
|
||||||
List<Package> _packages = [];
|
|
||||||
List<Carton> _mixBoxes = [];
|
|
||||||
Carton _selectedShipmentBox = new Carton();
|
|
||||||
List<CargoType> _cargoTypes = [];
|
|
||||||
DeliveryAddress _deliveryAddress = new DeliveryAddress();
|
DeliveryAddress _deliveryAddress = new DeliveryAddress();
|
||||||
TextEditingController _widthController = new TextEditingController();
|
TextEditingController _widthController = new TextEditingController();
|
||||||
TextEditingController _heightController = new TextEditingController();
|
TextEditingController _heightController = new TextEditingController();
|
||||||
@@ -63,7 +56,6 @@ class _CartonInfoState extends State<CartonInfo> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_box = widget.box;
|
_box = widget.box;
|
||||||
_selectedCartonType = _box.cartonType;
|
|
||||||
|
|
||||||
//for shipment weight
|
//for shipment weight
|
||||||
volumetricRatio = Provider.of<ShipmentRateModel>(context, listen: false)
|
volumetricRatio = Provider.of<ShipmentRateModel>(context, listen: false)
|
||||||
@@ -81,7 +73,6 @@ class _CartonInfoState extends State<CartonInfo> {
|
|||||||
_widthController.text = _box.width.toString();
|
_widthController.text = _box.width.toString();
|
||||||
_heightController.text = _box.height.toString();
|
_heightController.text = _box.height.toString();
|
||||||
_lengthController.text = _box.length.toString();
|
_lengthController.text = _box.length.toString();
|
||||||
_cargoTypes = _box.cargoTypes;
|
|
||||||
_deliveryAddress = _box.deliveryAddress;
|
_deliveryAddress = _box.deliveryAddress;
|
||||||
isMixBox = _box.cartonType == carton_mix_box;
|
isMixBox = _box.cartonType == carton_mix_box;
|
||||||
isFromShipments = _box.cartonType == carton_from_shipments;
|
isFromShipments = _box.cartonType == carton_from_shipments;
|
||||||
@@ -101,7 +92,8 @@ class _CartonInfoState extends State<CartonInfo> {
|
|||||||
List<Package> packages = await packageModel.getPackages(_box.userID, [
|
List<Package> packages = await packageModel.getPackages(_box.userID, [
|
||||||
package_processed_status,
|
package_processed_status,
|
||||||
package_packed_status,
|
package_packed_status,
|
||||||
package_shipped_status
|
package_shipped_status,
|
||||||
|
package_delivered_status
|
||||||
]);
|
]);
|
||||||
packages = packages.where((p) => _box.packageIDs.contains(p.id)).toList();
|
packages = packages.where((p) => _box.packageIDs.contains(p.id)).toList();
|
||||||
packages.forEach((p) {
|
packages.forEach((p) {
|
||||||
@@ -155,38 +147,6 @@ class _CartonInfoState extends State<CartonInfo> {
|
|||||||
iconData: Icons.person,
|
iconData: Icons.person,
|
||||||
);
|
);
|
||||||
|
|
||||||
final shipmentBoxTitle = Container(
|
|
||||||
padding: EdgeInsets.only(left: 15, right: 10.0, top: 20),
|
|
||||||
child: Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Expanded(
|
|
||||||
child:
|
|
||||||
LocalText(context, 'box.shipment_number', color: Colors.grey),
|
|
||||||
),
|
|
||||||
LocalText(context, 'box.shipment.desc', color: Colors.grey),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final shipmentBoxRow = Container(
|
|
||||||
padding: EdgeInsets.only(left: 15.0, right: 10.0, top: 5.0, bottom: 5.0),
|
|
||||||
child: Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Expanded(
|
|
||||||
child: new Text(
|
|
||||||
_selectedShipmentBox.shipmentNumber == null
|
|
||||||
? ""
|
|
||||||
: _selectedShipmentBox.shipmentNumber,
|
|
||||||
style: textStyle,
|
|
||||||
)),
|
|
||||||
new Text(
|
|
||||||
_selectedShipmentBox.desc == null ? "" : _selectedShipmentBox.desc,
|
|
||||||
style: textStyle,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final lengthBox = LengthPicker(
|
final lengthBox = LengthPicker(
|
||||||
controller: _lengthController,
|
controller: _lengthController,
|
||||||
lableKey: "box.length",
|
lableKey: "box.length",
|
||||||
@@ -276,30 +236,6 @@ class _CartonInfoState extends State<CartonInfo> {
|
|||||||
packages: _box.packages,
|
packages: _box.packages,
|
||||||
)
|
)
|
||||||
: Container(),
|
: Container(),
|
||||||
isFromPackages
|
|
||||||
? Container()
|
|
||||||
: isFromShipments
|
|
||||||
? Column(
|
|
||||||
children: [
|
|
||||||
LocalTitle(textKey: "box.shipment.boxes"),
|
|
||||||
shipmentBoxTitle,
|
|
||||||
Divider(
|
|
||||||
color: Colors.grey[400],
|
|
||||||
),
|
|
||||||
shipmentBoxRow
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: Container(),
|
|
||||||
// : _selectedCartonType == "Mix carton"
|
|
||||||
// ? CartonMixTable(
|
|
||||||
// cartons: _box.cartons,
|
|
||||||
// onSelect: (c, check) {
|
|
||||||
// setState(() {
|
|
||||||
// c.isChecked = check;
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// )
|
|
||||||
// : Container(),
|
|
||||||
isMixBox ? Container() : LocalTitle(textKey: "box.cargo.type"),
|
isMixBox ? Container() : LocalTitle(textKey: "box.cargo.type"),
|
||||||
isMixBox ? Container() : cargoTableBox,
|
isMixBox ? Container() : cargoTableBox,
|
||||||
...(isFromPackages
|
...(isFromPackages
|
||||||
|
|||||||
@@ -179,14 +179,15 @@ class CartonModel extends BaseModel {
|
|||||||
String path = "/$cartons_collection";
|
String path = "/$cartons_collection";
|
||||||
var querySnap = await Firestore.instance
|
var querySnap = await Firestore.instance
|
||||||
.collection(path)
|
.collection(path)
|
||||||
// .where("fcs_shipment_id", isEqualTo: fcsShipmentID)
|
.where("fcs_shipment_id", isEqualTo: fcsShipmentID)
|
||||||
.where("user_id", isEqualTo: userID)
|
.where("user_id", isEqualTo: userID)
|
||||||
.where("is_deleted", isEqualTo: false)
|
.where("is_deleted", isEqualTo: false)
|
||||||
.where("is_invoiced", isEqualTo: false)
|
.where("is_invoiced", isEqualTo: false)
|
||||||
.getDocuments();
|
.getDocuments();
|
||||||
return querySnap.documents
|
List<Carton> cartons = querySnap.documents
|
||||||
.map((e) => Carton.fromMap(e.data, e.documentID))
|
.map((e) => Carton.fromMap(e.data, e.documentID))
|
||||||
.toList();
|
.toList();
|
||||||
|
return cartons;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Carton>> getMixCartonsByFcsShipment(String fcsShipmentID) async {
|
Future<List<Carton>> getMixCartonsByFcsShipment(String fcsShipmentID) async {
|
||||||
|
|||||||
@@ -99,7 +99,8 @@ class _DeliveryInfoState extends State<DeliveryInfo> {
|
|||||||
List<Package> packages = await packageModel.getPackages(_box.userID, [
|
List<Package> packages = await packageModel.getPackages(_box.userID, [
|
||||||
package_processed_status,
|
package_processed_status,
|
||||||
package_packed_status,
|
package_packed_status,
|
||||||
package_shipped_status
|
package_shipped_status,
|
||||||
|
package_delivered_status
|
||||||
]);
|
]);
|
||||||
packages = packages.where((p) => _box.packageIDs.contains(p.id)).toList();
|
packages = packages.where((p) => _box.packageIDs.contains(p.id)).toList();
|
||||||
packages.forEach((p) {
|
packages.forEach((p) {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import 'discount_editor.dart';
|
|||||||
class DiscountList extends StatefulWidget {
|
class DiscountList extends StatefulWidget {
|
||||||
final bool selectionMode;
|
final bool selectionMode;
|
||||||
|
|
||||||
const DiscountList({Key key, this.selectionMode}) : super(key: key);
|
const DiscountList({Key key, this.selectionMode = false}) : super(key: key);
|
||||||
@override
|
@override
|
||||||
_DiscountListState createState() => _DiscountListState();
|
_DiscountListState createState() => _DiscountListState();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,9 +49,9 @@ class DiscountModel extends BaseModel {
|
|||||||
.orderBy("code", descending: false)
|
.orderBy("code", descending: false)
|
||||||
.snapshots()
|
.snapshots()
|
||||||
.listen((snaps) {
|
.listen((snaps) {
|
||||||
discounts.clear();
|
_discounts.clear();
|
||||||
snaps.documents.forEach((d) {
|
snaps.documents.forEach((d) {
|
||||||
discounts.add(Discount.fromMap(d.data, d.documentID));
|
_discounts.add(Discount.fromMap(d.data, d.documentID));
|
||||||
});
|
});
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
});
|
});
|
||||||
@@ -73,6 +73,27 @@ class DiscountModel extends BaseModel {
|
|||||||
return paginator;
|
return paginator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<Discount>> getDiscount(String userID) async {
|
||||||
|
String path = "/$discounts_collection";
|
||||||
|
try {
|
||||||
|
var q = Firestore.instance
|
||||||
|
.collection("$path")
|
||||||
|
.where("customer_id", isEqualTo: userID)
|
||||||
|
.where("status", isEqualTo: "available");
|
||||||
|
var snaps = await q.getDocuments(source: Source.server);
|
||||||
|
List<Discount> discounts = snaps.documents.map((snap) {
|
||||||
|
if (snap.exists) {
|
||||||
|
var s = Discount.fromMap(snap.data, snap.documentID);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}).toList();
|
||||||
|
return discounts;
|
||||||
|
} catch (e) {
|
||||||
|
log.warning("Error!! $e");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> loadMore() async {
|
Future<void> loadMore() async {
|
||||||
if (_used.ended || _selectedIndex == 1) return;
|
if (_used.ended || _selectedIndex == 1) return;
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:fcs/pages/main/util.dart';
|
|||||||
import 'package:fcs/pages/widgets/display_text.dart';
|
import 'package:fcs/pages/widgets/display_text.dart';
|
||||||
import 'package:fcs/pages/widgets/local_button.dart';
|
import 'package:fcs/pages/widgets/local_button.dart';
|
||||||
import 'package:fcs/pages/widgets/local_text.dart';
|
import 'package:fcs/pages/widgets/local_text.dart';
|
||||||
|
import 'package:fcs/pages/widgets/pdf_screen.dart';
|
||||||
import 'package:fcs/pages/widgets/popupmenu.dart';
|
import 'package:fcs/pages/widgets/popupmenu.dart';
|
||||||
import 'package:fcs/pages/widgets/progress.dart';
|
import 'package:fcs/pages/widgets/progress.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
@@ -200,13 +201,8 @@ class _FcsShipmentInfoState extends State<FcsShipmentInfo> {
|
|||||||
return PopupMenuButton<PopupMenu>(
|
return PopupMenuButton<PopupMenu>(
|
||||||
elevation: 3.2,
|
elevation: 3.2,
|
||||||
icon: Icon(Icons.more_vert, color: primaryColor),
|
icon: Icon(Icons.more_vert, color: primaryColor),
|
||||||
tooltip: 'This is tooltip',
|
|
||||||
onSelected: (choice) {
|
onSelected: (choice) {
|
||||||
print(choice.id);
|
_showPDF(choice.id);
|
||||||
if (choice.id == 1) {
|
|
||||||
} else if (choice.id == 2) {
|
|
||||||
} else if (choice.id == 3) {
|
|
||||||
} else if (choice.id == 4) {}
|
|
||||||
},
|
},
|
||||||
itemBuilder: (BuildContext context) {
|
itemBuilder: (BuildContext context) {
|
||||||
return menuPopup.map((PopupMenu choice) {
|
return menuPopup.map((PopupMenu choice) {
|
||||||
@@ -252,4 +248,39 @@ class _FcsShipmentInfoState extends State<FcsShipmentInfo> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_showPDF(int id) async {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = true;
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
var reportName = "";
|
||||||
|
if (id == 1) {
|
||||||
|
reportName = "commercial_invoice";
|
||||||
|
} else if (id == 2) {
|
||||||
|
reportName = "packing_list";
|
||||||
|
} else if (id == 3) {
|
||||||
|
reportName = "dms";
|
||||||
|
} else if (id == 4) {
|
||||||
|
reportName = "manifest";
|
||||||
|
}
|
||||||
|
_fcsShipment.reportName = reportName;
|
||||||
|
|
||||||
|
FcsShipmentModel fcsShipmentModel =
|
||||||
|
Provider.of<FcsShipmentModel>(context, listen: false);
|
||||||
|
String url = await fcsShipmentModel.report(_fcsShipment);
|
||||||
|
Navigator.of(context).push(CupertinoPageRoute(
|
||||||
|
builder: (context) => PDFScreen(
|
||||||
|
title: "",
|
||||||
|
url: url,
|
||||||
|
)));
|
||||||
|
// Navigator.pop(context, true);
|
||||||
|
} catch (e) {
|
||||||
|
showMsgDialog(context, "Error", e.toString());
|
||||||
|
} finally {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -172,4 +172,8 @@ class FcsShipmentModel extends BaseModel {
|
|||||||
Future<void> ship(FcsShipment fcsShipment) {
|
Future<void> ship(FcsShipment fcsShipment) {
|
||||||
return Services.instance.fcsShipmentService.ship(fcsShipment);
|
return Services.instance.fcsShipmentService.ship(fcsShipment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<String> report(FcsShipment fcsShipment) {
|
||||||
|
return Services.instance.fcsShipmentService.report(fcsShipment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:fcs/domain/entities/carton.dart';
|
import 'package:fcs/domain/entities/carton.dart';
|
||||||
import 'package:fcs/domain/entities/package.dart';
|
|
||||||
import 'package:fcs/domain/entities/rate.dart';
|
import 'package:fcs/domain/entities/rate.dart';
|
||||||
import 'package:fcs/helpers/theme.dart';
|
import 'package:fcs/helpers/theme.dart';
|
||||||
import 'package:fcs/pages/widgets/local_text.dart';
|
import 'package:fcs/pages/widgets/local_text.dart';
|
||||||
@@ -20,7 +19,7 @@ class InvoiceCartonTable extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final tableTitle = Container(
|
final tableTitle = Container(
|
||||||
padding: EdgeInsets.only(right: 10.0, top: 20),
|
padding: EdgeInsets.only(right: 10.0, top: 5),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
SizedBox(
|
SizedBox(
|
||||||
@@ -76,10 +75,6 @@ class InvoiceCartonTable extends StatelessWidget {
|
|||||||
p.value.cartonNumber,
|
p.value.cartonNumber,
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
),
|
),
|
||||||
Text(
|
|
||||||
p.value.shipmentNumber ?? "",
|
|
||||||
style: textStyle,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
Flexible(
|
Flexible(
|
||||||
88
lib/pages/invoice/editor/invoice_discount_list.dart
Normal file
88
lib/pages/invoice/editor/invoice_discount_list.dart
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import 'package:fcs/domain/entities/discount.dart';
|
||||||
|
import 'package:fcs/helpers/theme.dart';
|
||||||
|
import 'package:fcs/pages/widgets/local_text.dart';
|
||||||
|
import 'package:fcs/pages/widgets/my_data_table.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class InvoiceDiscountList extends StatelessWidget {
|
||||||
|
final List<Discount> discounts;
|
||||||
|
|
||||||
|
const InvoiceDiscountList({
|
||||||
|
Key key,
|
||||||
|
this.discounts,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
centerTitle: true,
|
||||||
|
leading: new IconButton(
|
||||||
|
icon: new Icon(CupertinoIcons.back),
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
),
|
||||||
|
backgroundColor: primaryColor,
|
||||||
|
title: LocalText(
|
||||||
|
context,
|
||||||
|
"invoice.shipment.discount.title",
|
||||||
|
fontSize: 20,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: table(context),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget table(BuildContext context) {
|
||||||
|
return MyDataTable(
|
||||||
|
headingRowHeight: 40,
|
||||||
|
columns: [
|
||||||
|
MyDataColumn(
|
||||||
|
label: LocalText(
|
||||||
|
context,
|
||||||
|
"discount.code",
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
MyDataColumn(
|
||||||
|
label: LocalText(
|
||||||
|
context,
|
||||||
|
"discount.amount",
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
rows: getRows(context),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<MyDataRow> getRows(BuildContext context) {
|
||||||
|
if (discounts == null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
var rows = discounts.map((c) {
|
||||||
|
return MyDataRow(
|
||||||
|
onSelectChanged: (value) => Navigator.pop(context, c),
|
||||||
|
cells: [
|
||||||
|
MyDataCell(new Text(
|
||||||
|
c.code ?? "",
|
||||||
|
style: textStyle,
|
||||||
|
)),
|
||||||
|
MyDataCell(
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text(c.amount?.toStringAsFixed(2) ?? "0", style: textStyle),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
452
lib/pages/invoice/editor/invoice_editor.dart
Normal file
452
lib/pages/invoice/editor/invoice_editor.dart
Normal file
@@ -0,0 +1,452 @@
|
|||||||
|
import 'package:fcs/domain/entities/cargo_type.dart';
|
||||||
|
import 'package:fcs/domain/entities/carton.dart';
|
||||||
|
import 'package:fcs/domain/entities/custom_duty.dart';
|
||||||
|
import 'package:fcs/domain/entities/discount.dart';
|
||||||
|
import 'package:fcs/domain/entities/fcs_shipment.dart';
|
||||||
|
import 'package:fcs/domain/entities/invoice.dart';
|
||||||
|
import 'package:fcs/domain/entities/payment_method.dart';
|
||||||
|
import 'package:fcs/domain/entities/shipment.dart';
|
||||||
|
import 'package:fcs/domain/entities/user.dart';
|
||||||
|
import 'package:fcs/helpers/theme.dart';
|
||||||
|
import 'package:fcs/pages/carton/model/carton_model.dart';
|
||||||
|
import 'package:fcs/pages/discount/model/discount_model.dart';
|
||||||
|
import 'package:fcs/pages/invoice/editor/invoice_carton_table.dart';
|
||||||
|
import 'package:fcs/pages/invoice/editor/invoice_discount_list.dart';
|
||||||
|
import 'package:fcs/pages/invoice/editor/invoice_handling_fee_list.dart';
|
||||||
|
import 'package:fcs/pages/invoice/invoice_table.dart';
|
||||||
|
import 'package:fcs/pages/invoice/model/invoice_model.dart';
|
||||||
|
import 'package:fcs/pages/main/model/main_model.dart';
|
||||||
|
import 'package:fcs/pages/main/util.dart';
|
||||||
|
import 'package:fcs/pages/payment_methods/model/payment_method_model.dart';
|
||||||
|
import 'package:fcs/pages/rates/custom_list.dart';
|
||||||
|
import 'package:fcs/pages/rates/model/shipment_rate_model.dart';
|
||||||
|
import 'package:fcs/pages/shipment/model/shipment_model.dart';
|
||||||
|
import 'package:fcs/pages/widgets/display_text.dart';
|
||||||
|
import 'package:fcs/pages/widgets/fcs_icons.dart';
|
||||||
|
import 'package:fcs/pages/widgets/local_button.dart';
|
||||||
|
import 'package:fcs/pages/widgets/local_dropdown.dart';
|
||||||
|
import 'package:fcs/pages/widgets/local_popup_menu_button.dart';
|
||||||
|
import 'package:fcs/pages/widgets/local_popupmenu.dart';
|
||||||
|
import 'package:fcs/pages/widgets/local_text.dart';
|
||||||
|
import 'package:fcs/pages/widgets/progress.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_icons/flutter_icons.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class InvoiceEditor extends StatefulWidget {
|
||||||
|
final Invoice invoice;
|
||||||
|
final User customer;
|
||||||
|
final FcsShipment fcsShipment;
|
||||||
|
InvoiceEditor({this.invoice, this.customer, this.fcsShipment});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_InvoiceEditorState createState() => _InvoiceEditorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _InvoiceEditorState extends State<InvoiceEditor> {
|
||||||
|
var dateFormatter = new DateFormat('dd MMM yyyy');
|
||||||
|
|
||||||
|
Invoice _invoice;
|
||||||
|
bool _isLoading = false;
|
||||||
|
bool _isNew;
|
||||||
|
User _user;
|
||||||
|
|
||||||
|
bool _showCartons = false;
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_isNew = widget.invoice == null;
|
||||||
|
|
||||||
|
if (widget.invoice != null) {
|
||||||
|
_invoice = widget.invoice;
|
||||||
|
} else {
|
||||||
|
_invoice = Invoice(
|
||||||
|
customDuties: [],
|
||||||
|
cartons: [],
|
||||||
|
shipments: [],
|
||||||
|
invoiceDate: DateTime.now());
|
||||||
|
}
|
||||||
|
_user = widget.customer;
|
||||||
|
_loadAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadAll() async {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = true;
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await _loadCartons();
|
||||||
|
await _loadShipments();
|
||||||
|
await _loadDiscount();
|
||||||
|
} catch (e) {} finally {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadCartons() async {
|
||||||
|
CartonModel cartonModel = Provider.of<CartonModel>(context, listen: false);
|
||||||
|
List<Carton> cartons = await cartonModel.getCartonsForInvoice(
|
||||||
|
widget.fcsShipment.id, widget.customer.id);
|
||||||
|
cartons.forEach((c) {
|
||||||
|
c.isChecked = true;
|
||||||
|
});
|
||||||
|
setState(() {
|
||||||
|
_invoice.cartons = cartons;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadShipments() async {
|
||||||
|
ShipmentModel shipmentModel =
|
||||||
|
Provider.of<ShipmentModel>(context, listen: false);
|
||||||
|
List<Shipment> shipments = await shipmentModel.getShipmentWithHandlingFee(
|
||||||
|
widget.fcsShipment.id, widget.customer.id);
|
||||||
|
shipments.forEach((s) {
|
||||||
|
s.isSelected = true;
|
||||||
|
});
|
||||||
|
setState(() {
|
||||||
|
_invoice.shipments = shipments;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Discount> discounts = [];
|
||||||
|
_loadDiscount() async {
|
||||||
|
DiscountModel discountModel =
|
||||||
|
Provider.of<DiscountModel>(context, listen: false);
|
||||||
|
discounts = await discountModel.getDiscount(widget.customer.id);
|
||||||
|
if (discounts != null && discounts.length > 0) {
|
||||||
|
setState(() {
|
||||||
|
_invoice.discount = discounts.first;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var mainModel = Provider.of<MainModel>(context);
|
||||||
|
var paymentMethodModel = Provider.of<PaymentMethodModel>(context);
|
||||||
|
var rateModel = Provider.of<ShipmentRateModel>(context);
|
||||||
|
var rate = rateModel.rate;
|
||||||
|
|
||||||
|
final invoiceNumberBox = DisplayText(
|
||||||
|
labelTextKey: 'invoice.number',
|
||||||
|
iconData: FontAwesomeIcons.fileInvoice,
|
||||||
|
text: _invoice?.invoiceNumber ?? "");
|
||||||
|
final statusBox = DisplayText(
|
||||||
|
text: _invoice?.status ?? "",
|
||||||
|
iconData: Icons.av_timer,
|
||||||
|
labelTextKey: 'invoice.status');
|
||||||
|
final cartonTable = InvoiceCartonTable(
|
||||||
|
cartons: _invoice.cartons,
|
||||||
|
rate: rate,
|
||||||
|
onSelect: (c, checked) {
|
||||||
|
setState(() {
|
||||||
|
c.isChecked = checked;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
final paymentTypesBox = LocalDropdown<PaymentMethod>(
|
||||||
|
callback: (v) {
|
||||||
|
setState(() {
|
||||||
|
_invoice.paymentMethod = v;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
labelKey: "invoice.payment_method",
|
||||||
|
iconData: FontAwesome.money,
|
||||||
|
display: (u) => u.name,
|
||||||
|
selectedValue: _invoice.paymentMethod,
|
||||||
|
values: paymentMethodModel.paymentMethods,
|
||||||
|
);
|
||||||
|
final invoiceTableBox = InvoiceTable(
|
||||||
|
invoice: _invoice,
|
||||||
|
rate: rate,
|
||||||
|
deliveryFeeSelected: (selected) {
|
||||||
|
setState(() {
|
||||||
|
if (selected) {
|
||||||
|
_invoice.deliveryFee = rate.deliveryFee;
|
||||||
|
} else {
|
||||||
|
_invoice.deliveryFee = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
discountSelected: (discount) {
|
||||||
|
setState(() {
|
||||||
|
_invoice.discount = discount;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onRemove: (i) {
|
||||||
|
if (i.invoiceDataType == InvoiceDataType.CustomFeeDataType) {
|
||||||
|
_removeCustom(i.data);
|
||||||
|
}
|
||||||
|
if (i.invoiceDataType == InvoiceDataType.DiscountDataType) {
|
||||||
|
setState(() {
|
||||||
|
_invoice.discount = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (i.invoiceDataType == InvoiceDataType.DeliveryFeeType) {
|
||||||
|
setState(() {
|
||||||
|
_invoice.deliveryFee = 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (i.invoiceDataType == InvoiceDataType.HandlingFeeType) {
|
||||||
|
setState(() {
|
||||||
|
_removeShipment(i.data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
final toggleButtonsBox = ToggleButtons(
|
||||||
|
color: Colors.black45,
|
||||||
|
selectedColor: Colors.black45,
|
||||||
|
disabledColor: Colors.grey,
|
||||||
|
selectedBorderColor: primaryColor,
|
||||||
|
borderColor: Colors.transparent,
|
||||||
|
fillColor: Colors.transparent,
|
||||||
|
highlightColor: Colors.black45,
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(cartonIconData),
|
||||||
|
],
|
||||||
|
onPressed: (int index) {
|
||||||
|
setState(() {
|
||||||
|
_showCartons = !_showCartons;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isSelected: [_showCartons],
|
||||||
|
);
|
||||||
|
final popupMenu = LocalPopupMenuButton(
|
||||||
|
buttonIcon: Icons.add_circle,
|
||||||
|
selectable: false,
|
||||||
|
buttonColor: Colors.black45,
|
||||||
|
popmenus: [
|
||||||
|
LocalPopupMenu(
|
||||||
|
id: 1,
|
||||||
|
textKey: "invoice.add.custom.fee.menu",
|
||||||
|
),
|
||||||
|
LocalPopupMenu(
|
||||||
|
id: 2,
|
||||||
|
textKey: "invoice.add.handling.fee.menu",
|
||||||
|
),
|
||||||
|
LocalPopupMenu(
|
||||||
|
id: 3,
|
||||||
|
textKey: "invoice.add.discount.menu",
|
||||||
|
),
|
||||||
|
LocalPopupMenu(
|
||||||
|
id: 4,
|
||||||
|
textKey: "invoice.delivery_fee",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
popupMenuCallback: (p) async {
|
||||||
|
if (p.id == 1) {
|
||||||
|
CustomDuty customDuty = await Navigator.of(context).push(
|
||||||
|
CupertinoPageRoute(
|
||||||
|
builder: (context) => CustomList(selected: true)));
|
||||||
|
_addCustom(customDuty);
|
||||||
|
} else if (p.id == 2) {
|
||||||
|
Shipment shipment = await Navigator.of(context).push(
|
||||||
|
CupertinoPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
InvoiceHandlingFeeList(shipments: _invoice.shipments)));
|
||||||
|
_addShipment(shipment);
|
||||||
|
} else if (p.id == 3) {
|
||||||
|
Discount discount =
|
||||||
|
await Navigator.of(context).push(CupertinoPageRoute(
|
||||||
|
builder: (context) => InvoiceDiscountList(
|
||||||
|
discounts: discounts,
|
||||||
|
)));
|
||||||
|
if (discount != null) {
|
||||||
|
setState(() {
|
||||||
|
_invoice.discount = discount;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (p.id == 4) {
|
||||||
|
setState(() {
|
||||||
|
_invoice.deliveryFee = rate.deliveryFee;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final headerBox = Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(dateFormatter.format(_invoice.invoiceDate)),
|
||||||
|
SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
Text(_user?.name ?? ""),
|
||||||
|
Text(
|
||||||
|
_user?.fcsID ?? "",
|
||||||
|
style: TextStyle(fontSize: 12),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Spacer(),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
toggleButtonsBox,
|
||||||
|
popupMenu,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
final createBtn = LocalButton(
|
||||||
|
textKey: "invoice.issue.btn",
|
||||||
|
callBack: _save,
|
||||||
|
);
|
||||||
|
|
||||||
|
return LocalProgress(
|
||||||
|
inAsyncCall: _isLoading,
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
centerTitle: true,
|
||||||
|
leading: new IconButton(
|
||||||
|
icon: new Icon(CupertinoIcons.back, color: primaryColor),
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
shadowColor: Colors.transparent,
|
||||||
|
title: LocalText(context, 'invoice.form.title',
|
||||||
|
color: primaryColor, fontSize: 20),
|
||||||
|
),
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: ListView(
|
||||||
|
children: <Widget>[
|
||||||
|
headerBox,
|
||||||
|
_isNew ? Container() : invoiceNumberBox,
|
||||||
|
_isNew ? Container() : statusBox,
|
||||||
|
_showCartons ? cartonTable : Container(),
|
||||||
|
_showCartons
|
||||||
|
? Divider(
|
||||||
|
color: primaryColor,
|
||||||
|
thickness: 2,
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
invoiceTableBox,
|
||||||
|
SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
paymentTypesBox,
|
||||||
|
SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
_isNew
|
||||||
|
? createBtn
|
||||||
|
: mainModel.isCustomer()
|
||||||
|
? Container()
|
||||||
|
: Container(
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
fcsButton(context,
|
||||||
|
getLocalString(context, 'invoice.btn_save'))
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
_isNew
|
||||||
|
? Container()
|
||||||
|
: fcsButton(context,
|
||||||
|
getLocalString(context, 'invoice.btn_payment_receipt'))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_addCustom(CustomDuty customDuty) {
|
||||||
|
if (customDuty == null) return;
|
||||||
|
setState(() {
|
||||||
|
_invoice.customDuties.remove(customDuty);
|
||||||
|
_invoice.customDuties.add(customDuty);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_addShipment(Shipment shipment) {
|
||||||
|
if (shipment == null) return;
|
||||||
|
shipment.isSelected = true;
|
||||||
|
setState(() {
|
||||||
|
_invoice.shipments.remove(shipment);
|
||||||
|
_invoice.shipments.add(shipment);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_removeShipment(Shipment shipment) {
|
||||||
|
if (shipment == null) return;
|
||||||
|
shipment.isSelected = false;
|
||||||
|
setState(() {
|
||||||
|
_invoice.shipments.remove(shipment);
|
||||||
|
_invoice.shipments.add(shipment);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_removeCustom(CustomDuty customDuty) {
|
||||||
|
setState(() {
|
||||||
|
_invoice.customDuties.remove(customDuty);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_save() async {
|
||||||
|
var rateModel = Provider.of<ShipmentRateModel>(context, listen: false);
|
||||||
|
double amount = _invoice.getNetAmount(rateModel.rate);
|
||||||
|
if (_invoice.paymentMethod == null) {
|
||||||
|
showMsgDialog(context, "Error", "Payment method required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<CargoType> cargoTypes = _invoice.getCargoTypes(rateModel.rate);
|
||||||
|
if (cargoTypes == null || cargoTypes.length == 0) {
|
||||||
|
showMsgDialog(context, "Error", "Expected at least one cargo type");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((amount ?? 0) <= 0) {
|
||||||
|
showMsgDialog(context, "Error", "Expected positive amount");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_isLoading = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
InvoiceModel invoiceModel =
|
||||||
|
Provider.of<InvoiceModel>(context, listen: false);
|
||||||
|
|
||||||
|
Invoice invoice = Invoice();
|
||||||
|
invoice.cargoTypes = cargoTypes;
|
||||||
|
invoice.amount = amount;
|
||||||
|
invoice.handlingFee = _invoice.getHandlingFee();
|
||||||
|
invoice.cartons = _invoice.cartons.where((c) => c.isChecked).toList();
|
||||||
|
invoice.shipments =
|
||||||
|
_invoice.shipments.where((s) => s.isSelected).toList();
|
||||||
|
invoice.discount = _invoice.discount;
|
||||||
|
invoice.deliveryFee = _invoice.deliveryFee;
|
||||||
|
|
||||||
|
invoice.userID = widget.customer.id;
|
||||||
|
invoice.fcsShipmentID = widget.fcsShipment.id;
|
||||||
|
invoice.invoiceDate = _invoice.invoiceDate;
|
||||||
|
invoice.paymentMethod = _invoice.paymentMethod;
|
||||||
|
invoice.customDuties = _invoice.customDuties;
|
||||||
|
|
||||||
|
await invoiceModel.createInvoice(invoice);
|
||||||
|
Navigator.pop(context, true);
|
||||||
|
} catch (e) {
|
||||||
|
showMsgDialog(context, "Error", e.toString());
|
||||||
|
} finally {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,38 +1,60 @@
|
|||||||
import 'package:fcs/domain/entities/custom_duty.dart';
|
import 'package:fcs/domain/entities/shipment.dart';
|
||||||
import 'package:fcs/helpers/theme.dart';
|
import 'package:fcs/helpers/theme.dart';
|
||||||
import 'package:fcs/pages/widgets/local_text.dart';
|
import 'package:fcs/pages/widgets/local_text.dart';
|
||||||
import 'package:fcs/pages/widgets/my_data_table.dart';
|
import 'package:fcs/pages/widgets/my_data_table.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
typedef OnAdd(CustomDuty customDuty);
|
typedef OnAdd(Shipment shipment);
|
||||||
typedef OnRemove(CustomDuty customDuty);
|
typedef OnRemove(Shipment shipment);
|
||||||
|
|
||||||
class InvoiceCustomTable extends StatelessWidget {
|
class InvoiceHandlingFeeList extends StatelessWidget {
|
||||||
final List<CustomDuty> customDuties;
|
final List<Shipment> shipments;
|
||||||
final OnAdd onAdd;
|
final OnAdd onAdd;
|
||||||
final OnRemove onRemove;
|
final OnRemove onRemove;
|
||||||
|
|
||||||
const InvoiceCustomTable(
|
const InvoiceHandlingFeeList(
|
||||||
{Key key, this.customDuties, this.onAdd, this.onRemove})
|
{Key key, this.shipments, this.onAdd, this.onRemove})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
centerTitle: true,
|
||||||
|
leading: new IconButton(
|
||||||
|
icon: new Icon(CupertinoIcons.back),
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
),
|
||||||
|
backgroundColor: primaryColor,
|
||||||
|
title: LocalText(
|
||||||
|
context,
|
||||||
|
"invoice.shipment.handling.fee.title",
|
||||||
|
fontSize: 20,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: table(context),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget table(BuildContext context) {
|
||||||
return MyDataTable(
|
return MyDataTable(
|
||||||
headingRowHeight: 40,
|
headingRowHeight: 40,
|
||||||
columns: [
|
columns: [
|
||||||
MyDataColumn(
|
MyDataColumn(
|
||||||
label: LocalText(
|
label: LocalText(
|
||||||
context,
|
context,
|
||||||
"rate.cutom.product_type",
|
"invoice.shipment.number",
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
MyDataColumn(
|
MyDataColumn(
|
||||||
label: LocalText(
|
label: LocalText(
|
||||||
context,
|
context,
|
||||||
"rate.custom.fee",
|
"invoice.add.handling.fee.menu",
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -42,23 +64,23 @@ class InvoiceCustomTable extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<MyDataRow> getRows(BuildContext context) {
|
List<MyDataRow> getRows(BuildContext context) {
|
||||||
if (customDuties == null) {
|
if (shipments == null) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
double total = 0;
|
var rows = shipments.map((c) {
|
||||||
var rows = customDuties.map((c) {
|
|
||||||
total += c.fee;
|
|
||||||
return MyDataRow(
|
return MyDataRow(
|
||||||
|
onSelectChanged: (value) => Navigator.pop(context, c),
|
||||||
cells: [
|
cells: [
|
||||||
MyDataCell(new Text(
|
MyDataCell(new Text(
|
||||||
c.productType == null ? "" : c.productType,
|
c.shipmentNumber ?? "",
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
)),
|
)),
|
||||||
MyDataCell(
|
MyDataCell(
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
Text(c.fee == null ? "0" : c.fee.toString(), style: textStyle),
|
Text(c.handlingFee?.toStringAsFixed(2) ?? "0",
|
||||||
|
style: textStyle),
|
||||||
onRemove == null
|
onRemove == null
|
||||||
? SizedBox(
|
? SizedBox(
|
||||||
width: 50,
|
width: 50,
|
||||||
@@ -78,30 +100,6 @@ class InvoiceCustomTable extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
var totalRow = MyDataRow(
|
|
||||||
onSelectChanged: (bool selected) {},
|
|
||||||
cells: [
|
|
||||||
MyDataCell(Align(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: LocalText(
|
|
||||||
context,
|
|
||||||
"invoice.total_custom_fee",
|
|
||||||
color: Colors.black87,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
MyDataCell(
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 48.0),
|
|
||||||
child: Align(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: Text(total.toString(),
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
rows.add(totalRow);
|
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,350 +0,0 @@
|
|||||||
import 'package:fcs/domain/entities/cargo_type.dart';
|
|
||||||
import 'package:fcs/domain/entities/discount.dart';
|
|
||||||
import 'package:fcs/domain/entities/invoice.dart';
|
|
||||||
import 'package:fcs/domain/entities/rate.dart';
|
|
||||||
import 'package:fcs/helpers/theme.dart';
|
|
||||||
import 'package:fcs/pages/discount/discount_list.dart';
|
|
||||||
import 'package:fcs/pages/main/util.dart';
|
|
||||||
import 'package:fcs/pages/widgets/local_text.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
|
|
||||||
typedef OnDiscountSelected(Discount discount);
|
|
||||||
typedef OnDeliveryFeeSelected(bool selected);
|
|
||||||
final formatter = new NumberFormat("#,###.00");
|
|
||||||
|
|
||||||
class InvoiceCargoTable extends StatelessWidget {
|
|
||||||
final Invoice invoice;
|
|
||||||
final Rate rate;
|
|
||||||
final OnDiscountSelected discountSelected;
|
|
||||||
final OnDeliveryFeeSelected deliveryFeeSelected;
|
|
||||||
|
|
||||||
const InvoiceCargoTable(
|
|
||||||
{Key key,
|
|
||||||
this.invoice,
|
|
||||||
this.discountSelected,
|
|
||||||
this.deliveryFeeSelected,
|
|
||||||
this.rate})
|
|
||||||
: super(key: key);
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Column(children: getRows(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
getRows(BuildContext context) {
|
|
||||||
List<CargoType> _cargoTypes = invoice.getCargoTypes(rate);
|
|
||||||
double total = 0;
|
|
||||||
List<Widget> dataRow = _cargoTypes.map((cargo) {
|
|
||||||
var amount = cargo.calWeight * cargo.calRate;
|
|
||||||
total += amount;
|
|
||||||
return Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border(bottom: BorderSide(color: Colors.grey))),
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 5.0, right: 5.0, top: 15.0, bottom: 15.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 2, child: Text('${cargo.name}')),
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Text(
|
|
||||||
'${cargo.calWeight.toStringAsFixed(2)} x ${cargo.calRate.toStringAsFixed(2)}',
|
|
||||||
textAlign: TextAlign.center)),
|
|
||||||
Expanded(
|
|
||||||
child: Text('\$ ${amount.toStringAsFixed(2)}',
|
|
||||||
textAlign: TextAlign.end,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
)))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList();
|
|
||||||
dataRow.insert(
|
|
||||||
0,
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 5.0, right: 5.0, top: 15.0, bottom: 15.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border(bottom: BorderSide(color: Colors.grey))),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Text(getLocalString(context, 'invoice.box.cargo_type'),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.grey))),
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Text(
|
|
||||||
getLocalString(context, 'cargo.weight') +
|
|
||||||
' x ' +
|
|
||||||
getLocalString(context, 'cargo.rate'),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.grey))),
|
|
||||||
Expanded(
|
|
||||||
child: Text(getLocalString(context, 'invoice.amount'),
|
|
||||||
textAlign: TextAlign.end,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.grey)))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
dataRow.length,
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 5.0, right: 5.0, top: 10.0, bottom: 10.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 1,
|
|
||||||
child: Container(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: LocalText(
|
|
||||||
context,
|
|
||||||
'invoice.total',
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(width: 40),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
'\$ ${total.toStringAsFixed(2)}',
|
|
||||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
|
||||||
textAlign: TextAlign.end,
|
|
||||||
))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
dataRow.length,
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(left: 5.0, right: 5.0, top: 0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 1,
|
|
||||||
child: Container(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: LocalText(
|
|
||||||
context,
|
|
||||||
'invoice.discount_value',
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
new IconButton(
|
|
||||||
icon: Icon(Icons.search, color: primaryColor),
|
|
||||||
onPressed: () async {
|
|
||||||
Discount discount = await Navigator.of(context).push(
|
|
||||||
CupertinoPageRoute(
|
|
||||||
builder: (context) =>
|
|
||||||
DiscountList(selectionMode: true)));
|
|
||||||
if (discountSelected != null) {
|
|
||||||
discountSelected(discount);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
Expanded(
|
|
||||||
child:
|
|
||||||
Text('\$ ( ${invoice.getDiscount().toStringAsFixed(2)} )',
|
|
||||||
textAlign: TextAlign.end,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
)))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
dataRow.length,
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 5.0, right: 5.0, top: 10.0, bottom: 0.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 1,
|
|
||||||
child: Container(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: LocalText(
|
|
||||||
context,
|
|
||||||
'invoice.custom_fee',
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(width: 40),
|
|
||||||
Expanded(
|
|
||||||
child: Text('\$ ${invoice.getCustomFee().toStringAsFixed(2)}',
|
|
||||||
textAlign: TextAlign.end,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
dataRow.length,
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(left: 5.0, right: 5.0, top: 20.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 1,
|
|
||||||
child: Container(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: LocalText(
|
|
||||||
context,
|
|
||||||
'invoice.handling_fee',
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(width: 50),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
'\$ ${invoice.getHandlingFee().toStringAsFixed(2) ?? ""}',
|
|
||||||
textAlign: TextAlign.end,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
)))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
dataRow.length,
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 5.0, right: 5.0, top: 10.0, bottom: 10.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 1,
|
|
||||||
child: Container(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: LocalText(
|
|
||||||
context,
|
|
||||||
'invoice.delivery_fee',
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
Switch(
|
|
||||||
value: (invoice.deliveryFee ?? 0) > 0,
|
|
||||||
onChanged: (value) {
|
|
||||||
if (deliveryFeeSelected != null) {
|
|
||||||
deliveryFeeSelected(value);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
activeTrackColor: primaryColor.withOpacity(0.8),
|
|
||||||
activeColor: primaryColor,
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child:
|
|
||||||
Text('\$ ${invoice.getDeliveryFee().toStringAsFixed(2)}',
|
|
||||||
textAlign: TextAlign.end,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
)))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
dataRow.length,
|
|
||||||
Container(
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(child: Text('')),
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Divider(
|
|
||||||
thickness: 3,
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
)));
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
dataRow.length,
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 5.0, right: 5.0, top: 10.0, bottom: 10.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Center(
|
|
||||||
child: LocalText(
|
|
||||||
context,
|
|
||||||
'invoice.net_amount',
|
|
||||||
color: Colors.black,
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
'\$ ${invoice.getNetAmount(rate).toStringAsFixed(2)}',
|
|
||||||
textAlign: TextAlign.end,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: primaryColor)))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
dataRow.length,
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 5.0, right: 5.0, top: 10.0, bottom: 10.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Center(
|
|
||||||
child: LocalText(
|
|
||||||
context,
|
|
||||||
'invoice.balance',
|
|
||||||
color: Colors.black,
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
'\$ ${invoice.getTotalBalance(rate).toStringAsFixed(2)}',
|
|
||||||
textAlign: TextAlign.end,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: primaryColor)))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
return dataRow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@ import 'package:fcs/domain/entities/fcs_shipment.dart';
|
|||||||
import 'package:fcs/domain/entities/user.dart';
|
import 'package:fcs/domain/entities/user.dart';
|
||||||
import 'package:fcs/helpers/theme.dart';
|
import 'package:fcs/helpers/theme.dart';
|
||||||
import 'package:fcs/pages/customer/model/customer_model.dart';
|
import 'package:fcs/pages/customer/model/customer_model.dart';
|
||||||
import 'package:fcs/pages/invoice/invoice_editor.dart';
|
import 'package:fcs/pages/invoice/editor/invoice_editor.dart';
|
||||||
import 'package:fcs/pages/widgets/local_text.dart';
|
import 'package:fcs/pages/widgets/local_text.dart';
|
||||||
import 'package:fcs/pages/widgets/progress.dart';
|
import 'package:fcs/pages/widgets/progress.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
@@ -82,12 +82,15 @@ class _InvoiceCustomerListState extends State<InvoiceCustomerList> {
|
|||||||
|
|
||||||
Widget _item(User customer) {
|
Widget _item(User customer) {
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () {
|
onTap: () async {
|
||||||
Navigator.of(context).push(CupertinoPageRoute(
|
bool created = await Navigator.of(context).push(CupertinoPageRoute(
|
||||||
builder: (context) => InvoiceEditor(
|
builder: (context) => InvoiceEditor(
|
||||||
customer: customer,
|
customer: customer,
|
||||||
fcsShipment: widget.fcsShipment,
|
fcsShipment: widget.fcsShipment,
|
||||||
)));
|
)));
|
||||||
|
if (created ?? false) {
|
||||||
|
_load();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(left: 12.0, right: 12),
|
padding: const EdgeInsets.only(left: 12.0, right: 12),
|
||||||
|
|||||||
@@ -1,327 +0,0 @@
|
|||||||
import 'package:fcs/domain/entities/cargo_type.dart';
|
|
||||||
import 'package:fcs/domain/entities/carton.dart';
|
|
||||||
import 'package:fcs/domain/entities/custom_duty.dart';
|
|
||||||
import 'package:fcs/domain/entities/discount.dart';
|
|
||||||
import 'package:fcs/domain/entities/fcs_shipment.dart';
|
|
||||||
import 'package:fcs/domain/entities/invoice.dart';
|
|
||||||
import 'package:fcs/domain/entities/payment_method.dart';
|
|
||||||
import 'package:fcs/domain/entities/user.dart';
|
|
||||||
import 'package:fcs/helpers/theme.dart';
|
|
||||||
import 'package:fcs/pages/carton/model/carton_model.dart';
|
|
||||||
import 'package:fcs/pages/discount/discount_list.dart';
|
|
||||||
import 'package:fcs/pages/discount/model/discount_model.dart';
|
|
||||||
import 'package:fcs/pages/invoice/invoice_cargo_table.dart';
|
|
||||||
import 'package:fcs/pages/invoice/invoice_carton_table.dart';
|
|
||||||
import 'package:fcs/pages/main/model/main_model.dart';
|
|
||||||
import 'package:fcs/pages/main/util.dart';
|
|
||||||
import 'package:fcs/pages/payment_methods/model/payment_method_model.dart';
|
|
||||||
import 'package:fcs/pages/rates/custom_list.dart';
|
|
||||||
import 'package:fcs/pages/rates/model/shipment_rate_model.dart';
|
|
||||||
import 'package:fcs/pages/widgets/display_text.dart';
|
|
||||||
import 'package:fcs/pages/widgets/fcs_id_icon.dart';
|
|
||||||
import 'package:fcs/pages/widgets/local_dropdown.dart';
|
|
||||||
import 'package:fcs/pages/widgets/local_text.dart';
|
|
||||||
import 'package:fcs/pages/widgets/local_title.dart';
|
|
||||||
import 'package:fcs/pages/widgets/multi_img_controller.dart';
|
|
||||||
import 'package:fcs/pages/widgets/progress.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_icons/flutter_icons.dart';
|
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
|
|
||||||
import 'invoice_custom_table.dart';
|
|
||||||
|
|
||||||
class InvoiceEditor extends StatefulWidget {
|
|
||||||
final Invoice invoice;
|
|
||||||
final User customer;
|
|
||||||
final FcsShipment fcsShipment;
|
|
||||||
InvoiceEditor({this.invoice, this.customer, this.fcsShipment});
|
|
||||||
|
|
||||||
@override
|
|
||||||
_InvoiceEditorState createState() => _InvoiceEditorState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _InvoiceEditorState extends State<InvoiceEditor> {
|
|
||||||
User user;
|
|
||||||
|
|
||||||
var dateFormatter = new DateFormat('dd MMM yyyy');
|
|
||||||
TextEditingController _invoiceNumberController = new TextEditingController();
|
|
||||||
TextEditingController _dateController = new TextEditingController();
|
|
||||||
TextEditingController _nameController = new TextEditingController();
|
|
||||||
TextEditingController _phoneController = new TextEditingController();
|
|
||||||
TextEditingController _discountController = new TextEditingController();
|
|
||||||
TextEditingController _amountController = new TextEditingController();
|
|
||||||
TextEditingController _statusController = new TextEditingController();
|
|
||||||
TextEditingController _handlingFeeController = new TextEditingController();
|
|
||||||
TextEditingController _customFeeController = new TextEditingController();
|
|
||||||
MultiImgController multiImgController = MultiImgController();
|
|
||||||
TextEditingController _descriptionController = new TextEditingController();
|
|
||||||
TextEditingController _balanceController = new TextEditingController();
|
|
||||||
|
|
||||||
Invoice _invoice;
|
|
||||||
bool _isLoading = false;
|
|
||||||
List<Carton> _cartons = [];
|
|
||||||
bool isSwitched = false;
|
|
||||||
int deliveryfee = 0;
|
|
||||||
double customFee = 10.0;
|
|
||||||
double handlingFee = 10.0; // it will get from shipment
|
|
||||||
double total = 0;
|
|
||||||
Discount _discount;
|
|
||||||
bool _isNew = false;
|
|
||||||
Discount selectedDiscount;
|
|
||||||
int selectedDiscountAmt;
|
|
||||||
PaymentMethod _paymentMethod;
|
|
||||||
double volumetricRatio = 0;
|
|
||||||
|
|
||||||
List<Carton> selectedBoxes = [];
|
|
||||||
List<CustomDuty> customs = [];
|
|
||||||
|
|
||||||
// List<CargoType> _cargoTypes = [
|
|
||||||
// CargoType(name: 'General Cargo', weight: 33, rate: 6),
|
|
||||||
// CargoType(name: 'Medicine', weight: 33, rate: 7),
|
|
||||||
// CargoType(name: 'Dangerous Cargo', weight: 33, rate: 8)
|
|
||||||
// ];
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
volumetricRatio = Provider.of<ShipmentRateModel>(context, listen: false)
|
|
||||||
.rate
|
|
||||||
.volumetricRatio;
|
|
||||||
|
|
||||||
if (widget.invoice != null) {
|
|
||||||
_isNew = false;
|
|
||||||
_invoice = widget.invoice;
|
|
||||||
_invoiceNumberController.text = _invoice.invoiceNumber;
|
|
||||||
_dateController.text = dateFormatter.format(_invoice.invoiceDate);
|
|
||||||
_nameController.text = _invoice.customerName;
|
|
||||||
_phoneController.text = _invoice.customerPhoneNumber;
|
|
||||||
// _amountController.text = _invoice.getAmount.toString();
|
|
||||||
_amountController.text = _invoice.amount.toString();
|
|
||||||
_statusController.text = _invoice.status.toString();
|
|
||||||
_handlingFeeController.text = '0';
|
|
||||||
_customFeeController.text = '0';
|
|
||||||
// multiImgController.setImageUrls = _receipts;
|
|
||||||
_descriptionController.text = 'For Electronics goods';
|
|
||||||
_balanceController.text =
|
|
||||||
(_invoice.amount - _invoice.receipts[0].amount).toString();
|
|
||||||
// _boxes = _invoice.packages;
|
|
||||||
} else {
|
|
||||||
_isNew = true;
|
|
||||||
_dateController.text = dateFormatter.format(DateTime.now());
|
|
||||||
_amountController.text = '0';
|
|
||||||
_handlingFeeController.text = '0';
|
|
||||||
_customFeeController.text = '0';
|
|
||||||
_descriptionController.text = '';
|
|
||||||
_balanceController.text = '0';
|
|
||||||
_invoice = Invoice(customDuties: [], cartons: []);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (widget.customer != null && widget.invoice == null) {
|
|
||||||
user = widget.customer;
|
|
||||||
setState(() {
|
|
||||||
_isNew = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_loadCartons();
|
|
||||||
}
|
|
||||||
|
|
||||||
_loadCartons() async {
|
|
||||||
CartonModel cartonModel = Provider.of<CartonModel>(context, listen: false);
|
|
||||||
List<Carton> cartons = await cartonModel.getCartonsForInvoice(
|
|
||||||
widget.fcsShipment.id, widget.customer.id);
|
|
||||||
setState(() {
|
|
||||||
_cartons = cartons;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
var mainModel = Provider.of<MainModel>(context);
|
|
||||||
var discountModel = Provider.of<DiscountModel>(context);
|
|
||||||
var paymentMethodModel = Provider.of<PaymentMethodModel>(context);
|
|
||||||
var rateModel = Provider.of<ShipmentRateModel>(context);
|
|
||||||
var rate = rateModel.rate;
|
|
||||||
|
|
||||||
final nameBox = DisplayText(
|
|
||||||
iconData: Feather.user,
|
|
||||||
labelTextKey: 'invoice.customer_name',
|
|
||||||
text: user != null ? user.name : 'Ko Nyi');
|
|
||||||
|
|
||||||
final statusBox = DisplayText(
|
|
||||||
text: _statusController.text,
|
|
||||||
iconData: Icons.av_timer,
|
|
||||||
labelTextKey: 'invoice.status');
|
|
||||||
|
|
||||||
final fcsIDBox = DisplayText(
|
|
||||||
text: user != null ? user.fcsID : "FCS-KRUTUG",
|
|
||||||
labelTextKey: "box.fcs.id",
|
|
||||||
icon: FcsIDIcon(),
|
|
||||||
);
|
|
||||||
final cartonTable = InvoiceCartonTable(
|
|
||||||
cartons: _cartons,
|
|
||||||
rate: rate,
|
|
||||||
onSelect: (c, checked) {
|
|
||||||
setState(() {
|
|
||||||
c.isChecked = checked;
|
|
||||||
});
|
|
||||||
if (checked) {
|
|
||||||
_invoice.cartons.add(c);
|
|
||||||
} else {
|
|
||||||
_invoice.cartons.remove(c);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
final customTableHeaderBox = LocalTitle(
|
|
||||||
textKey: "invoice.custom_fee",
|
|
||||||
trailing: IconButton(
|
|
||||||
icon: Icon(Icons.add_circle, color: primaryColor),
|
|
||||||
onPressed: () async {
|
|
||||||
CustomDuty customDuty = await Navigator.of(context).push(
|
|
||||||
CupertinoPageRoute(
|
|
||||||
builder: (context) => CustomList(selected: true)));
|
|
||||||
_addCustom(customDuty);
|
|
||||||
}));
|
|
||||||
final customTableBox = InvoiceCustomTable(
|
|
||||||
customDuties: _invoice.customDuties,
|
|
||||||
onAdd: (c) => _addCustom(c),
|
|
||||||
onRemove: (c) => _removeCustom(c),
|
|
||||||
);
|
|
||||||
var paymentTypesBox = LocalDropdown<PaymentMethod>(
|
|
||||||
callback: (v) {
|
|
||||||
setState(() {
|
|
||||||
_paymentMethod = v;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
labelKey: "invoice.payment_method",
|
|
||||||
iconData: FontAwesome.money,
|
|
||||||
display: (u) => u.name,
|
|
||||||
selectedValue: _paymentMethod,
|
|
||||||
values: paymentMethodModel.paymentMethods,
|
|
||||||
);
|
|
||||||
final cargoTypeTableBox = InvoiceCargoTable(
|
|
||||||
invoice: _invoice,
|
|
||||||
rate: rate,
|
|
||||||
deliveryFeeSelected: (selected) {
|
|
||||||
setState(() {
|
|
||||||
if (selected) {
|
|
||||||
_invoice.deliveryFee = rate.deliveryFee;
|
|
||||||
} else {
|
|
||||||
_invoice.deliveryFee = 0;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
discountSelected: (discount) {
|
|
||||||
setState(() {
|
|
||||||
_invoice.discount = discount;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return LocalProgress(
|
|
||||||
inAsyncCall: _isLoading,
|
|
||||||
child: Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
centerTitle: true,
|
|
||||||
leading: new IconButton(
|
|
||||||
icon: new Icon(CupertinoIcons.back, color: primaryColor),
|
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
|
||||||
),
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
shadowColor: Colors.transparent,
|
|
||||||
title: LocalText(context, 'invoice.form.title',
|
|
||||||
color: primaryColor, fontSize: 20),
|
|
||||||
),
|
|
||||||
body: Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: ListView(
|
|
||||||
children: <Widget>[
|
|
||||||
LocalTitle(textKey: "invoice.customer_info"),
|
|
||||||
DisplayText(
|
|
||||||
labelTextKey: 'invoice.date',
|
|
||||||
iconData: Icons.date_range,
|
|
||||||
text: _dateController.text),
|
|
||||||
widget.invoice == null
|
|
||||||
? Container()
|
|
||||||
: DisplayText(
|
|
||||||
labelTextKey: 'invoice.number',
|
|
||||||
iconData: FontAwesomeIcons.fileInvoice,
|
|
||||||
text: _invoiceNumberController.text),
|
|
||||||
fcsIDBox,
|
|
||||||
nameBox,
|
|
||||||
_isNew ? Container() : statusBox,
|
|
||||||
SizedBox(height: 20),
|
|
||||||
customTableHeaderBox,
|
|
||||||
customTableBox,
|
|
||||||
SizedBox(height: 20),
|
|
||||||
cartonTable,
|
|
||||||
LocalTitle(textKey: "invoice.cargo_type"),
|
|
||||||
cargoTypeTableBox,
|
|
||||||
// Column(children: getCargoTableByBox(context)),
|
|
||||||
SizedBox(height: 20),
|
|
||||||
paymentTypesBox,
|
|
||||||
SizedBox(height: 20),
|
|
||||||
_isNew
|
|
||||||
? Container()
|
|
||||||
: LocalTitle(
|
|
||||||
textKey: "invoice.payment_attachment",
|
|
||||||
trailing: IconButton(
|
|
||||||
icon: Icon(Icons.add_circle, color: primaryColor),
|
|
||||||
onPressed: () async {})),
|
|
||||||
widget.invoice == null
|
|
||||||
? fcsButton(
|
|
||||||
context, getLocalString(context, 'invoice.btn_create'))
|
|
||||||
: mainModel.isCustomer()
|
|
||||||
? Container()
|
|
||||||
: Container(
|
|
||||||
child: Column(
|
|
||||||
children: <Widget>[
|
|
||||||
fcsButton(context,
|
|
||||||
getLocalString(context, 'invoice.btn_save'))
|
|
||||||
],
|
|
||||||
)),
|
|
||||||
_isNew
|
|
||||||
? Container()
|
|
||||||
: fcsButton(context,
|
|
||||||
getLocalString(context, 'invoice.btn_payment_receipt'))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getTotalBalance(total) {
|
|
||||||
double balance = 0;
|
|
||||||
double custom = customFee != 0 ? customFee.toDouble() : 0;
|
|
||||||
double discount = _discount != null ? _discount.amount.toDouble() : 0;
|
|
||||||
double deliveryFee = deliveryfee != 0 ? deliveryfee.toDouble() : 0;
|
|
||||||
balance = (total + custom + deliveryFee) - discount;
|
|
||||||
return balance;
|
|
||||||
}
|
|
||||||
|
|
||||||
_addCustom(CustomDuty customDuty) {
|
|
||||||
if (customDuty == null) return;
|
|
||||||
setState(() {
|
|
||||||
_invoice.customDuties.remove(customDuty);
|
|
||||||
_invoice.customDuties.add(customDuty);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_removeCustom(CustomDuty customDuty) {
|
|
||||||
setState(() {
|
|
||||||
_invoice.customDuties.remove(customDuty);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_save() {}
|
|
||||||
}
|
|
||||||
@@ -1,172 +1,61 @@
|
|||||||
|
import 'package:fcs/domain/constants.dart';
|
||||||
import 'package:fcs/domain/entities/carton.dart';
|
import 'package:fcs/domain/entities/carton.dart';
|
||||||
import 'package:fcs/domain/entities/cargo_type.dart';
|
|
||||||
import 'package:fcs/domain/entities/custom_duty.dart';
|
|
||||||
import 'package:fcs/domain/entities/discount.dart';
|
|
||||||
import 'package:fcs/domain/entities/invoice.dart';
|
import 'package:fcs/domain/entities/invoice.dart';
|
||||||
import 'package:fcs/domain/entities/payment_method.dart';
|
|
||||||
import 'package:fcs/domain/entities/user.dart';
|
|
||||||
import 'package:fcs/helpers/theme.dart';
|
import 'package:fcs/helpers/theme.dart';
|
||||||
import 'package:fcs/localization/app_translations.dart';
|
import 'package:fcs/pages/carton/model/carton_model.dart';
|
||||||
import 'package:fcs/pages/discount/discount_list.dart';
|
import 'package:fcs/pages/invoice/editor/invoice_carton_table.dart';
|
||||||
import 'package:fcs/pages/discount/model/discount_model.dart';
|
import 'package:fcs/pages/invoice/invoice_table.dart';
|
||||||
import 'package:fcs/pages/invoice/invoice_editor.dart';
|
import 'package:fcs/pages/invoice/model/invoice_model.dart';
|
||||||
import 'package:fcs/pages/main/model/language_model.dart';
|
import 'package:fcs/pages/invoice/widgets.dart';
|
||||||
import 'package:fcs/pages/main/model/main_model.dart';
|
|
||||||
import 'package:fcs/pages/main/util.dart';
|
import 'package:fcs/pages/main/util.dart';
|
||||||
import 'package:fcs/pages/payment_methods/model/payment_method_model.dart';
|
|
||||||
import 'package:fcs/pages/rates/custom_list.dart';
|
|
||||||
import 'package:fcs/pages/rates/model/shipment_rate_model.dart';
|
import 'package:fcs/pages/rates/model/shipment_rate_model.dart';
|
||||||
import 'package:fcs/pages/user_search/user_serach.dart';
|
|
||||||
import 'package:fcs/pages/widgets/bottom_up_page_route.dart';
|
|
||||||
import 'package:fcs/pages/widgets/discount_dropdown.dart';
|
|
||||||
import 'package:fcs/pages/widgets/display_text.dart';
|
import 'package:fcs/pages/widgets/display_text.dart';
|
||||||
import 'package:fcs/pages/widgets/fcs_id_icon.dart';
|
import 'package:fcs/pages/widgets/fcs_icons.dart';
|
||||||
import 'package:fcs/pages/widgets/input_text.dart';
|
import 'package:fcs/pages/widgets/local_button.dart';
|
||||||
import 'package:fcs/pages/widgets/local_dropdown.dart';
|
|
||||||
import 'package:fcs/pages/widgets/local_text.dart';
|
import 'package:fcs/pages/widgets/local_text.dart';
|
||||||
import 'package:fcs/pages/widgets/local_title.dart';
|
|
||||||
import 'package:fcs/pages/widgets/multi_img_controller.dart';
|
|
||||||
import 'package:fcs/pages/widgets/multi_img_file.dart';
|
|
||||||
import 'package:fcs/pages/widgets/my_data_table.dart';
|
|
||||||
import 'package:fcs/pages/widgets/progress.dart';
|
import 'package:fcs/pages/widgets/progress.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_icons/flutter_icons.dart';
|
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class InvoiceInfoPage extends StatefulWidget {
|
class InvoiceInfo extends StatefulWidget {
|
||||||
final Invoice invoice;
|
final Invoice invoice;
|
||||||
final User customer;
|
final bool forCustomer;
|
||||||
InvoiceInfoPage({this.invoice, this.customer});
|
InvoiceInfo({this.invoice, this.forCustomer});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_InvoiceInfoPageState createState() => _InvoiceInfoPageState();
|
_InvoiceInfoState createState() => _InvoiceInfoState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _InvoiceInfoPageState extends State<InvoiceInfoPage> {
|
class _InvoiceInfoState extends State<InvoiceInfo> {
|
||||||
User user;
|
|
||||||
|
|
||||||
var dateFormatter = new DateFormat('dd MMM yyyy');
|
var dateFormatter = new DateFormat('dd MMM yyyy');
|
||||||
TextEditingController _invoiceNumberController = new TextEditingController();
|
|
||||||
TextEditingController _dateController = new TextEditingController();
|
|
||||||
TextEditingController _nameController = new TextEditingController();
|
|
||||||
TextEditingController _phoneController = new TextEditingController();
|
|
||||||
TextEditingController _discountController = new TextEditingController();
|
|
||||||
TextEditingController _amountController = new TextEditingController();
|
|
||||||
TextEditingController _statusController = new TextEditingController();
|
|
||||||
TextEditingController _customFeeController = new TextEditingController();
|
|
||||||
MultiImgController multiImgController = MultiImgController();
|
|
||||||
TextEditingController _descriptionController = new TextEditingController();
|
|
||||||
TextEditingController _balanceController = new TextEditingController();
|
|
||||||
|
|
||||||
Invoice _invoice;
|
Invoice _invoice;
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
List<Carton> _boxes = [];
|
|
||||||
bool isSwitched = false;
|
|
||||||
int deliveryfee = 0;
|
|
||||||
double customFee = 0;
|
|
||||||
double total = 0;
|
|
||||||
Discount _discount;
|
|
||||||
bool isNew = false;
|
|
||||||
Discount selectedDiscount;
|
|
||||||
int selectedDiscountAmt;
|
|
||||||
PaymentMethod paymentMethod;
|
|
||||||
double volumetricRatio = 0;
|
|
||||||
|
|
||||||
List<Carton> selectedBoxes = [];
|
|
||||||
List<CustomDuty> customs = [];
|
|
||||||
|
|
||||||
List<CargoType> _cargoTypes = [
|
|
||||||
CargoType(id: "1", name: 'General Cargo', weight: 33, rate: 6),
|
|
||||||
CargoType(id: "2", name: 'Medicine', weight: 33, rate: 7),
|
|
||||||
CargoType(id: "3", name: 'Dangerous Cargo', weight: 33, rate: 8)
|
|
||||||
];
|
|
||||||
|
|
||||||
List<String> _receipts = [
|
|
||||||
"assets/buying_online_with_first_last_name.png",
|
|
||||||
];
|
|
||||||
|
|
||||||
|
bool _showCartons = false;
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
volumetricRatio = Provider.of<ShipmentRateModel>(context, listen: false)
|
|
||||||
.rate
|
|
||||||
.volumetricRatio;
|
|
||||||
|
|
||||||
if (widget.invoice != null) {
|
|
||||||
_invoice = widget.invoice;
|
_invoice = widget.invoice;
|
||||||
_invoiceNumberController.text = _invoice.invoiceNumber;
|
_invoice.shipments?.forEach((s) {
|
||||||
_dateController.text = dateFormatter.format(_invoice.invoiceDate);
|
s.isSelected = true;
|
||||||
_nameController.text = _invoice.customerName;
|
|
||||||
_phoneController.text = _invoice.customerPhoneNumber;
|
|
||||||
// _amountController.text = _invoice.getAmount.toString();
|
|
||||||
_amountController.text = _invoice.amount.toString();
|
|
||||||
_statusController.text = _invoice.status.toString();
|
|
||||||
_customFeeController.text = '0';
|
|
||||||
// multiImgController.setImageUrls = _receipts;
|
|
||||||
_descriptionController.text = 'For Electronics goods';
|
|
||||||
_balanceController.text =
|
|
||||||
(_invoice.amount - _invoice.receipts[0].amount).toString();
|
|
||||||
// _boxes = _invoice.packages;
|
|
||||||
} else {
|
|
||||||
_dateController.text = dateFormatter.format(DateTime.now());
|
|
||||||
_amountController.text = '0';
|
|
||||||
_customFeeController.text = '0';
|
|
||||||
_descriptionController.text = '';
|
|
||||||
_balanceController.text = '0';
|
|
||||||
setState(() {
|
|
||||||
isNew = true;
|
|
||||||
});
|
});
|
||||||
|
_loadCartons();
|
||||||
}
|
}
|
||||||
|
|
||||||
_boxes = [
|
_loadCartons() async {
|
||||||
Carton(
|
CartonModel cartonModel = Provider.of<CartonModel>(context, listen: false);
|
||||||
shipmentNumber: "A202",
|
List<Carton> cartons = [];
|
||||||
receiverNumber: "3",
|
for (var c in _invoice?.cartons ?? []) {
|
||||||
receiverName: "Ko Myo Min",
|
var _carton = await cartonModel.getCarton(c.id);
|
||||||
boxNumber: "1",
|
_carton.isChecked = true;
|
||||||
rate: 7,
|
cartons.add(_carton);
|
||||||
packageType: "General",
|
}
|
||||||
weight: 75,
|
setState(() {
|
||||||
status: "Packed",
|
_invoice.cartons = cartons;
|
||||||
receiverAddress: '1 Bo Yar Nyunt St.\nDagon Tsp, Yangon',
|
});
|
||||||
cargoDesc: "Clothes",
|
|
||||||
arrivedDate: DateTime(2020, 6, 1),
|
|
||||||
width: 10,
|
|
||||||
height: 10,
|
|
||||||
length: 10,
|
|
||||||
// packages: packages,
|
|
||||||
// statusHistory: statusHistory,
|
|
||||||
cargoTypes: [
|
|
||||||
CargoType(name: 'General Cargo', weight: 25),
|
|
||||||
CargoType(name: 'Medicine', weight: 20),
|
|
||||||
CargoType(name: 'Dangerous Cargo', weight: 30)
|
|
||||||
]),
|
|
||||||
Carton(
|
|
||||||
shipmentNumber: "A202",
|
|
||||||
receiverNumber: "3",
|
|
||||||
receiverName: "Ko Myo Min",
|
|
||||||
boxNumber: "2",
|
|
||||||
rate: 7,
|
|
||||||
packageType: "General",
|
|
||||||
weight: 75,
|
|
||||||
status: "Packed",
|
|
||||||
cargoDesc: "Clothes",
|
|
||||||
arrivedDate: DateTime(2020, 6, 1),
|
|
||||||
width: 10,
|
|
||||||
height: 10,
|
|
||||||
length: 10,
|
|
||||||
// statusHistory: statusHistory,
|
|
||||||
// packages: packages,
|
|
||||||
receiverAddress: '1 Bo Yar Nyunt St.\nDagon Tsp, Yangon',
|
|
||||||
cargoTypes: [
|
|
||||||
CargoType(name: 'General Cargo', weight: 25),
|
|
||||||
CargoType(name: 'Medicine', weight: 20),
|
|
||||||
CargoType(name: 'Dangerous Cargo', weight: 30)
|
|
||||||
])
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -176,21 +65,88 @@ class _InvoiceInfoPageState extends State<InvoiceInfoPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final nameBox = DisplayText(
|
bool isCanceled = _invoice.status == invoice_cancel_status;
|
||||||
iconData: Feather.user,
|
bool isPaid = _invoice.status == invoice_paid_status;
|
||||||
labelTextKey: 'invoice.customer_name',
|
var rateModel = Provider.of<ShipmentRateModel>(context);
|
||||||
text: user != null ? user.name : 'Ko Nyi');
|
var rate = rateModel.rate;
|
||||||
|
|
||||||
final fcsIDBox = DisplayText(
|
final cartonTable = InvoiceCartonTable(
|
||||||
text: user != null ? user.fcsID : "FCS-KRUTUG",
|
cartons: _invoice.cartons,
|
||||||
labelTextKey: "box.fcs.id",
|
rate: rate,
|
||||||
icon: FcsIDIcon(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final statusBox = DisplayText(
|
final invoiceTableBox = InvoiceTable(
|
||||||
text: _statusController.text,
|
invoice: _invoice,
|
||||||
iconData: Icons.av_timer,
|
rate: rate,
|
||||||
labelTextKey: 'invoice.status');
|
deliveryFeeSelected: (selected) {
|
||||||
|
setState(() {
|
||||||
|
if (selected) {
|
||||||
|
_invoice.deliveryFee = rate.deliveryFee;
|
||||||
|
} else {
|
||||||
|
_invoice.deliveryFee = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
discountSelected: (discount) {
|
||||||
|
setState(() {
|
||||||
|
_invoice.discount = discount;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
final toggleButtonsBox = ToggleButtons(
|
||||||
|
color: Colors.black45,
|
||||||
|
selectedColor: Colors.black45,
|
||||||
|
disabledColor: Colors.grey,
|
||||||
|
selectedBorderColor: primaryColor,
|
||||||
|
borderColor: Colors.transparent,
|
||||||
|
fillColor: Colors.transparent,
|
||||||
|
highlightColor: Colors.black45,
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(cartonIconData),
|
||||||
|
],
|
||||||
|
onPressed: (int index) {
|
||||||
|
setState(() {
|
||||||
|
_showCartons = !_showCartons;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isSelected: [_showCartons],
|
||||||
|
);
|
||||||
|
|
||||||
|
final headerBox = Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(dateFormatter.format(_invoice.invoiceDate)),
|
||||||
|
SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
Text(_invoice?.userName ?? ""),
|
||||||
|
Text(
|
||||||
|
_invoice?.fcsID ?? "",
|
||||||
|
style: TextStyle(fontSize: 12),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Spacer(),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
toggleButtonsBox,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
final paymentMethodBox = DisplayText(
|
||||||
|
labelTextKey: "invoice.payment_method",
|
||||||
|
text: _invoice.paymentMethod.name,
|
||||||
|
);
|
||||||
|
|
||||||
|
final cancelBtn = LocalButton(
|
||||||
|
textKey: "invoice.cancel.btn",
|
||||||
|
callBack: _cancel,
|
||||||
|
);
|
||||||
|
|
||||||
return LocalProgress(
|
return LocalProgress(
|
||||||
inAsyncCall: _isLoading,
|
inAsyncCall: _isLoading,
|
||||||
@@ -205,600 +161,59 @@ class _InvoiceInfoPageState extends State<InvoiceInfoPage> {
|
|||||||
shadowColor: Colors.transparent,
|
shadowColor: Colors.transparent,
|
||||||
title: LocalText(context, 'invoice.form.title',
|
title: LocalText(context, 'invoice.form.title',
|
||||||
color: primaryColor, fontSize: 20),
|
color: primaryColor, fontSize: 20),
|
||||||
actions: [
|
|
||||||
new IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
Icons.delete,
|
|
||||||
color: primaryColor,
|
|
||||||
),
|
|
||||||
onPressed: () {}),
|
|
||||||
new IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
Icons.edit,
|
|
||||||
color: primaryColor,
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).push(CupertinoPageRoute(
|
|
||||||
builder: (context) => InvoiceEditor(invoice: _invoice)));
|
|
||||||
})
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
body: Padding(
|
body: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
LocalTitle(textKey: "invoice.customer_info"),
|
getInvoiceStatus(context, _invoice),
|
||||||
DisplayText(
|
headerBox,
|
||||||
labelTextKey: 'invoice.date',
|
_showCartons ? cartonTable : Container(),
|
||||||
iconData: Icons.date_range,
|
_showCartons
|
||||||
text: _dateController.text),
|
? Divider(
|
||||||
widget.invoice == null
|
color: primaryColor,
|
||||||
? Container()
|
thickness: 2,
|
||||||
: DisplayText(
|
|
||||||
labelTextKey: 'invoice.number',
|
|
||||||
iconData: FontAwesomeIcons.fileInvoice,
|
|
||||||
text: _invoiceNumberController.text),
|
|
||||||
fcsIDBox,
|
|
||||||
nameBox,
|
|
||||||
statusBox,
|
|
||||||
LocalTitle(textKey: "invoice.box_info"),
|
|
||||||
Center(child: Column(children: getCartonRows(context))),
|
|
||||||
SizedBox(height: 20),
|
|
||||||
LocalTitle(textKey: "invoice.custom_fee"),
|
|
||||||
Column(children: getCustomFeeRows(context)),
|
|
||||||
SizedBox(height: 20),
|
|
||||||
LocalTitle(textKey: "invoice.cargo_type"),
|
|
||||||
Column(children: getCargoTableByBox(context)),
|
|
||||||
SizedBox(height: 20),
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.only(top: 5, left: 18),
|
|
||||||
child: Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Expanded(
|
|
||||||
child: LocalText(context, 'invoice.payment_method',
|
|
||||||
fontSize: 16,
|
|
||||||
color: Colors.grey,
|
|
||||||
fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'${paymentMethod != null ? paymentMethod.accountName : "KBZ Bank"}',
|
|
||||||
style: TextStyle(fontSize: 16)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 30),
|
|
||||||
!isNew
|
|
||||||
? LocalTitle(textKey: "invoice.payment_attachment")
|
|
||||||
: Container(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getCartonRows(BuildContext context) {
|
|
||||||
List<Widget> dataRow = [];
|
|
||||||
|
|
||||||
dataRow = _boxes.map((box) {
|
|
||||||
return Container(
|
|
||||||
height: 50,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border(bottom: BorderSide(color: Colors.grey))),
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.only(left: 5.0, right: 5.0, top: 5.0, bottom: 5.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 1,
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(box.packageNumber),
|
|
||||||
Text(box.shipmentNumber),
|
|
||||||
],
|
|
||||||
)),
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Text(
|
|
||||||
box.length == null
|
|
||||||
? ""
|
|
||||||
: box.length.toString() +
|
|
||||||
' x ' +
|
|
||||||
box.length.toString() +
|
|
||||||
' x ' +
|
|
||||||
box.height.toString(),
|
|
||||||
textAlign: TextAlign.center)),
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Center(
|
|
||||||
child: Text(
|
|
||||||
box.getShipmentWeight(volumetricRatio).toString()))),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList();
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
0,
|
|
||||||
Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border(bottom: BorderSide(color: Colors.grey))),
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 5.0, right: 5.0, top: 5.0, bottom: 15.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 1,
|
|
||||||
child: Center(
|
|
||||||
child: LocalText(
|
|
||||||
context,
|
|
||||||
"invoice.box.number",
|
|
||||||
color: Colors.grey,
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Center(
|
|
||||||
child: Text('L x W x H',
|
|
||||||
style: TextStyle(color: Colors.grey)))),
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Center(
|
|
||||||
child: LocalText(
|
|
||||||
context,
|
|
||||||
"invoice.shipment_weight",
|
|
||||||
color: Colors.grey,
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
return dataRow;
|
|
||||||
}
|
|
||||||
|
|
||||||
getCustomFeeRows(BuildContext context) {
|
|
||||||
customFee = 0;
|
|
||||||
List<Widget> dataRow = [];
|
|
||||||
|
|
||||||
dataRow = customs.map((custom) {
|
|
||||||
customFee += custom.fee;
|
|
||||||
return Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border(bottom: BorderSide(color: Colors.grey))),
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.only(left: 5.0, right: 5.0, top: 5.0, bottom: 5.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 2, child: Text('${custom.productType}')),
|
|
||||||
Expanded(
|
|
||||||
flex: 1,
|
|
||||||
child: Text('\$ ${custom.fee}', textAlign: TextAlign.center)),
|
|
||||||
Expanded(child: SizedBox(height: 40))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList();
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
0,
|
|
||||||
Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border(bottom: BorderSide(color: Colors.grey))),
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 5.0, right: 5.0, top: 15.0, bottom: 15.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Text('Product', style: TextStyle(color: Colors.grey))),
|
|
||||||
Expanded(
|
|
||||||
flex: 1,
|
|
||||||
child: Text('Fee',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(color: Colors.grey))),
|
|
||||||
Expanded(flex: 1, child: Container())
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
dataRow.length,
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 5.0, right: 5.0, top: 15.0, bottom: 15.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Center(
|
|
||||||
child: LocalText(
|
|
||||||
context,
|
|
||||||
'invoice.total_custom_fee',
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
flex: 1,
|
|
||||||
child: Center(
|
|
||||||
child: Text('\$ $customFee',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold)))),
|
|
||||||
Expanded(
|
|
||||||
child: Container(),
|
|
||||||
)
|
)
|
||||||
|
: Container(),
|
||||||
|
invoiceTableBox,
|
||||||
|
SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
paymentMethodBox,
|
||||||
|
SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
isCanceled || isPaid || widget.forCustomer
|
||||||
|
? Container()
|
||||||
|
: cancelBtn,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
));
|
),
|
||||||
return dataRow;
|
|
||||||
}
|
|
||||||
|
|
||||||
getCargoTableByBox(BuildContext context) {
|
|
||||||
total = 0;
|
|
||||||
List<Widget> dataRow = _cargoTypes.map((cargo) {
|
|
||||||
var amount = cargo.weight * cargo.rate;
|
|
||||||
total += amount;
|
|
||||||
return Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border(bottom: BorderSide(color: Colors.grey))),
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 5.0, right: 5.0, top: 15.0, bottom: 15.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 2, child: Text('${cargo.rate}')),
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Text('${cargo.weight} x ${cargo.rate}',
|
|
||||||
textAlign: TextAlign.center)),
|
|
||||||
Expanded(
|
|
||||||
child: Text('\$ $amount',
|
|
||||||
textAlign: TextAlign.end,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
)))
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}).toList();
|
|
||||||
dataRow.insert(
|
|
||||||
0,
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 5.0, right: 5.0, top: 15.0, bottom: 15.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border(bottom: BorderSide(color: Colors.grey))),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Text(getLocalString(context, 'invoice.box.cargo_type'),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.grey))),
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Text(
|
|
||||||
getLocalString(context, 'cargo.weight') +
|
|
||||||
' x ' +
|
|
||||||
getLocalString(context, 'cargo.rate'),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.grey))),
|
|
||||||
Expanded(
|
|
||||||
child: Text(getLocalString(context, 'invoice.amount'),
|
|
||||||
textAlign: TextAlign.end,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.grey)))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
dataRow.length,
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 5.0, right: 5.0, top: 10.0, bottom: 10.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Center(
|
|
||||||
child: LocalText(
|
|
||||||
context,
|
|
||||||
'invoice.total',
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
'\$ $total',
|
|
||||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
|
||||||
textAlign: TextAlign.end,
|
|
||||||
))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
dataRow.length,
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(left: 5.0, right: 5.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Center(
|
|
||||||
child: LocalText(
|
|
||||||
context,
|
|
||||||
'invoice.discount_value',
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
'\$ ${_discount != null ? _discount.amount.toInt() : 0}',
|
|
||||||
textAlign: TextAlign.end,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
)))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
dataRow.length,
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 5.0, right: 5.0, top: 10.0, bottom: 0.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Center(
|
|
||||||
child: LocalText(
|
|
||||||
context,
|
|
||||||
'invoice.custom_fee',
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
Expanded(
|
|
||||||
child: Text('\$ $customFee',
|
|
||||||
textAlign: TextAlign.end,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
dataRow.length,
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 5.0, right: 5.0, top: 20.0, bottom: 5.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Center(
|
|
||||||
child: LocalText(
|
|
||||||
context,
|
|
||||||
'invoice.handling_fee',
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
Expanded(
|
|
||||||
child: Text('\$ 10.0',
|
|
||||||
textAlign: TextAlign.end,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
dataRow.length,
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 5.0, right: 5.0, top: 10.0, bottom: 10.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Center(
|
|
||||||
child: LocalText(
|
|
||||||
context,
|
|
||||||
'invoice.delivery_fee',
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Text('\$ $deliveryfee',
|
|
||||||
textAlign: TextAlign.end,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
)))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
dataRow.length,
|
|
||||||
Container(
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(child: Text('')),
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Divider(
|
|
||||||
thickness: 3,
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
)));
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
dataRow.length,
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 5.0, right: 5.0, top: 10.0, bottom: 10.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Center(
|
|
||||||
child: LocalText(
|
|
||||||
context,
|
|
||||||
'invoice.net_amount',
|
|
||||||
color: Colors.black,
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Text('\$ ${getTotalBalance(total)}',
|
|
||||||
textAlign: TextAlign.end,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: primaryColor)))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
dataRow.length,
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 5.0, right: 5.0, top: 10.0, bottom: 10.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Center(
|
|
||||||
child: LocalText(
|
|
||||||
context,
|
|
||||||
'invoice.balance',
|
|
||||||
color: Colors.black,
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Text('\$ ${getTotalBalance(total)}',
|
|
||||||
textAlign: TextAlign.end,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: primaryColor)))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
return dataRow;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getTotalBalance(total) {
|
_cancel() {
|
||||||
double balance = 0;
|
showConfirmDialog(context, "invoice.cancel.confirm", _cancelInvoice);
|
||||||
double custom = customFee != 0 ? customFee.toDouble() : 0;
|
|
||||||
double deliveryFee = deliveryfee != 0 ? deliveryfee.toDouble() : 0;
|
|
||||||
double discount = _discount != null ? _discount.amount.toDouble() : 0;
|
|
||||||
balance = (total + custom + deliveryFee) - discount;
|
|
||||||
return balance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<MyDataRow> getBoxRow(BuildContext context) {
|
_cancelInvoice() async {
|
||||||
return _boxes.map((p) {
|
|
||||||
p.cargoTypes.map((cargo) {
|
|
||||||
_cargoTypes.asMap().map((index, _cargo) {
|
|
||||||
if (_cargo.id == cargo.id) {
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_cargoTypes[index].weight += cargo.weight;
|
_isLoading = true;
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return MyDataRow(
|
|
||||||
onSelectChanged: (bool selected) {},
|
|
||||||
cells: [
|
|
||||||
MyDataCell(Checkbox(
|
|
||||||
value: true,
|
|
||||||
onChanged: (value) {
|
|
||||||
selectedBoxes.add(p);
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
MyDataCell(new Text(
|
|
||||||
p.boxNumber == null
|
|
||||||
? ""
|
|
||||||
: '${p.shipmentNumber}-${p.receiverNumber} #${p.boxNumber}',
|
|
||||||
style: textStyle,
|
|
||||||
)),
|
|
||||||
MyDataCell(new Text(
|
|
||||||
p.length == null
|
|
||||||
? ""
|
|
||||||
: p.length.toString() +
|
|
||||||
' x ' +
|
|
||||||
p.length.toString() +
|
|
||||||
' x ' +
|
|
||||||
p.height.toString(),
|
|
||||||
style: textStyle,
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<MyDataRow> getCargoDataRow(BuildContext context) {
|
try {
|
||||||
return _cargoTypes.asMap().entries.map((c) {
|
InvoiceModel invoiceModel =
|
||||||
var cargo = c.value;
|
Provider.of<InvoiceModel>(context, listen: false);
|
||||||
var amt = cargo.weight * cargo.rate;
|
|
||||||
return MyDataRow(
|
await invoiceModel.cancelInvoice(_invoice);
|
||||||
onSelectChanged: (bool selected) {},
|
Navigator.pop(context, true);
|
||||||
cells: [
|
} catch (e) {
|
||||||
MyDataCell(new Text(
|
showMsgDialog(context, "Error", e.toString());
|
||||||
cargo.name,
|
} finally {
|
||||||
style: textStyle,
|
setState(() {
|
||||||
)),
|
_isLoading = false;
|
||||||
MyDataCell(new Text(
|
});
|
||||||
cargo.weight.toString() + ' x ' + cargo.rate.toString(),
|
}
|
||||||
style: textStyle,
|
|
||||||
)),
|
|
||||||
MyDataCell(new Text(
|
|
||||||
"\$$amt",
|
|
||||||
style: textStyle,
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}).toList()
|
|
||||||
// .insert(_cargoTypes.length,MyDataRow(cells: [
|
|
||||||
// MyDataCell(new Text('')),
|
|
||||||
// MyDataCell(new Text('Total')),
|
|
||||||
// MyDataCell(new Text(
|
|
||||||
// "\$5000",
|
|
||||||
// style: textStyle,
|
|
||||||
// )),
|
|
||||||
// ])
|
|
||||||
// )
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ class InvoiceList extends StatefulWidget {
|
|||||||
|
|
||||||
class _InvoiceListState extends State<InvoiceList> {
|
class _InvoiceListState extends State<InvoiceList> {
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
bool _showPaid = false;
|
|
||||||
var _controller = ScrollController();
|
var _controller = ScrollController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -34,7 +33,9 @@ class _InvoiceListState extends State<InvoiceList> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Provider.of<InvoiceModel>(context, listen: false).initData(false);
|
InvoiceModel invoiceModel =
|
||||||
|
Provider.of<InvoiceModel>(context, listen: false);
|
||||||
|
invoiceModel.initData(widget.forCustomer, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -44,22 +45,35 @@ class _InvoiceListState extends State<InvoiceList> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var owner = true;
|
|
||||||
var invoiceModel = Provider.of<InvoiceModel>(context);
|
var invoiceModel = Provider.of<InvoiceModel>(context);
|
||||||
|
|
||||||
final popupMenu = LocalPopupMenuButton(
|
final popupMenu = LocalPopupMenuButton(
|
||||||
popmenus: [
|
popmenus: [
|
||||||
LocalPopupMenu(
|
LocalPopupMenu(
|
||||||
id: 1,
|
id: 1,
|
||||||
textKey: "invoice.popupmenu.pending",
|
textKey: "invoice.popupmenu.issused",
|
||||||
selected: invoiceModel.selectedIndex == 1),
|
selected: invoiceModel.selectedIndex == 1),
|
||||||
LocalPopupMenu(
|
LocalPopupMenu(
|
||||||
id: 2,
|
id: 2,
|
||||||
textKey: "invoice.popupmenu.paid",
|
textKey: "invoice.popupmenu.paid",
|
||||||
selected: invoiceModel.selectedIndex == 2)
|
selected: invoiceModel.selectedIndex == 2),
|
||||||
|
LocalPopupMenu(
|
||||||
|
id: 3,
|
||||||
|
textKey: "invoice.popupmenu.cancel",
|
||||||
|
selected: invoiceModel.selectedIndex == 3)
|
||||||
],
|
],
|
||||||
popupMenuCallback: (p) => this.setState(() {
|
popupMenuCallback: (p) => this.setState(() {
|
||||||
_showPaid = p.id == 2;
|
invoiceModel.selectedIndex = p.id;
|
||||||
|
if (p.id == 2) {
|
||||||
|
Provider.of<InvoiceModel>(context, listen: false)
|
||||||
|
.initData(widget.forCustomer, false, true);
|
||||||
|
} else if (p.id == 3) {
|
||||||
|
Provider.of<InvoiceModel>(context, listen: false)
|
||||||
|
.initData(widget.forCustomer, true, false);
|
||||||
|
} else {
|
||||||
|
Provider.of<InvoiceModel>(context, listen: false)
|
||||||
|
.initData(widget.forCustomer, true, false);
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -79,8 +93,9 @@ class _InvoiceListState extends State<InvoiceList> {
|
|||||||
color: Colors.white, fontSize: 20),
|
color: Colors.white, fontSize: 20),
|
||||||
actions: <Widget>[popupMenu],
|
actions: <Widget>[popupMenu],
|
||||||
),
|
),
|
||||||
floatingActionButton: owner
|
floatingActionButton: widget.forCustomer
|
||||||
? FloatingActionButton.extended(
|
? null
|
||||||
|
: FloatingActionButton.extended(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_newInvoice();
|
_newInvoice();
|
||||||
},
|
},
|
||||||
@@ -88,8 +103,7 @@ class _InvoiceListState extends State<InvoiceList> {
|
|||||||
label:
|
label:
|
||||||
LocalText(context, 'invoices.add', color: Colors.white),
|
LocalText(context, 'invoices.add', color: Colors.white),
|
||||||
backgroundColor: primaryColor,
|
backgroundColor: primaryColor,
|
||||||
)
|
),
|
||||||
: null,
|
|
||||||
body: Column(
|
body: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -99,15 +113,17 @@ class _InvoiceListState extends State<InvoiceList> {
|
|||||||
controller: _controller,
|
controller: _controller,
|
||||||
separatorBuilder: (context, index) => Divider(
|
separatorBuilder: (context, index) => Divider(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
|
height: 1,
|
||||||
),
|
),
|
||||||
scrollDirection: Axis.vertical,
|
scrollDirection: Axis.vertical,
|
||||||
padding: EdgeInsets.only(top: 15),
|
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: invoiceModel.invoices.length,
|
itemCount: invoiceModel.invoices.length,
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
return InvoiceListRow(
|
return InvoiceListRow(
|
||||||
key: ValueKey(invoiceModel.invoices[index].id),
|
key: ValueKey(invoiceModel.invoices[index].id),
|
||||||
invoice: invoiceModel.invoices[index]);
|
invoice: invoiceModel.invoices[index],
|
||||||
|
forCustomer: widget.forCustomer,
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
onRefresh: () => invoiceModel.refresh(),
|
onRefresh: () => invoiceModel.refresh(),
|
||||||
),
|
),
|
||||||
@@ -131,7 +147,7 @@ class _InvoiceListState extends State<InvoiceList> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_newInvoice() {
|
_newInvoice() async {
|
||||||
Navigator.of(context)
|
Navigator.of(context)
|
||||||
.push(CupertinoPageRoute(builder: (context) => InvoiceShipmentList()));
|
.push(CupertinoPageRoute(builder: (context) => InvoiceShipmentList()));
|
||||||
}
|
}
|
||||||
@@ -1,93 +1,36 @@
|
|||||||
import 'dart:async';
|
import 'package:fcs/domain/constants.dart';
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:fcs/domain/entities/invoice.dart';
|
import 'package:fcs/domain/entities/invoice.dart';
|
||||||
import 'package:fcs/helpers/theme.dart';
|
import 'package:fcs/helpers/theme.dart';
|
||||||
import 'package:fcs/pages/invoice/invoice_info.dart';
|
import 'package:fcs/pages/invoice/invoice_info.dart';
|
||||||
import 'package:fcs/pages/widgets/bottom_up_page_route.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
|
|
||||||
import 'invoice_editor.dart';
|
import 'payment/payment_page.dart';
|
||||||
import 'payment_page.dart';
|
import '../widgets/pdf_screen.dart';
|
||||||
import 'payment_pdf_screen.dart';
|
|
||||||
|
|
||||||
class InvoiceListRow extends StatefulWidget {
|
class InvoiceListRow extends StatelessWidget {
|
||||||
|
final dateFormatter = new DateFormat('dd MMM yyyy');
|
||||||
final Invoice invoice;
|
final Invoice invoice;
|
||||||
InvoiceListRow({Key key, this.invoice}) : super(key: key);
|
final bool forCustomer;
|
||||||
|
InvoiceListRow({Key key, this.invoice, this.forCustomer}) : super(key: key);
|
||||||
@override
|
|
||||||
_InvoiceListRowState createState() => _InvoiceListRowState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _InvoiceListRowState extends State<InvoiceListRow> {
|
|
||||||
var dateFormatter = new DateFormat('dd MMM yyyy');
|
|
||||||
final double dotSize = 15.0;
|
|
||||||
Invoice _invoice = new Invoice();
|
|
||||||
String pdfPath = '';
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
if (widget.invoice != null) {
|
|
||||||
_invoice = widget.invoice;
|
|
||||||
}
|
|
||||||
|
|
||||||
fromAsset('assets/Invoice-A092(A)-32.pdf', 'Invoice-A092(A)-32.pdf')
|
|
||||||
.then((f) {
|
|
||||||
setState(() {
|
|
||||||
pdfPath = f.path;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<File> fromAsset(String asset, String filename) async {
|
|
||||||
// To open from assets, you can copy them to the app storage folder, and the access them "locally"
|
|
||||||
Completer<File> completer = Completer();
|
|
||||||
print('asset => $asset');
|
|
||||||
print('assest => ${await rootBundle.load(asset)}');
|
|
||||||
try {
|
|
||||||
var dir = await getApplicationDocumentsDirectory();
|
|
||||||
File file = File("${dir.path}/$filename");
|
|
||||||
var data = await rootBundle.load(asset);
|
|
||||||
print('data => $data');
|
|
||||||
var bytes = data.buffer.asUint8List();
|
|
||||||
await file.writeAsBytes(bytes, flush: true);
|
|
||||||
completer.complete(file);
|
|
||||||
} catch (e) {
|
|
||||||
throw Exception('Error parsing asset file! ===> ' + e.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return completer.future;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var owner = true;
|
return InkWell(
|
||||||
return Container(
|
onTap: () {
|
||||||
padding: EdgeInsets.only(left: 15, right: 15),
|
Navigator.of(context).push(CupertinoPageRoute(
|
||||||
|
builder: (context) => PDFScreen(
|
||||||
|
title: invoice.invoiceNumber,
|
||||||
|
url: invoice.invoiceURL,
|
||||||
|
)));
|
||||||
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: new Padding(
|
child: new Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||||
child: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
owner
|
|
||||||
? Navigator.of(context).push(CupertinoPageRoute(
|
|
||||||
builder: (context) => PaymentPDFScreen(
|
|
||||||
path: pdfPath,
|
|
||||||
)))
|
|
||||||
: Navigator.of(context).push(CupertinoPageRoute(
|
|
||||||
builder: (context) => PaymentPDFScreen(
|
|
||||||
path: pdfPath,
|
|
||||||
)));
|
|
||||||
},
|
|
||||||
child: new Row(
|
child: new Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
@@ -99,40 +42,35 @@ class _InvoiceListRowState extends State<InvoiceListRow> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
new Expanded(
|
new Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 0),
|
||||||
child: new Column(
|
child: new Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Padding(
|
new Text(
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
invoice.invoiceNumber ?? "",
|
||||||
child: new Text(
|
|
||||||
_invoice.invoiceNumber == null
|
|
||||||
? ''
|
|
||||||
: _invoice.invoiceNumber,
|
|
||||||
style: new TextStyle(
|
style: new TextStyle(
|
||||||
fontSize: 15.0, color: Colors.black),
|
fontSize: 15.0, color: Colors.black),
|
||||||
),
|
),
|
||||||
|
new Text(
|
||||||
|
invoice.status ?? "",
|
||||||
|
style: new TextStyle(
|
||||||
|
fontSize: 13.0, color: primaryColor),
|
||||||
),
|
),
|
||||||
Padding(
|
new Text(
|
||||||
padding: const EdgeInsets.only(left: 10.0, top: 10),
|
dateFormatter.format(invoice.invoiceDate),
|
||||||
child: new Text(
|
|
||||||
dateFormatter.format(_invoice.invoiceDate),
|
|
||||||
style: new TextStyle(
|
style: new TextStyle(
|
||||||
fontSize: 15.0, color: Colors.grey),
|
fontSize: 15.0, color: Colors.grey),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
invoice.status == invoice_issued_status
|
||||||
// Padding(
|
|
||||||
// padding: const EdgeInsets.all(0),
|
|
||||||
// child: getStatus(_invoice.status),
|
|
||||||
// ),
|
|
||||||
_invoice.status == "Pending"
|
|
||||||
? Padding(
|
? Padding(
|
||||||
padding: const EdgeInsets.only(left: 10.0),
|
padding: const EdgeInsets.only(left: 10.0),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
@@ -144,27 +82,32 @@ class _InvoiceListRowState extends State<InvoiceListRow> {
|
|||||||
color: primaryColor,
|
color: primaryColor,
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
padding: const EdgeInsets.only(left: 3.0),
|
||||||
child: Text("Payment"),
|
child: Text(
|
||||||
|
"Payment",
|
||||||
|
style: TextStyle(fontSize: 12, color: Colors.black),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).push(CupertinoPageRoute(
|
Navigator.of(context).push(CupertinoPageRoute(
|
||||||
builder: (context) =>
|
builder: (context) => PaymentPage(
|
||||||
PaymentPage(invoice: _invoice)));
|
invoice: invoice,
|
||||||
|
forCustomer: forCustomer,
|
||||||
|
)));
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
: Container(),
|
: Container(),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
child: InkWell(
|
child: IconButton(
|
||||||
child: Icon(
|
icon: Icon(
|
||||||
Icons.more_vert,
|
Icons.more_vert,
|
||||||
color: primaryColor,
|
color: primaryColor,
|
||||||
),
|
),
|
||||||
onTap: () {
|
onPressed: () {
|
||||||
var act = actionSheet(context);
|
var act = actionSheet(context);
|
||||||
showCupertinoModalPopup(
|
showCupertinoModalPopup(
|
||||||
context: context, builder: (BuildContext context) => act);
|
context: context, builder: (BuildContext context) => act);
|
||||||
@@ -191,10 +134,34 @@ class _InvoiceListRowState extends State<InvoiceListRow> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
//to go invoice info page
|
//to go invoice info page
|
||||||
|
Navigator.pop(context);
|
||||||
Navigator.of(context).push(CupertinoPageRoute(
|
Navigator.of(context).push(CupertinoPageRoute(
|
||||||
builder: (context) => InvoiceInfoPage(invoice: _invoice)));
|
builder: (context) =>
|
||||||
|
InvoiceInfo(invoice: invoice, forCustomer: forCustomer)));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
CupertinoActionSheetAction(
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
|
child: Text(
|
||||||
|
'Payments',
|
||||||
|
style: TextStyle(fontSize: 16, color: Colors.black),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onPressed: () async {
|
||||||
|
//to go invoice info page
|
||||||
|
Navigator.pop(context);
|
||||||
|
Navigator.of(context).push(CupertinoPageRoute(
|
||||||
|
builder: (context) => PaymentPage(
|
||||||
|
invoice: invoice,
|
||||||
|
forCustomer: forCustomer,
|
||||||
|
)));
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import 'package:fcs/domain/entities/fcs_shipment.dart';
|
import 'package:fcs/domain/entities/fcs_shipment.dart';
|
||||||
import 'package:fcs/helpers/theme.dart';
|
import 'package:fcs/helpers/theme.dart';
|
||||||
import 'package:fcs/pages/fcs_shipment/model/fcs_shipment_model.dart';
|
import 'package:fcs/pages/fcs_shipment/model/fcs_shipment_model.dart';
|
||||||
import 'package:fcs/pages/shipment/model/shipment_model.dart';
|
|
||||||
import 'package:fcs/pages/widgets/local_text.dart';
|
import 'package:fcs/pages/widgets/local_text.dart';
|
||||||
import 'package:fcs/pages/widgets/progress.dart';
|
import 'package:fcs/pages/widgets/progress.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
import 'invoice_customer_list.dart';
|
||||||
import 'invoice_shipment_list_row.dart';
|
import 'invoice_shipment_list_row.dart';
|
||||||
|
|
||||||
class InvoiceShipmentList extends StatefulWidget {
|
class InvoiceShipmentList extends StatefulWidget {
|
||||||
@@ -65,7 +65,14 @@ class _InvoiceShipmentListState extends State<InvoiceShipmentList> {
|
|||||||
itemCount: _fcsShipments.length,
|
itemCount: _fcsShipments.length,
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
return InvoiceShipmentListRow(
|
return InvoiceShipmentListRow(
|
||||||
fcsShipment: _fcsShipments[index]);
|
fcsShipment: _fcsShipments[index],
|
||||||
|
onSelect: (f) {
|
||||||
|
Navigator.of(context).push(CupertinoPageRoute(
|
||||||
|
builder: (context) => InvoiceCustomerList(
|
||||||
|
fcsShipment: f,
|
||||||
|
)));
|
||||||
|
},
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -8,9 +8,12 @@ import 'package:intl/intl.dart';
|
|||||||
import '../main/util.dart';
|
import '../main/util.dart';
|
||||||
import 'invoice_customer_list.dart';
|
import 'invoice_customer_list.dart';
|
||||||
|
|
||||||
|
typedef OnSelect(FcsShipment fcsShipment);
|
||||||
|
|
||||||
class InvoiceShipmentListRow extends StatefulWidget {
|
class InvoiceShipmentListRow extends StatefulWidget {
|
||||||
|
final OnSelect onSelect;
|
||||||
final FcsShipment fcsShipment;
|
final FcsShipment fcsShipment;
|
||||||
const InvoiceShipmentListRow({this.fcsShipment});
|
const InvoiceShipmentListRow({this.fcsShipment, this.onSelect});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_InvoiceShipmentListRowState createState() => _InvoiceShipmentListRowState();
|
_InvoiceShipmentListRowState createState() => _InvoiceShipmentListRowState();
|
||||||
@@ -35,10 +38,7 @@ class _InvoiceShipmentListRowState extends State<InvoiceShipmentListRow> {
|
|||||||
padding: EdgeInsets.only(left: 15, right: 15),
|
padding: EdgeInsets.only(left: 15, right: 15),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).push(CupertinoPageRoute(
|
if (widget.onSelect != null) widget.onSelect(widget.fcsShipment);
|
||||||
builder: (context) => InvoiceCustomerList(
|
|
||||||
fcsShipment: _fcsShipment,
|
|
||||||
)));
|
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
|||||||
230
lib/pages/invoice/invoice_table.dart
Normal file
230
lib/pages/invoice/invoice_table.dart
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
import 'package:fcs/domain/entities/cargo_type.dart';
|
||||||
|
import 'package:fcs/domain/entities/discount.dart';
|
||||||
|
import 'package:fcs/domain/entities/invoice.dart';
|
||||||
|
import 'package:fcs/domain/entities/rate.dart';
|
||||||
|
import 'package:fcs/pages/main/util.dart';
|
||||||
|
import 'package:fcs/pages/widgets/local_text.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
typedef OnDiscountSelected(Discount discount);
|
||||||
|
typedef OnDeliveryFeeSelected(bool selected);
|
||||||
|
typedef OnRemove(InvoiceTableRow row);
|
||||||
|
final formatter = new NumberFormat("#,###.00");
|
||||||
|
|
||||||
|
enum InvoiceDataType {
|
||||||
|
CargoDataType,
|
||||||
|
DiscountDataType,
|
||||||
|
CustomFeeDataType,
|
||||||
|
HandlingFeeType,
|
||||||
|
DeliveryFeeType,
|
||||||
|
}
|
||||||
|
|
||||||
|
class InvoiceTableRow {
|
||||||
|
final dynamic data;
|
||||||
|
final String id;
|
||||||
|
final InvoiceDataType invoiceDataType;
|
||||||
|
final String desc;
|
||||||
|
final String rate;
|
||||||
|
final String amount;
|
||||||
|
|
||||||
|
InvoiceTableRow(
|
||||||
|
{this.id,
|
||||||
|
this.data,
|
||||||
|
this.invoiceDataType,
|
||||||
|
this.desc,
|
||||||
|
this.rate,
|
||||||
|
this.amount});
|
||||||
|
}
|
||||||
|
|
||||||
|
class InvoiceTable extends StatelessWidget {
|
||||||
|
final Invoice invoice;
|
||||||
|
final Rate rate;
|
||||||
|
final OnDiscountSelected discountSelected;
|
||||||
|
final OnDeliveryFeeSelected deliveryFeeSelected;
|
||||||
|
final OnRemove onRemove;
|
||||||
|
|
||||||
|
const InvoiceTable(
|
||||||
|
{Key key,
|
||||||
|
this.invoice,
|
||||||
|
this.discountSelected,
|
||||||
|
this.deliveryFeeSelected,
|
||||||
|
this.onRemove,
|
||||||
|
this.rate})
|
||||||
|
: super(key: key);
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(children: getRows(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<InvoiceTableRow> getTableRows() {
|
||||||
|
List<InvoiceTableRow> tableRows = [];
|
||||||
|
// add cargo types
|
||||||
|
List<CargoType> _cargoTypes = invoice.getCargoTypes(rate) ?? [];
|
||||||
|
_cargoTypes.forEach((c) {
|
||||||
|
tableRows.add(InvoiceTableRow(
|
||||||
|
invoiceDataType: InvoiceDataType.CargoDataType,
|
||||||
|
desc: c.name,
|
||||||
|
rate:
|
||||||
|
"${c.calWeight.toStringAsFixed(2)} x ${c.calRate.toStringAsFixed(2)}",
|
||||||
|
amount: "${c.calAmount.toStringAsFixed(2)}"));
|
||||||
|
});
|
||||||
|
invoice.shipments.where((ss) => (ss.isSelected ?? false)).forEach((s) {
|
||||||
|
tableRows.add(InvoiceTableRow(
|
||||||
|
data: s,
|
||||||
|
invoiceDataType: InvoiceDataType.HandlingFeeType,
|
||||||
|
desc: "Handling fee\n${s.shipmentNumber}",
|
||||||
|
rate: "",
|
||||||
|
amount: "${s.handlingFee.toStringAsFixed(2)}"));
|
||||||
|
});
|
||||||
|
// // add custom fee
|
||||||
|
invoice.customDuties.forEach((c) {
|
||||||
|
tableRows.add(InvoiceTableRow(
|
||||||
|
data: c,
|
||||||
|
invoiceDataType: InvoiceDataType.CustomFeeDataType,
|
||||||
|
desc: "${c.productType} custom fee",
|
||||||
|
rate: "",
|
||||||
|
amount: "${c.fee.toStringAsFixed(2)}"));
|
||||||
|
});
|
||||||
|
// // add delivery fee
|
||||||
|
tableRows.add(InvoiceTableRow(
|
||||||
|
data: invoice.deliveryFee == null || invoice.deliveryFee == 0
|
||||||
|
? null
|
||||||
|
: invoice.deliveryFee,
|
||||||
|
invoiceDataType: InvoiceDataType.DeliveryFeeType,
|
||||||
|
desc: "Delivery fee",
|
||||||
|
rate: "",
|
||||||
|
amount: "${invoice?.deliveryFee?.toStringAsFixed(2) ?? '0'}"));
|
||||||
|
|
||||||
|
// // add discounts
|
||||||
|
if (invoice.discount != null) {
|
||||||
|
tableRows.add(InvoiceTableRow(
|
||||||
|
data: invoice.discount,
|
||||||
|
invoiceDataType: InvoiceDataType.DiscountDataType,
|
||||||
|
desc: "Discount\n${invoice?.discount?.code ?? ""}",
|
||||||
|
rate: "",
|
||||||
|
amount: "(${invoice?.discount?.amount?.toStringAsFixed(2) ?? ''})"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return tableRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRows(BuildContext context) {
|
||||||
|
List<InvoiceTableRow> tableRows = getTableRows();
|
||||||
|
|
||||||
|
List<Widget> dataRow = tableRows.map((r) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(bottom: BorderSide(color: Colors.grey))),
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 5.0, right: 5.0, top: 10.0, bottom: 10.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Flexible(child: Text('${r.desc}')),
|
||||||
|
SizedBox(
|
||||||
|
width: 5,
|
||||||
|
),
|
||||||
|
r.data == null || onRemove == null
|
||||||
|
? Container()
|
||||||
|
: InkWell(
|
||||||
|
onTap: () => onRemove(r),
|
||||||
|
child: Icon(
|
||||||
|
Icons.remove_circle,
|
||||||
|
color: Colors.black45,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Text(
|
||||||
|
'${r.rate}',
|
||||||
|
textAlign: TextAlign.end,
|
||||||
|
style: TextStyle(fontSize: 12),
|
||||||
|
)),
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Text('\$ ${r.amount}',
|
||||||
|
textAlign: TextAlign.end,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
)))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
dataRow.insert(
|
||||||
|
0,
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 5.0, right: 5.0, top: 15.0, bottom: 15.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(bottom: BorderSide(color: Colors.grey))),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: Text(getLocalString(context, 'invoice.box.desc'),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.grey))),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: Text(
|
||||||
|
getLocalString(context, 'invoice.weight') +
|
||||||
|
' x ' +
|
||||||
|
getLocalString(context, 'invoice.rate'),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.grey))),
|
||||||
|
Expanded(
|
||||||
|
child: Text(getLocalString(context, 'invoice.amount'),
|
||||||
|
textAlign: TextAlign.end,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.grey)))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
dataRow.insert(
|
||||||
|
dataRow.length,
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 5.0, right: 5.0, top: 10.0, bottom: 10.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Container(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: LocalText(
|
||||||
|
context,
|
||||||
|
'invoice.total',
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 20),
|
||||||
|
Text(
|
||||||
|
'\$ ${invoice.getNetAmount(rate).toStringAsFixed(2)}',
|
||||||
|
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||||
|
textAlign: TextAlign.end,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
return dataRow;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,60 +1,28 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
import 'package:fcs/data/services/services.dart';
|
import 'package:fcs/data/services/services.dart';
|
||||||
|
import 'package:fcs/domain/constants.dart';
|
||||||
import 'package:fcs/domain/entities/invoice.dart';
|
import 'package:fcs/domain/entities/invoice.dart';
|
||||||
import 'package:fcs/domain/entities/package.dart';
|
import 'package:fcs/domain/entities/payment.dart';
|
||||||
import 'package:fcs/domain/entities/receipt.dart';
|
import 'package:fcs/helpers/firebase_helper.dart';
|
||||||
import 'package:fcs/domain/vo/message.dart';
|
|
||||||
import 'package:fcs/helpers/paginator.dart';
|
import 'package:fcs/helpers/paginator.dart';
|
||||||
import 'package:fcs/pages/main/model/base_model.dart';
|
import 'package:fcs/pages/main/model/base_model.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:fcs/domain/constants.dart';
|
import 'package:path/path.dart' as Path;
|
||||||
|
|
||||||
class InvoiceModel extends BaseModel {
|
class InvoiceModel extends BaseModel {
|
||||||
final log = Logger('InvoiceModel');
|
final log = Logger('InvoiceModel');
|
||||||
|
|
||||||
StreamSubscription<QuerySnapshot> listener;
|
StreamSubscription<QuerySnapshot> listener;
|
||||||
|
|
||||||
List<Invoice> _invoices = [
|
List<Invoice> _invoices = [];
|
||||||
Invoice(
|
|
||||||
invoiceNumber: 'A092(A)-33',
|
|
||||||
invoiceDate: DateTime(2020, 4, 6, 12, 15),
|
|
||||||
customerName: 'Ko Myo Min',
|
|
||||||
customerPhoneNumber: '+959 555555555',
|
|
||||||
amount: 300,
|
|
||||||
status: 'Pending',
|
|
||||||
receipts: [
|
|
||||||
Receipt(amount: 200, date: '1 Jun 2020'),
|
|
||||||
],
|
|
||||||
packages: [
|
|
||||||
Package(
|
|
||||||
shipmentNumber: "A201",
|
|
||||||
receiverNumber: "1",
|
|
||||||
boxNumber: "1",
|
|
||||||
rate: 9,
|
|
||||||
packageType: "Dangerous",
|
|
||||||
weight: 25,
|
|
||||||
status: "Delivered",
|
|
||||||
arrivedDate: DateTime(2020, 5, 21),
|
|
||||||
receiverAddress: '3 Kambzwza St, Bahan Tsp, Yangon'),
|
|
||||||
Package(
|
|
||||||
shipmentNumber: "A201",
|
|
||||||
receiverNumber: "1",
|
|
||||||
boxNumber: "2",
|
|
||||||
rate: 7,
|
|
||||||
packageType: "General",
|
|
||||||
weight: 5,
|
|
||||||
status: "Delivered",
|
|
||||||
arrivedDate: DateTime(2020, 5, 21),
|
|
||||||
receiverAddress: '3 Kambzwza St, Bahan Tsp, Yangon'),
|
|
||||||
])
|
|
||||||
];
|
|
||||||
|
|
||||||
List<Invoice> get invoices =>
|
List<Invoice> get invoices =>
|
||||||
_selectedIndex == 1 ? _invoices : List<Invoice>.from(_paid.values);
|
_selectedIndex == 1 ? _invoices : List<Invoice>.from(_paginator.values);
|
||||||
|
|
||||||
Paginator _paid;
|
Paginator _paginator;
|
||||||
|
|
||||||
bool endOfPaidInvoices = false;
|
bool endOfPaidInvoices = false;
|
||||||
bool isLoading = false;
|
bool isLoading = false;
|
||||||
@@ -72,15 +40,17 @@ class InvoiceModel extends BaseModel {
|
|||||||
super.privilegeChanged();
|
super.privilegeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
initData(bool forCustomer) {
|
initData(bool forCustomer, bool isCanceled, bool isPaid) {
|
||||||
_selectedIndex = 1;
|
_loadInvoices(forCustomer);
|
||||||
// _loadFcsInvoices(forCustomer);
|
|
||||||
|
|
||||||
if (_paid != null) _paid.close();
|
if (_paginator != null) _paginator.close();
|
||||||
_paid = _getPaid(forCustomer);
|
_paginator = _getPaginator(forCustomer, isCanceled, isPaid);
|
||||||
|
_paginator.load(onFinished: () {
|
||||||
|
notifyListeners();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadFcsInvoices(bool forCustomer) async {
|
Future<void> _loadInvoices(bool forCustomer) async {
|
||||||
if (user == null) return;
|
if (user == null) return;
|
||||||
if (!forCustomer && !user.hasInvoices()) return;
|
if (!forCustomer && !user.hasInvoices()) return;
|
||||||
String path = "/$invoices_collection";
|
String path = "/$invoices_collection";
|
||||||
@@ -90,7 +60,7 @@ class InvoiceModel extends BaseModel {
|
|||||||
try {
|
try {
|
||||||
var q = Firestore.instance
|
var q = Firestore.instance
|
||||||
.collection("$path")
|
.collection("$path")
|
||||||
.where("is_paid", isEqualTo: false)
|
.where("status", isEqualTo: invoice_issued_status)
|
||||||
.where("is_deleted", isEqualTo: false);
|
.where("is_deleted", isEqualTo: false);
|
||||||
|
|
||||||
if (forCustomer) {
|
if (forCustomer) {
|
||||||
@@ -111,18 +81,24 @@ class InvoiceModel extends BaseModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Paginator _getPaid(bool isCustomer) {
|
Paginator _getPaginator(bool isCustomer, bool isCanceled, bool isPaid) {
|
||||||
if (!isCustomer) {
|
if (!isCustomer) {
|
||||||
if (user == null || !(user.hasInvoices())) throw "No privilege";
|
if (user == null || !(user.hasInvoices())) throw "No privilege";
|
||||||
}
|
}
|
||||||
var pageQuery = Firestore.instance
|
var pageQuery = Firestore.instance
|
||||||
.collection("/$packages_collection")
|
.collection("/$invoices_collection")
|
||||||
.where("is_delivered", isEqualTo: true)
|
|
||||||
.where("is_deleted", isEqualTo: false);
|
.where("is_deleted", isEqualTo: false);
|
||||||
if (isCustomer) {
|
if (isCustomer) {
|
||||||
pageQuery = pageQuery.where("user_id", isEqualTo: user.id);
|
pageQuery = pageQuery.where("user_id", isEqualTo: user.id);
|
||||||
}
|
}
|
||||||
pageQuery = pageQuery.orderBy("status_date", descending: true);
|
if (isCanceled) {
|
||||||
|
pageQuery = pageQuery.where("status", isEqualTo: invoice_cancel_status);
|
||||||
|
}
|
||||||
|
if (isPaid) {
|
||||||
|
pageQuery = pageQuery.where("status", isEqualTo: invoice_paid_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
pageQuery = pageQuery.orderBy("created_at", descending: true);
|
||||||
var paginator = new Paginator(pageQuery, rowPerLoad: 20, toObj: (data, id) {
|
var paginator = new Paginator(pageQuery, rowPerLoad: 20, toObj: (data, id) {
|
||||||
return Invoice.fromMap(data, id);
|
return Invoice.fromMap(data, id);
|
||||||
});
|
});
|
||||||
@@ -130,11 +106,11 @@ class InvoiceModel extends BaseModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> loadMore({bool isCustomer}) async {
|
Future<void> loadMore({bool isCustomer}) async {
|
||||||
if (_paid.ended || _selectedIndex == 1)
|
if (_paginator.ended || _selectedIndex == 1)
|
||||||
return; // when paid menu is not selected return
|
return; // when paid menu is not selected return
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
await _paid.load(onFinished: () {
|
await _paginator.load(onFinished: () {
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
});
|
});
|
||||||
@@ -142,7 +118,7 @@ class InvoiceModel extends BaseModel {
|
|||||||
|
|
||||||
Future<void> refresh({bool isCustomer}) async {
|
Future<void> refresh({bool isCustomer}) async {
|
||||||
if (_selectedIndex == 1) return; // when paid menu is not selected return
|
if (_selectedIndex == 1) return; // when paid menu is not selected return
|
||||||
await _paid.refresh(onFinished: () {
|
await _paginator.refresh(onFinished: () {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -152,16 +128,46 @@ class InvoiceModel extends BaseModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logout() async {
|
logout() async {
|
||||||
if (_paid != null) _paid.close();
|
if (_paginator != null) _paginator.close();
|
||||||
if (listener != null) await listener.cancel();
|
if (listener != null) await listener.cancel();
|
||||||
_invoices = [];
|
_invoices = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> createInvoice(Invoice invoice) {
|
Future<Invoice> getInvoice(String id) async {
|
||||||
// return Services.instance.invoiceService.createInvoice(invoice);
|
String path = "/$invoices_collection";
|
||||||
|
try {
|
||||||
|
var ref = Firestore.instance.collection("$path").document(id);
|
||||||
|
var snap = await ref.get(source: Source.server);
|
||||||
|
if (snap.exists) {
|
||||||
|
var s = Invoice.fromMap(snap.data, snap.documentID);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log.warning("Error!! $e");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> pay(Payment payment, File file) async {
|
||||||
|
String path = Path.join(receipt_labels_files_path, user.id);
|
||||||
|
String url = await uploadStorage(path, file);
|
||||||
|
payment.paymentReceiptURL = url;
|
||||||
|
return Services.instance.invoiceService.pay(payment);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updatePaymentStatus(Payment payment) async {
|
||||||
|
return Services.instance.invoiceService.updatPaymentStatus(payment);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> createInvoice(Invoice invoice) async {
|
||||||
|
return Services.instance.invoiceService.createInvoice(invoice);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateInvoice(Invoice invoice) {
|
Future<void> updateInvoice(Invoice invoice) {
|
||||||
// return Services.instance.invoiceService.updateInvoice(invoice);
|
return Services.instance.invoiceService.updateInvoice(invoice);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> cancelInvoice(Invoice invoice) {
|
||||||
|
return Services.instance.invoiceService.cancelInvoice(invoice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
327
lib/pages/invoice/payment/payment_page.dart
Normal file
327
lib/pages/invoice/payment/payment_page.dart
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:fcs/domain/constants.dart';
|
||||||
|
import 'package:fcs/domain/entities/invoice.dart';
|
||||||
|
import 'package:fcs/domain/entities/payment.dart';
|
||||||
|
import 'package:fcs/helpers/theme.dart';
|
||||||
|
import 'package:fcs/localization/app_translations.dart';
|
||||||
|
import 'package:fcs/pages/invoice/model/invoice_model.dart';
|
||||||
|
import 'package:fcs/pages/main/util.dart';
|
||||||
|
import 'package:fcs/pages/widgets/img_picker.dart';
|
||||||
|
import 'package:fcs/pages/widgets/input_text.dart';
|
||||||
|
import 'package:fcs/pages/widgets/local_button.dart';
|
||||||
|
import 'package:fcs/pages/widgets/local_text.dart';
|
||||||
|
import 'package:fcs/pages/widgets/local_title.dart';
|
||||||
|
import 'package:fcs/pages/widgets/progress.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class PaymentPage extends StatefulWidget {
|
||||||
|
final Invoice invoice;
|
||||||
|
final bool forCustomer;
|
||||||
|
PaymentPage({this.invoice, this.forCustomer});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_PaymentPageState createState() => _PaymentPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PaymentPageState extends State<PaymentPage> {
|
||||||
|
TextEditingController _amountController = new TextEditingController();
|
||||||
|
var dateFormatter = new DateFormat('dd MMM yyyy');
|
||||||
|
|
||||||
|
Invoice _invoice = new Invoice();
|
||||||
|
bool _isLoading = false;
|
||||||
|
|
||||||
|
bool isNew;
|
||||||
|
File _file;
|
||||||
|
bool _hasBalance;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_invoice = widget.invoice;
|
||||||
|
_hasBalance = widget.invoice.balance > 0;
|
||||||
|
_loadInvoice();
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadInvoice() async {
|
||||||
|
InvoiceModel invoiceModel =
|
||||||
|
Provider.of<InvoiceModel>(context, listen: false);
|
||||||
|
Invoice i = await invoiceModel.getInvoice(_invoice.id);
|
||||||
|
setState(() {
|
||||||
|
_invoice = i;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
final DateFormat dateFormat = DateFormat("d MMM yyyy");
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final amountBox = InputText(
|
||||||
|
labelTextKey: 'pm.amount',
|
||||||
|
controller: _amountController,
|
||||||
|
iconData: FontAwesomeIcons.moneyBill);
|
||||||
|
|
||||||
|
final receiptFileBox = Row(children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 8.0),
|
||||||
|
child: Text("Attach receipt"),
|
||||||
|
),
|
||||||
|
LocalImagePicker(
|
||||||
|
color: primaryColor,
|
||||||
|
title: "Receipt",
|
||||||
|
onFile: (f) => _file = f,
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
|
||||||
|
final payBtnBox = LocalButton(
|
||||||
|
callBack: _pay,
|
||||||
|
textKey: "pm.pay",
|
||||||
|
);
|
||||||
|
|
||||||
|
return LocalProgress(
|
||||||
|
inAsyncCall: _isLoading,
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
centerTitle: true,
|
||||||
|
leading: new IconButton(
|
||||||
|
icon: new Icon(CupertinoIcons.back),
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
),
|
||||||
|
backgroundColor: primaryColor,
|
||||||
|
title: Text(AppTranslations.of(context).text("pm_.title")),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
padding: const EdgeInsets.all(10.0),
|
||||||
|
children: <Widget>[
|
||||||
|
_hasBalance ? amountBox : Container(),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
_hasBalance
|
||||||
|
? Align(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: receiptFileBox,
|
||||||
|
),
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
_hasBalance ? payBtnBox : Container(),
|
||||||
|
SizedBox(height: 15),
|
||||||
|
LocalTitle(textKey: "pm.receipt"),
|
||||||
|
Column(
|
||||||
|
children: getCustomFeeRows(context),
|
||||||
|
),
|
||||||
|
SizedBox(height: 25),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCustomFeeRows(BuildContext context) {
|
||||||
|
List<Widget> dataRow = [];
|
||||||
|
|
||||||
|
dataRow = _invoice?.payments?.map((p) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(bottom: BorderSide(color: Colors.grey))),
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 5.0, right: 5.0, top: 5.0, bottom: 5.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
|
child: Text(
|
||||||
|
'${p.paymentDate != null ? dateFormatter.format(p.paymentDate) : ""}'),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
LocalImagePicker(
|
||||||
|
key: ValueKey(p.id),
|
||||||
|
enabled: false,
|
||||||
|
initialImgUrl: p.paymentReceiptURL,
|
||||||
|
title: "Receipt",
|
||||||
|
color: primaryColor,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
children: [Text('\$ ${p.amount}'), Text('${p.status}')],
|
||||||
|
))),
|
||||||
|
widget.forCustomer
|
||||||
|
? Container()
|
||||||
|
: Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: p.status == payment_pending_status
|
||||||
|
? [
|
||||||
|
InkWell(
|
||||||
|
onTap: () => _confirm(p),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Icon(
|
||||||
|
Icons.check,
|
||||||
|
color: primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
onTap: () => _cancel(p),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Icon(Icons.close,
|
||||||
|
color: primaryColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
: [],
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
})?.toList() ??
|
||||||
|
[];
|
||||||
|
|
||||||
|
dataRow.insert(
|
||||||
|
0,
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(bottom: BorderSide(color: Colors.grey))),
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 5.0, right: 5.0, top: 10.0, bottom: 15.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Text('Receipt', style: TextStyle(color: Colors.grey))),
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Text('Amount',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(color: Colors.grey))),
|
||||||
|
widget.forCustomer
|
||||||
|
? Container()
|
||||||
|
: Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Text('Actions',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(color: Colors.grey))),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
dataRow.insert(
|
||||||
|
dataRow.length,
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 5.0, right: 5.0, top: 15.0, bottom: 15.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Center(
|
||||||
|
child: LocalText(
|
||||||
|
context,
|
||||||
|
'pm.remaining_balance',
|
||||||
|
color: Colors.black,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'\$ ${widget.invoice.balance.toStringAsFixed(2)}',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold, fontSize: 16.0)))),
|
||||||
|
widget.forCustomer
|
||||||
|
? Container()
|
||||||
|
: Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
return dataRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
_confirm(Payment payment) {
|
||||||
|
payment.status = payment_confirmed_status;
|
||||||
|
showConfirmDialog(context, "invoice.payment.confirm.confirm",
|
||||||
|
() => _updatePayment(payment));
|
||||||
|
}
|
||||||
|
|
||||||
|
_cancel(Payment payment) {
|
||||||
|
payment.status = payment_canceled_status;
|
||||||
|
showConfirmDialog(context, "invoice.payment.cancel.confirm",
|
||||||
|
() => _updatePayment(payment));
|
||||||
|
}
|
||||||
|
|
||||||
|
_updatePayment(Payment payment) async {
|
||||||
|
payment.invoiceID = widget.invoice.id;
|
||||||
|
setState(() {
|
||||||
|
_isLoading = true;
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
InvoiceModel invoiceModel =
|
||||||
|
Provider.of<InvoiceModel>(context, listen: false);
|
||||||
|
await invoiceModel.updatePaymentStatus(payment);
|
||||||
|
Navigator.pop(context, true);
|
||||||
|
} catch (e) {
|
||||||
|
showMsgDialog(context, "Error", e.toString());
|
||||||
|
} finally {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_pay() async {
|
||||||
|
if (_file == null) {
|
||||||
|
showMsgDialog(context, "Error", "Expect receipt attachment");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double amount = double.tryParse(_amountController.text) ?? 0;
|
||||||
|
if (amount <= 0) {
|
||||||
|
showMsgDialog(context, "Error", "Expect valid amount");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Payment payment = Payment(invoiceID: _invoice.id, amount: amount);
|
||||||
|
setState(() {
|
||||||
|
_isLoading = true;
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
InvoiceModel invoiceModel =
|
||||||
|
Provider.of<InvoiceModel>(context, listen: false);
|
||||||
|
await invoiceModel.pay(payment, _file);
|
||||||
|
Navigator.pop(context, true);
|
||||||
|
} catch (e) {
|
||||||
|
showMsgDialog(context, "Error", e.toString());
|
||||||
|
} finally {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,301 +0,0 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:fcs/domain/entities/invoice.dart';
|
|
||||||
import 'package:fcs/helpers/theme.dart';
|
|
||||||
import 'package:fcs/localization/app_translations.dart';
|
|
||||||
import 'package:fcs/pages/invoice/payment_page_edit.dart';
|
|
||||||
import 'package:fcs/pages/widgets/image_file_picker.dart';
|
|
||||||
import 'package:fcs/pages/widgets/input_text.dart';
|
|
||||||
import 'package:fcs/pages/widgets/local_text.dart';
|
|
||||||
import 'package:fcs/pages/widgets/local_title.dart';
|
|
||||||
import 'package:fcs/pages/widgets/progress.dart';
|
|
||||||
import 'package:fcs/pages/widgets/show_img.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|
||||||
import 'package:image_picker/image_picker.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
|
|
||||||
class PaymentPage extends StatefulWidget {
|
|
||||||
final Invoice invoice;
|
|
||||||
PaymentPage({this.invoice});
|
|
||||||
|
|
||||||
@override
|
|
||||||
_PaymentPageState createState() => _PaymentPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PaymentPageState extends State<PaymentPage> {
|
|
||||||
TextEditingController _amountController = new TextEditingController();
|
|
||||||
var dateFormatter = new DateFormat('dd MMM yyyy');
|
|
||||||
|
|
||||||
Invoice _invoice = new Invoice();
|
|
||||||
bool _isLoading = false;
|
|
||||||
|
|
||||||
bool isNew;
|
|
||||||
File _file;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
if (widget.invoice != null) {
|
|
||||||
_invoice = widget.invoice;
|
|
||||||
}
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
final DateFormat dateFormat = DateFormat("d MMM yyyy");
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final amountBox = InputText(
|
|
||||||
labelTextKey: 'pm.amount',
|
|
||||||
controller: _amountController,
|
|
||||||
iconData: FontAwesomeIcons.moneyBill);
|
|
||||||
|
|
||||||
final receiptFileBox = Row(children: [
|
|
||||||
LocalText(context, 'pm.attachment', fontSize: 16, color: Colors.grey),
|
|
||||||
_file != null
|
|
||||||
? InkWell(
|
|
||||||
onTap: () async {
|
|
||||||
await _dialog(context);
|
|
||||||
},
|
|
||||||
child: Chip(
|
|
||||||
label: Icon(Icons.image, color: primaryColor),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: IconButton(
|
|
||||||
icon: Icon(Icons.attachment, color: primaryColor),
|
|
||||||
onPressed: () async {
|
|
||||||
await _dialog(context);
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
|
|
||||||
final payBox = Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
|
||||||
Container(
|
|
||||||
height: 50,
|
|
||||||
width: 100,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: Text(AppTranslations.of(context).text("pm.pay"),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(color: Colors.white, fontSize: 14)),
|
|
||||||
color: primaryColor)
|
|
||||||
]);
|
|
||||||
|
|
||||||
return LocalProgress(
|
|
||||||
inAsyncCall: _isLoading,
|
|
||||||
child: Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
centerTitle: true,
|
|
||||||
leading: new IconButton(
|
|
||||||
icon: new Icon(CupertinoIcons.back),
|
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
|
||||||
),
|
|
||||||
backgroundColor: primaryColor,
|
|
||||||
title: Text(AppTranslations.of(context).text("pm_.title")),
|
|
||||||
),
|
|
||||||
body: ListView(
|
|
||||||
padding: const EdgeInsets.all(10.0),
|
|
||||||
children: <Widget>[
|
|
||||||
amountBox,
|
|
||||||
SizedBox(height: 10),
|
|
||||||
receiptFileBox,
|
|
||||||
SizedBox(height: 10),
|
|
||||||
payBox,
|
|
||||||
Divider(),
|
|
||||||
SizedBox(height: 10),
|
|
||||||
LocalTitle(textKey: "pm.receipt"),
|
|
||||||
Column(
|
|
||||||
children: getCustomFeeRows(context),
|
|
||||||
),
|
|
||||||
SizedBox(height: 25),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getCustomFeeRows(BuildContext context) {
|
|
||||||
List<Widget> dataRow = [];
|
|
||||||
|
|
||||||
dataRow = _invoice.receipts.asMap().entries.map((receipt) {
|
|
||||||
var r = receipt.value;
|
|
||||||
var k = receipt.key + 1;
|
|
||||||
return Container(
|
|
||||||
height: 50,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border(bottom: BorderSide(color: Colors.grey))),
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.only(left: 5.0, right: 5.0, top: 5.0, bottom: 5.0),
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
Navigator.of(context).push(CupertinoPageRoute(
|
|
||||||
builder: (context) => PaymentPageEdit(receipt: r)));
|
|
||||||
},
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(flex: 2, child: Text('${r.date}')),
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => ShowImage(
|
|
||||||
// localImage: attachment.value.fileUrl,
|
|
||||||
localImage: 'assets/logo.jpg',
|
|
||||||
url: null,
|
|
||||||
fileName: 'image')));
|
|
||||||
},
|
|
||||||
child: Chip(
|
|
||||||
label: Icon(Icons.image, color: primaryColor),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(flex: 1, child: Center(child: Text('pending'))),
|
|
||||||
Expanded(flex: 1, child: Center(child: Text('\$ ${r.amount}'))),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList();
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
0,
|
|
||||||
Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border(bottom: BorderSide(color: Colors.grey))),
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 5.0, right: 5.0, top: 10.0, bottom: 15.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Text('Date', style: TextStyle(color: Colors.grey))),
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Text('File',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(color: Colors.grey))),
|
|
||||||
Expanded(
|
|
||||||
flex: 1,
|
|
||||||
child: Text('Status',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(color: Colors.grey))),
|
|
||||||
Expanded(
|
|
||||||
flex: 1,
|
|
||||||
child: Text('Fee',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(color: Colors.grey))),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
dataRow.insert(
|
|
||||||
dataRow.length,
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 5.0, right: 5.0, top: 15.0, bottom: 15.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Center(
|
|
||||||
child: LocalText(
|
|
||||||
context,
|
|
||||||
'pm.remaining_balance',
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Container(),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
flex: 1,
|
|
||||||
child: Center(
|
|
||||||
child: Text('\$ 300',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.bold, fontSize: 16.0)))),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
return dataRow;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _dialog(BuildContext context) {
|
|
||||||
return showDialog<void>(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
|
||||||
content: Container(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
||||||
children: <Widget>[
|
|
||||||
Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: <Widget>[
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
FontAwesomeIcons.camera,
|
|
||||||
size: 30,
|
|
||||||
color: primaryColor,
|
|
||||||
),
|
|
||||||
onPressed: () async {
|
|
||||||
// Navigator.pop(context);
|
|
||||||
// cameraPress();
|
|
||||||
var selectedFile =
|
|
||||||
await pickImage(ImageSource.camera);
|
|
||||||
setState(() {
|
|
||||||
_file = selectedFile;
|
|
||||||
});
|
|
||||||
Navigator.pop(context);
|
|
||||||
}),
|
|
||||||
Text("Camera")
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: <Widget>[
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
Icons.photo_library,
|
|
||||||
size: 30,
|
|
||||||
color: primaryColor,
|
|
||||||
),
|
|
||||||
onPressed: () async {
|
|
||||||
// Navigator.pop(context);
|
|
||||||
// photoPress();
|
|
||||||
var selectedFile =
|
|
||||||
await pickImage(ImageSource.gallery);
|
|
||||||
setState(() {
|
|
||||||
_file = selectedFile;
|
|
||||||
});
|
|
||||||
Navigator.pop(context);
|
|
||||||
}),
|
|
||||||
Text("Gallery")
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pickImage(ImageSource source) async {
|
|
||||||
var tempImage = await ImagePicker.pickImage(source: source);
|
|
||||||
return tempImage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:fcs/helpers/theme.dart';
|
|
||||||
import 'package:fcs/pages/main/model/main_model.dart';
|
|
||||||
import 'package:fcs/pages/widgets/local_text.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_pdfview/flutter_pdfview.dart';
|
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:share/share.dart';
|
|
||||||
|
|
||||||
class PaymentPDFScreen extends StatefulWidget {
|
|
||||||
final String path;
|
|
||||||
|
|
||||||
PaymentPDFScreen({Key key, this.path}) : super(key: key);
|
|
||||||
|
|
||||||
_PaymentPDFScreenState createState() => _PaymentPDFScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PaymentPDFScreenState extends State<PaymentPDFScreen>
|
|
||||||
with WidgetsBindingObserver {
|
|
||||||
final Completer<PDFViewController> _controller =
|
|
||||||
Completer<PDFViewController>();
|
|
||||||
int pages = 0;
|
|
||||||
int currentPage = 0;
|
|
||||||
bool isReady = false;
|
|
||||||
String errorMessage = '';
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
centerTitle: true,
|
|
||||||
backgroundColor: primaryColor,
|
|
||||||
title: LocalText(context, 'invoice.pdf',
|
|
||||||
color: Colors.white, fontSize: 20),
|
|
||||||
leading: new IconButton(
|
|
||||||
icon: new Icon(CupertinoIcons.back),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
}),
|
|
||||||
actions: <Widget>[
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.file_download),
|
|
||||||
onPressed: () async {
|
|
||||||
var file = await copyAsset();
|
|
||||||
print('file=> $file');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.share),
|
|
||||||
onPressed: () => _share(widget.path),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
body: Stack(
|
|
||||||
children: <Widget>[
|
|
||||||
PDFView(
|
|
||||||
filePath: widget.path,
|
|
||||||
enableSwipe: true,
|
|
||||||
swipeHorizontal: true,
|
|
||||||
autoSpacing: false,
|
|
||||||
pageFling: true,
|
|
||||||
pageSnap: true,
|
|
||||||
defaultPage: currentPage,
|
|
||||||
fitPolicy: FitPolicy.BOTH,
|
|
||||||
preventLinkNavigation:
|
|
||||||
false, // if set to true the link is handled in flutter
|
|
||||||
onRender: (_pages) {
|
|
||||||
print(('pages => $pages'));
|
|
||||||
setState(() {
|
|
||||||
pages = _pages;
|
|
||||||
isReady = true;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onViewCreated: (PDFViewController pdfViewController) {
|
|
||||||
_controller.complete(pdfViewController);
|
|
||||||
},
|
|
||||||
onLinkHandler: (String uri) {
|
|
||||||
print('goto uri: $uri');
|
|
||||||
},
|
|
||||||
onPageChanged: (int page, int total) {
|
|
||||||
print('page change: $page/$total');
|
|
||||||
setState(() {
|
|
||||||
currentPage = page;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_share(String url) async {
|
|
||||||
MainModel mainModel = Provider.of<MainModel>(context, listen: false);
|
|
||||||
String appUrl = mainModel.setting.appUrl;
|
|
||||||
final RenderBox box = context.findRenderObject();
|
|
||||||
await Share.share(
|
|
||||||
"Join us on FCS Logistics App. Here is the link:\n $appUrl\n" + url,
|
|
||||||
subject: "Invitation to FCS Logistics App",
|
|
||||||
sharePositionOrigin: box.localToGlobal(Offset.zero) & box.size);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<File> copyAsset() async {
|
|
||||||
Directory tempDir = await getTemporaryDirectory();
|
|
||||||
String tempPath = tempDir.path;
|
|
||||||
File tempFile = File('$tempPath/Invoice-A092(A)-32.pdf');
|
|
||||||
ByteData bd = await rootBundle.load('assets/Invoice-A092(A)-32.pdf');
|
|
||||||
await tempFile.writeAsBytes(bd.buffer.asUint8List(), flush: true);
|
|
||||||
return tempFile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
24
lib/pages/invoice/widgets.dart
Normal file
24
lib/pages/invoice/widgets.dart
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import 'package:fcs/domain/entities/invoice.dart';
|
||||||
|
import 'package:fcs/helpers/theme.dart';
|
||||||
|
import 'package:fcs/pages/widgets/local_text.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
Widget getInvoiceStatus(BuildContext context, Invoice invoice) {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
LocalText(
|
||||||
|
context,
|
||||||
|
'',
|
||||||
|
text: invoice.invoiceNumber ?? "",
|
||||||
|
color: primaryColor,
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
|
child: Chip(label: Text(invoice.status ?? "")),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ import 'package:fcs/pages/delivery/delivery_list.dart';
|
|||||||
import 'package:fcs/pages/discount/discount_list.dart';
|
import 'package:fcs/pages/discount/discount_list.dart';
|
||||||
import 'package:fcs/pages/faq/faq_list_page.dart';
|
import 'package:fcs/pages/faq/faq_list_page.dart';
|
||||||
import 'package:fcs/pages/fcs_shipment/fcs_shipment_list.dart';
|
import 'package:fcs/pages/fcs_shipment/fcs_shipment_list.dart';
|
||||||
import 'package:fcs/pages/invoice/invoce_list.dart';
|
import 'package:fcs/pages/invoice/invoice_list.dart';
|
||||||
import 'package:fcs/pages/main/model/language_model.dart';
|
import 'package:fcs/pages/main/model/language_model.dart';
|
||||||
import 'package:fcs/pages/main/model/main_model.dart';
|
import 'package:fcs/pages/main/model/main_model.dart';
|
||||||
import 'package:fcs/pages/main/util.dart';
|
import 'package:fcs/pages/main/util.dart';
|
||||||
@@ -298,11 +298,11 @@ class _HomePageState extends State<HomePage> {
|
|||||||
final invoicesBtn = TaskButton("invoices.btn",
|
final invoicesBtn = TaskButton("invoices.btn",
|
||||||
icon: FontAwesomeIcons.fileInvoice,
|
icon: FontAwesomeIcons.fileInvoice,
|
||||||
btnCallback: () => Navigator.of(context).push<void>(CupertinoPageRoute(
|
btnCallback: () => Navigator.of(context).push<void>(CupertinoPageRoute(
|
||||||
builder: (context) => InvoiceList(forCustomer: false))));
|
builder: (context) => InvoiceList(forCustomer: true))));
|
||||||
final invoicesBtnFcs = TaskButton("invoices.btn",
|
final invoicesBtnFcs = TaskButton("invoices.btn",
|
||||||
icon: FontAwesomeIcons.fileInvoice,
|
icon: FontAwesomeIcons.fileInvoice,
|
||||||
btnCallback: () => Navigator.of(context).push<void>(CupertinoPageRoute(
|
btnCallback: () => Navigator.of(context).push<void>(CupertinoPageRoute(
|
||||||
builder: (context) => InvoiceList(forCustomer: true))));
|
builder: (context) => InvoiceList(forCustomer: false))));
|
||||||
|
|
||||||
final discountBtn = TaskButton("discount.btn",
|
final discountBtn = TaskButton("discount.btn",
|
||||||
icon: Entypo.price_ribbon,
|
icon: Entypo.price_ribbon,
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import 'package:fcs/domain/constants.dart';
|
|||||||
import 'package:fcs/domain/entities/package.dart';
|
import 'package:fcs/domain/entities/package.dart';
|
||||||
import 'package:fcs/domain/entities/user.dart';
|
import 'package:fcs/domain/entities/user.dart';
|
||||||
import 'package:fcs/domain/vo/delivery_address.dart';
|
import 'package:fcs/domain/vo/delivery_address.dart';
|
||||||
import 'package:fcs/domain/vo/message.dart';
|
|
||||||
import 'package:fcs/helpers/firebase_helper.dart';
|
import 'package:fcs/helpers/firebase_helper.dart';
|
||||||
import 'package:fcs/helpers/paginator.dart';
|
import 'package:fcs/helpers/paginator.dart';
|
||||||
import 'package:fcs/pages/main/model/base_model.dart';
|
import 'package:fcs/pages/main/model/base_model.dart';
|
||||||
@@ -186,7 +185,6 @@ class PackageModel extends BaseModel {
|
|||||||
.where("status", whereIn: status)
|
.where("status", whereIn: status)
|
||||||
.where("user_id", isEqualTo: userID)
|
.where("user_id", isEqualTo: userID)
|
||||||
.where("is_deleted", isEqualTo: false)
|
.where("is_deleted", isEqualTo: false)
|
||||||
.where("is_delivered", isEqualTo: false)
|
|
||||||
.getDocuments(source: Source.server);
|
.getDocuments(source: Source.server);
|
||||||
packages = snaps.documents.map((documentSnapshot) {
|
packages = snaps.documents.map((documentSnapshot) {
|
||||||
var p =
|
var p =
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ class _ShipmentRatesCalState extends State<ShipmentRatesCal> {
|
|||||||
callback: (v) {
|
callback: (v) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_cargoType = v;
|
_cargoType = v;
|
||||||
|
_calShipmentWeight();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
labelKey: "cargo.type",
|
labelKey: "cargo.type",
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'package:barcode_scan/barcode_scan.dart';
|
|
||||||
import 'package:fcs/domain/entities/package.dart';
|
import 'package:fcs/domain/entities/package.dart';
|
||||||
import 'package:fcs/domain/entities/user.dart';
|
import 'package:fcs/domain/entities/user.dart';
|
||||||
import 'package:fcs/helpers/theme.dart';
|
import 'package:fcs/helpers/theme.dart';
|
||||||
@@ -137,7 +136,8 @@ class _ReceivingEditorState extends State<ReceivingEditor> {
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
leading: new IconButton(
|
leading: new IconButton(
|
||||||
icon: new Icon(CupertinoIcons.back, color: primaryColor, size: 30),
|
icon:
|
||||||
|
new Icon(CupertinoIcons.back, color: primaryColor, size: 30),
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
),
|
),
|
||||||
shadowColor: Colors.transparent,
|
shadowColor: Colors.transparent,
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
|
||||||
import 'package:fcs/helpers/firebase_helper.dart';
|
|
||||||
import 'package:path/path.dart' as Path;
|
|
||||||
|
|
||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
import 'package:fcs/data/services/services.dart';
|
import 'package:fcs/data/services/services.dart';
|
||||||
import 'package:fcs/domain/constants.dart';
|
import 'package:fcs/domain/constants.dart';
|
||||||
import 'package:fcs/domain/entities/shipment.dart';
|
import 'package:fcs/domain/entities/shipment.dart';
|
||||||
import 'package:fcs/domain/vo/message.dart';
|
|
||||||
import 'package:fcs/helpers/paginator.dart';
|
import 'package:fcs/helpers/paginator.dart';
|
||||||
import 'package:fcs/pages/main/model/base_model.dart';
|
import 'package:fcs/pages/main/model/base_model.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
@@ -36,10 +32,12 @@ class ShipmentModel extends BaseModel {
|
|||||||
|
|
||||||
initData(bool forCustomer, {bool myPickup = false}) {
|
initData(bool forCustomer, {bool myPickup = false}) {
|
||||||
logout();
|
logout();
|
||||||
_menuSelectedIndex = 1;
|
|
||||||
_loadShipments(forCustomer, myPickup);
|
_loadShipments(forCustomer, myPickup);
|
||||||
_delivered = _getDelivered(forCustomer);
|
_delivered = _getDelivered(forCustomer);
|
||||||
_delivered.load();
|
_delivered.load(onFinished: () {
|
||||||
|
isLoading = false;
|
||||||
|
notifyListeners();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -132,6 +130,11 @@ class ShipmentModel extends BaseModel {
|
|||||||
shipment_courier_dropoff
|
shipment_courier_dropoff
|
||||||
];
|
];
|
||||||
|
|
||||||
|
Shipment getActiveShipment(String shipmentID) {
|
||||||
|
return _shipments?.firstWhere((e) => e.id == shipmentID,
|
||||||
|
orElse: () => null);
|
||||||
|
}
|
||||||
|
|
||||||
Future<Shipment> getShipment(String shipmentID) async {
|
Future<Shipment> getShipment(String shipmentID) async {
|
||||||
String path = "/$shipments_collection";
|
String path = "/$shipments_collection";
|
||||||
try {
|
try {
|
||||||
@@ -147,6 +150,30 @@ class ShipmentModel extends BaseModel {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<Shipment>> getShipmentWithHandlingFee(
|
||||||
|
String fcsShipmentID, String userID) async {
|
||||||
|
String path = "/$shipments_collection";
|
||||||
|
try {
|
||||||
|
var q = Firestore.instance
|
||||||
|
.collection("$path")
|
||||||
|
.where("user_id", isEqualTo: userID)
|
||||||
|
.where("is_deleted", isEqualTo: false)
|
||||||
|
.where("handling_fee", isGreaterThan: 0)
|
||||||
|
.where("fcs_shipment_id", isEqualTo: fcsShipmentID);
|
||||||
|
var snaps = await q.getDocuments(source: Source.server);
|
||||||
|
List<Shipment> shipments = snaps.documents.map((snap) {
|
||||||
|
if (snap.exists) {
|
||||||
|
var s = Shipment.fromMap(snap.data, snap.documentID);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}).toList();
|
||||||
|
return shipments;
|
||||||
|
} catch (e) {
|
||||||
|
log.warning("Error!! $e");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
void initUser(user) {
|
void initUser(user) {
|
||||||
super.initUser(user);
|
super.initUser(user);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -311,9 +311,7 @@ class _ShipmentInfoState extends State<ShipmentInfo> {
|
|||||||
children: getBoxList(context, _shipment.boxes),
|
children: getBoxList(context, _shipment.boxes),
|
||||||
),
|
),
|
||||||
!_isCustomer ? fcsShipmentNumberBox : Container(),
|
!_isCustomer ? fcsShipmentNumberBox : Container(),
|
||||||
...(_shipment.isAssigned ||
|
...(!_shipment.isPending
|
||||||
_shipment.isConfirmed ||
|
|
||||||
_shipment.isPending
|
|
||||||
? [
|
? [
|
||||||
handlingFeeBox,
|
handlingFeeBox,
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -68,9 +68,9 @@ class _ShipmentListState extends State<ShipmentList> {
|
|||||||
if (p.id == 3) {
|
if (p.id == 3) {
|
||||||
Provider.of<ShipmentModel>(context, listen: false)
|
Provider.of<ShipmentModel>(context, listen: false)
|
||||||
.initData(widget.forCustomer, myPickup: true);
|
.initData(widget.forCustomer, myPickup: true);
|
||||||
} else if (p.id == 1) {
|
} else {
|
||||||
Provider.of<ShipmentModel>(context, listen: false)
|
Provider.of<ShipmentModel>(context, listen: false)
|
||||||
.initData(widget.forCustomer);
|
.initData(widget.forCustomer, myPickup: false);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
5
lib/pages/widgets/fcs_icons.dart
Normal file
5
lib/pages/widgets/fcs_icons.dart
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import 'package:flutter_icons/flutter_icons.dart';
|
||||||
|
|
||||||
|
const cartonIconData = MaterialCommunityIcons.package;
|
||||||
|
const customFeeIconData = MaterialCommunityIcons.security;
|
||||||
|
const shipmentIconData = SimpleLineIcons.direction;
|
||||||
162
lib/pages/widgets/img_picker.dart
Normal file
162
lib/pages/widgets/img_picker.dart
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
|
||||||
|
import 'show_img.dart';
|
||||||
|
|
||||||
|
typedef OnFile = void Function(File);
|
||||||
|
|
||||||
|
class LocalImagePicker extends StatefulWidget {
|
||||||
|
final Color color;
|
||||||
|
final String title;
|
||||||
|
final OnFile onFile;
|
||||||
|
final bool enabled;
|
||||||
|
final String initialImgUrl;
|
||||||
|
final ImageSource imageSource;
|
||||||
|
|
||||||
|
const LocalImagePicker(
|
||||||
|
{Key key,
|
||||||
|
this.title,
|
||||||
|
this.onFile,
|
||||||
|
this.enabled = true,
|
||||||
|
this.initialImgUrl,
|
||||||
|
this.imageSource = ImageSource.gallery,
|
||||||
|
this.color})
|
||||||
|
: super(key: key);
|
||||||
|
@override
|
||||||
|
_LocalImagePickerState createState() => _LocalImagePickerState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LocalImagePickerState extends State<LocalImagePicker> {
|
||||||
|
String url;
|
||||||
|
File file;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
this.url = widget.initialImgUrl == null || widget.initialImgUrl == ""
|
||||||
|
? null
|
||||||
|
: widget.initialImgUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
height: 30,
|
||||||
|
child: this.file == null && this.url == null
|
||||||
|
? IconButton(
|
||||||
|
padding: const EdgeInsets.all(3.0),
|
||||||
|
icon: Icon(Icons.attach_file, color: widget.color ?? Colors.blue),
|
||||||
|
onPressed: () async {
|
||||||
|
if (!widget.enabled) return;
|
||||||
|
bool camera = false, gallery = false;
|
||||||
|
await _dialog(
|
||||||
|
context, () => camera = true, () => gallery = true);
|
||||||
|
if (camera || gallery) {
|
||||||
|
var selectedFile = await ImagePicker.pickImage(
|
||||||
|
source: camera ? ImageSource.camera : ImageSource.gallery,
|
||||||
|
imageQuality: 80,
|
||||||
|
maxWidth: 1000);
|
||||||
|
if (selectedFile != null) {
|
||||||
|
setState(() {
|
||||||
|
this.file = selectedFile;
|
||||||
|
});
|
||||||
|
if (widget.onFile != null) {
|
||||||
|
widget.onFile(selectedFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: InkWell(
|
||||||
|
onTap: () => {
|
||||||
|
Navigator.of(context).push(CupertinoPageRoute(
|
||||||
|
builder: (context) => ShowImage(
|
||||||
|
imageFile: file,
|
||||||
|
url: file == null ? widget.initialImgUrl : null,
|
||||||
|
fileName: widget.title))),
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 3.0),
|
||||||
|
child: Chip(
|
||||||
|
avatar: Icon(
|
||||||
|
Icons.image,
|
||||||
|
color: widget.color ?? Colors.blue,
|
||||||
|
),
|
||||||
|
onDeleted: !widget.enabled
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
setState(() {
|
||||||
|
this.file = null;
|
||||||
|
this.url = null;
|
||||||
|
if (widget.onFile != null) {
|
||||||
|
widget.onFile(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deleteIcon: Icon(
|
||||||
|
Icons.close,
|
||||||
|
color: widget.color ?? Colors.blue,
|
||||||
|
),
|
||||||
|
label: Text(widget.title),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _dialog(BuildContext context, cameraPress(), photoPress()) {
|
||||||
|
return showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
content: Container(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: <Widget>[
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
FontAwesomeIcons.camera,
|
||||||
|
size: 30,
|
||||||
|
color: widget.color ?? Colors.blue,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
cameraPress();
|
||||||
|
}),
|
||||||
|
Text("Camera")
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.photo_library,
|
||||||
|
size: 30,
|
||||||
|
color: widget.color ?? Colors.blue,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
photoPress();
|
||||||
|
}),
|
||||||
|
Text("Gallery")
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ class LocalPopupMenuButton extends StatefulWidget {
|
|||||||
final bool multiSelect;
|
final bool multiSelect;
|
||||||
final bool selectable;
|
final bool selectable;
|
||||||
final IconData buttonIcon;
|
final IconData buttonIcon;
|
||||||
|
final Color buttonColor;
|
||||||
|
|
||||||
const LocalPopupMenuButton(
|
const LocalPopupMenuButton(
|
||||||
{Key key,
|
{Key key,
|
||||||
@@ -19,7 +20,8 @@ class LocalPopupMenuButton extends StatefulWidget {
|
|||||||
this.popmenus,
|
this.popmenus,
|
||||||
this.buttonIcon,
|
this.buttonIcon,
|
||||||
this.selectable = true,
|
this.selectable = true,
|
||||||
this.multiSelect = false})
|
this.multiSelect = false,
|
||||||
|
this.buttonColor = primaryColor})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -76,7 +78,7 @@ class _LocalPopupMenuButtonState extends State<LocalPopupMenuButton> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Icon(
|
Icon(
|
||||||
widget.buttonIcon ?? Icons.filter_list,
|
widget.buttonIcon ?? Icons.filter_list,
|
||||||
color: primaryColor,
|
color: widget.buttonColor ?? primaryColor,
|
||||||
),
|
),
|
||||||
hightlight
|
hightlight
|
||||||
? Positioned(
|
? Positioned(
|
||||||
|
|||||||
125
lib/pages/widgets/pdf_screen.dart
Normal file
125
lib/pages/widgets/pdf_screen.dart
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:fcs/helpers/cache_mgr.dart';
|
||||||
|
import 'package:fcs/helpers/theme.dart';
|
||||||
|
import 'package:fcs/pages/main/util.dart';
|
||||||
|
import 'package:fcs/pages/widgets/progress.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_pdfview/flutter_pdfview.dart';
|
||||||
|
import 'package:share/share.dart';
|
||||||
|
|
||||||
|
class PDFScreen extends StatefulWidget {
|
||||||
|
final String title;
|
||||||
|
final String url;
|
||||||
|
|
||||||
|
PDFScreen({Key key, this.url, this.title}) : super(key: key);
|
||||||
|
|
||||||
|
_PDFScreenState createState() => _PDFScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PDFScreenState extends State<PDFScreen> with WidgetsBindingObserver {
|
||||||
|
final Completer<PDFViewController> _controller =
|
||||||
|
Completer<PDFViewController>();
|
||||||
|
int pages = 0;
|
||||||
|
int currentPage = 0;
|
||||||
|
bool isReady = false;
|
||||||
|
String errorMessage = '';
|
||||||
|
bool _isLoading = true;
|
||||||
|
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
download();
|
||||||
|
}
|
||||||
|
|
||||||
|
File file;
|
||||||
|
Future<void> download() async {
|
||||||
|
try {
|
||||||
|
File f = await PdfCacheMgr.pdfs.getSingleFile(widget.url);
|
||||||
|
setState(() {
|
||||||
|
file = f;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
showMsgDialog(context, "Error", e.toString());
|
||||||
|
} finally {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return LocalProgress(
|
||||||
|
inAsyncCall: _isLoading,
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
centerTitle: true,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
shadowColor: Colors.transparent,
|
||||||
|
title:
|
||||||
|
Text(widget.title ?? "", style: TextStyle(color: primaryColor)),
|
||||||
|
leading: new IconButton(
|
||||||
|
icon: new Icon(CupertinoIcons.back, color: primaryColor),
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.share,
|
||||||
|
color: primaryColor,
|
||||||
|
),
|
||||||
|
onPressed: _share,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: Stack(
|
||||||
|
children: <Widget>[
|
||||||
|
_isLoading
|
||||||
|
? Container()
|
||||||
|
: PDFView(
|
||||||
|
filePath: file?.path ?? "",
|
||||||
|
enableSwipe: true,
|
||||||
|
swipeHorizontal: true,
|
||||||
|
autoSpacing: false,
|
||||||
|
pageFling: true,
|
||||||
|
pageSnap: true,
|
||||||
|
defaultPage: currentPage,
|
||||||
|
fitPolicy: FitPolicy.BOTH,
|
||||||
|
preventLinkNavigation:
|
||||||
|
false, // if set to true the link is handled in flutter
|
||||||
|
onRender: (_pages) {
|
||||||
|
print(('pages => $pages'));
|
||||||
|
setState(() {
|
||||||
|
pages = _pages;
|
||||||
|
isReady = true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onViewCreated: (PDFViewController pdfViewController) {
|
||||||
|
_controller.complete(pdfViewController);
|
||||||
|
},
|
||||||
|
onLinkHandler: (String uri) {
|
||||||
|
print('goto uri: $uri');
|
||||||
|
},
|
||||||
|
onPageChanged: (int page, int total) {
|
||||||
|
print('page change: $page/$total');
|
||||||
|
setState(() {
|
||||||
|
currentPage = page;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_share() async {
|
||||||
|
final RenderBox box = context.findRenderObject();
|
||||||
|
await Share.shareFiles([file.path],
|
||||||
|
mimeTypes: ["application/pdf"],
|
||||||
|
subject: "File",
|
||||||
|
sharePositionOrigin: box.localToGlobal(Offset.zero) & box.size);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:fcs/helpers/theme.dart';
|
import 'package:fcs/helpers/theme.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:photo_view/photo_view.dart';
|
import 'package:photo_view/photo_view.dart';
|
||||||
|
|
||||||
@@ -21,12 +22,19 @@ class _ShowImageState extends State<ShowImage> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
leading: new IconButton(
|
||||||
|
icon: new Icon(Icons.close),
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
),
|
||||||
backgroundColor: primaryColor,
|
backgroundColor: primaryColor,
|
||||||
title: Text(widget.fileName, style: TextStyle(color: Colors.white)),
|
shadowColor: Colors.transparent,
|
||||||
iconTheme: new IconThemeData(color: Colors.white),
|
iconTheme: new IconThemeData(color: Colors.white),
|
||||||
),
|
),
|
||||||
body: Center(
|
body: Center(
|
||||||
child: PhotoView(
|
child: PhotoView(
|
||||||
|
backgroundDecoration: const BoxDecoration(
|
||||||
|
color: primaryColor,
|
||||||
|
),
|
||||||
imageProvider: widget.url != null
|
imageProvider: widget.url != null
|
||||||
? NetworkImage(widget.url)
|
? NetworkImage(widget.url)
|
||||||
: widget.imageFile != null
|
: widget.imageFile != null
|
||||||
|
|||||||
29
pubspec.lock
29
pubspec.lock
@@ -28,7 +28,7 @@ packages:
|
|||||||
name: cached_network_image
|
name: cached_network_image
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.2+1"
|
version: "2.3.3"
|
||||||
camera:
|
camera:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -176,6 +176,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
|
ffi:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ffi
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.3"
|
||||||
file:
|
file:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -259,12 +266,12 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.0"
|
version: "0.5.0"
|
||||||
flutter_cache_manager:
|
flutter_cache_manager:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_cache_manager
|
name: flutter_cache_manager
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.2"
|
version: "2.0.0"
|
||||||
flutter_datetime_picker:
|
flutter_datetime_picker:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -447,7 +454,7 @@ packages:
|
|||||||
name: path_provider
|
name: path_provider
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.6.14"
|
version: "1.6.22"
|
||||||
path_provider_linux:
|
path_provider_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -469,6 +476,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.3"
|
version: "1.0.3"
|
||||||
|
path_provider_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_windows
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.4+1"
|
||||||
pedantic:
|
pedantic:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -754,6 +768,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.8"
|
version: "2.0.8"
|
||||||
|
win32:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.7.3"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ dependencies:
|
|||||||
flutter_local_notifications: ^1.4.4+4
|
flutter_local_notifications: ^1.4.4+4
|
||||||
share: ^0.6.5
|
share: ^0.6.5
|
||||||
cached_network_image: ^2.3.2+1
|
cached_network_image: ^2.3.2+1
|
||||||
|
flutter_cache_manager: ^2.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user