From 036fbd7ac8c2da7fba9a71434a39f8119cba50b8 Mon Sep 17 00:00:00 2001 From: Gianfranco Gasbarri Date: Mon, 2 Mar 2020 23:44:54 +0000 Subject: [PATCH] Added Tax Rate Report --- lib/data/models/company_model.dart | 1 + lib/ui/reports/quote_report.dart | 24 +--- lib/ui/reports/reports_screen.dart | 38 +++--- lib/ui/reports/reports_screen_vm.dart | 13 ++ lib/ui/reports/tax_rate_report.dart | 177 ++++++++++++++++++++++++++ 5 files changed, 218 insertions(+), 35 deletions(-) create mode 100644 lib/ui/reports/tax_rate_report.dart diff --git a/lib/data/models/company_model.dart b/lib/data/models/company_model.dart index 88c686d55..f9a219dc0 100644 --- a/lib/data/models/company_model.dart +++ b/lib/data/models/company_model.dart @@ -566,6 +566,7 @@ abstract class UserSettingsEntity // TODO remove this @nullable @BuiltValueField(wireName: 'accent_color') + @nullable String get accentColor; @BuiltValueField(wireName: 'table_columns') diff --git a/lib/ui/reports/quote_report.dart b/lib/ui/reports/quote_report.dart index 29d8f2071..7ce1b3e83 100644 --- a/lib/ui/reports/quote_report.dart +++ b/lib/ui/reports/quote_report.dart @@ -18,10 +18,6 @@ enum QuoteReportFields { client_address2, client_shipping_address1, client_shipping_address2, - //vendor, - //vendor_city, - //vendor_state, - //vendor_country, status, discount, po_number, @@ -83,7 +79,6 @@ ReportResult quoteReport( QuoteReportFields.balance, QuoteReportFields.due_date, QuoteReportFields.client, - //QuoteReportFields.vendor, ]; if (quoteReportSettings.columns.isNotEmpty) { @@ -134,18 +129,6 @@ ReportResult quoteReport( case QuoteReportFields.client_shipping_address2: value = client.shippingAddress2; break; - //case QuoteReportFields.vendor: - // value = vendor.listDisplayName; - // break; - //case QuoteReportFields.vendor_city: - // value = vendor.city; - // break; - //case QuoteReportFields.vendor_state: - // value = vendor.state; - // break; - //case QuoteReportFields.vendor_country: - // value = staticState.countryMap[vendor.countryId].listDisplayName; - // break; case QuoteReportFields.status: value = staticState.invoiceStatusMap[quote.statusId].name; break; @@ -244,13 +227,14 @@ ReportResult quoteReport( } } - data.sort( - (rowA, rowB) => sortReportTableRows(rowA, rowB, quoteReportSettings)); + final selectedColumns = columns.map((item) => EnumUtils.parse(item)).toList(); + data.sort((rowA, rowB) => + sortReportTableRows(rowA, rowB, quoteReportSettings, selectedColumns)); return ReportResult( allColumns: QuoteReportFields.values.map((e) => EnumUtils.parse(e)).toList(), - columns: columns.map((item) => EnumUtils.parse(item)).toList(), + columns: selectedColumns, defaultColumns: defaultColumns.map((item) => EnumUtils.parse(item)).toList(), data: data, diff --git a/lib/ui/reports/reports_screen.dart b/lib/ui/reports/reports_screen.dart index db9df759f..552bf8da3 100644 --- a/lib/ui/reports/reports_screen.dart +++ b/lib/ui/reports/reports_screen.dart @@ -160,7 +160,7 @@ class ReportsScreen extends StatelessWidget { //kReportProduct, //kReportProfitAndLoss, //kReportTask, - //kReportTaxRate, + kReportTaxRate, kReportQuote, ] .map((report) => @@ -401,19 +401,22 @@ class _ReportDataTableState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - DataTable( - sortColumnIndex: reportSettings.sortTotalsIndex != null && - reportResult.columns.length > - reportSettings.sortTotalsIndex - ? reportSettings.sortTotalsIndex - : null, - sortAscending: reportSettings.sortTotalsAscending ?? true, - columns: reportResult.totalColumns( - context, - (index, ascending) => - widget.viewModel - .onReportTotalsSorted(index, ascending)), - rows: reportResult.totalRows(context), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: DataTable( + sortColumnIndex: reportSettings.sortTotalsIndex != null && + reportResult.columns.length > + reportSettings.sortTotalsIndex + ? reportSettings.sortTotalsIndex + : null, + sortAscending: reportSettings.sortTotalsAscending ?? true, + columns: reportResult.totalColumns( + context, + (index, ascending) => + widget.viewModel + .onReportTotalsSorted(index, ascending)), + rows: reportResult.totalRows(context), + ), ), ], ), @@ -472,9 +475,14 @@ ReportColumnType getReportColumnType(String column, BuildContext context) { 'paid_to_date', 'amount', 'quantity', - 'pridce', + 'price', 'cost', 'total', + 'invoice_amount', + 'tax_rate', + 'tax_amount', + 'tax_paid', + 'payment_amount' ].contains(column)) { return ReportColumnType.number; } else if (['is_active'].contains(column)) { diff --git a/lib/ui/reports/reports_screen_vm.dart b/lib/ui/reports/reports_screen_vm.dart index a47a8a848..3e3f99612 100644 --- a/lib/ui/reports/reports_screen_vm.dart +++ b/lib/ui/reports/reports_screen_vm.dart @@ -21,6 +21,7 @@ import 'package:invoiceninja_flutter/ui/reports/product_report.dart'; import 'package:invoiceninja_flutter/ui/reports/quote_report.dart'; import 'package:invoiceninja_flutter/ui/reports/reports_screen.dart'; import 'package:invoiceninja_flutter/ui/reports/task_report.dart'; +import 'package:invoiceninja_flutter/ui/reports/tax_rate_report.dart'; import 'package:invoiceninja_flutter/utils/completers.dart'; import 'package:invoiceninja_flutter/utils/formatting.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; @@ -171,6 +172,18 @@ class ReportsScreenVM { state.staticState, ); break; + case kReportTaxRate: + reportResult = memoizedTaxRateReport( + state.userCompany, + state.uiState.reportsUIState, + state.taxRateState.map, + state.invoiceState.map, + state.clientState.map, + state.paymentState.map, + state.userState.map, + state.staticState, + ); + break; // TODO: Obtain credit map //case kReportCredit: // reportResult = memoizedCreditReport( diff --git a/lib/ui/reports/tax_rate_report.dart b/lib/ui/reports/tax_rate_report.dart new file mode 100644 index 000000000..6a65fb71d --- /dev/null +++ b/lib/ui/reports/tax_rate_report.dart @@ -0,0 +1,177 @@ +import 'package:built_collection/built_collection.dart'; +import 'package:invoiceninja_flutter/utils/enums.dart'; +import 'package:invoiceninja_flutter/constants.dart'; +import 'package:invoiceninja_flutter/data/models/tax_rate_model.dart'; +import 'package:invoiceninja_flutter/data/models/company_model.dart'; +import 'package:invoiceninja_flutter/data/models/models.dart'; +import 'package:invoiceninja_flutter/redux/reports/reports_state.dart'; +import 'package:invoiceninja_flutter/redux/static/static_state.dart'; +import 'package:invoiceninja_flutter/ui/reports/reports_screen.dart'; +import 'package:memoize/memoize.dart'; + +enum TaxRateReportFields { + client, + invoice, + invoice_amount, + tax_name, + tax_rate, + tax_amount, + tax_paid, + payment_amount, + currency +} + +var memoizedTaxRateReport = memo8(( + UserCompanyEntity userCompany, + ReportsUIState reportsUIState, + BuiltMap taxRateMap, + BuiltMap invoiceMap, + BuiltMap clientMap, + BuiltMap paymentMap, + BuiltMap userMap, + StaticState staticState, +) => + taxRateReport(userCompany, reportsUIState, taxRateMap, invoiceMap, + clientMap, paymentMap, userMap, staticState)); + +ReportResult taxRateReport( + UserCompanyEntity userCompany, + ReportsUIState reportsUIState, + BuiltMap taxRateMap, + BuiltMap invoiceMap, + BuiltMap clientMap, + BuiltMap paymentMap, + BuiltMap userMap, + StaticState staticState, +) { + final List> data = []; + BuiltList columns; + + final reportSettings = userCompany.settings.reportSettings; + final taxRateReportSettings = + reportSettings != null && reportSettings.containsKey(kReportTaxRate) + ? reportSettings[kReportTaxRate] + : ReportSettingsEntity(); + + final defaultColumns = [ + TaxRateReportFields.tax_name, + TaxRateReportFields.tax_amount, + TaxRateReportFields.tax_paid, + TaxRateReportFields.invoice_amount, + TaxRateReportFields.invoice, + ]; + + if (taxRateReportSettings.columns.isNotEmpty) { + columns = BuiltList(taxRateReportSettings.columns + .map((e) => EnumUtils.fromString(TaxRateReportFields.values, e)) + .toList()); + } else { + columns = BuiltList(defaultColumns); + } + + for (var invoiceId in invoiceMap.keys) { + final invoice = invoiceMap[invoiceId]; + final client = clientMap[invoice.clientId]; + + final invoiceTaxAmount = invoice.calculateTaxes(invoice.usesInclusiveTaxes); + final invoicePaidAmount = invoice.amount - invoice.balance; + + for (var i in Iterable.generate(3)) { + bool skip = false; + final List row = []; + + final taxName = [invoice.taxName1, invoice.taxName2, invoice.taxName3][i]; + final taxRate = [invoice.taxRate1, invoice.taxRate2, invoice.taxRate3][i]; + + if (taxRate == null) { + continue; + } + + final invoiceCurrentTaxAmount = invoiceTaxAmount[taxName]; + + if (invoiceCurrentTaxAmount == null || invoiceCurrentTaxAmount == 0) + continue; + + double invoiceCurrentTaxPaidAmount; + if (invoice.amount != null && + invoice.amount > 0 && + invoiceCurrentTaxAmount != null && + invoiceCurrentTaxAmount > 0) { + invoiceCurrentTaxPaidAmount = + invoicePaidAmount / invoice.amount * invoiceCurrentTaxAmount; + } else { + invoiceCurrentTaxPaidAmount = 0; + } + + for (var column in columns) { + dynamic value = ''; + + switch (column) { + case TaxRateReportFields.client: + value = client.displayName; + break; + case TaxRateReportFields.invoice: + value = invoice.listDisplayName; + break; + case TaxRateReportFields.invoice_amount: + value = invoice.amount; + break; + case TaxRateReportFields.tax_name: + value = taxName; + break; + case TaxRateReportFields.tax_rate: + value = taxRate; + break; + case TaxRateReportFields.tax_amount: + value = invoiceCurrentTaxAmount ?? 0; + break; + case TaxRateReportFields.tax_paid: + value = invoiceCurrentTaxPaidAmount; + break; + case TaxRateReportFields.payment_amount: + value = invoicePaidAmount; + break; + case TaxRateReportFields.currency: + value = staticState.currencyMap[client.currencyId]?.name ?? + staticState.currencyMap[client.settings.currencyId]?.name; + break; + } + + if (!ReportResult.matchField( + value: value, + userCompany: userCompany, + reportsUIState: reportsUIState, + column: EnumUtils.parse(column), + )) { + skip = true; + } + + if (value.runtimeType == bool) { + row.add(invoice.getReportBool(value: value)); + } else if (value.runtimeType == double || value.runtimeType == int) { + row.add(invoice.getReportNumber( + value: value, currencyId: client.settings.currencyId)); + } else { + row.add(invoice.getReportString(value: value)); + } + } + + if (!skip) { + data.add(row); + } + } + } + + final selectedColumns = columns.map((item) => EnumUtils.parse(item)).toList(); + data.sort((rowA, rowB) => + sortReportTableRows(rowA, rowB, taxRateReportSettings, selectedColumns)); + + return ReportResult( + allColumns: + TaxRateReportFields.values.map((e) => EnumUtils.parse(e)).toList(), + columns: columns.map((item) => EnumUtils.parse(item)).toList(), + defaultColumns: + defaultColumns.map((item) => EnumUtils.parse(item)).toList(), + data: data, + ); +}