add invoice pdf

This commit is contained in:
Sai Naw Wun
2020-10-26 04:41:24 +06:30
parent feec3c8687
commit 2786acfd08
33 changed files with 787 additions and 1114 deletions

View File

@@ -441,7 +441,7 @@
"invoice.payment_attachment":"Payment attachment",
"invoice.box_info":"Cartons",
"invoice.cargo_table":"Cargo table",
"invoice.btn_create":"Create invoice",
"invoice.issue.btn":"Issue invoice",
"invoice.btn_save":"Save invoice",
"invoice.btn_payment_receipt":"Attachment payment receipt",
"invoice.description": "Description",
@@ -458,8 +458,9 @@
"invoice.pdf": "Invoice PDF",
"invoice.total_custom_fee":"Total custom fee",
"invoice.shipment_weight":"Shipment weight",
"invoice.popupmenu.pending":"Pending",
"invoice.popupmenu.issused":"Issused",
"invoice.popupmenu.paid":"Paid",
"invoice.popupmenu.cancel":"Canceled",
"invoice.shipment.title":"Select FCS shipment",
"invoice.customer.title":"Select Customer",
"invoice.add.custom.fee.menu":"Custom fee",
@@ -468,6 +469,8 @@
"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?",
"Invoices End ================================================================":"",
"Discount Start ================================================================":"",

View File

@@ -441,7 +441,8 @@
"invoice.payment_attachment":"ပေးချေပြီးဖိုင်များ",
"invoice.box_info":"Cartons",
"invoice.cargo_table":"ကုန်ပစ္စည်းဇယား",
"invoice.btn_create":"ငွေတောင်းခံလွှာ ပြုလုပ်ရန်",
"invoice.issue.btn":"ငွေတောင်းခံလွှာ ထုတ်မည်",
"invoice.btn_save":"ငွေတောင်းခံလွှာ သိမ်းရန်",
"invoice.btn_payment_receipt":"ငွေလက်ခံဖြတ်ပိုင်း ထည့်ရန်",
"invoice.box.cargo_type": "ကုန်ပစ္စည်းအမျိုးအစားများ",
@@ -458,8 +459,9 @@
"invoice.pdf": "Invoice PDF",
"invoice.total_custom_fee":"အခွန်စုစုပေါင်း",
"invoice.shipment_weight":"Shipment weight",
"invoice.popupmenu.pending":"Pending",
"invoice.popupmenu.issused":"Issused",
"invoice.popupmenu.paid":"Paid",
"invoice.popupmenu.cancel":"Canceled",
"invoice.shipment.title":"Select FCS shipment",
"invoice.customer.title":"Select Customer",
"invoice.add.custom.fee.menu":"Custom fee",
@@ -468,6 +470,8 @@
"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?",
"Invoices End ================================================================":"",
"Discount Start ================================================================":"",

View File

@@ -0,0 +1,25 @@
import 'package:fcs/domain/entities/invoice.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());
}
}

View File

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

View File

@@ -0,0 +1,8 @@
import 'package:fcs/domain/entities/invoice.dart';
import 'package:fcs/domain/entities/shipment.dart';
abstract class InvoiceService {
Future<void> createInvoice(Invoice invoice);
Future<void> updateInvoice(Invoice invoice);
Future<void> cancelInvoice(Invoice invoice);
}

View File

