From fe02eeaeb483d1adb70a84dba3f0014a51a20acf Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 26 Oct 2021 10:51:12 +0300 Subject: [PATCH] Add contact report --- lib/constants.dart | 1 + lib/ui/reports/contact_report.dart | 351 ++++++++++++++++++++++++++ lib/ui/reports/reports_screen.dart | 1 + lib/ui/reports/reports_screen_vm.dart | 10 + 4 files changed, 363 insertions(+) create mode 100644 lib/ui/reports/contact_report.dart diff --git a/lib/constants.dart b/lib/constants.dart index 5ecc01a7b..6a8c6159c 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -465,6 +465,7 @@ const List kAdvancedSettings = [ ]; const String kReportClient = 'client'; +const String kReportContact = 'contact'; const String kReportCredit = 'credit'; const String kReportDocument = 'document'; const String kReportExpense = 'expense'; diff --git a/lib/ui/reports/contact_report.dart b/lib/ui/reports/contact_report.dart new file mode 100644 index 000000000..237e5faa1 --- /dev/null +++ b/lib/ui/reports/contact_report.dart @@ -0,0 +1,351 @@ +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/client_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:invoiceninja_flutter/utils/formatting.dart'; +import 'package:invoiceninja_flutter/utils/money.dart'; +import 'package:memoize/memoize.dart'; + +enum ContactReportFields { + name, + website, + currency, + language, + private_notes, + public_notes, + industry, + size, + address1, + address2, + city, + state, + postal_code, + phone, + country, + shipping_address1, + shipping_address2, + shipping_city, + shipping_state, + shipping_postal_code, + shipping_country, + client1, + client2, + client3, + client4, + created_by, + assigned_to, + balance, + credit_balance, + paid_to_date, + total, + converted_balance, + converted_credit_balance, + converted_paid_to_date, + converted_total, + number, + id_number, + vat_number, + contact_full_name, + contact_first_name, + contact_last_name, + contact_email, + contact_phone, + contact1, + contact2, + contact3, + contact4, + contact_last_login, + is_active, + created_at, + updated_at, +} + +var memoizedContactReport = memo5(( + UserCompanyEntity userCompany, + ReportsUIState reportsUIState, + BuiltMap clientMap, + BuiltMap userMap, + StaticState staticState, +) => + contactReport( + userCompany, reportsUIState, clientMap, userMap, staticState)); + +ReportResult contactReport( + UserCompanyEntity userCompany, + ReportsUIState reportsUIState, + BuiltMap clientMap, + BuiltMap userMap, + StaticState staticState, +) { + final List> data = []; + BuiltList columns; + + final reportSettings = userCompany.settings.reportSettings; + final clientReportSettings = + reportSettings != null && reportSettings.containsKey(kReportClient) + ? reportSettings[kReportClient] + : ReportSettingsEntity(); + + final defaultColumns = [ + ContactReportFields.name, + ContactReportFields.contact_email, + ContactReportFields.id_number, + ContactReportFields.vat_number, + ContactReportFields.currency, + ContactReportFields.balance, + ContactReportFields.paid_to_date, + ContactReportFields.country, + ]; + + if (clientReportSettings.columns.isNotEmpty) { + columns = BuiltList(clientReportSettings.columns + .map((e) => EnumUtils.fromString(ContactReportFields.values, e)) + .where((element) => element != null) + .toList()); + } else { + columns = BuiltList(defaultColumns); + } + + for (var clientId in clientMap.keys) { + final client = clientMap[clientId]; + if (client.isDeleted) { + continue; + } + + for (var contact in client.contacts) { + bool skip = false; + final List row = []; + + final exchangeRate = getExchangeRate(staticState.currencyMap, + fromCurrencyId: client.currencyId, + toCurrencyId: userCompany.company.currencyId); + + for (var column in columns) { + dynamic value = ''; + + switch (column) { + case ContactReportFields.name: + value = client.displayName; + break; + case ContactReportFields.website: + value = client.website; + break; + case ContactReportFields.currency: + value = + staticState.currencyMap[client.currencyId]?.listDisplayName ?? + ''; + break; + case ContactReportFields.language: + value = + staticState.languageMap[client.languageId]?.listDisplayName ?? + ''; + break; + case ContactReportFields.private_notes: + value = client.privateNotes; + break; + case ContactReportFields.public_notes: + value = client.publicNotes; + break; + case ContactReportFields.industry: + value = + staticState.industryMap[client.industryId]?.listDisplayName ?? + ''; + break; + case ContactReportFields.size: + value = staticState.sizeMap[client.sizeId]?.listDisplayName ?? ''; + break; + case ContactReportFields.client1: + value = client.customValue1; + break; + case ContactReportFields.client2: + value = client.customValue2; + break; + case ContactReportFields.client3: + value = client.customValue3; + break; + case ContactReportFields.client4: + value = client.customValue4; + break; + case ContactReportFields.address1: + value = client.address1; + break; + case ContactReportFields.address2: + value = client.address2; + break; + case ContactReportFields.city: + value = client.city; + break; + case ContactReportFields.state: + value = client.state; + break; + case ContactReportFields.postal_code: + value = client.postalCode; + break; + case ContactReportFields.country: + value = + staticState.countryMap[client.countryId]?.listDisplayName ?? ''; + break; + case ContactReportFields.shipping_address1: + value = client.shippingAddress1; + break; + case ContactReportFields.shipping_address2: + value = client.shippingAddress2; + break; + case ContactReportFields.shipping_city: + value = client.shippingCity; + break; + case ContactReportFields.shipping_state: + value = client.shippingState; + break; + case ContactReportFields.shipping_postal_code: + value = client.shippingPostalCode; + break; + case ContactReportFields.shipping_country: + value = staticState + .countryMap[client.shippingCountryId]?.listDisplayName ?? + ''; + break; + case ContactReportFields.phone: + value = client.phone; + break; + case ContactReportFields.number: + value = client.number; + break; + case ContactReportFields.id_number: + value = client.idNumber; + break; + case ContactReportFields.vat_number: + value = client.vatNumber; + break; + case ContactReportFields.assigned_to: + value = userMap[client.assignedUserId]?.listDisplayName ?? ''; + break; + case ContactReportFields.created_by: + value = userMap[client.createdUserId]?.listDisplayName ?? ''; + break; + case ContactReportFields.contact_full_name: + value = contact.fullName; + break; + case ContactReportFields.contact_first_name: + value = contact.firstName; + break; + case ContactReportFields.contact_last_name: + value = contact.lastName; + break; + case ContactReportFields.contact_email: + value = contact.email; + break; + case ContactReportFields.contact_phone: + value = contact.phone; + break; + case ContactReportFields.contact1: + value = contact.customValue1; + break; + case ContactReportFields.contact2: + value = contact.customValue2; + break; + case ContactReportFields.contact3: + value = contact.customValue3; + break; + case ContactReportFields.contact4: + value = contact.customValue4; + break; + case ContactReportFields.contact_last_login: + value = convertTimestampToDateString(contact.lastLogin); + break; + case ContactReportFields.total: + value = + contact.isPrimary ? (client.balance + client.paidToDate) : 0.0; + break; + case ContactReportFields.balance: + value = contact.isPrimary ? client.balance : 0.0; + break; + case ContactReportFields.credit_balance: + value = contact.isPrimary ? client.creditBalance : 0.0; + break; + case ContactReportFields.paid_to_date: + value = contact.isPrimary ? client.paidToDate : 0.0; + break; + case ContactReportFields.converted_total: + value = contact.isPrimary + ? ((client.balance + client.paidToDate) * exchangeRate) + : 0.0; + break; + case ContactReportFields.converted_balance: + value = contact.isPrimary ? (client.balance * exchangeRate) : 0.0; + break; + case ContactReportFields.converted_credit_balance: + value = + contact.isPrimary ? (client.creditBalance * exchangeRate) : 0.0; + break; + case ContactReportFields.converted_paid_to_date: + value = + contact.isPrimary ? (client.paidToDate * exchangeRate) : 0.0; + break; + case ContactReportFields.is_active: + value = client.isActive; + break; + case ContactReportFields.updated_at: + value = convertTimestampToDateString(client.updatedAt); + break; + case ContactReportFields.created_at: + value = convertTimestampToDateString(client.createdAt); + break; + } + + if (!ReportResult.matchField( + value: value, + userCompany: userCompany, + reportsUIState: reportsUIState, + column: EnumUtils.parse(column), + )) { + skip = true; + } + + if (value.runtimeType == bool) { + row.add(client.getReportBool(value: value)); + } else if (value.runtimeType == double || value.runtimeType == int) { + String currencyId = client.currencyId; + if ([ + ContactReportFields.converted_balance, + ContactReportFields.converted_credit_balance, + ContactReportFields.converted_paid_to_date, + ContactReportFields.converted_total, + ].contains(column)) { + currencyId = userCompany.company.currencyId; + } + row.add(client.getReportDouble( + value: value, + currencyId: currencyId, + exchangeRate: exchangeRate, + )); + } else { + row.add(client.getReportString(value: '$value')); + } + } + + if (!skip) { + data.add(row); + } + } + } + + final selectedColumns = columns.map((item) => EnumUtils.parse(item)).toList(); + data.sort((rowA, rowB) => + sortReportTableRows(rowA, rowB, clientReportSettings, selectedColumns)); + + return ReportResult( + allColumns: ContactReportFields.values + .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 8cf349569..cc91c7536 100644 --- a/lib/ui/reports/reports_screen.dart +++ b/lib/ui/reports/reports_screen.dart @@ -76,6 +76,7 @@ class ReportsScreen extends StatelessWidget { final reports = [ kReportClient, + kReportContact, if (state.company.isModuleEnabled(EntityType.invoice)) ...[ kReportInvoice, kReportInvoiceItem, diff --git a/lib/ui/reports/reports_screen_vm.dart b/lib/ui/reports/reports_screen_vm.dart index 486c36b56..693f3729a 100644 --- a/lib/ui/reports/reports_screen_vm.dart +++ b/lib/ui/reports/reports_screen_vm.dart @@ -14,6 +14,7 @@ import 'package:invoiceninja_flutter/redux/reports/reports_actions.dart'; import 'package:invoiceninja_flutter/redux/reports/reports_state.dart'; import 'package:invoiceninja_flutter/redux/settings/settings_actions.dart'; import 'package:invoiceninja_flutter/ui/reports/client_report.dart'; +import 'package:invoiceninja_flutter/ui/reports/contact_report.dart'; import 'package:invoiceninja_flutter/ui/reports/document_report.dart'; import 'package:invoiceninja_flutter/ui/reports/expense_report.dart'; import 'package:invoiceninja_flutter/ui/reports/invoice_report.dart'; @@ -252,6 +253,15 @@ class ReportsScreenVM { state.staticState, ); break; + case kReportContact: + reportResult = memoizedContactReport( + state.userCompany, + state.uiState.reportsUIState, + state.clientState.map, + state.userState.map, + state.staticState, + ); + break; default: reportResult = memoizedClientReport( state.userCompany,