// Dart imports: import 'dart:convert'; // Package imports: import 'package:built_collection/built_collection.dart'; import 'package:built_value/built_value.dart'; import 'package:built_value/serializer.dart'; // Project imports: import 'package:invoiceninja_flutter/.env.dart'; import 'package:invoiceninja_flutter/constants.dart'; import 'package:invoiceninja_flutter/data/models/account_model.dart'; import 'package:invoiceninja_flutter/data/models/company_gateway_model.dart'; import 'package:invoiceninja_flutter/data/models/group_model.dart'; import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/data/models/payment_term_model.dart'; import 'package:invoiceninja_flutter/data/models/system_log_model.dart'; import 'package:invoiceninja_flutter/data/models/tax_model.dart'; import 'package:invoiceninja_flutter/main_app.dart'; import 'package:invoiceninja_flutter/redux/dashboard/dashboard_state.dart'; import 'package:invoiceninja_flutter/utils/formatting.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:invoiceninja_flutter/utils/strings.dart'; part 'company_model.g.dart'; class CompanyFields { static const String name = 'name'; static const String email = 'email'; static const String address1 = 'address1'; static const String address2 = 'address2'; static const String city = 'city'; static const String postalCode = 'postal_code'; static const String country = 'country'; static const String vatNumber = 'vat_number'; static const String idNumber = 'id_number'; static const String state = 'state'; static const String phone = 'phone'; static const String website = 'website'; static const String custom1 = 'custom1'; static const String custom2 = 'custom2'; static const String custom3 = 'custom3'; static const String custom4 = 'custom4'; static const String cityStatePostal = 'city_state_postal'; static const String postalCityState = 'postal_city_state'; static const String postalCity = 'postal_city'; } abstract class CompanyEntity extends Object with BaseEntity implements Built { factory CompanyEntity() { return _$CompanyEntity._( entityType: EntityType.company, id: '', updatedAt: 0, archivedAt: 0, assignedUserId: '', createdAt: 0, createdUserId: '', isChanged: false, isDeleted: false, companyKey: '', settings: SettingsEntity(), sizeId: '', industryId: '', enabledModules: 0, firstMonthOfYear: '0', firstDayOfWeek: '0', subdomain: '', portalDomain: '', portalMode: kClientPortalModeSubdomain, updateProducts: true, fillProducts: true, showProductDetails: true, enableProductCost: false, enableProductQuantity: true, enableProductDiscount: false, defaultQuantity: true, defaultTaskIsDateBased: false, slackWebhookUrl: '', googleAnalyticsKey: '', matomoUrl: '', matomoId: '', clientCanRegister: true, autoStartTasks: false, invoiceTaskTimelog: true, invoiceTaskDatelog: true, invoiceTaskProject: false, invoiceTaskHours: false, invoiceTaskItemDescription: true, invoiceTaskProjectHeader: true, isLarge: false, enableShopApi: false, convertProductExchangeRate: false, convertRateToClient: true, enableCustomSurchargeTaxes1: false, enableCustomSurchargeTaxes2: false, enableCustomSurchargeTaxes3: false, enableCustomSurchargeTaxes4: false, numberOfInvoiceTaxRates: 0, numberOfItemTaxRates: 0, numberOfExpenseTaxRates: 0, invoiceExpenseDocuments: false, markExpensesInvoiceable: false, markExpensesPaid: false, showTasksTable: false, showTaskEndDate: false, invoiceTaskDocuments: false, isDisabled: false, calculateExpenseTaxByAmount: false, expenseInclusiveTaxes: false, sessionTimeout: 0, passwordTimeout: 30 * 60 * 1000, oauthPasswordRequired: false, markdownEnabled: true, markdownEmailEnabled: true, useCommaAsDecimalPlace: false, reportIncludeDrafts: false, reportIncludeDeleted: false, stopOnUnpaidRecurring: false, useQuoteTermsOnConversion: false, enableApplyingPayments: false, trackInventory: false, stockNotificationThreshold: 0, stockNotification: true, invoiceTaskLock: false, convertPaymentCurrency: false, convertExpenseCurrency: false, notifyVendorWhenPaid: false, calculateTaxes: false, hasEInvoiceCertificate: false, hasEInvoiceCertificatePassphrase: false, eInvoiceCertificatePassphrase: '', smtpHost: '', smtpPort: 587, smtpEncryption: SMTP_ENCRYPTION_TLS, smtpUsername: '', smtpPassword: '', smtpLocalDomain: '', smtpVerifyPeer: true, taxData: TaxDataEntity(), taxConfig: TaxConfigEntity(), groups: BuiltList(), taxRates: BuiltList(), taskStatuses: BuiltList(), taskStatusMap: BuiltMap(), companyGateways: BuiltList(), expenseCategories: BuiltList(), users: BuiltList(), customFields: BuiltMap(), activities: BuiltList(), clients: BuiltList(), products: BuiltList(), invoices: BuiltList(), recurringInvoices: BuiltList(), recurringExpenses: BuiltList(), payments: BuiltList(), quotes: BuiltList(), credits: BuiltList(), tasks: BuiltList(), expenses: BuiltList(), projects: BuiltList(), vendors: BuiltList(), designs: BuiltList(), paymentTerms: BuiltList(), tokens: BuiltList(), webhooks: BuiltList(), documents: BuiltList(), subscriptions: BuiltList(), systemLogs: BuiltList(), clientRegistrationFields: BuiltList(), purchaseOrders: BuiltList(), bankAccounts: BuiltList(), transactions: BuiltList(), transactionRules: BuiltList(), schedules: BuiltList(), ); } CompanyEntity._(); static const USE_CREDITS_ALWAYS = 'always'; static const USE_CREDITS_OPTION = 'option'; static const USE_CREDITS_OFF = 'off'; static const SMTP_ENCRYPTION_TLS = 'TLS'; static const SMTP_ENCRYPTION_STARTTLS = 'STARTTLS'; @override @memoized int get hashCode; @BuiltValueField(wireName: 'custom_surcharge_taxes1') bool get enableCustomSurchargeTaxes1; @BuiltValueField(wireName: 'custom_surcharge_taxes2') bool get enableCustomSurchargeTaxes2; @BuiltValueField(wireName: 'custom_surcharge_taxes3') bool get enableCustomSurchargeTaxes3; @BuiltValueField(wireName: 'custom_surcharge_taxes4') bool get enableCustomSurchargeTaxes4; @BuiltValueField(wireName: 'size_id') String get sizeId; @BuiltValueField(wireName: 'industry_id') String get industryId; @BuiltValueField(wireName: 'subdomain') String get subdomain; @BuiltValueField(wireName: 'portal_mode') String get portalMode; @BuiltValueField(wireName: 'portal_domain') String get portalDomain; @BuiltValueField(wireName: 'update_products') bool get updateProducts; @BuiltValueField(wireName: 'convert_products') bool get convertProductExchangeRate; @BuiltValueField(wireName: 'convert_rate_to_client') bool get convertRateToClient; @BuiltValueField(wireName: 'fill_products') bool get fillProducts; @BuiltValueField(wireName: 'enable_product_cost') bool get enableProductCost; @BuiltValueField(wireName: 'enable_product_quantity') bool get enableProductQuantity; @BuiltValueField(wireName: 'enable_product_discount') bool get enableProductDiscount; @BuiltValueField(wireName: 'default_task_is_date_based') bool get defaultTaskIsDateBased; @BuiltValueField(wireName: 'default_quantity') bool get defaultQuantity; @BuiltValueField(wireName: 'show_product_details') bool get showProductDetails; @BuiltValueField(wireName: 'client_can_register') bool get clientCanRegister; @BuiltValueField(wireName: 'is_large') bool get isLarge; @BuiltValueField(wireName: 'is_disabled') bool get isDisabled; @BuiltValueField(wireName: 'enable_shop_api') bool get enableShopApi; @BuiltValueField(wireName: 'company_key') String get companyKey; @BuiltValueField(wireName: 'first_day_of_week') String get firstDayOfWeek; @BuiltValueField(wireName: 'first_month_of_year') String get firstMonthOfYear; @BuiltValueField(wireName: 'enabled_tax_rates') int get numberOfInvoiceTaxRates; @BuiltValueField(wireName: 'enabled_item_tax_rates') int get numberOfItemTaxRates; @BuiltValueField(wireName: 'enabled_expense_tax_rates') int get numberOfExpenseTaxRates; @BuiltValueField(wireName: 'expense_inclusive_taxes') bool get expenseInclusiveTaxes; @BuiltValueField(wireName: 'session_timeout') int get sessionTimeout; @BuiltValueField(wireName: 'default_password_timeout') int get passwordTimeout; @BuiltValueField(wireName: 'oauth_password_required') bool get oauthPasswordRequired; @BuiltValueField(wireName: 'markdown_enabled') bool get markdownEnabled; @BuiltValueField(wireName: 'markdown_email_enabled') bool get markdownEmailEnabled; @BuiltValueField(wireName: 'use_comma_as_decimal_place') bool get useCommaAsDecimalPlace; @BuiltValueField(wireName: 'report_include_drafts') bool get reportIncludeDrafts; @BuiltValueField(wireName: 'report_include_deleted') bool get reportIncludeDeleted; @BuiltValueField(wireName: 'use_quote_terms_on_conversion') bool get useQuoteTermsOnConversion; @BuiltValueField(wireName: 'enable_applying_payments') bool get enableApplyingPayments; @BuiltValueField(wireName: 'track_inventory') bool get trackInventory; @BuiltValueField(wireName: 'inventory_notification_threshold') int get stockNotificationThreshold; @BuiltValueField(wireName: 'stock_notification') bool get stockNotification; @BuiltValueField(wireName: 'invoice_task_lock') bool get invoiceTaskLock; @BuiltValueField(wireName: 'convert_payment_currency') bool get convertPaymentCurrency; @BuiltValueField(wireName: 'convert_expense_currency') bool get convertExpenseCurrency; @BuiltValueField(wireName: 'notify_vendor_when_paid') bool get notifyVendorWhenPaid; @BuiltValueField(wireName: 'smtp_host') String get smtpHost; @BuiltValueField(wireName: 'smtp_port') int get smtpPort; @BuiltValueField(wireName: 'smtp_encryption') String get smtpEncryption; @BuiltValueField(wireName: 'smtp_username') String get smtpUsername; @BuiltValueField(wireName: 'smtp_password') String get smtpPassword; @BuiltValueField(wireName: 'smtp_local_domain') String get smtpLocalDomain; @BuiltValueField(wireName: 'smtp_verify_peer') bool get smtpVerifyPeer; BuiltList get groups; BuiltList get activities; @BuiltValueField(wireName: 'tax_rates') BuiltList get taxRates; @BuiltValueField(wireName: 'task_statuses') BuiltList get taskStatuses; BuiltMap get taskStatusMap; @BuiltValueField(wireName: 'company_gateways') BuiltList get companyGateways; @BuiltValueField(wireName: 'expense_categories') BuiltList get expenseCategories; BuiltList get users; BuiltList get clients; BuiltList get products; BuiltList get invoices; @BuiltValueField(wireName: 'recurring_invoices') BuiltList get recurringInvoices; @BuiltValueField(wireName: 'recurring_expenses') BuiltList get recurringExpenses; BuiltList get payments; BuiltList get quotes; BuiltList get credits; @BuiltValueField(wireName: 'purchase_orders') BuiltList get purchaseOrders; @BuiltValueField(wireName: 'bank_integrations') BuiltList get bankAccounts; @BuiltValueField(wireName: 'bank_transactions') BuiltList get transactions; @BuiltValueField(wireName: 'bank_transaction_rules') BuiltList get transactionRules; BuiltList get tasks; BuiltList get projects; BuiltList get expenses; BuiltList get vendors; BuiltList get designs; BuiltList get documents; @BuiltValueField(wireName: 'task_schedulers') BuiltList get schedules; @BuiltValueField(wireName: 'tokens_hashed') BuiltList get tokens; BuiltList get webhooks; BuiltList get subscriptions; @BuiltValueField(wireName: 'payment_terms') BuiltList get paymentTerms; @BuiltValueField(wireName: 'system_logs') BuiltList get systemLogs; @BuiltValueField(wireName: 'client_registration_fields') BuiltList get clientRegistrationFields; @BuiltValueField(wireName: 'custom_fields') BuiltMap get customFields; @BuiltValueField(wireName: 'slack_webhook_url') String get slackWebhookUrl; @BuiltValueField(wireName: 'google_analytics_key') String get googleAnalyticsKey; @BuiltValueField(wireName: 'matomo_url') String get matomoUrl; @BuiltValueField(wireName: 'matomo_id') String get matomoId; @BuiltValueField(wireName: 'mark_expenses_invoiceable') bool get markExpensesInvoiceable; @BuiltValueField(wireName: 'mark_expenses_paid') bool get markExpensesPaid; @BuiltValueField(wireName: 'invoice_expense_documents') bool get invoiceExpenseDocuments; @BuiltValueField(wireName: 'invoice_task_documents') bool get invoiceTaskDocuments; @BuiltValueField(wireName: 'invoice_task_timelog') bool get invoiceTaskTimelog; @BuiltValueField(wireName: 'invoice_task_datelog') bool get invoiceTaskDatelog; @BuiltValueField(wireName: 'invoice_task_project') bool get invoiceTaskProject; @BuiltValueField(wireName: 'invoice_task_hours') bool get invoiceTaskHours; @BuiltValueField(wireName: 'invoice_task_item_description') bool get invoiceTaskItemDescription; @BuiltValueField(wireName: 'invoice_task_project_header') bool get invoiceTaskProjectHeader; @BuiltValueField(wireName: 'auto_start_tasks') bool get autoStartTasks; @BuiltValueField(wireName: 'show_tasks_table') bool get showTasksTable; @BuiltValueField(wireName: 'show_task_end_date') bool get showTaskEndDate; SettingsEntity get settings; @BuiltValueField(wireName: 'enabled_modules') int get enabledModules; @BuiltValueField(wireName: 'calculate_expense_tax_by_amount') bool get calculateExpenseTaxByAmount; @BuiltValueField(wireName: 'stop_on_unpaid_recurring') bool get stopOnUnpaidRecurring; @BuiltValueField(wireName: 'calculate_taxes') bool get calculateTaxes; @BuiltValueField(wireName: 'tax_data') TaxConfigEntity get taxConfig; @BuiltValueField(wireName: 'origin_tax_data') TaxDataEntity get taxData; @BuiltValueField(wireName: 'has_e_invoice_certificate') bool get hasEInvoiceCertificate; @BuiltValueField(wireName: 'has_e_invoice_certificate_passphrase') bool get hasEInvoiceCertificatePassphrase; @BuiltValueField(wireName: 'e_invoice_certificate_passphrase') String get eInvoiceCertificatePassphrase; String get displayName => settings.name ?? ''; @override bool get isActive => true; @override bool matchesFilter(String? filter) { for (final user in users) { if (user.matchesFilter(filter)) { return true; } } for (final project in projects) { if (project.matchesFilter(filter)) { return true; } } for (final product in products) { if (product.matchesFilter(filter)) { return true; } } return matchesStrings( haystacks: [subdomain, displayName, companyKey], needle: filter, ); } @override String? matchesFilterValue(String? filter) { for (final user in users) { final value = user.matchesFilterValue(filter); if (value != null) { return value; } } for (final project in projects) { final value = project.matchesFilterValue(filter); if (value != null) { return value; } } for (final product in products) { final value = product.matchesFilterValue(filter); if (value != null) { return value; } } return matchesStringsValue( haystacks: [subdomain, displayName, companyKey], needle: filter, ); } @override double? get listDisplayAmount => null; @override FormatNumberType? get listDisplayAmountType => null; @override String get listDisplayName => settings.name ?? ''; bool hasCustomField(String field) => getCustomFieldLabel(field).isNotEmpty; bool get enableFirstInvoiceTaxRate => numberOfInvoiceTaxRates >= 1; bool get enableSecondInvoiceTaxRate => numberOfInvoiceTaxRates >= 2; bool get enableThirdInvoiceTaxRate => numberOfInvoiceTaxRates >= 3; bool get enableFirstItemTaxRate => numberOfItemTaxRates >= 1; bool get enableSecondItemTaxRate => numberOfItemTaxRates >= 2; bool get enableThirdItemTaxRate => numberOfItemTaxRates >= 3; bool get enableFirstExpenseTaxRate => numberOfExpenseTaxRates >= 1; bool get enableSecondExpenseTaxRate => numberOfExpenseTaxRates >= 2; bool get enableThirdExpenseTaxRate => numberOfExpenseTaxRates >= 3; bool get hasInvoiceTaxes => numberOfInvoiceTaxRates > 0; bool get hasItemTaxes => numberOfItemTaxRates > 0; bool get hasTaxes => hasInvoiceTaxes || hasItemTaxes; bool get isSmall => !isLarge; bool get hasName => (settings.name ?? '').isNotEmpty; bool get hasCustomSurcharge => hasCustomField(CustomFieldType.surcharge1) || hasCustomField(CustomFieldType.surcharge2) || hasCustomField(CustomFieldType.surcharge3) || hasCustomField(CustomFieldType.surcharge4); bool hasCustomProductField(String label) { return [ getCustomFieldLabel(CustomFieldType.product1).toLowerCase(), getCustomFieldLabel(CustomFieldType.product2).toLowerCase(), getCustomFieldLabel(CustomFieldType.product3).toLowerCase(), getCustomFieldLabel(CustomFieldType.product4).toLowerCase(), ].contains(label.toLowerCase()); } String getCustomFieldLabel(String field) { field = field.replaceFirst('\$', ''); // Convert '$client.custom1' to 'client1' if (field.contains('.custom')) { field = field.replaceFirst('.custom', ''); } // Convert '$product.product1' to 'product1' if (field.contains('.product')) { field = field.replaceFirst('.product', ''); } else if (field.contains('.task')) { field = field.replaceFirst('.task', ''); } // Conver quote/credit to invoice if (field.contains('quote')) { field = field.replaceFirst('quote', 'invoice'); } else if (field.contains('credit')) { field = field.replaceFirst('credit', 'invoice'); } else if (field.contains('recurring_invoice')) { field = field.replaceFirst('recurring_invoice', 'invoice'); } else if (field.contains('purchase_order')) { field = field.replaceFirst('purchase_order', 'invoice'); } if (field.contains('recurring_expense')) { field = field.replaceFirst('recurring_expense', 'expense'); } if (customFields.containsKey(field)) { return customFields[field]!.split('|').first; } else { return ''; } } String getCustomFieldType(String? field) { if ((customFields[field] ?? '').contains('|')) { final value = customFields[field]!.split('|').last; if ([kFieldTypeSingleLineText, kFieldTypeDate, kFieldTypeSwitch] .contains(value)) { return value; } else { return kFieldTypeDropdown; } } else { return kFieldTypeMultiLineText; } } String formatCustomFieldValue(String field, String value) { final context = navigatorKey.currentContext!; final type = getCustomFieldType(field); final localization = AppLocalization.of(context); if (type == kFieldTypeDate) { value = formatDate(value, context); } else if (type == kFieldTypeSwitch) { value = value == kSwitchValueYes ? localization!.yes : localization!.no; } return getCustomFieldLabel(field) + ': $value'; } List getCustomFieldValues(String field, {bool excludeBlank = false}) { final values = customFields[field]; if (values == null || !values.contains('|')) { return []; } else { final parts = values.split('|'); final data = parts.last.split(','); if (parts.length == 2) { if ([kFieldTypeDate, kFieldTypeSwitch, kFieldTypeSingleLineText] .contains(parts[1])) { return []; } } if (excludeBlank) { return data.where((data) => data.isNotEmpty).toList(); } else { return data; } } } // TODO make sure to clear everything CompanyEntity get coreCompany => rebuild( (b) => b ..clients.clear() ..products.clear() ..invoices.clear() ..payments.clear() ..quotes.clear() ..purchaseOrders.clear() ..bankAccounts.clear() ..transactions.clear() ..transactionRules.clear() ..credits.clear() ..tasks.clear() ..projects.clear() ..vendors.clear() ..expenses.clear() ..webhooks.clear() ..designs.clear() ..companyGateways.clear(), ); bool isModuleEnabled(EntityType? entityType) { if ((entityType == EntityType.invoice || entityType == EntityType.payment) && enabledModules & kModuleInvoices == 0) { return false; } else if (entityType == EntityType.credit && enabledModules & kModuleCredits == 0) { return false; } else if (entityType == EntityType.quote && enabledModules & kModuleQuotes == 0) { return false; } else if (entityType == EntityType.task && enabledModules & kModuleTasks == 0) { return false; } else if (entityType == EntityType.project && enabledModules & kModuleProjects == 0) { return false; } else if (entityType == EntityType.vendor && enabledModules & kModuleVendors == 0) { return false; } else if (entityType == EntityType.expense && enabledModules & kModuleExpenses == 0) { return false; } else if (entityType == EntityType.recurringInvoice && enabledModules & kModuleRecurringInvoices == 0) { return false; } else if (entityType == EntityType.recurringExpense && enabledModules & kModuleRecurringExpenses == 0) { return false; } else if (entityType == EntityType.purchaseOrder && enabledModules & kModulePurchaseOrders == 0) { return false; } else if (entityType == EntityType.transaction && enabledModules & kModuleTransactions == 0) { return false; } else if (entityType == EntityType.document && enabledModules & kModuleDocuments == 0) { return false; } return true; } int get daysActive => DateTime.now().difference(convertTimestampToDate(createdAt)).inDays; String get currencyId => settings.currencyId ?? kDefaultCurrencyId; String get languageId => settings.languageId ?? kDefaultLanguageId; bool get supportsQrIban => settings.countryId == kCountrySwitzerland; // ignore: unused_element static void _initializeBuilder(CompanyEntityBuilder builder) => builder ..entityType = EntityType.company ..calculateExpenseTaxByAmount = false ..enableProductDiscount = false ..defaultTaskIsDateBased = false ..sessionTimeout = 0 ..passwordTimeout = 30 * 60 * 1000 ..oauthPasswordRequired = false ..invoiceTaskDatelog = true ..showTaskEndDate = false ..markdownEnabled = true ..markdownEmailEnabled = true ..useCommaAsDecimalPlace = false ..useQuoteTermsOnConversion = false ..enableApplyingPayments = false ..trackInventory = false ..stockNotificationThreshold = 0 ..stockNotification = true ..reportIncludeDrafts = false ..reportIncludeDeleted = false ..convertRateToClient = true ..stopOnUnpaidRecurring = false ..numberOfExpenseTaxRates = 0 ..invoiceTaskProject = false ..invoiceTaskHours = false ..invoiceTaskLock = false ..invoiceTaskItemDescription = true ..invoiceTaskProjectHeader = true ..matomoUrl = '' ..matomoId = '' ..convertPaymentCurrency = false ..convertExpenseCurrency = false ..notifyVendorWhenPaid = false ..calculateTaxes = false ..hasEInvoiceCertificate = false ..hasEInvoiceCertificatePassphrase = false ..eInvoiceCertificatePassphrase = '' ..enableCustomSurchargeTaxes1 = false ..enableCustomSurchargeTaxes2 = false ..enableCustomSurchargeTaxes3 = false ..enableCustomSurchargeTaxes4 = false ..sizeId = '' ..industryId = '' ..subdomain = '' ..portalMode = '' ..portalDomain = '' ..updateProducts = false ..convertProductExchangeRate = false ..fillProducts = false ..enableProductCost = false ..enableProductQuantity = true ..defaultQuantity = true ..showProductDetails = true ..clientCanRegister = false ..isLarge = false ..isDisabled = false ..enableShopApi = false ..companyKey = '' ..firstDayOfWeek = '' ..firstMonthOfYear = '' ..numberOfInvoiceTaxRates = 0 ..numberOfItemTaxRates = 0 ..expenseInclusiveTaxes = false ..slackWebhookUrl = '' ..googleAnalyticsKey = '' ..markExpensesInvoiceable = false ..markExpensesPaid = false ..invoiceExpenseDocuments = false ..invoiceTaskDocuments = false ..invoiceTaskTimelog = false ..autoStartTasks = false ..showTasksTable = false ..enabledModules = 0 ..createdAt = 0 ..updatedAt = 0 ..archivedAt = 0 ..id = '' ..smtpHost = '' ..smtpPort = 587 ..smtpEncryption = SMTP_ENCRYPTION_TLS ..smtpUsername = '' ..smtpPassword = '' ..smtpLocalDomain = '' ..smtpVerifyPeer = true ..taxConfig.replace(TaxConfigEntity()) ..taxData.replace(TaxDataEntity()) ..systemLogs.replace(BuiltList()) ..subscriptions.replace(BuiltList()) ..recurringExpenses.replace(BuiltList()) ..clientRegistrationFields.replace(BuiltList()) ..purchaseOrders.replace(BuiltList()) ..bankAccounts.replace(BuiltList()) ..transactions.replace(BuiltList()) ..transactionRules.replace(BuiltList()) ..schedules.replace(BuiltList()); static Serializer get serializer => _$companyEntitySerializer; } abstract class GatewayEntity extends Object with SelectableEntity implements Built { factory GatewayEntity() { return _$GatewayEntity._( id: BaseEntity.nextId, name: '', sortOrder: 0, fields: '', defaultGatewayTypeId: kGatewayTypeCreditCard, isOffsite: false, isVisible: false, options: BuiltMap(), siteUrl: '', ); } GatewayEntity._(); @override @memoized int get hashCode; @override @BuiltValueField(wireName: 'key') String get id; String get name; @BuiltValueField(wireName: 'is_offsite') bool get isOffsite; @BuiltValueField(wireName: 'visible') bool get isVisible; @BuiltValueField(wireName: 'sort_order') int get sortOrder; @BuiltValueField(wireName: 'default_gateway_type_id') String get defaultGatewayTypeId; @BuiltValueField(wireName: 'site_url') String get siteUrl; @BuiltValueField(wireName: 'options') BuiltMap get options; String get fields; bool get supportsTokenBilling => options.keys .where((typeId) => options[typeId]!.supportTokenBilling) .isNotEmpty; bool supportsRefunds(String gatewayTypeId) => options[gatewayTypeId]?.supportRefunds ?? false; Map? get parsedFields => fields.isEmpty ? {} : jsonDecode(fields); int compareTo(GatewayEntity gateway, String sortField, bool sortAscending) { int response = 0; final GatewayEntity gatewayA = sortAscending ? this : gateway; final GatewayEntity gatewayB = sortAscending ? gateway : this; switch (sortField) { case CreditFields.amount: response = gatewayA.name.compareTo(gatewayB.name); break; default: print('## ERROR: sort by gateway.$sortField is not implemented'); break; } return response; } @override bool matchesFilter(String? filter) { if (filter == null || filter.isEmpty) { return true; } filter = filter.toLowerCase(); if (name.toLowerCase().contains(filter)) { return true; } if ('autobill'.contains(filter) && options.values .where((option) => option.supportTokenBilling) .isNotEmpty) { return true; } final gatewayIds = options.keys.toList(); final localization = AppLocalization.of(navigatorKey.currentContext!); for (var i = 0; i < gatewayIds.length; i++) { final gatewayTypeId = gatewayIds[i]; final gatewayType = localization!.lookup(kGatewayTypes[gatewayTypeId]); if (gatewayType.toLowerCase().contains(filter)) { return true; } } return false; } @override String? matchesFilterValue(String? filter) { if (filter == null || filter.isEmpty) { return null; } if ('autobill'.contains(filter) && options.values .where((option) => option.supportTokenBilling) .isNotEmpty) { return 'Auto-Bill'; } final gatewayIds = options.keys.toList(); final localization = AppLocalization.of(navigatorKey.currentContext!); for (var i = 0; i < gatewayIds.length; i++) { final gatewayTypeId = gatewayIds[i]; final gatewayType = localization!.lookup(kGatewayTypes[gatewayTypeId]); if (gatewayType.toLowerCase().contains(filter)) { return gatewayType; } } return null; } @override String get listDisplayName => name; @override double? get listDisplayAmount => null; static String? getClientUrl({String? gatewayId, String? customerReference}) { switch (gatewayId) { case kGatewayStripe: case kGatewayStripeConnect: return 'https://dashboard.stripe.com/customers/$customerReference'; default: return null; } } static String? getPaymentUrl( {String? gatewayId, String? transactionReference}) { switch (gatewayId) { case kGatewayStripe: case kGatewayStripeConnect: if (transactionReference!.startsWith('src')) return 'https://dashboard.stripe.com/sources/$transactionReference'; else return 'https://dashboard.stripe.com/payments/$transactionReference'; default: return null; } } List supportedEvents() { final events = []; options.forEach((key, option) { events.addAll((option.webhooks ?? []).toList()); }); return events.toSet().toList(); } @override FormatNumberType? get listDisplayAmountType => null; // ignore: unused_element static void _initializeBuilder(GatewayEntityBuilder builder) => builder..siteUrl = ''; static Serializer get serializer => _$gatewayEntitySerializer; } abstract class GatewayOptionsEntity implements Built { factory GatewayOptionsEntity() { return _$GatewayOptionsEntity._( supportRefunds: false, supportTokenBilling: false, ); } GatewayOptionsEntity._(); @override @memoized int get hashCode; @BuiltValueField(wireName: 'refund') bool get supportRefunds; @BuiltValueField(wireName: 'token_billing') bool get supportTokenBilling; BuiltList? get webhooks; static Serializer get serializer => _$gatewayOptionsEntitySerializer; } abstract class UserCompanyEntity implements Built { factory UserCompanyEntity(bool reportErrors) { return _$UserCompanyEntity._( isAdmin: false, isOwner: false, permissions: '', ninjaPortalUrl: '', permissionsUpdatedAt: 0, settings: UserSettingsEntity(), company: CompanyEntity(), user: UserEntity(), token: TokenEntity(), account: AccountEntity(reportErrors), notifications: BuiltMap>().rebuild((b) => b ..[kNotificationChannelEmail] = BuiltList([kNotificationsAll])), ); } UserCompanyEntity._(); @override @memoized int get hashCode; @BuiltValueField(wireName: 'is_admin') bool get isAdmin; @BuiltValueField(wireName: 'is_owner') bool get isOwner; @BuiltValueField(wireName: 'permissions_updated_at') int get permissionsUpdatedAt; String get permissions; BuiltMap> get notifications; CompanyEntity get company; UserEntity get user; TokenEntity get token; AccountEntity get account; UserSettingsEntity get settings; @BuiltValueField(wireName: 'ninja_portal_url') String get ninjaPortalUrl; bool can(UserPermission permission, EntityType? entityType) { if (entityType == null) { return false; } if (!company.isModuleEnabled(entityType)) { return false; } if (Config.DEMO_MODE) { return true; } if (isAdmin) { return true; } return permissions.contains('${permission}_all') || permissions.contains('${permission}_${entityType.snakeCase}'); } bool receivesAllNotifications(String channel) => notifications.containsKey(channel) && notifications[channel]!.contains(kNotificationsAll); bool canView(EntityType? entityType) => can(UserPermission.view, entityType); bool canEdit(EntityType? entityType) => can(UserPermission.edit, entityType); bool canCreate(EntityType? entityType) => can(UserPermission.create, entityType); bool canViewCreateOrEdit(EntityType? entityType) => canView(entityType) || canCreate(entityType) || canEdit(entityType); bool canEditEntity(BaseEntity? entity) { if (entity == null) { return false; } if (entity.isNew) { return canCreate(entity.entityType); } else { return canEdit(entity.entityType) || user.canEdit(entity); } } bool get canViewReports { if (isAdmin) { return true; } return permissions.contains(kPermissionViewReports); } bool get canViewDashboard { if (isAdmin) { return true; } return permissions.contains(kPermissionViewDashboard); } // ignore: unused_element static void _initializeBuilder(UserCompanyEntityBuilder builder) => builder ..user.replace(UserEntity()) ..token.replace(TokenEntity()) ..account.replace(AccountEntity(false)) ..settings.replace(UserSettingsEntity()) ..notifications.replace(BuiltMap>().rebuild((b) => b ..[kNotificationChannelEmail] = BuiltList([kNotificationsAll]))) ..permissionsUpdatedAt = 0 ..ninjaPortalUrl = ''; static Serializer get serializer => _$userCompanyEntitySerializer; } abstract class UserSettingsEntity implements Built { factory UserSettingsEntity() { return _$UserSettingsEntity._( accentColor: kDefaultAccentColor, numberYearsActive: 3, tableColumns: BuiltMap>(), reportSettings: BuiltMap(), includeDeletedClients: false, dashboardFields: BuiltList([ DashboardField( field: DashboardUISettings.FIELD_ACTIVE_INVOICES, period: DashboardUISettings.PERIOD_CURRENT, ), DashboardField( field: DashboardUISettings.FIELD_OUTSTANDING_INVOICES, period: DashboardUISettings.PERIOD_CURRENT, ), DashboardField( field: DashboardUISettings.FIELD_COMPLETED_PAYMENTS, period: DashboardUISettings.PERIOD_CURRENT, ), ]), dashboardFieldsPerRowMobile: 1, dashboardFieldsPerRowDesktop: 3, ); } UserSettingsEntity._(); @override @memoized int get hashCode; @BuiltValueField(wireName: 'accent_color') String? get accentColor; @BuiltValueField(wireName: 'table_columns') BuiltMap> get tableColumns; @BuiltValueField(wireName: 'report_settings') BuiltMap get reportSettings; @BuiltValueField(wireName: 'number_years_active') int get numberYearsActive; @BuiltValueField(wireName: 'include_deleted_clients') bool get includeDeletedClients; @BuiltValueField(wireName: 'dashboard_fields') BuiltList get dashboardFields; @BuiltValueField(wireName: 'dashboard_fields_per_row_mobile') int get dashboardFieldsPerRowMobile; @BuiltValueField(wireName: 'dashboard_fields_per_row_desktop') int get dashboardFieldsPerRowDesktop; List? getTableColumns(EntityType entityType) { if (tableColumns.containsKey('$entityType')) { return tableColumns['$entityType']!.toList(); } else { return null; } } String? get validatedAccentColor { if ((accentColor ?? '').isEmpty) { return kDefaultAccentColor; } if (accentColor!.toLowerCase() == '#ffffff') { return kDefaultAccentColor; } return accentColor; } // ignore: unused_element static void _initializeBuilder(UserSettingsEntityBuilder builder) => builder ..accentColor = kDefaultAccentColor ..numberYearsActive = 3 ..tableColumns.replace(BuiltMap>()) ..reportSettings.replace(BuiltMap()) ..dashboardFields.replace(BuiltList([ DashboardField( field: DashboardUISettings.FIELD_ACTIVE_INVOICES, period: DashboardUISettings.PERIOD_CURRENT, ), DashboardField( field: DashboardUISettings.FIELD_OUTSTANDING_INVOICES, period: DashboardUISettings.PERIOD_CURRENT, ), DashboardField( field: DashboardUISettings.FIELD_COMPLETED_PAYMENTS, period: DashboardUISettings.PERIOD_CURRENT, ), ])) ..dashboardFieldsPerRowMobile = 1 ..dashboardFieldsPerRowDesktop = 3 ..includeDeletedClients = false; static Serializer get serializer => _$userSettingsEntitySerializer; } abstract class ReportSettingsEntity implements Built { factory ReportSettingsEntity({ String? sortColumn, bool? sortAscending, int? sortTotalsIndex, bool? sortTotalsAscending, }) { return _$ReportSettingsEntity._( sortColumn: sortColumn ?? '', sortAscending: sortAscending ?? true, sortTotalsIndex: sortTotalsIndex ?? 0, sortTotalsAscending: sortTotalsAscending ?? true, columns: BuiltList(), ); } ReportSettingsEntity._(); @override @memoized int get hashCode; @BuiltValueField(wireName: 'sort_column') String get sortColumn; @BuiltValueField(wireName: 'sort_ascending') bool get sortAscending; @BuiltValueField(wireName: 'sort_totals_index') int get sortTotalsIndex; @BuiltValueField(wireName: 'sort_totals_ascending') bool get sortTotalsAscending; BuiltList get columns; // ignore: unused_element static void _initializeBuilder(ReportSettingsEntityBuilder builder) => builder ..sortColumn = '' ..sortAscending = true ..sortTotalsAscending = true ..sortTotalsIndex = 0 ..columns.replace(BuiltList()); static Serializer get serializer => _$reportSettingsEntitySerializer; } abstract class CompanyItemResponse implements Built { factory CompanyItemResponse([void updates(CompanyItemResponseBuilder b)]) = _$CompanyItemResponse; CompanyItemResponse._(); @override @memoized int get hashCode; CompanyEntity get data; static Serializer get serializer => _$companyItemResponseSerializer; } abstract class RegistrationFieldEntity implements Built { factory RegistrationFieldEntity(bool reportErrors) { return _$RegistrationFieldEntity._( key: '', required: false, visible: false, ); } RegistrationFieldEntity._(); static const SETTING_HIDDEN = 'hidden'; static const SETTING_OPTIONAL = 'optional'; static const SETTING_REQUIRED = 'required'; @override @memoized int get hashCode; String get key; bool get required; bool get visible; String get setting { if (required) { return SETTING_REQUIRED; } else if (visible) { return SETTING_OPTIONAL; } else { return SETTING_HIDDEN; } } // ignore: unused_element static void _initializeBuilder(RegistrationFieldEntityBuilder builder) => builder..visible = false; static Serializer get serializer => _$registrationFieldEntitySerializer; } abstract class DashboardField implements Built { factory DashboardField({ String? field, String? period, String? type, }) { return _$DashboardField._( field: field ?? '', period: period ?? '', type: type ?? TYPE_SUM, ); } DashboardField._(); static const TYPE_SUM = 'sum'; static const TYPE_COUNT = 'count'; static const TYPE_AVERAGE = 'average'; @override @memoized int get hashCode; String get field; String get period; String get type; // ignore: unused_element static void _initializeBuilder(DashboardFieldBuilder builder) => builder..type = TYPE_SUM; static Serializer get serializer => _$dashboardFieldSerializer; }