@zeniai/client-epic-state 5.1.45-betaDI2 → 5.1.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
- package/lib/entity/snackbar/snackbarTypes.js +0 -3
- package/lib/entity/task/taskPayload.d.ts +1 -3
- package/lib/entity/task/taskPayload.js +1 -16
- package/lib/entity/task/taskReducer.d.ts +1 -8
- package/lib/entity/task/taskReducer.js +4 -29
- package/lib/entity/task/taskState.d.ts +0 -2
- package/lib/entity/tenant/epic/saveExternalConnectionEpic.d.ts +1 -2
- package/lib/entity/tenant/epic/saveExternalConnectionEpic.js +0 -17
- package/lib/epic.d.ts +3 -6
- package/lib/epic.js +3 -6
- package/lib/esm/entity/snackbar/snackbarTypes.js +0 -3
- package/lib/esm/entity/task/taskPayload.js +1 -16
- package/lib/esm/entity/task/taskReducer.js +3 -28
- package/lib/esm/entity/tenant/epic/saveExternalConnectionEpic.js +0 -17
- package/lib/esm/epic.js +3 -6
- package/lib/esm/index.js +8 -8
- package/lib/esm/view/aiCfoView/aiCfoViewReducer.js +1 -31
- package/lib/esm/view/aiCfoView/epics/fetchSkillsEpic.js +8 -33
- package/lib/esm/view/companyTaskManagerView/epics/fetchCompanyTaskManagerViewEpic.js +0 -3
- package/lib/esm/view/companyTaskManagerView/epics/fetchTaskManagerMetricsEpic.js +1 -7
- package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +0 -7
- package/lib/esm/view/taskManager/taskDetailView/epics/initializeTaskToLocalStoreEpic.js +0 -2
- package/lib/esm/view/taskManager/taskDetailView/epics/saveTaskDetailEpic.js +9 -70
- package/lib/esm/view/taskManager/taskDetailView/taskDetailReducer.js +2 -56
- package/lib/esm/view/taskManager/taskDetailView/taskDetailSelector.js +9 -32
- package/lib/esm/view/taskManager/taskListView/epics/fetchTaskListEpic.js +1 -3
- package/lib/esm/view/taskManager/taskListView/taskList.js +0 -1
- package/lib/esm/view/taskManager/taskListView/taskListReducer.js +13 -266
- package/lib/esm/view/taskManager/taskListView/taskListSelector.js +0 -1
- package/lib/esm/view/taskManager/taskListView/taskListViewHelpers.js +0 -58
- package/lib/esm/view/transactionDetail/epics/saveTransactionHelper.js +0 -22
- package/lib/esm/view/transactionDetail/epics/updateTransactionDetailEpic.js +3 -75
- package/lib/esm/view/transactionDetail/journalEntryLinesViewModel.js +0 -58
- package/lib/esm/view/transactionDetail/transactionDetailLocalDataHelper.js +8 -10
- package/lib/esm/view/transactionDetail/transactionDetailReducer.js +1 -66
- package/lib/index.d.ts +11 -12
- package/lib/index.js +28 -37
- package/lib/view/aiCfoView/aiCfoViewPayload.d.ts +0 -10
- package/lib/view/aiCfoView/aiCfoViewReducer.d.ts +2 -7
- package/lib/view/aiCfoView/aiCfoViewReducer.js +2 -32
- package/lib/view/aiCfoView/aiCfoViewState.d.ts +1 -5
- package/lib/view/aiCfoView/epics/fetchSkillsEpic.js +8 -33
- package/lib/view/companyTaskManagerView/epics/fetchCompanyTaskManagerViewEpic.js +0 -3
- package/lib/view/companyTaskManagerView/epics/fetchTaskManagerMetricsEpic.js +1 -7
- package/lib/view/companyView/types/cockpitTypes.d.ts +0 -3
- package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
- package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +0 -7
- package/lib/view/taskManager/taskDetailView/epics/initializeTaskToLocalStoreEpic.js +0 -2
- package/lib/view/taskManager/taskDetailView/epics/saveTaskDetailEpic.d.ts +2 -2
- package/lib/view/taskManager/taskDetailView/epics/saveTaskDetailEpic.js +8 -69
- package/lib/view/taskManager/taskDetailView/taskDetail.d.ts +0 -16
- package/lib/view/taskManager/taskDetailView/taskDetailReducer.d.ts +2 -14
- package/lib/view/taskManager/taskDetailView/taskDetailReducer.js +3 -57
- package/lib/view/taskManager/taskDetailView/taskDetailSelector.d.ts +1 -2
- package/lib/view/taskManager/taskDetailView/taskDetailSelector.js +7 -30
- package/lib/view/taskManager/taskListView/epics/fetchTaskListEpic.js +1 -3
- package/lib/view/taskManager/taskListView/taskList.d.ts +1 -1
- package/lib/view/taskManager/taskListView/taskList.js +0 -1
- package/lib/view/taskManager/taskListView/taskListPayload.d.ts +0 -4
- package/lib/view/taskManager/taskListView/taskListReducer.d.ts +2 -6
- package/lib/view/taskManager/taskListView/taskListReducer.js +14 -267
- package/lib/view/taskManager/taskListView/taskListSelector.js +0 -1
- package/lib/view/taskManager/taskListView/taskListViewHelpers.d.ts +0 -11
- package/lib/view/taskManager/taskListView/taskListViewHelpers.js +1 -60
- package/lib/view/transactionDetail/epics/saveTransactionHelper.js +0 -22
- package/lib/view/transactionDetail/epics/updateTransactionDetailEpic.d.ts +1 -5
- package/lib/view/transactionDetail/epics/updateTransactionDetailEpic.js +4 -77
- package/lib/view/transactionDetail/journalEntryLinesViewModel.d.ts +0 -27
- package/lib/view/transactionDetail/journalEntryLinesViewModel.js +1 -61
- package/lib/view/transactionDetail/transactionDetailLocalDataHelper.js +8 -10
- package/lib/view/transactionDetail/transactionDetailReducer.d.ts +2 -11
- package/lib/view/transactionDetail/transactionDetailReducer.js +2 -67
- package/lib/view/transactionDetail/transactionDetailState.d.ts +1 -8
- package/lib/view/transactionDetail/transactionDetailTypes.d.ts +0 -2
- package/package.json +1 -1
- package/lib/esm/view/aiCfoView/epics/submitFeedbackEpic.js +0 -39
- package/lib/esm/view/taskManager/taskDetailView/epics/createSubTaskEpic.js +0 -96
- package/lib/esm/view/taskManager/taskDetailView/epics/fetchSubTasksEpic.js +0 -53
- package/lib/view/aiCfoView/epics/submitFeedbackEpic.d.ts +0 -13
- package/lib/view/aiCfoView/epics/submitFeedbackEpic.js +0 -43
- package/lib/view/taskManager/taskDetailView/epics/createSubTaskEpic.d.ts +0 -9
- package/lib/view/taskManager/taskDetailView/epics/createSubTaskEpic.js +0 -100
- package/lib/view/taskManager/taskDetailView/epics/fetchSubTasksEpic.d.ts +0 -8
- package/lib/view/taskManager/taskDetailView/epics/fetchSubTasksEpic.js +0 -57
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createSlice } from '@reduxjs/toolkit';
|
|
2
|
-
import { updateTasks } from '../../../entity/task/taskReducer';
|
|
3
2
|
import { toPriorityCodeType, toTaskStatusCodeType, } from '../../../entity/task/taskState';
|
|
4
3
|
import { date, dateNow } from '../../../zeniDayJS';
|
|
5
4
|
import { toDueDateGroupKeyType, } from './taskList';
|
|
@@ -43,7 +42,6 @@ export const initialState = {
|
|
|
43
42
|
currentTab: 'live',
|
|
44
43
|
byTab: {
|
|
45
44
|
live: { ...initialTabData },
|
|
46
|
-
completed: { ...initialTabData },
|
|
47
45
|
archived: { ...initialTabData },
|
|
48
46
|
deleted: { ...initialTabData },
|
|
49
47
|
snoozed: { ...initialTabData },
|
|
@@ -65,12 +63,14 @@ export const initialState = {
|
|
|
65
63
|
},
|
|
66
64
|
filters: {
|
|
67
65
|
categoryCombinationOperator: 'AND',
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
66
|
+
categories: [
|
|
67
|
+
{
|
|
68
|
+
field: 'status',
|
|
69
|
+
matchingOperator: 'not_equal',
|
|
70
|
+
values: ['resolved'],
|
|
71
|
+
valuesCombinationOperator: 'ANY',
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
74
|
},
|
|
75
75
|
hasValidState() {
|
|
76
76
|
return this.fetchState == 'Completed';
|
|
@@ -102,16 +102,13 @@ const taskList = createSlice({
|
|
|
102
102
|
draft.error = undefined;
|
|
103
103
|
},
|
|
104
104
|
updateTaskList(draft, action) {
|
|
105
|
-
const { data, archived,
|
|
105
|
+
const { data, archived, deleted, snoozed } = action.payload;
|
|
106
106
|
draft.fetchState = 'Completed';
|
|
107
107
|
draft.error = undefined;
|
|
108
108
|
// Group live tasks
|
|
109
109
|
const liveGrouped = getGroupedTaskIds(data);
|
|
110
110
|
draft.byTab.live = toTabData(liveGrouped);
|
|
111
111
|
// Group other tabs if provided
|
|
112
|
-
if (completed != null) {
|
|
113
|
-
draft.byTab.completed = toTabData(getGroupedTaskIds(completed));
|
|
114
|
-
}
|
|
115
112
|
if (archived != null) {
|
|
116
113
|
draft.byTab.archived = toTabData(getGroupedTaskIds(archived));
|
|
117
114
|
}
|
|
@@ -357,26 +354,9 @@ const taskList = createSlice({
|
|
|
357
354
|
draft.taskIdsByAssignees[assigneeKey] = draft.taskIdsByAssignees[assigneeKey].filter((taskId) => taskId !== currentTaskState.id);
|
|
358
355
|
draft.taskIdsByTags[tagsKey] = draft.taskIdsByTags[tagsKey].filter((taskId) => taskId !== currentTaskState.id);
|
|
359
356
|
draft.taskIdsByDueDate[dueDateKey] = draft.taskIdsByDueDate[dueDateKey].filter((taskId) => taskId !== currentTaskState.id);
|
|
360
|
-
|
|
361
|
-
// just the destination bucket. Otherwise a detail-page save that
|
|
362
|
-
// moves a task from group A to group B leaves the row listed
|
|
363
|
-
// under both A and B until refetch.
|
|
364
|
-
const previousGroupIds = currentTaskState.taskGroupIds ?? [];
|
|
365
|
-
const allGroupIdsToClean = new Set([
|
|
366
|
-
...previousGroupIds,
|
|
367
|
-
taskGroupId,
|
|
368
|
-
]);
|
|
369
|
-
allGroupIdsToClean.forEach((gid) => {
|
|
370
|
-
if (draft.taskIdsByGroupsIds[gid] != null) {
|
|
371
|
-
draft.taskIdsByGroupsIds[gid] = draft.taskIdsByGroupsIds[gid].filter((id) => id !== currentTaskState.id);
|
|
372
|
-
}
|
|
373
|
-
});
|
|
357
|
+
draft.taskIdsByGroupsIds[taskGroupId] = draft.taskIdsByGroupsIds[taskGroupId].filter((taskId) => taskId !== currentTaskState.id);
|
|
374
358
|
}
|
|
375
359
|
doUpdateTaskList(draft, task, taskGroupId);
|
|
376
|
-
// Mirror top-level grouping into the active tab's byTab snapshot,
|
|
377
|
-
// otherwise the next updateTaskListTab call copies stale byTab data
|
|
378
|
-
// back over this optimistic update.
|
|
379
|
-
syncTopLevelToActiveTab(draft);
|
|
380
360
|
},
|
|
381
361
|
updateTaskListOnCreateTaskFromTemplateSuccess(draft, action) {
|
|
382
362
|
const { tasks } = action.payload;
|
|
@@ -400,7 +380,6 @@ const taskList = createSlice({
|
|
|
400
380
|
const taskId = task.id;
|
|
401
381
|
draft.updateTasksFetchState.fetchState = 'In-Progress';
|
|
402
382
|
draft.updateTasksFetchState.error = undefined;
|
|
403
|
-
let didMigrateTab = false;
|
|
404
383
|
if (updates.priority != null) {
|
|
405
384
|
const prevPriorityCode = toPriorityCodeType(task.priority.code);
|
|
406
385
|
draft.taskIdsByPriority[prevPriorityCode] = draft.taskIdsByPriority[prevPriorityCode].filter((id) => id != taskId);
|
|
@@ -412,25 +391,6 @@ const taskList = createSlice({
|
|
|
412
391
|
draft.taskIdsByStatus[prevStatusCode] = draft.taskIdsByStatus[prevStatusCode].filter((id) => id != taskId);
|
|
413
392
|
const newStatusCode = toTaskStatusCodeType(updates.status.code);
|
|
414
393
|
draft.taskIdsByStatus[newStatusCode].push(taskId);
|
|
415
|
-
// Optimistically re-bucket between the live and completed tabs
|
|
416
|
-
// when status transitions in/out of "resolved", so the task
|
|
417
|
-
// moves to the Completed tab without waiting for the next
|
|
418
|
-
// fetch. Mirrors backend split in task_handler.py: only
|
|
419
|
-
// `resolved` counts as completed; `done` stays Active.
|
|
420
|
-
//
|
|
421
|
-
// Subtasks travel with their parent — they appear nested under
|
|
422
|
-
// the parent row, not as standalone entries in byTab.taskIds.
|
|
423
|
-
// So we only rebucket parent tasks (tasks with no parentTaskId).
|
|
424
|
-
const isTopLevelTask = task.parentTaskId == null;
|
|
425
|
-
const wasCompleted = prevStatusCode === 'resolved';
|
|
426
|
-
const isCompleted = newStatusCode === 'resolved';
|
|
427
|
-
if (isTopLevelTask && wasCompleted !== isCompleted) {
|
|
428
|
-
const from = isCompleted ? 'live' : 'completed';
|
|
429
|
-
const to = isCompleted ? 'completed' : 'live';
|
|
430
|
-
const updatedTask = { ...task, ...updates };
|
|
431
|
-
migrateTaskBetweenTabs(draft, taskId, updatedTask, from, to);
|
|
432
|
-
didMigrateTab = true;
|
|
433
|
-
}
|
|
434
394
|
}
|
|
435
395
|
if (updates.assignees != null) {
|
|
436
396
|
const prevGroupId = getAssigneesGroupKey(task.assignees);
|
|
@@ -470,18 +430,7 @@ const taskList = createSlice({
|
|
|
470
430
|
};
|
|
471
431
|
}
|
|
472
432
|
}
|
|
473
|
-
|
|
474
|
-
// The migration moved the task out of the source byTab and into
|
|
475
|
-
// the destination byTab. The active tab's grouping arrays are
|
|
476
|
-
// now authoritative — mirror them back to the top-level so the
|
|
477
|
-
// UI sees the task gone from Live (or freshly arrived in
|
|
478
|
-
// Completed). Inverse of syncTopLevelToActiveTab, used only on
|
|
479
|
-
// this path.
|
|
480
|
-
copyTabDataToTopLevel(draft, draft.byTab[draft.currentTab]);
|
|
481
|
-
}
|
|
482
|
-
else {
|
|
483
|
-
syncTopLevelToActiveTab(draft);
|
|
484
|
-
}
|
|
433
|
+
syncTopLevelToActiveTab(draft);
|
|
485
434
|
},
|
|
486
435
|
updateTasksListOnUpdateTaskSuccess(draft) {
|
|
487
436
|
draft.updateTasksFetchState.fetchState = 'Completed';
|
|
@@ -494,80 +443,12 @@ const taskList = createSlice({
|
|
|
494
443
|
removeTaskFromList(draft, action) {
|
|
495
444
|
removeTaskIds(draft, [action.payload]);
|
|
496
445
|
},
|
|
497
|
-
/**
|
|
498
|
-
* Remove a task id from a single group bucket without touching tab
|
|
499
|
-
* membership or other groupings. Used when the user clears a task's
|
|
500
|
-
* group on the Detail page so the row stops appearing under the old
|
|
501
|
-
* group while saveTaskDetailEpic skips the standard list re-insert.
|
|
502
|
-
*/
|
|
503
|
-
removeTaskFromGroupBucket(draft, action) {
|
|
504
|
-
const { taskId, taskGroupId } = action.payload;
|
|
505
|
-
if (draft.taskIdsByGroupsIds[taskGroupId] != null) {
|
|
506
|
-
draft.taskIdsByGroupsIds[taskGroupId] = draft.taskIdsByGroupsIds[taskGroupId].filter((id) => id !== taskId);
|
|
507
|
-
}
|
|
508
|
-
// Mirror to the active tab's grouping so the UI re-renders.
|
|
509
|
-
const activeTab = draft.byTab[draft.currentTab];
|
|
510
|
-
if (activeTab.taskIdsByGroupsIds[taskGroupId] != null) {
|
|
511
|
-
activeTab.taskIdsByGroupsIds[taskGroupId] =
|
|
512
|
-
activeTab.taskIdsByGroupsIds[taskGroupId].filter((id) => id !== taskId);
|
|
513
|
-
}
|
|
514
|
-
},
|
|
515
446
|
clearTaskList(draft) {
|
|
516
447
|
Object.assign(draft, initialState);
|
|
517
448
|
},
|
|
518
449
|
},
|
|
519
|
-
extraReducers: (builder) => {
|
|
520
|
-
// When the entity store receives updated tasks (e.g. from task detail
|
|
521
|
-
// save, snooze/archive/unarchive, etc.), re-bucket parent tasks between
|
|
522
|
-
// the live and completed tabs based on the new status. Mirrors the
|
|
523
|
-
// backend split in task_handler.py: only `resolved` parents count as
|
|
524
|
-
// completed. Subtasks are nested under their parent row, so they
|
|
525
|
-
// never move between tabs on their own.
|
|
526
|
-
builder.addCase(updateTasks, (draft, action) => {
|
|
527
|
-
action.payload.forEach((taskPayload) => {
|
|
528
|
-
// Only re-bucket parents; subtasks (parent_task_id is a non-empty
|
|
529
|
-
// string) are nested under their parent row and don't move tabs.
|
|
530
|
-
if (taskPayload.parent_task_id != null &&
|
|
531
|
-
taskPayload.parent_task_id !== '') {
|
|
532
|
-
return;
|
|
533
|
-
}
|
|
534
|
-
const taskId = taskPayload.task_id;
|
|
535
|
-
// `TaskPayload.status` is required per the type — every producer
|
|
536
|
-
// in this repo sends it, and `mapTaskPayloadToTask` dereferences
|
|
537
|
-
// it unguarded. The optional chain + null guard are defensive
|
|
538
|
-
// only, in case a downstream consumer (web-app-ui / cockpit-ui)
|
|
539
|
-
// ever dispatches `updateTasks` with a partial payload; in that
|
|
540
|
-
// case we treat missing status as "no status change" and skip
|
|
541
|
-
// re-bucketing so we don't migrate a Completed task back into
|
|
542
|
-
// the Live tab.
|
|
543
|
-
const statusCode = taskPayload.status?.code;
|
|
544
|
-
if (statusCode == null) {
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
const isCompleted = statusCode === 'resolved';
|
|
548
|
-
const targetTab = isCompleted ? 'completed' : 'live';
|
|
549
|
-
const otherTab = isCompleted ? 'live' : 'completed';
|
|
550
|
-
// Skip if the task isn't currently in either of these buckets
|
|
551
|
-
// (archived/snoozed/deleted are managed by their own actions).
|
|
552
|
-
const inLive = draft.byTab.live.taskIds.includes(taskId);
|
|
553
|
-
const inCompleted = draft.byTab.completed.taskIds.includes(taskId);
|
|
554
|
-
if (!inLive && !inCompleted) {
|
|
555
|
-
return;
|
|
556
|
-
}
|
|
557
|
-
if (draft.byTab[otherTab].taskIds.includes(taskId)) {
|
|
558
|
-
migrateTaskPayloadBetweenTabs(draft, taskId, taskPayload, otherTab, targetTab);
|
|
559
|
-
}
|
|
560
|
-
else if (!draft.byTab[targetTab].taskIds.includes(taskId)) {
|
|
561
|
-
draft.byTab[targetTab].taskIds.push(taskId);
|
|
562
|
-
}
|
|
563
|
-
});
|
|
564
|
-
// Re-sync the top-level taskIds / grouping arrays for whichever
|
|
565
|
-
// tab the user is currently looking at.
|
|
566
|
-
copyTabDataToTopLevel(draft, draft.byTab[draft.currentTab]);
|
|
567
|
-
});
|
|
568
|
-
},
|
|
569
450
|
});
|
|
570
|
-
export const { fetchTaskListPage, fetchTaskList, updateTaskList, updateTaskListTab, updateTaskListFetchStatus, updateTaskListSearchText, updateTaskListUIState, updateTaskFilters, updateTaskListOnNewGroupCreationSuccess, bulkUpdateTaskList, updateTasksListOnBulkUpdateTasksSuccess, updateTasksListOnBulkUpdateTasksFailure, initiateTaskListLocalData, updateTaskListLocalData, updateTaskListOnTaskGroupDeletion, dragNDropTasks, updateTaskListOnNewTaskCreationSuccess, updateTaskListOnTaskGroupNameUpdate, updateTaskFromListView, updateTasksListOnUpdateTaskSuccess, updateTasksListOnUpdateTaskFailure, updateTaskListOnCreateTaskFromTemplateSuccess, removeTaskFromList,
|
|
451
|
+
export const { fetchTaskListPage, fetchTaskList, updateTaskList, updateTaskListTab, updateTaskListFetchStatus, updateTaskListSearchText, updateTaskListUIState, updateTaskFilters, updateTaskListOnNewGroupCreationSuccess, bulkUpdateTaskList, updateTasksListOnBulkUpdateTasksSuccess, updateTasksListOnBulkUpdateTasksFailure, initiateTaskListLocalData, updateTaskListLocalData, updateTaskListOnTaskGroupDeletion, dragNDropTasks, updateTaskListOnNewTaskCreationSuccess, updateTaskListOnTaskGroupNameUpdate, updateTaskFromListView, updateTasksListOnUpdateTaskSuccess, updateTasksListOnUpdateTaskFailure, updateTaskListOnCreateTaskFromTemplateSuccess, removeTaskFromList, clearTaskList, } = taskList.actions;
|
|
571
452
|
export default taskList.reducer;
|
|
572
453
|
/**
|
|
573
454
|
* helpers
|
|
@@ -674,115 +555,6 @@ const copyTabDataToTopLevel = (draft, tabData) => {
|
|
|
674
555
|
draft.taskIdsByDueDate = tabData.taskIdsByDueDate;
|
|
675
556
|
draft.taskIdsByTags = tabData.taskIdsByTags;
|
|
676
557
|
};
|
|
677
|
-
/**
|
|
678
|
-
* Migrate a parent task between two `byTab` buckets (live ↔ completed)
|
|
679
|
-
* when its status flips into / out of `resolved`. The destination tab
|
|
680
|
-
* picks up the task in EVERY grouping dimension (status, priority,
|
|
681
|
-
* group id, assignees, dueDate, tags) using the UPDATED task as the
|
|
682
|
-
* source-of-truth for the new bucket keys. Otherwise the Completed tab
|
|
683
|
-
* shows the task in the right tab-level list but the grouped views
|
|
684
|
-
* (Group by Status, Group by Priority, etc.) miss it until the next
|
|
685
|
-
* full fetch.
|
|
686
|
-
*/
|
|
687
|
-
const migrateTaskBetweenTabs = (draft, taskId, updatedTask, from, to) => {
|
|
688
|
-
const fromTab = draft.byTab[from];
|
|
689
|
-
const toTab = draft.byTab[to];
|
|
690
|
-
// Source tab: drop the task from every grouping dimension.
|
|
691
|
-
removeIdsFromTabData(fromTab, [taskId]);
|
|
692
|
-
// Destination tab: add the task to taskIds (deduped) plus the
|
|
693
|
-
// appropriate bucket inside each grouping dimension based on the
|
|
694
|
-
// updated task.
|
|
695
|
-
if (!toTab.taskIds.includes(taskId)) {
|
|
696
|
-
toTab.taskIds.push(taskId);
|
|
697
|
-
}
|
|
698
|
-
const priorityKey = toPriorityCodeType(updatedTask.priority.code);
|
|
699
|
-
if (!toTab.taskIdsByPriority[priorityKey].includes(taskId)) {
|
|
700
|
-
toTab.taskIdsByPriority[priorityKey].push(taskId);
|
|
701
|
-
}
|
|
702
|
-
const statusKey = toTaskStatusCodeType(updatedTask.status.code);
|
|
703
|
-
if (!toTab.taskIdsByStatus[statusKey].includes(taskId)) {
|
|
704
|
-
toTab.taskIdsByStatus[statusKey].push(taskId);
|
|
705
|
-
}
|
|
706
|
-
const dueDateKey = updatedTask.dueDate != null
|
|
707
|
-
? getDueDateGroupKey(updatedTask.dueDate.toString())
|
|
708
|
-
: 'upcoming';
|
|
709
|
-
if (!toTab.taskIdsByDueDate[dueDateKey].includes(taskId)) {
|
|
710
|
-
toTab.taskIdsByDueDate[dueDateKey].push(taskId);
|
|
711
|
-
}
|
|
712
|
-
const assigneeKey = getAssigneesGroupKey(updatedTask.assignees);
|
|
713
|
-
if (toTab.taskIdsByAssignees[assigneeKey] == null) {
|
|
714
|
-
toTab.taskIdsByAssignees[assigneeKey] = [taskId];
|
|
715
|
-
}
|
|
716
|
-
else if (!toTab.taskIdsByAssignees[assigneeKey].includes(taskId)) {
|
|
717
|
-
toTab.taskIdsByAssignees[assigneeKey].push(taskId);
|
|
718
|
-
}
|
|
719
|
-
const tagIds = [...updatedTask.tagIds].sort();
|
|
720
|
-
const tagsKey = tagIds.join(',');
|
|
721
|
-
if (toTab.taskIdsByTags[tagsKey] == null) {
|
|
722
|
-
toTab.taskIdsByTags[tagsKey] = [taskId];
|
|
723
|
-
}
|
|
724
|
-
else if (!toTab.taskIdsByTags[tagsKey].includes(taskId)) {
|
|
725
|
-
toTab.taskIdsByTags[tagsKey].push(taskId);
|
|
726
|
-
}
|
|
727
|
-
(updatedTask.taskGroupIds ?? []).forEach((groupId) => {
|
|
728
|
-
if (toTab.taskIdsByGroupsIds[groupId] == null) {
|
|
729
|
-
toTab.taskIdsByGroupsIds[groupId] = [taskId];
|
|
730
|
-
}
|
|
731
|
-
else if (!toTab.taskIdsByGroupsIds[groupId].includes(taskId)) {
|
|
732
|
-
toTab.taskIdsByGroupsIds[groupId].push(taskId);
|
|
733
|
-
}
|
|
734
|
-
});
|
|
735
|
-
};
|
|
736
|
-
/**
|
|
737
|
-
* `updateTasks` extraReducer variant — same migration semantics but
|
|
738
|
-
* keyed off the raw `TaskPayload` instead of the rich `Task` entity.
|
|
739
|
-
*/
|
|
740
|
-
const migrateTaskPayloadBetweenTabs = (draft, taskId, payload, from, to) => {
|
|
741
|
-
const fromTab = draft.byTab[from];
|
|
742
|
-
const toTab = draft.byTab[to];
|
|
743
|
-
removeIdsFromTabData(fromTab, [taskId]);
|
|
744
|
-
if (!toTab.taskIds.includes(taskId)) {
|
|
745
|
-
toTab.taskIds.push(taskId);
|
|
746
|
-
}
|
|
747
|
-
if (payload.priority?.code != null) {
|
|
748
|
-
const priorityKey = toPriorityCodeType(payload.priority.code);
|
|
749
|
-
if (!toTab.taskIdsByPriority[priorityKey].includes(taskId)) {
|
|
750
|
-
toTab.taskIdsByPriority[priorityKey].push(taskId);
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
if (payload.status?.code != null) {
|
|
754
|
-
const statusKey = toTaskStatusCodeType(payload.status.code);
|
|
755
|
-
if (!toTab.taskIdsByStatus[statusKey].includes(taskId)) {
|
|
756
|
-
toTab.taskIdsByStatus[statusKey].push(taskId);
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
const dueDateKey = getDueDateGroupKey(payload.due_date);
|
|
760
|
-
if (!toTab.taskIdsByDueDate[dueDateKey].includes(taskId)) {
|
|
761
|
-
toTab.taskIdsByDueDate[dueDateKey].push(taskId);
|
|
762
|
-
}
|
|
763
|
-
const assigneeKey = getAssigneesGroupKey(payload.assignees ?? []);
|
|
764
|
-
if (toTab.taskIdsByAssignees[assigneeKey] == null) {
|
|
765
|
-
toTab.taskIdsByAssignees[assigneeKey] = [taskId];
|
|
766
|
-
}
|
|
767
|
-
else if (!toTab.taskIdsByAssignees[assigneeKey].includes(taskId)) {
|
|
768
|
-
toTab.taskIdsByAssignees[assigneeKey].push(taskId);
|
|
769
|
-
}
|
|
770
|
-
const tagsKey = getTagsGroupKey(payload.tags ?? []);
|
|
771
|
-
if (toTab.taskIdsByTags[tagsKey] == null) {
|
|
772
|
-
toTab.taskIdsByTags[tagsKey] = [taskId];
|
|
773
|
-
}
|
|
774
|
-
else if (!toTab.taskIdsByTags[tagsKey].includes(taskId)) {
|
|
775
|
-
toTab.taskIdsByTags[tagsKey].push(taskId);
|
|
776
|
-
}
|
|
777
|
-
(payload.task_group_ids ?? []).forEach((groupId) => {
|
|
778
|
-
if (toTab.taskIdsByGroupsIds[groupId] == null) {
|
|
779
|
-
toTab.taskIdsByGroupsIds[groupId] = [taskId];
|
|
780
|
-
}
|
|
781
|
-
else if (!toTab.taskIdsByGroupsIds[groupId].includes(taskId)) {
|
|
782
|
-
toTab.taskIdsByGroupsIds[groupId].push(taskId);
|
|
783
|
-
}
|
|
784
|
-
});
|
|
785
|
-
};
|
|
786
558
|
const removeIdsFromTabData = (tabData, taskIds) => {
|
|
787
559
|
const removedSet = new Set(taskIds);
|
|
788
560
|
const isRemoved = (id) => removedSet.has(id);
|
|
@@ -824,38 +596,13 @@ const getGroupedTaskIds = (tasks) => {
|
|
|
824
596
|
...initialTaskIdsByDueDate,
|
|
825
597
|
};
|
|
826
598
|
const taskIdsByTags = {};
|
|
827
|
-
// A task is a "nested subtask" (hidden from the flat top-level list +
|
|
828
|
-
// dropped from the tab count) iff a parent in the same payload claims
|
|
829
|
-
// it via its `subtasks: [...]` array. Match this criterion exactly to
|
|
830
|
-
// WC's `allSubtaskIds` union in TaskManagementPage.tsx — otherwise
|
|
831
|
-
// the tab count and the visible top-level row count disagree
|
|
832
|
-
// whenever the BE emits `parent_task_id` on a subtask but omits it
|
|
833
|
-
// from the parent's `subtasks` array (a real inconsistency this UI
|
|
834
|
-
// sees today).
|
|
835
|
-
//
|
|
836
|
-
// Using `parent_task_id` here would sometimes drop a task from the
|
|
837
|
-
// count that WC still renders as a flat top-level row — that's the
|
|
838
|
-
// bug we're fixing.
|
|
839
|
-
const nestedSubtaskIds = new Set();
|
|
840
599
|
tasks.forEach((task) => {
|
|
841
|
-
(task.
|
|
842
|
-
});
|
|
843
|
-
tasks.forEach((task) => {
|
|
844
|
-
const isNestedSubtask = nestedSubtaskIds.has(task.task_id);
|
|
845
|
-
// Subtasks stay in every groupBy array so the UI can render them
|
|
846
|
-
// nested under their parent (WC's TaskManagementPage builds
|
|
847
|
-
// `subtasksByParentId` from `group.tasks` and then filters them
|
|
848
|
-
// out of top-level rows via `filterOutSubtasks`). Only the flat
|
|
849
|
-
// top-level `taskIds` array — which drives the Active/Completed/…
|
|
850
|
-
// tab counts — must exclude nested subtasks.
|
|
600
|
+
taskIds.push(task.task_id);
|
|
851
601
|
const priorityKey = toPriorityCodeType(task.priority.code);
|
|
852
602
|
const statusKey = toTaskStatusCodeType(task.status.code);
|
|
853
603
|
const assigneeKey = getAssigneesGroupKey(task.assignees);
|
|
854
604
|
const dueDateKey = getDueDateGroupKey(task.due_date);
|
|
855
605
|
const tagsKey = getTagsGroupKey(task.tags);
|
|
856
|
-
if (!isNestedSubtask) {
|
|
857
|
-
taskIds.push(task.task_id);
|
|
858
|
-
}
|
|
859
606
|
taskIdsGroupedByPriority[priorityKey] = [
|
|
860
607
|
...taskIdsGroupedByPriority[priorityKey],
|
|
861
608
|
task.task_id,
|
|
@@ -334,7 +334,6 @@ export const getAllTasks = createSelector((state) => state.taskListState, (state
|
|
|
334
334
|
const taskGroupTemplates = getTaskGroupTemplateByIds(taskGroupTemplateState, taskGroupTemplateViewState.taskGroupTemplateIds);
|
|
335
335
|
const tabCounts = {
|
|
336
336
|
live: taskListState.byTab.live.taskIds.length,
|
|
337
|
-
completed: taskListState.byTab.completed.taskIds.length,
|
|
338
337
|
archived: taskListState.byTab.archived.taskIds.length,
|
|
339
338
|
deleted: taskListState.byTab.deleted.taskIds.length,
|
|
340
339
|
snoozed: taskListState.byTab.snoozed.taskIds.length,
|
|
@@ -48,61 +48,3 @@ export const getTaskUpdates = (groupByKey, groupId) => {
|
|
|
48
48
|
taskGroupIds: [groupId],
|
|
49
49
|
};
|
|
50
50
|
};
|
|
51
|
-
/**
|
|
52
|
-
* Sort a subtask list by one of the sortable columns. Pure — no state
|
|
53
|
-
* dependency. `statusOrder` is derived from `allTaskStatus` so any new
|
|
54
|
-
* status added upstream slots in by its enum position instead of
|
|
55
|
-
* falling into a catch-all bucket.
|
|
56
|
-
*/
|
|
57
|
-
export const sortSubtasks = (subtasks, sortKey, sortOrder, userList, tagList) => {
|
|
58
|
-
if (sortKey == null) {
|
|
59
|
-
return subtasks;
|
|
60
|
-
}
|
|
61
|
-
const statusOrder = allTaskStatus.reduce((acc, status, index) => {
|
|
62
|
-
acc[status.code] = index;
|
|
63
|
-
return acc;
|
|
64
|
-
}, {});
|
|
65
|
-
const unknownStatusRank = allTaskStatus.length;
|
|
66
|
-
const cmpStr = (a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' });
|
|
67
|
-
const keyFn = (task) => {
|
|
68
|
-
if (sortKey === 'name') {
|
|
69
|
-
return task.name ?? '';
|
|
70
|
-
}
|
|
71
|
-
if (sortKey === 'status') {
|
|
72
|
-
return statusOrder[task.status.code] ?? unknownStatusRank;
|
|
73
|
-
}
|
|
74
|
-
if (sortKey === 'assignee') {
|
|
75
|
-
const firstId = task.assignees?.[0];
|
|
76
|
-
if (firstId == null) {
|
|
77
|
-
return '';
|
|
78
|
-
}
|
|
79
|
-
const user = userList.find((candidate) => candidate.userId === firstId);
|
|
80
|
-
return user
|
|
81
|
-
? `${user.firstName ?? ''} ${user.lastName ?? ''}`.trim()
|
|
82
|
-
: '';
|
|
83
|
-
}
|
|
84
|
-
if (sortKey === 'tag') {
|
|
85
|
-
const firstTagId = task.tagIds?.[0];
|
|
86
|
-
if (firstTagId == null) {
|
|
87
|
-
return '';
|
|
88
|
-
}
|
|
89
|
-
const tag = tagList.find((candidate) => candidate.tagId === firstTagId);
|
|
90
|
-
return tag?.name ?? '';
|
|
91
|
-
}
|
|
92
|
-
return '';
|
|
93
|
-
};
|
|
94
|
-
const copy = [...subtasks];
|
|
95
|
-
copy.sort((a, b) => {
|
|
96
|
-
const av = keyFn(a);
|
|
97
|
-
const bv = keyFn(b);
|
|
98
|
-
let cmp;
|
|
99
|
-
if (typeof av === 'number' && typeof bv === 'number') {
|
|
100
|
-
cmp = av - bv;
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
cmp = cmpStr(String(av), String(bv));
|
|
104
|
-
}
|
|
105
|
-
return sortOrder === 'asc' ? cmp : -cmp;
|
|
106
|
-
});
|
|
107
|
-
return copy;
|
|
108
|
-
};
|
|
@@ -45,15 +45,7 @@ export const toTransactionUpdatesFromTransactionDetailLocalData = (transactionDe
|
|
|
45
45
|
let lastUpdatedCategory;
|
|
46
46
|
let lastUpdatedClass;
|
|
47
47
|
// single recommendation to just pick the last recommendation in the list of lines
|
|
48
|
-
const existingLineIds = new Set(transaction.lines?.map((l) => l.id) ?? []);
|
|
49
|
-
const deletedLineIds = new Set((transactionDetailLocalData.deletedLineIds ?? []).filter((id) => existingLineIds.has(id)));
|
|
50
|
-
// Collect new lines — in lineItemById but not yet in transaction.lines.
|
|
51
|
-
const addedLines = Object.values(transactionDetailLocalData.lineItemById).filter((item) => !existingLineIds.has(item.id));
|
|
52
48
|
transaction.lines?.forEach((line) => {
|
|
53
|
-
// Skip lines the user deleted in edit mode.
|
|
54
|
-
if (deletedLineIds.has(line.id)) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
49
|
const lineItem = transactionDetailLocalData.lineItemById[line.id];
|
|
58
50
|
if (line.type === 'transaction_with_account_and_class_line' ||
|
|
59
51
|
line.type === 'transaction_with_product_or_service_line' ||
|
|
@@ -64,7 +56,6 @@ export const toTransactionUpdatesFromTransactionDetailLocalData = (transactionDe
|
|
|
64
56
|
const entityDetails = getEntityDetails(selectedEntity, lineItem, transactionDetailLocalData.vendorUpdates, transactionDetailLocalData.customerUpdates);
|
|
65
57
|
updatesByLineId[line.id] = {
|
|
66
58
|
lineDescription: lineItem.lineDescription,
|
|
67
|
-
amount: lineItem.amount,
|
|
68
59
|
class: lineItem.class,
|
|
69
60
|
account: lineItem.account,
|
|
70
61
|
// Project is at the transaction level for invoice/credit_memo; omit from lines.
|
|
@@ -86,17 +77,6 @@ export const toTransactionUpdatesFromTransactionDetailLocalData = (transactionDe
|
|
|
86
77
|
currentSelectedEntityLine.type === 'vendor'
|
|
87
78
|
? lineItem.vendor
|
|
88
79
|
: undefined,
|
|
89
|
-
// Carry the JE debit/credit side so a split edit that flips an
|
|
90
|
-
// existing line's posting side persists through the line patch.
|
|
91
|
-
...(line.type === 'journal_entry_transaction_line'
|
|
92
|
-
? {
|
|
93
|
-
postingType: lineItem.postingType,
|
|
94
|
-
postingTypeName: lineItem.postingType != null
|
|
95
|
-
? lineItem.postingType.charAt(0).toUpperCase() +
|
|
96
|
-
lineItem.postingType.slice(1)
|
|
97
|
-
: lineItem.postingTypeName,
|
|
98
|
-
}
|
|
99
|
-
: {}),
|
|
100
80
|
};
|
|
101
81
|
}
|
|
102
82
|
let hasCategoryChanged = false;
|
|
@@ -183,8 +163,6 @@ export const toTransactionUpdatesFromTransactionDetailLocalData = (transactionDe
|
|
|
183
163
|
and all the line items are NOT uncategorized*/
|
|
184
164
|
markAsMiscatUpdates: isTransactionMisCategorized,
|
|
185
165
|
anyBulkUpdates: isBulkUpdate,
|
|
186
|
-
addedLines: addedLines.length > 0 ? addedLines : undefined,
|
|
187
|
-
deletedLineIds: deletedLineIds.size > 0 ? [...deletedLineIds] : undefined,
|
|
188
166
|
};
|
|
189
167
|
};
|
|
190
168
|
const doUpdateEntityUpdates = (entityUpdates, entityId, entityType, recommendation, isBulkUpdate = false, updatedEntityId, entityName) => {
|
|
@@ -58,9 +58,7 @@ export const updateTransactionDetailEpic = (actions$, state$, zeniAPI) => action
|
|
|
58
58
|
if (response.data.vendors != null) {
|
|
59
59
|
actions.push(updateVendors(response.data.vendors, 'merge'));
|
|
60
60
|
}
|
|
61
|
-
|
|
62
|
-
// keep any line absent from the server response (deletion bug).
|
|
63
|
-
actions.push(updateTransaction(transactionDetailKeyForResponse, response.data.transaction, 'replace'));
|
|
61
|
+
actions.push(updateTransaction(transactionDetailKeyForResponse, response.data.transaction));
|
|
64
62
|
actions.push(initializeTransactionDetailLocalData({
|
|
65
63
|
transactionId: transactionIdResp,
|
|
66
64
|
vendorId: response.data.vendors?.[0]?.vendor_id,
|
|
@@ -161,7 +159,7 @@ function preparePayload(transactionState, vendorState, transactionDetailState, t
|
|
|
161
159
|
};
|
|
162
160
|
}
|
|
163
161
|
if (detail != null) {
|
|
164
|
-
const transUpdates = toTransactionUpdatePayload(saveUpdates.updates, detail, cotTransactionTracking, isAccountingProjectsEnabled, vendorUpdates, customerUpdates, currentSelectedEntity
|
|
162
|
+
const transUpdates = toTransactionUpdatePayload(saveUpdates.updates, detail, cotTransactionTracking, isAccountingProjectsEnabled, vendorUpdates, customerUpdates, currentSelectedEntity);
|
|
165
163
|
const entityUpdates = toEntityUpdatePayload(saveUpdates.entityRelatedUpdates);
|
|
166
164
|
return {
|
|
167
165
|
entity_related_updates: entityUpdates,
|
|
@@ -171,7 +169,7 @@ function preparePayload(transactionState, vendorState, transactionDetailState, t
|
|
|
171
169
|
}
|
|
172
170
|
return undefined;
|
|
173
171
|
}
|
|
174
|
-
export const toTransactionUpdatePayload = (updates, detail, cotTransactionTracking, isAccountingProjectsEnabled, vendorUpdates, customerUpdates, tranSelectedEntity
|
|
172
|
+
export const toTransactionUpdatePayload = (updates, detail, cotTransactionTracking, isAccountingProjectsEnabled, vendorUpdates, customerUpdates, tranSelectedEntity) => {
|
|
175
173
|
const transaction = Object.assign({}, detail);
|
|
176
174
|
transaction.memo = updates.memo ?? '';
|
|
177
175
|
// Unlike `memo` (always reset to '' when absent), `documentId` falls back to
|
|
@@ -180,11 +178,6 @@ export const toTransactionUpdatePayload = (updates, detail, cotTransactionTracki
|
|
|
180
178
|
// either the unchanged current value or undefined, and we must not send an
|
|
181
179
|
// empty `document_id` that the server would treat as clearing the number.
|
|
182
180
|
transaction.documentId = updates.documentId ?? transaction.documentId;
|
|
183
|
-
// Remove lines deleted by the user in edit mode before patching or serializing.
|
|
184
|
-
if (deletedLineIds != null && deletedLineIds.length > 0) {
|
|
185
|
-
const deletedSet = new Set(deletedLineIds);
|
|
186
|
-
transaction.lines = transaction.lines?.filter((line) => !deletedSet.has(line.id));
|
|
187
|
-
}
|
|
188
181
|
const recommendationUpdates = {};
|
|
189
182
|
for (const key in updates.updatesByLineId) {
|
|
190
183
|
const lineUpdates = updates.updatesByLineId[key];
|
|
@@ -192,9 +185,6 @@ export const toTransactionUpdatePayload = (updates, detail, cotTransactionTracki
|
|
|
192
185
|
if (line.id === key) {
|
|
193
186
|
const lineTobeUpdated = Object.assign({}, line);
|
|
194
187
|
lineTobeUpdated.lineDescription = lineUpdates.lineDescription ?? '';
|
|
195
|
-
if (lineUpdates.amount != null) {
|
|
196
|
-
lineTobeUpdated.amount = lineUpdates.amount;
|
|
197
|
-
}
|
|
198
188
|
if (lineUpdates.class != null) {
|
|
199
189
|
lineTobeUpdated.class = lineUpdates.class;
|
|
200
190
|
}
|
|
@@ -216,16 +206,6 @@ export const toTransactionUpdatePayload = (updates, detail, cotTransactionTracki
|
|
|
216
206
|
else {
|
|
217
207
|
lineTobeUpdated.vendor = undefined;
|
|
218
208
|
}
|
|
219
|
-
// Persist a debit↔credit flip on an existing JE line. `lineTobeUpdated`
|
|
220
|
-
// is typed as the account-and-class shape, but for a JE the runtime
|
|
221
|
-
// object is a JournalEntryTransactionLine, so cast to set the side.
|
|
222
|
-
if (lineUpdates.postingType != null) {
|
|
223
|
-
const journalEntryLine = lineTobeUpdated;
|
|
224
|
-
journalEntryLine.postingType = lineUpdates.postingType;
|
|
225
|
-
if (lineUpdates.postingTypeName != null) {
|
|
226
|
-
journalEntryLine.postingTypeName = lineUpdates.postingTypeName;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
209
|
if (lineUpdates.recommendation != null) {
|
|
230
210
|
recommendationUpdates[line.id] = lineUpdates.recommendation;
|
|
231
211
|
}
|
|
@@ -245,12 +225,6 @@ export const toTransactionUpdatePayload = (updates, detail, cotTransactionTracki
|
|
|
245
225
|
});
|
|
246
226
|
transaction.lines = newLineMap;
|
|
247
227
|
}
|
|
248
|
-
// Append lines added by the user in edit mode. Sending id='' tells the
|
|
249
|
-
// backend to create a new QBO line rather than updating an existing one.
|
|
250
|
-
if (addedLines != null && addedLines.length > 0) {
|
|
251
|
-
const newLines = addedLines.map(toNewTransactionLine);
|
|
252
|
-
transaction.lines = [...(transaction.lines ?? []), ...newLines];
|
|
253
|
-
}
|
|
254
228
|
transaction.recommendations = recommendationUpdates;
|
|
255
229
|
// Convert transaction to payload first
|
|
256
230
|
let basePayload;
|
|
@@ -325,52 +299,6 @@ const checkIfVendorOrCustomerMissingForAnyLineItem = (transactionPayload) => {
|
|
|
325
299
|
}
|
|
326
300
|
return isAnyVendorOrCustomerMissing;
|
|
327
301
|
};
|
|
328
|
-
// Converts a locally-created line item (from edit mode) into the wire shape
|
|
329
|
-
// expected by the categorize endpoint. Sending id='' signals the backend to
|
|
330
|
-
// create a new QBO line rather than update an existing one.
|
|
331
|
-
//
|
|
332
|
-
// Type-dispatched so each transaction family contributes its own new-line
|
|
333
|
-
// shape: journal entries carry debit/credit `postingType`, expenses carry
|
|
334
|
-
// `billable`. Phase 3 types extend this by adding a branch rather than forking
|
|
335
|
-
// the function.
|
|
336
|
-
const EMPTY_ACCOUNT = {
|
|
337
|
-
accountDescription: '',
|
|
338
|
-
accountId: '',
|
|
339
|
-
accountName: '',
|
|
340
|
-
labels: [],
|
|
341
|
-
};
|
|
342
|
-
export const toNewTransactionLine = (item) => {
|
|
343
|
-
if (item.type === 'journal_entry_transaction_line') {
|
|
344
|
-
const journalEntryLine = {
|
|
345
|
-
...item,
|
|
346
|
-
id: '',
|
|
347
|
-
type: 'journal_entry_transaction_line',
|
|
348
|
-
platformLineDetailType: item.platformLineDetailType ?? 'journal_entry_line_detail',
|
|
349
|
-
account: item.account ?? EMPTY_ACCOUNT,
|
|
350
|
-
billable: item.billable ?? 'not_billable',
|
|
351
|
-
isCategoryMiscategorized: false,
|
|
352
|
-
isClassMiscategorized: false,
|
|
353
|
-
lineTransactionType: item.lineTransactionType ?? 'journal_entry',
|
|
354
|
-
postingType: item.postingType ?? 'debit',
|
|
355
|
-
postingTypeName: item.postingTypeName != null && item.postingTypeName !== ''
|
|
356
|
-
? item.postingTypeName
|
|
357
|
-
: (item.postingType ?? 'debit').charAt(0).toUpperCase() +
|
|
358
|
-
(item.postingType ?? 'debit').slice(1),
|
|
359
|
-
};
|
|
360
|
-
return journalEntryLine;
|
|
361
|
-
}
|
|
362
|
-
const accountAndClassLine = {
|
|
363
|
-
...item,
|
|
364
|
-
id: '',
|
|
365
|
-
type: 'transaction_with_account_and_class_line',
|
|
366
|
-
platformLineDetailType: item.platformLineDetailType ?? 'account_based_expense_line_detail',
|
|
367
|
-
account: item.account ?? EMPTY_ACCOUNT,
|
|
368
|
-
billable: item.billable ?? 'not_billable',
|
|
369
|
-
isCategoryMiscategorized: false,
|
|
370
|
-
isClassMiscategorized: false,
|
|
371
|
-
};
|
|
372
|
-
return accountAndClassLine;
|
|
373
|
-
};
|
|
374
302
|
const toEntityUpdatePayload = (entityUpdates) => {
|
|
375
303
|
const entityUpdatesPayload = [];
|
|
376
304
|
let entityKey;
|