diff --git a/assets/local/localization_en.json b/assets/local/localization_en.json index adcf3fe..c60d571 100644 --- a/assets/local/localization_en.json +++ b/assets/local/localization_en.json @@ -471,6 +471,8 @@ "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 ================================================================":"", "Discount Start ================================================================":"", diff --git a/assets/local/localization_mu.json b/assets/local/localization_mu.json index 8e064c6..141d669 100644 --- a/assets/local/localization_mu.json +++ b/assets/local/localization_mu.json @@ -8,7 +8,7 @@ "btn.approve":"အတည်ပြုရန်", "btn.delete":"ဖျက်ရန်", "btn.select":"ရွေးချယ်ပါ", - "btn.cancel":"ဖျက်သိမ်းမည်", + "btn.cancel":"မလုပ်နဲ့", "btn.ok": "အိုကေ", "feet":"ပေ", "inch":"လက်မ", @@ -472,6 +472,8 @@ "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 ================================================================":"", "Discount Start ================================================================":"", diff --git a/lib/data/provider/fcs_shipment_data_provider.dart b/lib/data/provider/fcs_shipment_data_provider.dart index 530a85d..9fbf375 100644 --- a/lib/data/provider/fcs_shipment_data_provider.dart +++ b/lib/data/provider/fcs_shipment_data_provider.dart @@ -25,4 +25,10 @@ class FcsShipmentDataProvider { return await requestAPI("/fcs_shipments/ship", "PUT", payload: fcsShipment.toMap(), token: await getToken()); } + + Future reportFcsShipment(FcsShipment fcsShipment) async { + dynamic data = await requestAPI("/fcs_shipments/report", "POST", + payload: fcsShipment.toMap(), token: await getToken()); + return data["url"]; + } } diff --git a/lib/data/provider/invoice_data_provider.dart b/lib/data/provider/invoice_data_provider.dart index 1afee73..380f9e4 100644 --- a/lib/data/provider/invoice_data_provider.dart +++ b/lib/data/provider/invoice_data_provider.dart @@ -1,4 +1,5 @@ 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'; @@ -22,4 +23,14 @@ class InvoiceDataProvider { return await requestAPI("/invoices/cancel", "PUT", payload: {"id": invoice.id}, token: await getToken()); } + + Future pay(Payment payment) async { + return await requestAPI("/invoices/pay", "PUT", + payload: payment.toMap(), token: await getToken()); + } + + Future updatPaymentStatus(Payment payment) async { + return await requestAPI("/invoices/pay/status", "PUT", + payload: payment.toMap(), token: await getToken()); + } } diff --git a/lib/data/services/fcs_shipment_imp.dart b/lib/data/services/fcs_shipment_imp.dart index 4eb5e94..6ffaafa 100644 --- a/lib/data/services/fcs_shipment_imp.dart +++ b/lib/data/services/fcs_shipment_imp.dart @@ -33,4 +33,9 @@ class FcsShipmentServiceImp implements FcsShipmentService { Future ship(FcsShipment fcsShipment) { return shipmentDataProvider.ship(fcsShipment); } + + @override + Future report(FcsShipment fcsShipment) { + return shipmentDataProvider.reportFcsShipment(fcsShipment); + } } diff --git a/lib/data/services/fcs_shipment_service.dart b/lib/data/services/fcs_shipment_service.dart index 46e4ba8..2284122 100644 --- a/lib/data/services/fcs_shipment_service.dart +++ b/lib/data/services/fcs_shipment_service.dart @@ -5,4 +5,5 @@ abstract class FcsShipmentService { Future updateFcsShipment(FcsShipment fcsShipment); Future deleteFcsShipment(FcsShipment fcsShipment); Future ship(FcsShipment fcsShipment); + Future report(FcsShipment fcsShipment); } diff --git a/lib/data/services/invoice_imp.dart b/lib/data/services/invoice_imp.dart index 3fe5df1..c26dab4 100644 --- a/lib/data/services/invoice_imp.dart +++ b/lib/data/services/invoice_imp.dart @@ -3,6 +3,7 @@ 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'; @@ -31,4 +32,14 @@ class InvoiceServiceImp implements InvoiceService { Future updateInvoice(Invoice invoice) { return invoiceDataProvider.updateInvoice(invoice); } + + @override + Future pay(Payment payment) { + return invoiceDataProvider.pay(payment); + } + + @override + Future updatPaymentStatus(Payment payment) { + return invoiceDataProvider.updatPaymentStatus(payment); + } } diff --git a/lib/data/services/invoice_service.dart b/lib/data/services/invoice_service.dart index feed4e2..270fed7 100644 --- a/lib/data/services/invoice_service.dart +++ b/lib/data/services/invoice_service.dart @@ -1,8 +1,10 @@ import 'package:fcs/domain/entities/invoice.dart'; -import 'package:fcs/domain/entities/shipment.dart'; +import 'package:fcs/domain/entities/payment.dart'; abstract class InvoiceService { Future createInvoice(Invoice invoice); Future updateInvoice(Invoice invoice); Future cancelInvoice(Invoice invoice); + Future pay(Payment payment); + Future updatPaymentStatus(Payment payment); } diff --git a/lib/domain/constants.dart b/lib/domain/constants.dart index 7243933..fe1d6e4 100644 --- a/lib/domain/constants.dart +++ b/lib/domain/constants.dart @@ -27,6 +27,7 @@ const user_joined_status = "joined"; const pkg_files_path = "/packages"; const shipment_labels_files_path = "/shipment_labels"; +const receipt_labels_files_path = "/receipts"; // Link page const page_payment_methods = "payment_methods"; @@ -97,3 +98,8 @@ 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"; diff --git a/lib/domain/entities/fcs_shipment.dart b/lib/domain/entities/fcs_shipment.dart index 5a6264a..adfc4f2 100644 --- a/lib/domain/entities/fcs_shipment.dart +++ b/lib/domain/entities/fcs_shipment.dart @@ -13,6 +13,7 @@ class FcsShipment { String port; String destination; String status; + String reportName; FcsShipment({ this.id, this.shipmentNumber, @@ -24,6 +25,7 @@ class FcsShipment { this.consignee, this.port, this.destination, + this.reportName, }); factory FcsShipment.fromMap(Map map, String docID) { @@ -57,6 +59,7 @@ class FcsShipment { 'port': port, 'destination': destination, 'status': status, + 'report_name': reportName, }; } diff --git a/lib/domain/entities/invoice.dart b/lib/domain/entities/invoice.dart index b7c3460..b614fcc 100644 --- a/lib/domain/entities/invoice.dart +++ b/lib/domain/entities/invoice.dart @@ -4,6 +4,7 @@ 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/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/shipment.dart'; @@ -28,6 +29,7 @@ class Invoice { List cartons; List cargoTypes; List shipments; + List payments; Discount discount; PaymentMethod paymentMethod; String invoiceURL; @@ -70,6 +72,8 @@ class Invoice { return total; } + double get balance => (amount ?? 0) - (paidAmount ?? 0); + double getNetAmount(Rate rate) { List cargoTypes = getCargoTypes(rate); var total = cargoTypes.fold(0.0, (p, c) => c.calAmount + p); @@ -108,6 +112,7 @@ class Invoice { this.userName, this.phoneNumber, this.amount, + this.paidAmount, this.discount, this.status, this.customDuties, @@ -118,6 +123,7 @@ class Invoice { this.fcsShipmentID, this.shipments, this.invoiceURL, + this.payments, this.paymentMethod}); factory Invoice.fromMap(Map map, String docID) { @@ -142,6 +148,8 @@ class Invoice { : null; var discountMap = map['discount']; var discount = Discount.fromMap(discountMap, discountMap['id']); + var paymentMaps = List>.from(map['payments'] ?? []); + var payments = paymentMaps.map((e) => Payment.fromMap(e, e["id"])).toList(); return Invoice( id: docID, invoiceNumber: map['invoice_number'], @@ -150,6 +158,7 @@ class Invoice { fcsID: map['fcs_id'], phoneNumber: map['phone_number'], amount: map['amount'], + paidAmount: double.tryParse(map['paid_amount'].toString()) ?? 0, status: map['status'], cartons: cartons, cargoTypes: cargoTypes, @@ -159,6 +168,7 @@ class Invoice { invoiceURL: map['invoice_url'], paymentMethod: paymentMethod, discount: discount, + payments: payments, ); } diff --git a/lib/domain/entities/payment.dart b/lib/domain/entities/payment.dart new file mode 100644 index 0000000..d30eebc --- /dev/null +++ b/lib/domain/entities/payment.dart @@ -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 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 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; +} diff --git a/lib/helpers/api_helper.dart b/lib/helpers/api_helper.dart index 38415ef..06fca69 100644 --- a/lib/helpers/api_helper.dart +++ b/lib/helpers/api_helper.dart @@ -41,7 +41,7 @@ Future requestAPI( method: method, baseUrl: url == null ? Config.instance.apiURL : url, connectTimeout: 10000, - receiveTimeout: 10000, + receiveTimeout: 60000, headers: headers, ); log.info("baseUrl:${options.baseUrl}, path:$path"); diff --git a/lib/pages/fcs_shipment/fcs_shipment_info.dart b/lib/pages/fcs_shipment/fcs_shipment_info.dart index 81c2fa8..819e7f6 100644 --- a/lib/pages/fcs_shipment/fcs_shipment_info.dart +++ b/lib/pages/fcs_shipment/fcs_shipment_info.dart @@ -6,6 +6,7 @@ import 'package:fcs/pages/main/util.dart'; import 'package:fcs/pages/widgets/display_text.dart'; import 'package:fcs/pages/widgets/local_button.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/progress.dart'; import 'package:flutter/cupertino.dart'; @@ -200,13 +201,8 @@ class _FcsShipmentInfoState extends State { return PopupMenuButton( elevation: 3.2, icon: Icon(Icons.more_vert, color: primaryColor), - tooltip: 'This is tooltip', onSelected: (choice) { - print(choice.id); - if (choice.id == 1) { - } else if (choice.id == 2) { - } else if (choice.id == 3) { - } else if (choice.id == 4) {} + _showPDF(choice.id); }, itemBuilder: (BuildContext context) { return menuPopup.map((PopupMenu choice) { @@ -252,4 +248,39 @@ class _FcsShipmentInfoState extends State { }); } } + + _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(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; + }); + } + } } diff --git a/lib/pages/fcs_shipment/model/fcs_shipment_model.dart b/lib/pages/fcs_shipment/model/fcs_shipment_model.dart index 6d60365..aaf40a4 100644 --- a/lib/pages/fcs_shipment/model/fcs_shipment_model.dart +++ b/lib/pages/fcs_shipment/model/fcs_shipment_model.dart @@ -172,4 +172,8 @@ class FcsShipmentModel extends BaseModel { Future ship(FcsShipment fcsShipment) { return Services.instance.fcsShipmentService.ship(fcsShipment); } + + Future report(FcsShipment fcsShipment) { + return Services.instance.fcsShipmentService.report(fcsShipment); + } } diff --git a/lib/pages/invoice/editor/invoice_editor.dart b/lib/pages/invoice/editor/invoice_editor.dart index a56c531..783a4f2 100644 --- a/lib/pages/invoice/editor/invoice_editor.dart +++ b/lib/pages/invoice/editor/invoice_editor.dart @@ -437,6 +437,7 @@ class _InvoiceEditorState extends State { 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); diff --git a/lib/pages/invoice/invoice_list.dart b/lib/pages/invoice/invoice_list.dart index 14b1012..567c21d 100644 --- a/lib/pages/invoice/invoice_list.dart +++ b/lib/pages/invoice/invoice_list.dart @@ -1,11 +1,6 @@ -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'; @@ -38,8 +33,9 @@ class _InvoiceListState extends State { } }); - Provider.of(context, listen: false) - .initData(widget.forCustomer, true, false); + InvoiceModel invoiceModel = + Provider.of(context, listen: false); + invoiceModel.initData(widget.forCustomer, true, false); } @override @@ -49,7 +45,6 @@ class _InvoiceListState extends State { @override Widget build(BuildContext context) { - var owner = true; var invoiceModel = Provider.of(context); final popupMenu = LocalPopupMenuButton( @@ -98,8 +93,9 @@ class _InvoiceListState extends State { color: Colors.white, fontSize: 20), actions: [popupMenu], ), - floatingActionButton: owner - ? FloatingActionButton.extended( + floatingActionButton: widget.forCustomer + ? null + : FloatingActionButton.extended( onPressed: () { _newInvoice(); }, @@ -107,8 +103,7 @@ class _InvoiceListState extends State { label: LocalText(context, 'invoices.add', color: Colors.white), backgroundColor: primaryColor, - ) - : null, + ), body: Column( children: [ Expanded( @@ -118,9 +113,9 @@ class _InvoiceListState extends State { controller: _controller, separatorBuilder: (context, index) => Divider( color: Colors.black, + height: 1, ), scrollDirection: Axis.vertical, - padding: EdgeInsets.only(top: 15), shrinkWrap: true, itemCount: invoiceModel.invoices.length, itemBuilder: (BuildContext context, int index) { diff --git a/lib/pages/invoice/invoice_list_row.dart b/lib/pages/invoice/invoice_list_row.dart index 384f3af..a06d268 100644 --- a/lib/pages/invoice/invoice_list_row.dart +++ b/lib/pages/invoice/invoice_list_row.dart @@ -1,137 +1,75 @@ -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:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:intl/intl.dart'; -import 'package:path_provider/path_provider.dart'; -import 'payment_page.dart'; -import 'payment_pdf_screen.dart'; +import 'payment/payment_page.dart'; +import '../widgets/pdf_screen.dart'; -class InvoiceListRow extends StatefulWidget { +class InvoiceListRow extends StatelessWidget { + final dateFormatter = new DateFormat('dd MMM yyyy'); final Invoice invoice; InvoiceListRow({Key key, this.invoice}) : super(key: key); - @override - _InvoiceListRowState createState() => _InvoiceListRowState(); -} - -class _InvoiceListRowState extends State { - 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 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 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 Widget build(BuildContext context) { - var owner = true; - return Container( - padding: EdgeInsets.only(left: 15, right: 15), + return InkWell( + onTap: () { + Navigator.of(context).push(CupertinoPageRoute( + builder: (context) => PDFScreen( + title: invoice.invoiceNumber, + url: invoice.invoiceURL, + ))); + }, child: Row( children: [ Expanded( child: new Padding( padding: const EdgeInsets.symmetric(vertical: 10.0), - child: InkWell( - onTap: () { - owner - ? Navigator.of(context).push(CupertinoPageRoute( - builder: (context) => PaymentPDFScreen( - url: _invoice.invoiceURL, - ))) - : Navigator.of(context).push(CupertinoPageRoute( - builder: (context) => PaymentPDFScreen( - url: _invoice.invoiceURL, - ))); - }, - child: new Row( - children: [ - Container( - padding: EdgeInsets.only(left: 5, right: 10), - child: Icon( - FontAwesomeIcons.fileInvoice, - color: primaryColor, - size: 30, - ), + child: new Row( + children: [ + Container( + padding: EdgeInsets.only(left: 5, right: 10), + child: Icon( + FontAwesomeIcons.fileInvoice, + color: primaryColor, + size: 30, ), - new Expanded( + ), + new Expanded( + child: Padding( + padding: const EdgeInsets.only(left: 0), child: new Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsets.only(left: 8.0), - child: new Text( - _invoice.invoiceNumber == null - ? '' - : _invoice.invoiceNumber, - style: new TextStyle( - fontSize: 15.0, color: Colors.black), - ), + new Text( + invoice.invoiceNumber ?? "", + style: new TextStyle( + fontSize: 15.0, color: Colors.black), ), - Padding( - padding: const EdgeInsets.only(left: 10.0, top: 10), - child: new Text( - dateFormatter.format(_invoice.invoiceDate), - style: new TextStyle( - fontSize: 15.0, color: Colors.grey), - ), + new Text( + invoice.status ?? "", + style: new TextStyle( + fontSize: 13.0, color: primaryColor), + ), + new Text( + dateFormatter.format(invoice.invoiceDate), + style: new TextStyle( + fontSize: 15.0, color: Colors.grey), ) ], ), ), - ], - ), + ), + ], ), ), ), - // Padding( - // padding: const EdgeInsets.all(0), - // child: getStatus(_invoice.status), - // ), - _invoice.status == invoice_issued_status + invoice.status == invoice_issued_status ? Padding( padding: const EdgeInsets.only(left: 10.0), child: InkWell( @@ -143,27 +81,29 @@ class _InvoiceListRowState extends State { color: primaryColor, ), Padding( - padding: const EdgeInsets.only(left: 8.0), - child: Text("Payment"), + padding: const EdgeInsets.only(left: 3.0), + child: Text( + "Payment", + style: TextStyle(fontSize: 12, color: Colors.black), + ), ) ], ), onPressed: () { Navigator.of(context).push(CupertinoPageRoute( - builder: (context) => - PaymentPage(invoice: _invoice))); + builder: (context) => PaymentPage(invoice: invoice))); }, )), ) : Container(), Padding( padding: const EdgeInsets.only(left: 8.0), - child: InkWell( - child: Icon( + child: IconButton( + icon: Icon( Icons.more_vert, color: primaryColor, ), - onTap: () { + onPressed: () { var act = actionSheet(context); showCupertinoModalPopup( context: context, builder: (BuildContext context) => act); @@ -194,7 +134,7 @@ class _InvoiceListRowState extends State { //to go invoice info page Navigator.pop(context); Navigator.of(context).push(CupertinoPageRoute( - builder: (context) => InvoiceInfo(invoice: _invoice))); + builder: (context) => InvoiceInfo(invoice: invoice))); }, ) ], diff --git a/lib/pages/invoice/invoice_table.dart b/lib/pages/invoice/invoice_table.dart index a1a5ee3..b25c222 100644 --- a/lib/pages/invoice/invoice_table.dart +++ b/lib/pages/invoice/invoice_table.dart @@ -2,8 +2,6 @@ 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'; @@ -227,151 +225,6 @@ class InvoiceTable extends StatelessWidget { ), )); - // 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; } } diff --git a/lib/pages/invoice/model/invoice_model.dart b/lib/pages/invoice/model/invoice_model.dart index 2a6b007..f8f7758 100644 --- a/lib/pages/invoice/model/invoice_model.dart +++ b/lib/pages/invoice/model/invoice_model.dart @@ -1,18 +1,16 @@ 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/helpers/api_helper.dart'; +import 'package:fcs/domain/entities/payment.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'; +import 'package:path/path.dart' as Path; class InvoiceModel extends BaseModel { final log = Logger('InvoiceModel'); @@ -135,11 +133,33 @@ class InvoiceModel extends BaseModel { _invoices = []; } + Future getInvoice(String id) async { + 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 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 updatePaymentStatus(Payment payment) async { + return Services.instance.invoiceService.updatPaymentStatus(payment); + } + Future 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); } diff --git a/lib/pages/invoice/payment/payment_page.dart b/lib/pages/invoice/payment/payment_page.dart new file mode 100644 index 0000000..4a85022 --- /dev/null +++ b/lib/pages/invoice/payment/payment_page.dart @@ -0,0 +1,319 @@ +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/invoice/payment/payment_page_edit.dart'; +import 'package:fcs/pages/main/util.dart'; +import 'package:fcs/pages/widgets/image_file_picker.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: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'; +import 'package:provider/provider.dart'; + +class PaymentPage extends StatefulWidget { + final Invoice invoice; + PaymentPage({this.invoice}); + + @override + _PaymentPageState createState() => _PaymentPageState(); +} + +class _PaymentPageState extends State { + 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() { + super.initState(); + _invoice = widget.invoice; + _loadInvoice(); + } + + _loadInvoice() async { + InvoiceModel invoiceModel = + Provider.of(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: [ + amountBox, + SizedBox(height: 10), + Align( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: receiptFileBox, + ), + alignment: Alignment.centerLeft, + ), + payBtnBox, + SizedBox(height: 15), + LocalTitle(textKey: "pm.receipt"), + Column( + children: getCustomFeeRows(context), + ), + SizedBox(height: 25), + ], + ), + ), + ); + } + + getCustomFeeRows(BuildContext context) { + List 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}')], + ))), + 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))), + 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)))), + 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(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(context, listen: false); + await invoiceModel.pay(payment, _file); + Navigator.pop(context, true); + } catch (e) { + showMsgDialog(context, "Error", e.toString()); + } finally { + setState(() { + _isLoading = false; + }); + } + } +} diff --git a/lib/pages/invoice/payment_page_edit.dart b/lib/pages/invoice/payment/payment_page_edit.dart similarity index 100% rename from lib/pages/invoice/payment_page_edit.dart rename to lib/pages/invoice/payment/payment_page_edit.dart diff --git a/lib/pages/invoice/payment_page.dart b/lib/pages/invoice/payment_page.dart deleted file mode 100644 index 52ff087..0000000 --- a/lib/pages/invoice/payment_page.dart +++ /dev/null @@ -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 { - 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: [ - 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 dataRow = []; - - dataRow = [].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 _dialog(BuildContext context) { - return showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: Container( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Column( - mainAxisSize: MainAxisSize.min, - children: [ - 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: [ - 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; - } -} diff --git a/lib/pages/package/model/package_model.dart b/lib/pages/package/model/package_model.dart index f1608d6..efa1134 100644 --- a/lib/pages/package/model/package_model.dart +++ b/lib/pages/package/model/package_model.dart @@ -7,7 +7,6 @@ import 'package:fcs/domain/constants.dart'; import 'package:fcs/domain/entities/package.dart'; import 'package:fcs/domain/entities/user.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/paginator.dart'; import 'package:fcs/pages/main/model/base_model.dart'; diff --git a/lib/pages/receiving/receiving_editor.dart b/lib/pages/receiving/receiving_editor.dart index f8c59d3..e0dd8ad 100644 --- a/lib/pages/receiving/receiving_editor.dart +++ b/lib/pages/receiving/receiving_editor.dart @@ -1,4 +1,3 @@ -import 'package:barcode_scan/barcode_scan.dart'; import 'package:fcs/domain/entities/package.dart'; import 'package:fcs/domain/entities/user.dart'; import 'package:fcs/helpers/theme.dart'; @@ -137,7 +136,8 @@ class _ReceivingEditorState extends State { appBar: AppBar( centerTitle: true, 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(), ), shadowColor: Colors.transparent, diff --git a/lib/pages/widgets/img_picker.dart b/lib/pages/widgets/img_picker.dart new file mode 100644 index 0000000..71fc9c2 --- /dev/null +++ b/lib/pages/widgets/img_picker.dart @@ -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 { + 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 _dialog(BuildContext context, cameraPress(), photoPress()) { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: Container( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: Icon( + FontAwesomeIcons.camera, + size: 30, + color: widget.color ?? Colors.blue, + ), + onPressed: () { + Navigator.pop(context); + cameraPress(); + }), + Text("Camera") + ], + ), + Column( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: Icon( + Icons.photo_library, + size: 30, + color: widget.color ?? Colors.blue, + ), + onPressed: () { + Navigator.pop(context); + photoPress(); + }), + Text("Gallery") + ], + ), + ], + ), + ), + ), + ); + }, + ); + } +} diff --git a/lib/pages/invoice/payment_pdf_screen.dart b/lib/pages/widgets/pdf_screen.dart similarity index 88% rename from lib/pages/invoice/payment_pdf_screen.dart rename to lib/pages/widgets/pdf_screen.dart index 7f6ddf9..e9a1397 100644 --- a/lib/pages/invoice/payment_pdf_screen.dart +++ b/lib/pages/widgets/pdf_screen.dart @@ -4,23 +4,22 @@ 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/local_text.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 PaymentPDFScreen extends StatefulWidget { +class PDFScreen extends StatefulWidget { + final String title; final String url; - PaymentPDFScreen({Key key, this.url}) : super(key: key); + PDFScreen({Key key, this.url, this.title}) : super(key: key); - _PaymentPDFScreenState createState() => _PaymentPDFScreenState(); + _PDFScreenState createState() => _PDFScreenState(); } -class _PaymentPDFScreenState extends State - with WidgetsBindingObserver { +class _PDFScreenState extends State with WidgetsBindingObserver { final Completer _controller = Completer(); int pages = 0; @@ -59,8 +58,8 @@ class _PaymentPDFScreenState extends State centerTitle: true, backgroundColor: Colors.white, shadowColor: Colors.transparent, - title: LocalText(context, 'invoice.pdf', - color: Colors.white, fontSize: 20), + title: + Text(widget.title ?? "", style: TextStyle(color: primaryColor)), leading: new IconButton( icon: new Icon(CupertinoIcons.back, color: primaryColor), onPressed: () => Navigator.of(context).pop(), @@ -120,7 +119,7 @@ class _PaymentPDFScreenState extends State final RenderBox box = context.findRenderObject(); await Share.shareFiles([file.path], mimeTypes: ["application/pdf"], - subject: "Invoice", + subject: "File", sharePositionOrigin: box.localToGlobal(Offset.zero) & box.size); } } diff --git a/lib/pages/widgets/show_img.dart b/lib/pages/widgets/show_img.dart index a107dd6..130cdad 100644 --- a/lib/pages/widgets/show_img.dart +++ b/lib/pages/widgets/show_img.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:fcs/helpers/theme.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:photo_view/photo_view.dart'; @@ -21,12 +22,19 @@ class _ShowImageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( + leading: new IconButton( + icon: new Icon(Icons.close), + onPressed: () => Navigator.of(context).pop(), + ), backgroundColor: primaryColor, - title: Text(widget.fileName, style: TextStyle(color: Colors.white)), + shadowColor: Colors.transparent, iconTheme: new IconThemeData(color: Colors.white), ), body: Center( child: PhotoView( + backgroundDecoration: const BoxDecoration( + color: primaryColor, + ), imageProvider: widget.url != null ? NetworkImage(widget.url) : widget.imageFile != null