@@ -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/delivery_address_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/rate_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/fcs_shipment_imp.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_service.dart';
import 'package:fcs/data/services/shipment_imp.dart';
@@ -42,6 +45,7 @@ class Services {
RateService _rateService;
ShipmentService _shipmentService;
CartonService _cartonService;
InvoiceService _invoiceService;
Services._() {
_authService = AuthServiceImp(
authFb: AuthFb.instance,
@@ -65,6 +69,8 @@ class Services {
connectivity: null);
_cartonService = CartonServiceImp(
cartonDataProvider: CartonDataProvider.instance, connectivity: null);
_invoiceService = InvoiceServiceImp(
invoiceDataProvider: InvoiceDataProvider.instance, connectivity: null);
}
AuthService get authService => _authService;
@@ -77,4 +83,5 @@ class Services {
RateService get rateService => _rateService;
ShipmentService get shipmentService => _shipmentService;
CartonService get cartonService => _cartonService;
InvoiceService get invoiceService => _invoiceService;
}

View File

@@ -91,3 +91,9 @@ const shipment_pickuped_status = "pickuped";
const shipment_packed_status = "packed";
const shipment_shipped_status = "shipped";
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";

View File

@@ -4,7 +4,7 @@ class CargoType {
double rate;
double weight;
double amount;
double get calAmount => (calRate ?? 0) * (calWeight ?? 0);
double calRate;
double calWeight;
@@ -16,9 +16,16 @@ class CargoType {
rate: map['rate']?.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, this.calWeight});
CargoType(
{this.id,
this.name,
this.rate,
this.weight,
this.calWeight,
this.calRate});
Map<String, dynamic> toMap() {
return {
@@ -27,6 +34,7 @@ class CargoType {
'rate': rate,
'weight': weight,
'cal_weight': calWeight,
'cal_rate': calRate,
};
}

View File

@@ -171,7 +171,7 @@ class Carton {
'length': length,
'width': width,
'height': height,
'delivery_address': deliveryAddress.toMap(),
'delivery_address': deliveryAddress?.toMap(),
'carton_type': cartonType,
'mix_carton_id': mixCartonID,
};

View File

@@ -1,3 +1,4 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/domain/entities/cargo_type.dart';
import 'package:fcs/domain/entities/carton.dart';
import 'package:fcs/domain/entities/custom_duty.dart';
@@ -13,6 +14,7 @@ class Invoice {
DateTime invoiceDate;
String fcsShipmentID;
String userID;
String fcsID;
String userName;
String phoneNumber;
String status;
@@ -28,20 +30,23 @@ class Invoice {
List<Shipment> shipments;
Discount discount;
PaymentMethod paymentMethod;
String invoiceURL;
List<CargoType> getCargoTypes(Rate rate) {
List<CargoType> cargoTypes = [];
if (cargoTypes != null) return cargoTypes;
List<CargoType> _cargoTypes = [];
double totalCalWeight = 0;
cartons.forEach((carton) {
if (carton.isChecked) {
var _cartonsTypes =
carton.getCargoTypeForCalWeight(rate.volumetricRatio);
_cartonsTypes.forEach((ct) {
if (cargoTypes.contains(ct)) {
CargoType existing = cargoTypes.firstWhere((wc) => wc.id == ct.id);
if (_cargoTypes.contains(ct)) {
CargoType existing = _cargoTypes.firstWhere((wc) => wc.id == ct.id);
existing.calWeight += ct.calWeight;
} else {
cargoTypes.add(ct.clone());
_cargoTypes.add(ct.clone());
}
totalCalWeight += ct.calWeight;
});
@@ -51,25 +56,23 @@ class Invoice {
DiscountByWeight discountByWeight =
rate.getDiscountByWeight(totalCalWeight);
cargoTypes.forEach((e) {
_cargoTypes.forEach((e) {
double r =
e.rate - (discountByWeight != null ? discountByWeight.discount : 0);
double amount = e.calWeight * r;
e.calRate = r;
e.amount = amount;
});
return cargoTypes;
return _cargoTypes;
}
double getTotal(Rate 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;
}
double getNetAmount(Rate 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 += getDeliveryFee();
total += getHandlingFee();
@@ -78,7 +81,7 @@ class Invoice {
}
double getHandlingFee() {
return shipments?.where((sh) => sh.isSelected)?.fold(0, (p, s) {
return shipments?.where((sh) => sh.isSelected ?? false)?.fold(0, (p, s) {
return p + (s?.handlingFee ?? 0) - (s?.paidHandlingFee ?? 0);
});
}
@@ -97,33 +100,65 @@ class Invoice {
double getDiscount() => discount == null ? 0 : discount.amount;
Invoice({
this.id,
this.invoiceNumber,
this.invoiceDate,
this.userName,
this.phoneNumber,
this.amount,
this.discount,
this.status,
this.customDuties,
this.cartons,
this.cargoTypes,
this.handlingFee,
this.fcsShipmentID,
this.shipments,
});
Invoice(
{this.id,
this.invoiceNumber,
this.invoiceDate,
this.fcsID,
this.userName,
this.phoneNumber,
this.amount,
this.discount,
this.status,
this.customDuties,
this.cartons,
this.cargoTypes,
this.handlingFee,
this.deliveryFee,
this.fcsShipmentID,
this.shipments,
this.invoiceURL,
this.paymentMethod});
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']);
return Invoice(
id: docID,
invoiceNumber: map['invoice_number'],
invoiceDate: map['invoice_date'],
invoiceDate: invd?.toDate(),
userName: map['user_name'],
fcsID: map['fcs_id'],
phoneNumber: map['phone_number'],
amount: map['amount'],
status: map['status'],
discount: map['discount'],
cartons: cartons,
cargoTypes: cargoTypes,
shipments: handingShipments,
customDuties: customDuties,
deliveryFee: map['delivery_fee'],
invoiceURL: map['invoice_url'],
paymentMethod: paymentMethod,
discount: discount,
);
}
@@ -131,17 +166,24 @@ class Invoice {
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,
"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(),
};
}
}

View File

@@ -100,7 +100,7 @@ class Shipment {
}
Map<String, dynamic> toMap() {
List _boxes = boxes.map((l) => l.toMap()).toList();
List _boxes = boxes?.map((l) => l.toMap())?.toList() ?? [];
return {
"id": id,

View File

@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
@@ -5,9 +6,11 @@ import 'package:device_info/device_info.dart';
import 'package:dio/dio.dart';
import 'package:fcs/domain/vo/status.dart';
import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart';
import '../config.dart';
import 'dev_info.dart';
import 'firebase_helper.dart';
final log = Logger('requestAPI');
@@ -146,3 +149,92 @@ Future<dynamic> requestDownloadPDFAPI(String path, method,
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;
}

View 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,
),
);
}

View File

@@ -162,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() {
double l = double.parse(_lengthController.text, (s) => 0);
double w = double.parse(_widthController.text, (s) => 0);
@@ -196,6 +208,7 @@ class _CartonEditorState extends State<CartonEditor> {
setState(() {
_fcsShipment = v;
});
_loadMixCartons();
},
labelKey: "shipment.pack.fcs.shipment",
iconData: Ionicons.ios_airplane,
@@ -291,7 +304,6 @@ class _CartonEditorState extends State<CartonEditor> {
values: boxModel.cartonTypes,
selectedValue: _selectedCartonType,
callback: (v) {
print(v);
setState(() {
_selectedCartonType = v;
});

View File

@@ -5,10 +5,10 @@ import 'package:fcs/pages/widgets/my_data_table.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class InvoiceDiscountTable extends StatelessWidget {
class InvoiceDiscountList extends StatelessWidget {
final List<Discount> discounts;
const InvoiceDiscountTable({
const InvoiceDiscountList({
Key key,
this.discounts,
}) : super(key: key);

View File

@@ -1,3 +1,4 @@
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';
@@ -8,12 +9,12 @@ 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/discount_list.dart';
import 'package:fcs/pages/discount/model/discount_model.dart';
import 'package:fcs/pages/invoice/invoice_discount_table.dart';
import 'package:fcs/pages/invoice/invoice_shipment_table.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/invoice_carton_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';
@@ -22,12 +23,11 @@ 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/fcs_id_icon.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/local_title.dart';
import 'package:fcs/pages/widgets/progress.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@@ -36,8 +36,6 @@ 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;
@@ -49,15 +47,13 @@ class InvoiceEditor extends StatefulWidget {
}
class _InvoiceEditorState extends State<InvoiceEditor> {
User _user;
var dateFormatter = new DateFormat('dd MMM yyyy');
Invoice _invoice;
bool _isLoading = false;
bool _isNew;
User _user;
List<bool> isSelected = [false];
bool _showCartons = false;
@override
void initState() {
@@ -141,11 +137,14 @@ class _InvoiceEditorState extends State<InvoiceEditor> {
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,
@@ -155,21 +154,6 @@ class _InvoiceEditorState extends State<InvoiceEditor> {
});
},
);
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),
);
final paymentTypesBox = LocalDropdown<PaymentMethod>(
callback: (v) {
setState(() {
@@ -238,7 +222,6 @@ class _InvoiceEditorState extends State<InvoiceEditor> {
},
isSelected: [_showCartons],
);
final popupMenu = LocalPopupMenuButton(
buttonIcon: Icons.add_circle,
selectable: false,
@@ -271,12 +254,12 @@ class _InvoiceEditorState extends State<InvoiceEditor> {
Shipment shipment = await Navigator.of(context).push(
CupertinoPageRoute(
builder: (context) =>
InvoiceShipmentTable(shipments: _invoice.shipments)));
InvoiceHandlingFeeList(shipments: _invoice.shipments)));
_addShipment(shipment);
} else if (p.id == 3) {
Discount discount =
await Navigator.of(context).push(CupertinoPageRoute(
builder: (context) => InvoiceDiscountTable(
builder: (context) => InvoiceDiscountList(
discounts: discounts,
)));
if (discount != null) {
@@ -292,6 +275,39 @@ class _InvoiceEditorState extends State<InvoiceEditor> {
},
);
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(
@@ -310,39 +326,8 @@ class _InvoiceEditorState extends State<InvoiceEditor> {
padding: const EdgeInsets.all(8.0),
child: ListView(
children: <Widget>[
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,
],
),
],
),
_isNew
? Container()
: DisplayText(
labelTextKey: 'invoice.number',
iconData: FontAwesomeIcons.fileInvoice,
text: _invoice?.invoiceNumber ?? ""),
headerBox,
_isNew ? Container() : invoiceNumberBox,
_isNew ? Container() : statusBox,
_showCartons ? cartonTable : Container(),
_showCartons
@@ -360,15 +345,7 @@ class _InvoiceEditorState extends State<InvoiceEditor> {
height: 10,
),
_isNew
? Container()
: LocalTitle(
textKey: "invoice.payment_attachment",
trailing: IconButton(
icon: Icon(Icons.add_circle, color: primaryColor),
onPressed: () async {})),
_isNew
? fcsButton(
context, getLocalString(context, 'invoice.btn_create'))
? createBtn
: mainModel.isCustomer()
? Container()
: Container(
@@ -421,5 +398,54 @@ class _InvoiceEditorState extends State<InvoiceEditor> {
});
}
_save() {}
_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;
await invoiceModel.createInvoice(invoice);
Navigator.pop(context, true);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
}

View File

@@ -8,12 +8,12 @@ import 'package:flutter/material.dart';
typedef OnAdd(Shipment shipment);
typedef OnRemove(Shipment shipment);
class InvoiceShipmentTable extends StatelessWidget {
class InvoiceHandlingFeeList extends StatelessWidget {
final List<Shipment> shipments;
final OnAdd onAdd;
final OnRemove onRemove;
const InvoiceShipmentTable(
const InvoiceHandlingFeeList(
{Key key, this.shipments, this.onAdd, this.onRemove})
: super(key: key);

View File

@@ -1,107 +0,0 @@
import 'package:fcs/domain/entities/custom_duty.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';
typedef OnAdd(CustomDuty customDuty);
typedef OnRemove(CustomDuty customDuty);
class InvoiceCustomTable extends StatelessWidget {
final List<CustomDuty> customDuties;
final OnAdd onAdd;
final OnRemove onRemove;
const InvoiceCustomTable(
{Key key, this.customDuties, this.onAdd, this.onRemove})
: super(key: key);
@override
Widget build(BuildContext context) {
return MyDataTable(
headingRowHeight: 40,
columns: [
MyDataColumn(
label: LocalText(
context,
"rate.cutom.product_type",
color: Colors.grey,
),
),
MyDataColumn(
label: LocalText(
context,
"rate.custom.fee",
color: Colors.grey,
),
),
],
rows: getRows(context),
);
}
List<MyDataRow> getRows(BuildContext context) {
if (customDuties == null) {
return [];
}
double total = 0;
var rows = customDuties.map((c) {
total += c.fee;
return MyDataRow(
cells: [
MyDataCell(new Text(
c.productType == null ? "" : c.productType,
style: textStyle,
)),
MyDataCell(
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(c.fee == null ? "0" : c.fee.toString(), style: textStyle),
onRemove == null
? SizedBox(
width: 50,
)
: IconButton(
icon: Icon(
Icons.remove_circle,
color: primaryColor,
),
onPressed: () {
if (onRemove != null) onRemove(c);
})
],
),
),
],
);
}).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;
}
}

View File

@@ -2,7 +2,7 @@ import 'package:fcs/domain/entities/fcs_shipment.dart';
import 'package:fcs/domain/entities/user.dart';
import 'package:fcs/helpers/theme.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/progress.dart';
import 'package:flutter/cupertino.dart';
@@ -82,12 +82,15 @@ class _InvoiceCustomerListState extends State<InvoiceCustomerList> {
Widget _item(User customer) {
return InkWell(
onTap: () {
Navigator.of(context).push(CupertinoPageRoute(
onTap: () async {
bool created = await Navigator.of(context).push(CupertinoPageRoute(
builder: (context) => InvoiceEditor(
customer: customer,
fcsShipment: widget.fcsShipment,
)));
if (created ?? false) {
_load();
}
},
child: Padding(
padding: const EdgeInsets.only(left: 12.0, right: 12),

View File

@@ -1,170 +1,60 @@
import 'package:fcs/domain/constants.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/payment_method.dart';
import 'package:fcs/domain/entities/user.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/localization/app_translations.dart';
import 'package:fcs/pages/discount/discount_list.dart';
import 'package:fcs/pages/discount/model/discount_model.dart';
import 'package:fcs/pages/invoice/invoice_editor.dart';
import 'package:fcs/pages/main/model/language_model.dart';
import 'package:fcs/pages/main/model/main_model.dart';
import 'package:fcs/pages/carton/model/carton_model.dart';
import 'package:fcs/pages/invoice/editor/invoice_carton_table.dart';
import 'package:fcs/pages/invoice/invoice_table.dart';
import 'package:fcs/pages/invoice/model/invoice_model.dart';
import 'package:fcs/pages/invoice/widgets.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/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/fcs_id_icon.dart';
import 'package:fcs/pages/widgets/input_text.dart';
import 'package:fcs/pages/widgets/local_dropdown.dart';
import 'package:fcs/pages/widgets/fcs_icons.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/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: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 InvoiceInfoPage extends StatefulWidget {
class InvoiceInfo extends StatefulWidget {
final Invoice invoice;
final User customer;
InvoiceInfoPage({this.invoice, this.customer});
InvoiceInfo({this.invoice});
@override
_InvoiceInfoPageState createState() => _InvoiceInfoPageState();
_InvoiceInfoState createState() => _InvoiceInfoState();
}
class _InvoiceInfoPageState extends State<InvoiceInfoPage> {
User user;
class _InvoiceInfoState extends State<InvoiceInfo> {
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;
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
void initState() {
super.initState();
volumetricRatio = Provider.of<ShipmentRateModel>(context, listen: false)
.rate
.volumetricRatio;
_invoice = widget.invoice;
_invoice.shipments?.forEach((s) {
s.isSelected = true;
});
_loadCartons();
}
if (widget.invoice != null) {
_invoice = widget.invoice;
_invoiceNumberController.text = _invoice.invoiceNumber;
_dateController.text = dateFormatter.format(_invoice.invoiceDate);
_nameController.text = _invoice.userName;
_phoneController.text = _invoice.phoneNumber;
// _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';
// _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() async {
CartonModel cartonModel = Provider.of<CartonModel>(context, listen: false);
List<Carton> cartons = [];
for (var c in _invoice?.cartons ?? []) {
var _carton = await cartonModel.getCarton(c.id);
_carton.isChecked = true;
cartons.add(_carton);
}
_boxes = [
Carton(
shipmentNumber: "A202",
receiverNumber: "3",
receiverName: "Ko Myo Min",
boxNumber: "1",
rate: 7,
packageType: "General",
weight: 75,
status: "Packed",
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)
])
];
setState(() {
_invoice.cartons = cartons;
});
}
@override
@@ -174,21 +64,91 @@ class _InvoiceInfoPageState extends State<InvoiceInfoPage> {
@override
Widget build(BuildContext context) {
final nameBox = DisplayText(
iconData: Feather.user,
labelTextKey: 'invoice.customer_name',
text: user != null ? user.name : 'Ko Nyi');
bool isCanceled = _invoice.status == invoice_cancel_status;
var rateModel = Provider.of<ShipmentRateModel>(context);
var rate = rateModel.rate;
final fcsIDBox = DisplayText(
text: user != null ? user.fcsID : "FCS-KRUTUG",
labelTextKey: "box.fcs.id",
icon: FcsIDIcon(),
final cartonTable = InvoiceCartonTable(
cartons: _invoice.cartons,
rate: rate,
);
final statusBox = DisplayText(
text: _statusController.text,
iconData: Icons.av_timer,
labelTextKey: 'invoice.status');
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;
});
},
);
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 receiptsBtn = LocalButton(
textKey: "invoice.btn_payment_receipt",
callBack: _cancel,
);
final cancelBtn = LocalButton(
textKey: "invoice.cancel.btn",
callBack: _cancel,
);
return LocalProgress(
inAsyncCall: _isLoading,
@@ -203,71 +163,30 @@ class _InvoiceInfoPageState extends State<InvoiceInfoPage> {
shadowColor: Colors.transparent,
title: LocalText(context, 'invoice.form.title',
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(
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,
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")
getInvoiceStatus(context, _invoice),
headerBox,
_showCartons ? cartonTable : Container(),
_showCartons
? Divider(
color: primaryColor,
thickness: 2,
)
: Container(),
invoiceTableBox,
SizedBox(
height: 10,
),
paymentMethodBox,
SizedBox(
height: 10,
),
isCanceled ? Container() : receiptsBtn,
isCanceled ? Container() : cancelBtn,
],
),
),
@@ -275,528 +194,27 @@ class _InvoiceInfoPageState extends State<InvoiceInfoPage> {
);
}
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;
_cancel() {
showConfirmDialog(context, "invoice.cancel.confirm", _cancelInvoice);
}
getCustomFeeRows(BuildContext context) {
customFee = 0;
List<Widget> dataRow = [];
_cancelInvoice() async {
setState(() {
_isLoading = true;
});
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();
try {
InvoiceModel invoiceModel =
Provider.of<InvoiceModel>(context, listen: false);
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(),
)
],
),
));
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) {
double balance = 0;
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) {
return _boxes.map((p) {
p.cargoTypes.map((cargo) {
_cargoTypes.asMap().map((index, _cargo) {
if (_cargo.id == cargo.id) {
setState(() {
_cargoTypes[index].weight += cargo.weight;
});
}
});
await invoiceModel.cancelInvoice(_invoice);
Navigator.pop(context, true);
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {
setState(() {
_isLoading = false;
});
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) {
return _cargoTypes.asMap().entries.map((c) {
var cargo = c.value;
var amt = cargo.weight * cargo.rate;
return MyDataRow(
onSelectChanged: (bool selected) {},
cells: [
MyDataCell(new Text(
cargo.name,
style: textStyle,
)),
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,
// )),
// ])
// )
;
}
}
}

View File

@@ -1,6 +1,11 @@
import 'dart:io';
import 'package:fcs/helpers/api_helper.dart';
import 'package:fcs/helpers/firebase_helper.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/invoice/invoice_shipment_list.dart';
import 'package:fcs/pages/invoice/model/invoice_model.dart';
import 'package:fcs/pages/invoice/payment_pdf_screen.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';
@@ -21,7 +26,6 @@ class InvoiceList extends StatefulWidget {
class _InvoiceListState extends State<InvoiceList> {
bool _isLoading = false;
bool _showPaid = false;
var _controller = ScrollController();
@override
@@ -34,7 +38,8 @@ class _InvoiceListState extends State<InvoiceList> {
}
});
Provider.of<InvoiceModel>(context, listen: false).initData(false);
Provider.of<InvoiceModel>(context, listen: false)
.initData(widget.forCustomer, true, false);
}
@override
@@ -51,15 +56,29 @@ class _InvoiceListState extends State<InvoiceList> {
popmenus: [
LocalPopupMenu(
id: 1,
textKey: "invoice.popupmenu.pending",
textKey: "invoice.popupmenu.issused",
selected: invoiceModel.selectedIndex == 1),
LocalPopupMenu(
id: 2,
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(() {
_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);
}
}),
);
@@ -131,7 +150,7 @@ class _InvoiceListState extends State<InvoiceList> {
);
}
_newInvoice() {
_newInvoice() async {
Navigator.of(context)
.push(CupertinoPageRoute(builder: (context) => InvoiceShipmentList()));
}

View File

@@ -1,10 +1,10 @@
import 'dart:async';
import 'dart:io';
import 'package:fcs/domain/constants.dart';
import 'package:fcs/domain/entities/invoice.dart';
import 'package:fcs/helpers/theme.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/material.dart';
import 'package:flutter/services.dart';
@@ -12,7 +12,6 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:intl/intl.dart';
import 'package:path_provider/path_provider.dart';
import 'invoice_editor.dart';
import 'payment_page.dart';
import 'payment_pdf_screen.dart';
@@ -81,11 +80,11 @@ class _InvoiceListRowState extends State<InvoiceListRow> {
owner
? Navigator.of(context).push(CupertinoPageRoute(
builder: (context) => PaymentPDFScreen(
path: pdfPath,
url: _invoice.invoiceURL,
)))
: Navigator.of(context).push(CupertinoPageRoute(
builder: (context) => PaymentPDFScreen(
path: pdfPath,
url: _invoice.invoiceURL,
)));
},
child: new Row(
@@ -132,7 +131,7 @@ class _InvoiceListRowState extends State<InvoiceListRow> {
// padding: const EdgeInsets.all(0),
// child: getStatus(_invoice.status),
// ),
_invoice.status == "Pending"
_invoice.status == invoice_issued_status
? Padding(
padding: const EdgeInsets.only(left: 10.0),
child: InkWell(
@@ -191,10 +190,11 @@ class _InvoiceListRowState extends State<InvoiceListRow> {
),
],
),
onPressed: () {
onPressed: () async {
//to go invoice info page
Navigator.pop(context);
Navigator.of(context).push(CupertinoPageRoute(
builder: (context) => InvoiceInfoPage(invoice: _invoice)));
builder: (context) => InvoiceInfo(invoice: _invoice)));
},
)
],

View File

@@ -1,13 +1,13 @@
import 'package:fcs/domain/entities/fcs_shipment.dart';
import 'package:fcs/helpers/theme.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/progress.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'invoice_customer_list.dart';
import 'invoice_shipment_list_row.dart';
class InvoiceShipmentList extends StatefulWidget {
@@ -65,7 +65,14 @@ class _InvoiceShipmentListState extends State<InvoiceShipmentList> {
itemCount: _fcsShipments.length,
itemBuilder: (BuildContext context, int index) {
return InvoiceShipmentListRow(
fcsShipment: _fcsShipments[index]);
fcsShipment: _fcsShipments[index],
onSelect: (f) {
Navigator.of(context).push(CupertinoPageRoute(
builder: (context) => InvoiceCustomerList(
fcsShipment: f,
)));
},
);
}),
),
),

View File

@@ -8,9 +8,12 @@ import 'package:intl/intl.dart';
import '../main/util.dart';
import 'invoice_customer_list.dart';
typedef OnSelect(FcsShipment fcsShipment);
class InvoiceShipmentListRow extends StatefulWidget {
final OnSelect onSelect;
final FcsShipment fcsShipment;
const InvoiceShipmentListRow({this.fcsShipment});
const InvoiceShipmentListRow({this.fcsShipment, this.onSelect});
@override
_InvoiceShipmentListRowState createState() => _InvoiceShipmentListRowState();
@@ -35,10 +38,7 @@ class _InvoiceShipmentListRowState extends State<InvoiceShipmentListRow> {
padding: EdgeInsets.only(left: 15, right: 15),
child: InkWell(
onTap: () {
Navigator.of(context).push(CupertinoPageRoute(
builder: (context) => InvoiceCustomerList(
fcsShipment: _fcsShipment,
)));
if (widget.onSelect != null) widget.onSelect(widget.fcsShipment);
},
child: Row(
children: <Widget>[

View File

@@ -63,14 +63,14 @@ class InvoiceTable extends StatelessWidget {
List<InvoiceTableRow> getTableRows() {
List<InvoiceTableRow> tableRows = [];
// add cargo types
List<CargoType> _cargoTypes = invoice.getCargoTypes(rate);
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.amount.toStringAsFixed(2)}"));
amount: "${c.calAmount.toStringAsFixed(2)}"));
});
invoice.shipments.where((ss) => (ss.isSelected ?? false)).forEach((s) {
tableRows.add(InvoiceTableRow(
@@ -80,7 +80,7 @@ class InvoiceTable extends StatelessWidget {
rate: "",
amount: "${s.handlingFee.toStringAsFixed(2)}"));
});
// add custom fee
// // add custom fee
invoice.customDuties.forEach((c) {
tableRows.add(InvoiceTableRow(
data: c,
@@ -89,7 +89,7 @@ class InvoiceTable extends StatelessWidget {
rate: "",
amount: "${c.fee.toStringAsFixed(2)}"));
});
// add delivery fee
// // add delivery fee
tableRows.add(InvoiceTableRow(
data: invoice.deliveryFee == null || invoice.deliveryFee == 0
? null
@@ -99,7 +99,7 @@ class InvoiceTable extends StatelessWidget {
rate: "",
amount: "${invoice?.deliveryFee?.toStringAsFixed(2) ?? '0'}"));
// add discounts
// // add discounts
if (invoice.discount != null) {
tableRows.add(InvoiceTableRow(
data: invoice.discount,

View File

@@ -1,33 +1,30 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/config.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/receipt.dart';
import 'package:fcs/helpers/api_helper.dart';
import 'package:fcs/helpers/firebase_helper.dart';
import 'package:fcs/helpers/paginator.dart';
import 'package:fcs/pages/main/model/base_model.dart';
import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart';
class InvoiceModel extends BaseModel {
final log = Logger('InvoiceModel');
StreamSubscription<QuerySnapshot> listener;
List<Invoice> _invoices = [
Invoice(
invoiceNumber: 'A092(A)-33',
invoiceDate: DateTime(2020, 4, 6, 12, 15),
userName: 'Ko Myo Min',
phoneNumber: '+959 555555555',
amount: 300,
status: 'Pending',
)
];
List<Invoice> _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 isLoading = false;
@@ -45,15 +42,17 @@ class InvoiceModel extends BaseModel {
super.privilegeChanged();
}
initData(bool forCustomer) {
_selectedIndex = 1;
// _loadFcsInvoices(forCustomer);
initData(bool forCustomer, bool isCanceled, bool isPaid) {
_loadInvoices(forCustomer);
if (_paid != null) _paid.close();
_paid = _getPaid(forCustomer);
if (_paginator != null) _paginator.close();
_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 (!forCustomer && !user.hasInvoices()) return;
String path = "/$invoices_collection";
@@ -63,7 +62,7 @@ class InvoiceModel extends BaseModel {
try {
var q = Firestore.instance
.collection("$path")
.where("is_paid", isEqualTo: false)
.where("status", isEqualTo: invoice_issued_status)
.where("is_deleted", isEqualTo: false);
if (forCustomer) {
@@ -84,18 +83,24 @@ class InvoiceModel extends BaseModel {
}
}
Paginator _getPaid(bool isCustomer) {
Paginator _getPaginator(bool isCustomer, bool isCanceled, bool isPaid) {
if (!isCustomer) {
if (user == null || !(user.hasInvoices())) throw "No privilege";
}
var pageQuery = Firestore.instance
.collection("/$packages_collection")
.where("is_delivered", isEqualTo: true)
.collection("/$invoices_collection")
.where("is_deleted", isEqualTo: false);
if (isCustomer) {
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) {
return Invoice.fromMap(data, id);
});
@@ -103,11 +108,11 @@ class InvoiceModel extends BaseModel {
}
Future<void> loadMore({bool isCustomer}) async {
if (_paid.ended || _selectedIndex == 1)
if (_paginator.ended || _selectedIndex == 1)
return; // when paid menu is not selected return
isLoading = true;
notifyListeners();
await _paid.load(onFinished: () {
await _paginator.load(onFinished: () {
isLoading = false;
notifyListeners();
});
@@ -115,7 +120,7 @@ class InvoiceModel extends BaseModel {
Future<void> refresh({bool isCustomer}) async {
if (_selectedIndex == 1) return; // when paid menu is not selected return
await _paid.refresh(onFinished: () {
await _paginator.refresh(onFinished: () {
notifyListeners();
});
}
@@ -125,16 +130,24 @@ class InvoiceModel extends BaseModel {
}
logout() async {
if (_paid != null) _paid.close();
if (_paginator != null) _paginator.close();
if (listener != null) await listener.cancel();
_invoices = [];
}
Future<void> createInvoice(Invoice invoice) {
// return Services.instance.invoiceService.createInvoice(invoice);
Future<void> createInvoice(Invoice invoice) async {
File file = await downloadPDF("invoice", invoice.toMap());
String url = await uploadStorage("pdfs", file);
print("uploaded url: $url");
invoice.invoiceURL = url;
return Services.instance.invoiceService.createInvoice(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);
}
}

View File

@@ -1,23 +1,20 @@
import 'dart:async';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:fcs/helpers/cache_mgr.dart';
import 'package:fcs/helpers/theme.dart';
import 'package:fcs/pages/main/model/main_model.dart';
import 'package:fcs/pages/main/util.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/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;
final String url;
PaymentPDFScreen({Key key, this.path}) : super(key: key);
PaymentPDFScreen({Key key, this.url}) : super(key: key);
_PaymentPDFScreenState createState() => _PaymentPDFScreenState();
}
@@ -30,88 +27,100 @@ class _PaymentPDFScreenState extends State<PaymentPDFScreen>
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 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');
},
return LocalProgress(
inAsyncCall: _isLoading,
child: Scaffold(
appBar: AppBar(
centerTitle: true,
backgroundColor: Colors.white,
shadowColor: Colors.transparent,
title: LocalText(context, 'invoice.pdf',
color: Colors.white, fontSize: 20),
leading: new IconButton(
icon: new Icon(CupertinoIcons.back, color: primaryColor),
onPressed: () => Navigator.of(context).pop(),
),
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;
});
},
),
],
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(String url) async {
MainModel mainModel = Provider.of<MainModel>(context, listen: false);
String appUrl = mainModel.setting.appUrl;
_share() async {
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",
await Share.shareFiles([file.path],
mimeTypes: ["application/pdf"],
subject: "Invoice",
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;
}
}

View 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 ?? "")),
),
],
);
}

View File

@@ -16,7 +16,7 @@ import 'package:fcs/pages/delivery/delivery_list.dart';
import 'package:fcs/pages/discount/discount_list.dart';
import 'package:fcs/pages/faq/faq_list_page.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/main_model.dart';
import 'package:fcs/pages/main/util.dart';
@@ -298,11 +298,11 @@ class _HomePageState extends State<HomePage> {
final invoicesBtn = TaskButton("invoices.btn",
icon: FontAwesomeIcons.fileInvoice,
btnCallback: () => Navigator.of(context).push<void>(CupertinoPageRoute(
builder: (context) => InvoiceList(forCustomer: false))));
builder: (context) => InvoiceList(forCustomer: true))));
final invoicesBtnFcs = TaskButton("invoices.btn",
icon: FontAwesomeIcons.fileInvoice,
btnCallback: () => Navigator.of(context).push<void>(CupertinoPageRoute(
builder: (context) => InvoiceList(forCustomer: true))));
builder: (context) => InvoiceList(forCustomer: false))));
final discountBtn = TaskButton("discount.btn",
icon: Entypo.price_ribbon,

View File

@@ -68,9 +68,9 @@ class _ShipmentListState extends State<ShipmentList> {
if (p.id == 3) {
Provider.of<ShipmentModel>(context, listen: false)
.initData(widget.forCustomer, myPickup: true);
} else if (p.id == 1) {
} else {
Provider.of<ShipmentModel>(context, listen: false)
.initData(widget.forCustomer);
.initData(widget.forCustomer, myPickup: false);
}
}),
);

View File

@@ -28,7 +28,7 @@ packages:
name: cached_network_image
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.2+1"
version: "2.3.3"
camera:
dependency: "direct main"
description:
@@ -176,6 +176,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
ffi:
dependency: transitive
description:
name: ffi
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
file:
dependency: transitive
description:
@@ -246,13 +253,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.6"
fixnum:
dependency: transitive
description:
name: fixnum
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.11"
flutter:
dependency: "direct main"
description: flutter
@@ -266,12 +266,12 @@ packages:
source: hosted
version: "0.5.0"
flutter_cache_manager:
dependency: transitive
dependency: "direct main"
description:
name: flutter_cache_manager
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.2"
version: "2.0.0"
flutter_datetime_picker:
dependency: "direct main"
description:
@@ -454,7 +454,7 @@ packages:
name: path_provider
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.14"
version: "1.6.22"
path_provider_linux:
dependency: transitive
description:
@@ -476,6 +476,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
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:
dependency: transitive
description:
@@ -546,13 +553,6 @@ packages:
relative: true
source: path
version: "0.0.1"
protobuf:
dependency: transitive
description:
name: protobuf
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
provider:
dependency: "direct main"
description:
@@ -768,6 +768,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
win32:
dependency: transitive
description:
name: win32
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.3"
xdg_directories:
dependency: transitive
description:

View File

@@ -56,6 +56,7 @@ dependencies:
flutter_local_notifications: ^1.4.4+4
share: ^0.6.5
cached_network_image: ^2.3.2+1
flutter_cache_manager: ^2.0.0
dev_dependencies:
flutter_test: