// Flutter imports: import 'package:flutter/widgets.dart'; // Package imports: import 'package:built_collection/built_collection.dart'; import 'package:flutter_redux/flutter_redux.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:memoize/memoize.dart'; // Project imports: import 'package:invoiceninja_flutter/data/models/group_model.dart'; import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart'; import 'package:invoiceninja_flutter/utils/formatting.dart'; InvoiceItemEntity convertTaskToInvoiceItem({ BuildContext context, TaskEntity task, bool includeProjectHeader = false, }) { final state = StoreProvider.of(context).state; final project = state.projectState.get(task.projectId); final client = state.clientState.get(task.clientId); final group = state.groupState.get(client.groupId); final localization = AppLocalization.of(context); final company = state.company; var notes = ''; final dates = {}; if (project.isOld && includeProjectHeader) { notes += '## ${project.name}\n'; } notes += task.description; if (state.company.invoiceTaskDatelog || state.company.invoiceTaskTimelog) { if (notes.trim().isNotEmpty) { notes += '\n'; } notes += '
\n'; task .getTaskTimes() .where((time) => time.startDate != null && time.endDate != null) .forEach((time) { if (state.company.invoiceTaskDatelog && state.company.invoiceTaskTimelog) { final start = formatDate(time.startDate.toIso8601String(), context, showTime: true); final end = formatDate(time.endDate.toIso8601String(), context, showTime: true, showDate: false, showSeconds: true); notes += '$start - $end
\n'; } else if (state.company.invoiceTaskDatelog) { final date = formatDate(time.startDate.toIso8601String(), context, showTime: false); dates.add(date); } else { final start = formatDate(time.startDate.toIso8601String(), context, showTime: true, showDate: false); final end = formatDate(time.endDate.toIso8601String(), context, showTime: true, showDate: false, showSeconds: true); notes += '$start - $end
\n'; } }); notes += '
\n'; if (state.company.invoiceTaskDatelog && !state.company.invoiceTaskTimelog) { notes += '\n' + dates.join('\n'); } notes = notes.trim(); } String customValue1 = ''; String customValue2 = ''; String customValue3 = ''; String customValue4 = ''; final fieldLabel1 = company.getCustomFieldLabel(CustomFieldType.task1); final fieldLabel2 = company.getCustomFieldLabel(CustomFieldType.task2); final fieldLabel3 = company.getCustomFieldLabel(CustomFieldType.task3); final fieldLabel4 = company.getCustomFieldLabel(CustomFieldType.task4); final customValues = { company.getCustomFieldLabel(CustomFieldType.task1): task.customValue1, company.getCustomFieldLabel(CustomFieldType.task2): task.customValue2, company.getCustomFieldLabel(CustomFieldType.task3): task.customValue3, company.getCustomFieldLabel(CustomFieldType.task4): task.customValue4, localization.project: state.projectState.get(task.projectId).name, }; for (var label in customValues.keys) { final value = customValues[label]; if (fieldLabel1.toLowerCase() == label.toLowerCase()) { customValue1 = value; } else if (fieldLabel2.toLowerCase() == label.toLowerCase()) { customValue2 = value; } else if (fieldLabel3.toLowerCase() == label.toLowerCase()) { customValue3 = value; } else if (fieldLabel4.toLowerCase() == label.toLowerCase()) { customValue4 = value; } } return InvoiceItemEntity().rebuild((b) => b ..typeId = InvoiceItemEntity.TYPE_TASK ..taskId = task.id ..notes = notes ..cost = taskRateSelector( company: state.company, project: project, client: client, task: task, group: group, ) ..quantity = round(task.calculateDuration().inSeconds / 3600, 3) ..customValue1 = customValue1 ..customValue2 = customValue2 ..customValue3 = customValue3 ..customValue4 = customValue4); } var memoizedTaskList = memo5((BuiltMap taskMap, String clientId, BuiltMap userMap, BuiltMap clientMap, BuiltMap projectMap) => taskList(taskMap, clientId, userMap, clientMap, projectMap)); List taskList( BuiltMap taskMap, String clientId, BuiltMap userMap, BuiltMap clientMap, BuiltMap projectMap) { final list = taskMap.keys.where((taskId) { final task = taskMap[taskId]; if ((clientId ?? '').isNotEmpty && (task.clientId ?? '').isNotEmpty && task.clientId != clientId) { return false; } return task.isActive && task.isStopped && !task.isInvoiced; }).toList(); list.sort((idA, idB) => taskMap[idA].listDisplayName.compareTo(taskMap[idB].listDisplayName)); return list; } var memoizedDropdownTaskList = memo7(( BuiltMap taskMap, BuiltList taskList, BuiltMap userMap, BuiltMap clientMap, BuiltMap invoiceMap, BuiltMap projectMap, BuiltMap taskStatusMap, ) => dropdownTasksSelector( taskMap, taskList, userMap, clientMap, invoiceMap, projectMap, taskStatusMap, )); List dropdownTasksSelector( BuiltMap taskMap, BuiltList taskList, BuiltMap userMap, BuiltMap clientMap, BuiltMap invoiceMap, BuiltMap projectMap, BuiltMap taskStatusMap, ) { final list = taskList.where((taskId) => taskMap[taskId].isActive).toList(); list.sort((taskAId, taskBId) { final taskA = taskMap[taskAId]; final taskB = taskMap[taskBId]; return taskA.compareTo( taskB, TaskFields.updatedAt, false, userMap, clientMap, projectMap, invoiceMap, taskStatusMap, ); }); return list; } var memoizedKanbanTaskList = memo9((SelectionState selectionState, BuiltMap taskMap, BuiltMap clientMap, BuiltMap userMap, BuiltMap projectMap, BuiltMap invoiceMap, BuiltMap taskStatusMap, BuiltList taskList, ListUIState taskListState) => kanbanTasksSelector(selectionState, taskMap, clientMap, userMap, projectMap, invoiceMap, taskStatusMap, taskList, taskListState)); List kanbanTasksSelector( SelectionState selectionState, BuiltMap taskMap, BuiltMap clientMap, BuiltMap userMap, BuiltMap projectMap, BuiltMap invoiceMap, BuiltMap taskStatusMap, BuiltList taskList, ListUIState taskListState) { final filterEntityId = selectionState.filterEntityId; final filterEntityType = selectionState.filterEntityType; final list = taskList.where((taskId) { final task = taskMap[taskId]; final client = clientMap[task.clientId] ?? ClientEntity(id: task.clientId); if (!client.isActive && !client.matchesEntityFilter(filterEntityType, filterEntityId)) { return false; } if (task.isInvoiced) { return false; } return true; }).toList(); list.sort((taskAId, taskBId) { final taskA = taskMap[taskAId]; final taskB = taskMap[taskBId]; return taskA.compareTo( taskB, taskListState.sortField, taskListState.sortAscending, userMap, clientMap, projectMap, invoiceMap, taskStatusMap, ); }); return list; } var memoizedFilteredTaskList = memo9(( SelectionState selectionState, BuiltMap taskMap, BuiltMap clientMap, BuiltMap userMap, BuiltMap projectMap, BuiltMap invoiceMap, BuiltMap taskStatusMap, BuiltList taskList, ListUIState taskListState, ) => filteredTasksSelector( selectionState, taskMap, clientMap, userMap, projectMap, invoiceMap, taskStatusMap, taskList, taskListState, )); List filteredTasksSelector( SelectionState selectionState, BuiltMap taskMap, BuiltMap clientMap, BuiltMap userMap, BuiltMap projectMap, BuiltMap invoiceMap, BuiltMap taskStatusMap, BuiltList taskList, ListUIState taskListState) { final filterEntityId = selectionState.filterEntityId; final filterEntityType = selectionState.filterEntityType; final list = taskList.where((taskId) { final task = taskMap[taskId]; final client = clientMap[task.clientId] ?? ClientEntity(id: task.clientId); final project = projectMap[task.projectId] ?? ProjectEntity(id: task.projectId); if (task.id == selectionState.selectedId) { return true; } if (!client.isActive && !client.matchesEntityFilter(filterEntityType, filterEntityId)) { return false; } if (!task.matchesFilter(taskListState.filter) && !client.matchesNameOrEmail(taskListState.filter) && !project.matchesName(taskListState.filter)) { return false; } if (!task.matchesStates(taskListState.stateFilters)) { return false; } if (!task.matchesStatuses(taskListState.statusFilters)) { return false; } if (filterEntityId != null) { if (filterEntityType == EntityType.client && task.clientId != filterEntityId) { return false; } else if (filterEntityType == EntityType.project && task.projectId != filterEntityId) { return false; } else if (filterEntityType == EntityType.invoice && task.invoiceId != filterEntityId) { return false; } else if (filterEntityType == EntityType.user && task.assignedUserId != filterEntityId) { return false; } else if (filterEntityType == EntityType.taskStatus && task.statusId != filterEntityId) { return false; } else if (filterEntityType == EntityType.group && client.groupId != filterEntityId) { return false; } } else if (task.clientId != null && !client.isActive) { return false; } if (taskListState.custom1Filters.isNotEmpty && !taskListState.custom1Filters.contains(task.customValue1)) { return false; } else if (taskListState.custom2Filters.isNotEmpty && !taskListState.custom2Filters.contains(task.customValue2)) { return false; } else if (taskListState.custom3Filters.isNotEmpty && !taskListState.custom3Filters.contains(task.customValue3)) { return false; } else if (taskListState.custom4Filters.isNotEmpty && !taskListState.custom4Filters.contains(task.customValue4)) { return false; } return true; }).toList(); list.sort((taskAId, taskBId) { final taskA = taskMap[taskAId]; final taskB = taskMap[taskBId]; return taskA.compareTo( taskB, taskListState.sortField, taskListState.sortAscending, userMap, clientMap, projectMap, invoiceMap, taskStatusMap, ); }); return list; } double taskRateSelector({ @required CompanyEntity company, @required ProjectEntity project, @required ClientEntity client, @required TaskEntity task, @required GroupEntity group, }) { if (task != null && task.rate > 0) { return task.rate; } else if (project != null && project.taskRate > 0) { return project.taskRate; } else if (client != null && (client.settings.defaultTaskRate ?? 0) > 0) { return client.settings.defaultTaskRate; } else if (group != null && (group.settings.defaultTaskRate ?? 0) > 0) { return group.settings.defaultTaskRate; } else if (company != null && (company.settings.defaultTaskRate ?? 0) > 0) { return company.settings.defaultTaskRate; } return 0; } var memoizedTaskStatsForClient = memo2( (String clientId, BuiltMap taskMap) => taskStatsForClient(clientId, taskMap)); EntityStats taskStatsForClient( String clientId, BuiltMap taskMap) { int countActive = 0; int countArchived = 0; taskMap.forEach((taskId, task) { if (task.clientId == clientId) { if (task.isActive) { countActive++; } else if (task.isArchived) { countArchived++; } } }); return EntityStats(countActive: countActive, countArchived: countArchived); } var memoizedTaskStatsForProject = memo2(( String projectId, BuiltMap taskMap, ) => taskStatsForProject(projectId, taskMap)); EntityStats taskStatsForProject( String projectId, BuiltMap taskMap) { int countActive = 0; int countArchived = 0; taskMap.forEach((taskId, task) { if (task.projectId == projectId) { if (task.isActive) { countActive++; } else if (task.isArchived) { countArchived++; } } }); return EntityStats(countActive: countActive, countArchived: countArchived); } var memoizedTaskStatsForUser = memo2(( String userId, BuiltMap taskMap, ) => taskStatsForProject(userId, taskMap)); EntityStats taskStatsForUser( String userId, BuiltMap taskMap) { int countActive = 0; int countArchived = 0; taskMap.forEach((taskId, task) { if (task.assignedUserId == userId) { if (task.isActive) { countActive++; } else if (task.isArchived) { countArchived++; } } }); return EntityStats(countActive: countActive, countArchived: countArchived); }