import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_redux/flutter_redux.dart'; import 'package:intl/intl.dart'; import 'package:intl/number_symbols_data.dart'; import 'package:intl/number_symbols.dart'; import 'package:invoiceninja_flutter/constants.dart'; import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/redux/company/company_selectors.dart'; double round(double value, int precision) { final int fac = pow(10, precision); return (value * fac).round() / fac; } double parseDouble(String value) { // check for comma as decimal separator final RegExp regExp = RegExp(r',[\d]{1,2}$'); if (regExp.hasMatch(value)) { value = value.replaceAll('.', ''); value = value.replaceAll(',', '.'); } value = value.replaceAll(RegExp(r'[^0-9\.\-]'), ''); return double.tryParse(value) ?? 0.0; } enum FormatNumberType { money, // $1,000.00 percent, // 1,000.00% int, // 1,000 double, // 1,000.00 input, // 1000.00 duration, } String formatNumber( double value, BuildContext context, { int clientId, int currencyId, FormatNumberType formatNumberType = FormatNumberType.money, bool zeroIsNull = false, bool roundToTwo = false, }) { if ((zeroIsNull || formatNumberType == FormatNumberType.input) && value == 0) { return null; } else if (value == null) { return ''; } if (roundToTwo) { value = round(value, 2); } if (formatNumberType == FormatNumberType.duration) { return formatDuration(Duration(seconds: value.toInt())); } final state = StoreProvider.of(context).state; final CompanyEntity company = state.selectedCompany; final ClientEntity client = state.selectedCompanyState.clientState.map[clientId]; int countryId; if (client != null && client.countryId > 0) { countryId = client.countryId; } else if (company.countryId > 0) { countryId = company.countryId; } else { countryId = kCountryUnitedStates; } if (currencyId != null && currencyId > 0) { // do nothing } else if (client != null && client.currencyId > 0) { currencyId = client.currencyId; } else if (company.currencyId > 0) { currencyId = company.currencyId; } else { currencyId = kCurrencyUSDollar; } final CurrencyEntity currency = state.staticState.currencyMap[currencyId]; final CountryEntity country = state.staticState.countryMap[countryId]; if (currency == null) { return ''; } String thousandSeparator = currency.thousandSeparator; String decimalSeparator = currency.decimalSeparator; bool swapCurrencySymbol = currency.swapCurrencySymbol; if (currency.id == kCurrencyEuro) { swapCurrencySymbol = country.swapCurrencySymbol; if (country.thousandSeparator != null && country.thousandSeparator.isNotEmpty) { thousandSeparator = country.thousandSeparator; } if (country.decimalSeparator != null && country.decimalSeparator.isNotEmpty) { decimalSeparator = country.decimalSeparator; } } numberFormatSymbols['custom'] = NumberSymbols( NAME: 'custom', DECIMAL_SEP: decimalSeparator, GROUP_SEP: thousandSeparator, ZERO_DIGIT: '0', PLUS_SIGN: '+', MINUS_SIGN: '-', ); NumberFormat formatter; String formatted; if (formatNumberType == FormatNumberType.int) { return NumberFormat('#,##0', 'custom').format(value); } else if (formatNumberType == FormatNumberType.double) { return NumberFormat('#,##0.####', 'custom').format(value); } else if (formatNumberType == FormatNumberType.input) { return NumberFormat('#.####', 'custom').format(value); } else { if (formatNumberType == FormatNumberType.percent) { formatter = NumberFormat('#,##0.####', 'custom'); } else { formatter = NumberFormat('#,##0.00##', 'custom'); } formatted = formatter.format(value); } if (formatNumberType == FormatNumberType.percent) { return '$formatted%'; } else if (company.showCurrencyCode || currency.symbol.isEmpty) { return '$formatted ${currency.code}'; } else if (swapCurrencySymbol) { return '$formatted ${currency.symbol.trim()}'; } else { return '${currency.symbol}$formatted'; } } String cleanPhoneNumber(String phoneNumber) { return phoneNumber.replaceAll(RegExp(r'\D'), ''); } String formatURL(String url) { if (url.startsWith('http')) { return url; } return 'http://' + url; } String formatAddress( {dynamic object, bool isShipping = false, String delimiter = '\n'}) { var str = ''; final String address1 = (isShipping ? object.shippingAddress1 : object.address1) ?? ''; final String address2 = (isShipping ? object.shippingAddress2 : object.address2) ?? ''; final String city = (isShipping ? object.shippingCity : object.city) ?? ''; final String state = (isShipping ? object.shippingState : object.state) ?? ''; final String postalCode = (isShipping ? object.shippingPostalCode : object.postalCode) ?? ''; if (address1.isNotEmpty) { str += address1 + delimiter; } if (address2.isNotEmpty) { str += address2 + delimiter; } if (city.isNotEmpty || state.isNotEmpty || postalCode.isNotEmpty) { str += city + ',' + state + ' ' + postalCode; } return str; } String convertDateTimeToSqlDate([DateTime date]) { date = date ?? DateTime.now(); return date.toIso8601String().split('T').first; } DateTime convertTimestampToDate(int timestamp) => DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); String convertTimestampToDateString(int timestamp) => convertTimestampToDate(timestamp).toIso8601String(); String formatDuration(Duration duration, {bool showSeconds = true}) { final time = duration.toString().split('.')[0]; if (showSeconds) { return time; } else { final parts = time.split(':'); return '${parts[0]}:${parts[1]}'; } } DateTime convertTimeOfDayToDateTime(TimeOfDay timeOfDay, DateTime dateTime) { dateTime ??= DateTime.now(); return DateTime(dateTime.year, dateTime.month, dateTime.day, timeOfDay?.hour ?? 0, timeOfDay?.minute ?? 0) .toUtc(); } TimeOfDay convertDateTimeToTimeOfDay(DateTime dateTime) => TimeOfDay(hour: dateTime?.hour ?? 0, minute: dateTime?.minute ?? 0); String formatDateRange(String startDate, String endDate, BuildContext context) { final today = DateTime.now(); final startDateTime = DateTime.tryParse(startDate).toLocal(); final startFormatter = DateFormat(today.year == startDateTime.year ? 'MMM d' : 'MMM d, yyy'); final startDateTimeString = startFormatter.format(startDateTime); final endDateTime = DateTime.tryParse(endDate).toLocal(); final endFormatter = DateFormat(today.year == endDateTime.year ? 'MMM d' : 'MMM d, yyy'); final endDateTimeString = endFormatter.format(endDateTime); return '$startDateTimeString - $endDateTimeString'; } String formatDate(String value, BuildContext context, {bool showDate = true, bool showTime = false, bool showSeconds = true}) { if (value == null || value.isEmpty) { return ''; } final state = StoreProvider.of(context).state; final CompanyEntity company = state.selectedCompany; if (state.staticState.dateFormatMap.isEmpty) { return ''; } if (showTime) { String format; if (!showDate) { format = showSeconds ? company.enableMilitaryTime ? 'H:mm:ss' : 'h:mm:ss a' : company.enableMilitaryTime ? 'H:mm' : 'h:mm a'; } else { final dateFormats = state.staticState.datetimeFormatMap; final dateFormatId = company.datetimeFormatId > 0 ? company.datetimeFormatId : kDefaultDateTimeFormat; format = dateFormats[dateFormatId].format; if (company.enableMilitaryTime) { format = showSeconds ? format.replaceFirst('h:mm:ss a', 'H:mm:ss') : format.replaceFirst('h:mm a', 'H:mm'); } } final formatter = DateFormat(format, localeSelector(state)); return formatter.format(DateTime.tryParse(value).toLocal()); } else { final dateFormats = state.staticState.dateFormatMap; final dateFormatId = company.dateFormatId > 0 ? company.dateFormatId : kDefaultDateFormat; final formatter = DateFormat(dateFormats[dateFormatId].format, localeSelector(state)); return formatter.format(DateTime.tryParse(value)); } } String formatApiUrl(String url) => cleanApiUrl(url) + '/api/v1'; String cleanApiUrl(String url) => (url ?? '') .trim() .replaceFirst(RegExp(r'/api/v1'), '') .replaceFirst(RegExp(r'/$'), '');