@zeniai/client-epic-state 5.0.47-beta0ND → 5.0.48-betaRD1
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/project/projectPayload.d.ts +3 -3
- package/lib/entity/project/projectPayload.js +3 -3
- package/lib/entity/project/projectState.d.ts +4 -2
- package/lib/entity/tenant/SessionManager.d.ts +0 -4
- package/lib/entity/tenant/SessionManager.js +0 -6
- package/lib/entity/transaction/payloadTypes/transactionLinePayload.js +1 -1
- package/lib/entity/transaction/stateTypes/transactionLine.d.ts +2 -2
- package/lib/esm/entity/project/projectPayload.js +1 -1
- package/lib/esm/entity/tenant/SessionManager.js +0 -6
- package/lib/esm/entity/transaction/payloadTypes/transactionLinePayload.js +2 -2
- package/lib/esm/index.js +2 -1
- package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationViewEpic.js +6 -0
- package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/updateTransactionCategorizationEpic.js +3 -0
- package/lib/esm/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.js +19 -2
- package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +13 -4
- package/lib/esm/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +7 -2
- package/lib/esm/view/profitAndLossClassesView/profitAndLossHorizontalEnrichment.js +44 -0
- package/lib/index.d.ts +4 -3
- package/lib/index.js +30 -28
- package/lib/tsconfig.typecheck.tsbuildinfo +1 -1
- package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationViewEpic.d.ts +2 -1
- package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationViewEpic.js +6 -0
- package/lib/view/expenseAutomationView/epics/transactionCategorization/updateTransactionCategorizationEpic.js +3 -0
- package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +2 -1
- package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.js +19 -2
- package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.d.ts +3 -1
- package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +13 -4
- package/lib/view/expenseAutomationView/selectorTypes/transactionsViewSelectorTypes.d.ts +3 -0
- package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +6 -1
- package/lib/view/expenseAutomationView/types/transactionsViewState.d.ts +3 -0
- package/lib/view/profitAndLossClassesView/profitAndLossHorizontalEnrichment.d.ts +15 -1
- package/lib/view/profitAndLossClassesView/profitAndLossHorizontalEnrichment.js +45 -0
- package/lib/view/transactionDetail/transactionDetailState.d.ts +2 -2
- package/lib/view/transactionDetail/transactionDetailTypes.d.ts +2 -2
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Project } from './projectState';
|
|
1
|
+
import { Project, ProjectBase } from './projectState';
|
|
2
2
|
export interface ProjectMetadataPayload {
|
|
3
3
|
accounting_provider_project_customer_id: string | null;
|
|
4
4
|
accounting_provider_project_id: string | null;
|
|
@@ -15,6 +15,6 @@ export interface ProjectBasePayload {
|
|
|
15
15
|
accounting_provider_project_id?: string | null;
|
|
16
16
|
project_name?: string;
|
|
17
17
|
}
|
|
18
|
-
export declare const toProjectBasePayload: (project:
|
|
19
|
-
export declare const
|
|
18
|
+
export declare const toProjectBasePayload: (project: ProjectBase | undefined) => ProjectBasePayload;
|
|
19
|
+
export declare const mapProjectBasePayloadToProjectBase: (payload: ProjectBasePayload) => ProjectBase;
|
|
20
20
|
export declare const mapProjectMetadataPayloadToProject: (payload: ProjectMetadataPayload) => Project;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.mapProjectMetadataPayloadToProject = exports.
|
|
3
|
+
exports.mapProjectMetadataPayloadToProject = exports.mapProjectBasePayloadToProjectBase = exports.toProjectBasePayload = void 0;
|
|
4
4
|
const zeniDayJS_1 = require("../../zeniDayJS");
|
|
5
5
|
const toProjectBasePayload = (project) => ({
|
|
6
6
|
zeni_project_id: project?.projectId ?? '',
|
|
@@ -8,14 +8,14 @@ const toProjectBasePayload = (project) => ({
|
|
|
8
8
|
project_name: project?.projectName ?? '',
|
|
9
9
|
});
|
|
10
10
|
exports.toProjectBasePayload = toProjectBasePayload;
|
|
11
|
-
const
|
|
11
|
+
const mapProjectBasePayloadToProjectBase = (payload) => ({
|
|
12
12
|
projectId: payload.zeni_project_id,
|
|
13
13
|
projectName: payload.project_name ?? '',
|
|
14
14
|
...(payload.accounting_provider_project_id != null
|
|
15
15
|
? { accountingProviderProjectId: payload.accounting_provider_project_id }
|
|
16
16
|
: {}),
|
|
17
17
|
});
|
|
18
|
-
exports.
|
|
18
|
+
exports.mapProjectBasePayloadToProjectBase = mapProjectBasePayloadToProjectBase;
|
|
19
19
|
const mapProjectMetadataPayloadToProject = (payload) => ({
|
|
20
20
|
projectId: payload.zeni_project_id,
|
|
21
21
|
projectName: payload.project_name,
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { ID } from '../../commonStateTypes/common';
|
|
2
2
|
import { ZeniDate } from '../../zeniDayJS';
|
|
3
|
-
export interface
|
|
3
|
+
export interface ProjectBase {
|
|
4
4
|
projectId: ID;
|
|
5
5
|
projectName: string;
|
|
6
|
-
accountingProviderProjectCustomerId?: ID;
|
|
7
6
|
accountingProviderProjectId?: ID;
|
|
7
|
+
}
|
|
8
|
+
export interface Project extends ProjectBase {
|
|
9
|
+
accountingProviderProjectCustomerId?: ID;
|
|
8
10
|
parentCustomerAccountingProviderId?: ID;
|
|
9
11
|
startDate?: ZeniDate;
|
|
10
12
|
status?: string;
|
|
@@ -40,10 +40,6 @@ export declare class SessionManager {
|
|
|
40
40
|
continueSession(): void;
|
|
41
41
|
isWarningActive(): boolean;
|
|
42
42
|
getSecondsRemaining(): number;
|
|
43
|
-
/** Wall-clock timestamp of the last detected user activity. Surfaced so
|
|
44
|
-
* the auto-logout flow can pass this through to the signin page (e.g.
|
|
45
|
-
* to render a "Last active: X min ago" banner). */
|
|
46
|
-
getLastActivityTime(): number;
|
|
47
43
|
private onActivity;
|
|
48
44
|
private onVisibilityChange;
|
|
49
45
|
/** Single source of truth for marking activity locally. */
|
|
@@ -130,12 +130,6 @@ class SessionManager {
|
|
|
130
130
|
getSecondsRemaining() {
|
|
131
131
|
return this.secondsRemaining;
|
|
132
132
|
}
|
|
133
|
-
/** Wall-clock timestamp of the last detected user activity. Surfaced so
|
|
134
|
-
* the auto-logout flow can pass this through to the signin page (e.g.
|
|
135
|
-
* to render a "Last active: X min ago" banner). */
|
|
136
|
-
getLastActivityTime() {
|
|
137
|
-
return this.lastActivityTime;
|
|
138
|
-
}
|
|
139
133
|
// ─── Private ───────────────────────────────────────────────
|
|
140
134
|
onActivity() {
|
|
141
135
|
if (!this.running || this.warningActive) {
|
|
@@ -47,7 +47,7 @@ const toTransactionWithAccountAndClassLine = (payload, currency, type = 'transac
|
|
|
47
47
|
project: payload.line_detail.project != null &&
|
|
48
48
|
payload.line_detail.project.zeni_project_id != null &&
|
|
49
49
|
payload.line_detail.project.zeni_project_id !== ''
|
|
50
|
-
? (0, projectPayload_1.
|
|
50
|
+
? (0, projectPayload_1.mapProjectBasePayloadToProjectBase)(payload.line_detail.project)
|
|
51
51
|
: undefined,
|
|
52
52
|
billable: payload.line_detail.billable_status ?? 'not_billable',
|
|
53
53
|
isCategoryMiscategorized: payload.is_category_miscategorized ?? false,
|
|
@@ -4,7 +4,7 @@ import { COTLineItemTracking } from '../../../commonStateTypes/recommendationBas
|
|
|
4
4
|
import { AccountBase } from '../../account/accountState';
|
|
5
5
|
import { ClassBase } from '../../class/classState';
|
|
6
6
|
import { CustomerBase } from '../../customer/customerState';
|
|
7
|
-
import {
|
|
7
|
+
import { ProjectBase } from '../../project/projectState';
|
|
8
8
|
import { VendorBase } from '../../vendor/vendorState';
|
|
9
9
|
import { TransactionID } from './transaction';
|
|
10
10
|
import { TransactionCategory, TransactionType } from './transactionType';
|
|
@@ -40,7 +40,7 @@ export interface TransactionWithAccountAndClassLine extends TransactionLineBase
|
|
|
40
40
|
isClassMiscategorized: boolean;
|
|
41
41
|
class?: ClassBase;
|
|
42
42
|
customer?: CustomerBase;
|
|
43
|
-
project?:
|
|
43
|
+
project?: ProjectBase;
|
|
44
44
|
vendor?: VendorBase;
|
|
45
45
|
}
|
|
46
46
|
export interface TransactionWithAccountAndClassLineWithCOTTracking extends TransactionWithAccountAndClassLine {
|
|
@@ -4,7 +4,7 @@ export const toProjectBasePayload = (project) => ({
|
|
|
4
4
|
accounting_provider_project_id: project?.accountingProviderProjectId ?? null,
|
|
5
5
|
project_name: project?.projectName ?? '',
|
|
6
6
|
});
|
|
7
|
-
export const
|
|
7
|
+
export const mapProjectBasePayloadToProjectBase = (payload) => ({
|
|
8
8
|
projectId: payload.zeni_project_id,
|
|
9
9
|
projectName: payload.project_name ?? '',
|
|
10
10
|
...(payload.accounting_provider_project_id != null
|
|
@@ -127,12 +127,6 @@ export class SessionManager {
|
|
|
127
127
|
getSecondsRemaining() {
|
|
128
128
|
return this.secondsRemaining;
|
|
129
129
|
}
|
|
130
|
-
/** Wall-clock timestamp of the last detected user activity. Surfaced so
|
|
131
|
-
* the auto-logout flow can pass this through to the signin page (e.g.
|
|
132
|
-
* to render a "Last active: X min ago" banner). */
|
|
133
|
-
getLastActivityTime() {
|
|
134
|
-
return this.lastActivityTime;
|
|
135
|
-
}
|
|
136
130
|
// ─── Private ───────────────────────────────────────────────
|
|
137
131
|
onActivity() {
|
|
138
132
|
if (!this.running || this.warningActive) {
|
|
@@ -2,7 +2,7 @@ import { toAmountWC } from '../../../commonStateTypes/amount';
|
|
|
2
2
|
import { mapAccountBasePayloadToAccountBase, toAccountPayload, } from '../../account/accountPayload';
|
|
3
3
|
import { mapClassBasePayloadToClassBase, toClassBasePayload, } from '../../class/classPayload';
|
|
4
4
|
import { toCustomerBase, toCustomerBasePayload, } from '../../customer/customerPayload';
|
|
5
|
-
import {
|
|
5
|
+
import { mapProjectBasePayloadToProjectBase, toProjectBasePayload, } from '../../project/projectPayload';
|
|
6
6
|
import { mapVendorBasePayloadToVendorBase, toVendorBasePayload, } from '../../vendor/vendorPayload';
|
|
7
7
|
import { toCategorizationStatusType, toPlatformLineDetailType, } from '../stateTypes/transactionLine';
|
|
8
8
|
import { toTransactionCategory, toTransactionType, } from '../stateTypes/transactionType';
|
|
@@ -43,7 +43,7 @@ export const toTransactionWithAccountAndClassLine = (payload, currency, type = '
|
|
|
43
43
|
project: payload.line_detail.project != null &&
|
|
44
44
|
payload.line_detail.project.zeni_project_id != null &&
|
|
45
45
|
payload.line_detail.project.zeni_project_id !== ''
|
|
46
|
-
?
|
|
46
|
+
? mapProjectBasePayloadToProjectBase(payload.line_detail.project)
|
|
47
47
|
: undefined,
|
|
48
48
|
billable: payload.line_detail.billable_status ?? 'not_billable',
|
|
49
49
|
isCategoryMiscategorized: payload.is_category_miscategorized ?? false,
|
package/lib/esm/index.js
CHANGED
|
@@ -224,6 +224,7 @@ import { fetchProfitAndLoss, fetchProfitAndLossForTimeframe, resetProfitAndLossN
|
|
|
224
224
|
import { getPandLReportFetchState, getProfitAndLossReport, } from './view/profitAndLoss/profitAndLossSelector';
|
|
225
225
|
import { getEmptyHorizontalSectionBalancesSlice } from './view/profitAndLossClassesView/horizontalSectionEmptyBalancesSlice';
|
|
226
226
|
import { getProfitAndLossClassesHorizontalView } from './view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector';
|
|
227
|
+
import { aggregateHorizontalDescendantBalances } from './view/profitAndLossClassesView/profitAndLossHorizontalEnrichment';
|
|
227
228
|
import { HORIZONTAL_CLASS_TOTAL_BALANCE_ID, isHorizontalAccountReportData, } from './view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelectorTypes';
|
|
228
229
|
import { fetchProfitAndLossClassesView, resetProfitAndLossClassesNodeCollapseState, updateClassViewLayout, updateAccountViewMode as updateProfitAndLossAccountViewMode, updateClassesToFilterOut as updateProfitAndLossClassesToFilterOut, updateProfitAndLossClassesViewUIState, } from './view/profitAndLossClassesView/profitAndLossClassesViewReducer';
|
|
229
230
|
import { getProfitAndLossClassesView } from './view/profitAndLossClassesView/profitAndLossClassesViewSelector';
|
|
@@ -473,7 +474,7 @@ export { fetchDashboard, getDashboard, updateTreasuryVideoClosed, };
|
|
|
473
474
|
export { updateDashboardLayout };
|
|
474
475
|
export { getPandLWithForecast, } from './view/profitAndLoss/pAndLWithForecast/pAndLWithForecastSelector';
|
|
475
476
|
export { fetchProfitAndLoss, resetProfitAndLossNodeCollapseState, updateProfitAndLossUIState, getProfitAndLossReport, getPandLReportFetchState, fetchProfitAndLossForTimeframe, };
|
|
476
|
-
export { fetchProfitAndLossClassesView, updateProfitAndLossClassesToFilterOut, resetProfitAndLossClassesNodeCollapseState, updateProfitAndLossClassesViewUIState, updateProfitAndLossAccountViewMode, updateClassViewLayout, getProfitAndLossClassesView, getEmptyHorizontalSectionBalancesSlice, getProfitAndLossClassesHorizontalView, isPandLReportViewCalculatedSectionID, isPandLReportViewSectionID, HORIZONTAL_CLASS_TOTAL_BALANCE_ID, isHorizontalAccountReportData, };
|
|
477
|
+
export { fetchProfitAndLossClassesView, updateProfitAndLossClassesToFilterOut, resetProfitAndLossClassesNodeCollapseState, updateProfitAndLossClassesViewUIState, updateProfitAndLossAccountViewMode, updateClassViewLayout, getProfitAndLossClassesView, aggregateHorizontalDescendantBalances, getEmptyHorizontalSectionBalancesSlice, getProfitAndLossClassesHorizontalView, isPandLReportViewCalculatedSectionID, isPandLReportViewSectionID, HORIZONTAL_CLASS_TOTAL_BALANCE_ID, isHorizontalAccountReportData, };
|
|
477
478
|
export { clearProfitAndLossProjectView, fetchProfitAndLossForTimeframeProjectView, fetchProfitAndLossProjectView, resetProfitAndLossProjectNodeCollapseState, updateProjectViewAccountViewMode, updateProjectsToFilterOut, updateProfitAndLossProjectViewUIState, getProfitAndLossProjectView, };
|
|
478
479
|
export { getAccountingProviderAttachment };
|
|
479
480
|
export { fetchUserListByType, getUserList };
|
|
@@ -4,6 +4,7 @@ import { toMonthYearPeriodId, } from '../../../../commonStateTypes/timePeriod';
|
|
|
4
4
|
import { fetchAccountList } from '../../../accountList/accountListReducer';
|
|
5
5
|
import { fetchClassList } from '../../../classList/classListReducer';
|
|
6
6
|
import { fetchOwnerList } from '../../../ownerList/ownerListReducer';
|
|
7
|
+
import { fetchProjectList } from '../../../projectList/projectListReducer';
|
|
7
8
|
import { fetchTransactionCategorization, fetchTransactionCategorizationView, } from '../../reducers/transactionsViewReducer';
|
|
8
9
|
export const fetchTransactionCategorizationViewEpic = (actions$, state$) => actions$.pipe(filter(fetchTransactionCategorizationView.match), mergeMap((action) => {
|
|
9
10
|
const { selectedTab, cacheOverride, keepExistingListItems, pageToken, period, refreshViewInBackground, searchString, resetListItems, isUncategorizedExpenseCategoryEnabled, } = action.payload;
|
|
@@ -26,6 +27,11 @@ export const fetchTransactionCategorizationViewEpic = (actions$, state$) => acti
|
|
|
26
27
|
classList.fetchState === 'Not-Started') {
|
|
27
28
|
updateActions.push(fetchClassList());
|
|
28
29
|
}
|
|
30
|
+
const projectList = state$.value.projectListState;
|
|
31
|
+
if (projectList.hasValidState() === false &&
|
|
32
|
+
projectList.fetchState === 'Not-Started') {
|
|
33
|
+
updateActions.push(fetchProjectList());
|
|
34
|
+
}
|
|
29
35
|
const vendorOwnerList = state$.value.ownerListState;
|
|
30
36
|
if (vendorOwnerList.hasValidState() === false &&
|
|
31
37
|
vendorOwnerList.fetchState === 'Not-Started') {
|
|
@@ -110,6 +110,9 @@ const toTransactionUpdatePayload = (updates, detail, transactionId, cotTrackingB
|
|
|
110
110
|
if (lineUpdates.class != null) {
|
|
111
111
|
lineToBeUpdated.class = lineUpdates.class;
|
|
112
112
|
}
|
|
113
|
+
if (lineUpdates.project != null) {
|
|
114
|
+
lineToBeUpdated.project = lineUpdates.project;
|
|
115
|
+
}
|
|
113
116
|
if (lineUpdates.account != null) {
|
|
114
117
|
lineToBeUpdated.account = lineUpdates.account;
|
|
115
118
|
}
|
package/lib/esm/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.js
CHANGED
|
@@ -183,6 +183,7 @@ const buildLineItemById = (transaction, uncategorizedIncomeExpense, recommendati
|
|
|
183
183
|
const lineId = line.id;
|
|
184
184
|
record.account = accountLine.account;
|
|
185
185
|
record.class = accountLine.class;
|
|
186
|
+
record.project = accountLine.project;
|
|
186
187
|
record.vendor =
|
|
187
188
|
accountLine.vendor != null
|
|
188
189
|
? {
|
|
@@ -305,7 +306,7 @@ const buildLineItemById = (transaction, uncategorizedIncomeExpense, recommendati
|
|
|
305
306
|
});
|
|
306
307
|
return lineItemData;
|
|
307
308
|
};
|
|
308
|
-
export const toSetAllLineItemsToCategoryClass = (draft, transaction, transactionId, transactionLineId, transactionDetailLocalData, updatedItem, uncategorizedAccounts, selectedTab, vendorId, customerId, selectedAccount, selectedClassBase, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled) => {
|
|
309
|
+
export const toSetAllLineItemsToCategoryClass = (draft, transaction, transactionId, transactionLineId, transactionDetailLocalData, updatedItem, uncategorizedAccounts, selectedTab, vendorId, customerId, selectedAccount, selectedClassBase, selectedProject, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled) => {
|
|
309
310
|
let newLineItems = {};
|
|
310
311
|
let selectedCheckBoxTransactionIds = draft.selectedCheckBoxTransactionIds;
|
|
311
312
|
let similarTransactionUpdated = false;
|
|
@@ -325,7 +326,7 @@ export const toSetAllLineItemsToCategoryClass = (draft, transaction, transaction
|
|
|
325
326
|
const record = {
|
|
326
327
|
...transactionDetailLocalData.lineItemById[lineId],
|
|
327
328
|
};
|
|
328
|
-
// if the line is the one that is being updated, then set the account and
|
|
329
|
+
// if the line is the one that is being updated, then set the account, class, and project
|
|
329
330
|
if (transactionId === record.transactionId &&
|
|
330
331
|
transactionLineId === lineId) {
|
|
331
332
|
if (selectedAccount != null) {
|
|
@@ -334,6 +335,9 @@ export const toSetAllLineItemsToCategoryClass = (draft, transaction, transaction
|
|
|
334
335
|
if (selectedClassBase != null) {
|
|
335
336
|
record.class = selectedClassBase;
|
|
336
337
|
}
|
|
338
|
+
if (selectedProject != null) {
|
|
339
|
+
record.project = selectedProject;
|
|
340
|
+
}
|
|
337
341
|
const isClassSatisfied = isAccountingClassesEnabled === false || record.class != null;
|
|
338
342
|
if (isClassSatisfied && record.account != null) {
|
|
339
343
|
if (selectedCheckBoxTransactionIds.length < MAX_SELECTION_LIMIT &&
|
|
@@ -385,6 +389,18 @@ export const toSetAllLineItemsToCategoryClass = (draft, transaction, transaction
|
|
|
385
389
|
selectedCheckBoxTransactionIds.push(transaction.id);
|
|
386
390
|
}
|
|
387
391
|
}
|
|
392
|
+
if (selectedProject != null &&
|
|
393
|
+
updatedItem === 'project' &&
|
|
394
|
+
record.project?.projectId == null) {
|
|
395
|
+
record.project = selectedProject;
|
|
396
|
+
similarTransactionUpdated = true;
|
|
397
|
+
if (record.account != null &&
|
|
398
|
+
selectedCheckBoxTransactionIds.length < MAX_SELECTION_LIMIT &&
|
|
399
|
+
selectedCheckBoxTransactionIds.includes(transaction.id) ===
|
|
400
|
+
false) {
|
|
401
|
+
selectedCheckBoxTransactionIds.push(transaction.id);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
388
404
|
}
|
|
389
405
|
}
|
|
390
406
|
if (line.type === 'transaction_with_product_or_service_line') {
|
|
@@ -514,6 +530,7 @@ export const setEntityRecommendationForLineIdInLocalData = (draft, transaction,
|
|
|
514
530
|
...newLineItem,
|
|
515
531
|
class: matchingRecord.class,
|
|
516
532
|
account: matchingRecord.account,
|
|
533
|
+
project: matchingRecord.project,
|
|
517
534
|
lineEntity: matchingRecord.lineEntity,
|
|
518
535
|
};
|
|
519
536
|
}
|
|
@@ -282,7 +282,7 @@ const expenseAutomationTransactionsView = createSlice({
|
|
|
282
282
|
},
|
|
283
283
|
setAllItemsToCategoryClassInLocalDataForCategorization: {
|
|
284
284
|
reducer(draft, action) {
|
|
285
|
-
const { vendorId, customerId, selectedAccount, selectedClassBase, transactions, transactionId, lineId, selectedTab, uncategorizedAccounts, isAccountingClassesEnabled, isUncategorizedExpenseCategoryEnabled, } = action.payload;
|
|
285
|
+
const { vendorId, customerId, selectedAccount, selectedClassBase, selectedProject, transactions, transactionId, lineId, selectedTab, uncategorizedAccounts, isAccountingClassesEnabled, isUncategorizedExpenseCategoryEnabled, } = action.payload;
|
|
286
286
|
const transactionLocalData = recordGet(draft.transactionCategorizationView[selectedTab]
|
|
287
287
|
.transactionReviewLocalDataById, transactionId, undefined);
|
|
288
288
|
const existingAccount = transactionLocalData?.transactionReviewLocalData.lineItemById[lineId]
|
|
@@ -296,6 +296,7 @@ const expenseAutomationTransactionsView = createSlice({
|
|
|
296
296
|
...transactionLocalData.transactionReviewLocalData.lineItemById[lineId],
|
|
297
297
|
account: selectedAccount,
|
|
298
298
|
class: selectedClassBase,
|
|
299
|
+
project: selectedProject,
|
|
299
300
|
},
|
|
300
301
|
},
|
|
301
302
|
};
|
|
@@ -324,9 +325,16 @@ const expenseAutomationTransactionsView = createSlice({
|
|
|
324
325
|
const transactionLocalData = recordGet(draft.transactionCategorizationView[selectedTab]
|
|
325
326
|
.transactionReviewLocalDataById, transaction.id, undefined);
|
|
326
327
|
if (transactionLocalData != null) {
|
|
327
|
-
const
|
|
328
|
+
const existingClassId = transactionLocalData.transactionReviewLocalData.lineItemById[lineId]?.class?.classId;
|
|
329
|
+
const existingProjectId = transactionLocalData.transactionReviewLocalData.lineItemById[lineId]?.project?.projectId;
|
|
330
|
+
const updatedItem = existingAccount?.accountId !== selectedAccount?.accountId
|
|
328
331
|
? 'category'
|
|
329
|
-
:
|
|
332
|
+
: existingClassId !== selectedClassBase?.classId
|
|
333
|
+
? 'class'
|
|
334
|
+
: existingProjectId !== selectedProject?.projectId
|
|
335
|
+
? 'project'
|
|
336
|
+
: 'class';
|
|
337
|
+
const { localData, similarTransactionUpdated } = toSetAllLineItemsToCategoryClass(draft.transactionCategorizationView[selectedTab], transaction, transactionId, lineId, transactionLocalData.transactionReviewLocalData, updatedItem, uncategorizedAccounts, selectedTab, vendorId, customerId, selectedAccount, selectedClassBase, selectedProject, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled);
|
|
330
338
|
draft.transactionCategorizationView[selectedTab].transactionReviewLocalDataById[transaction.id] = {
|
|
331
339
|
transactionId: transaction.id,
|
|
332
340
|
transactionReviewLocalData: localData,
|
|
@@ -345,7 +353,7 @@ const expenseAutomationTransactionsView = createSlice({
|
|
|
345
353
|
...dirtyIds,
|
|
346
354
|
]);
|
|
347
355
|
},
|
|
348
|
-
prepare(selectedTab, transactionId, lineId, transactions, uncategorizedAccounts, vendorId, customerId, selectedAccount, selectedClassBase, lineEntity, recommendations, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled) {
|
|
356
|
+
prepare(selectedTab, transactionId, lineId, transactions, uncategorizedAccounts, vendorId, customerId, selectedAccount, selectedClassBase, selectedProject, lineEntity, recommendations, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled) {
|
|
349
357
|
return {
|
|
350
358
|
payload: {
|
|
351
359
|
selectedTab,
|
|
@@ -357,6 +365,7 @@ const expenseAutomationTransactionsView = createSlice({
|
|
|
357
365
|
lineEntity,
|
|
358
366
|
selectedAccount,
|
|
359
367
|
selectedClassBase,
|
|
368
|
+
selectedProject,
|
|
360
369
|
recommendations,
|
|
361
370
|
uncategorizedAccounts,
|
|
362
371
|
isAccountingClassesEnabled,
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import omit from 'lodash/omit';
|
|
2
2
|
import { reduceAllFetchState, reduceAnyFetchState, } from '../../../commonStateTypes/reduceFetchState';
|
|
3
3
|
import { toMonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
|
|
4
|
-
import { getIsAccountingClassesEnabled,
|
|
4
|
+
import { getCurrentTenant, getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, } from '../../../entity/tenant/tenantSelector';
|
|
5
5
|
import { getTransactionWithCOT } from '../../../entity/transaction/transactionHelper';
|
|
6
6
|
import { getSupportedTransactionsByIds } from '../../../entity/transaction/transactionSelector';
|
|
7
7
|
import { getAccountList, getNestedAccountListHierarchy, getUncategorizedAccounts, } from '../../accountList/accountListSelector';
|
|
8
8
|
import { getClassList, getNestedClassListHierarchy, } from '../../classList/classListSelector';
|
|
9
|
+
import { getProjectList } from '../../projectList/projectListSelector';
|
|
9
10
|
import { getAllSteps } from '../selectorTypes/expenseAutomationViewSelectorTypes';
|
|
10
11
|
export function getExpenseAutomationTransactionView(state) {
|
|
11
|
-
const { accountState, classState, classListState, accountListState, expenseAutomationTransactionsViewState, expenseAutomationViewState, transactionState, monthEndCloseChecksState, monthEndCloseChecksViewState, } = state;
|
|
12
|
+
const { accountState, classState, classListState, accountListState, expenseAutomationTransactionsViewState, expenseAutomationViewState, projectState, projectListState, transactionState, monthEndCloseChecksState, monthEndCloseChecksViewState, } = state;
|
|
12
13
|
const { selectedTransactionCategorizationTab } = expenseAutomationTransactionsViewState;
|
|
13
14
|
const { uiState, refreshStatus, saveStatus, markAsNotMiscategorizedStatus, transactionIdsBySelectedPeriod, transactionReviewLocalDataById, selectedCheckBoxTransactionIds, transactionIdsWithUnsavedData, uploadReceiptStatusById, selectedTransactionId, selectedTransactionLineId, } = expenseAutomationTransactionsViewState.transactionCategorizationView[selectedTransactionCategorizationTab];
|
|
14
15
|
const uncategorizedIncomeExpense = getUncategorizedAccounts(accountState, accountListState, 'accountList');
|
|
@@ -17,6 +18,8 @@ export function getExpenseAutomationTransactionView(state) {
|
|
|
17
18
|
const allClasses = classList.classes.map((classData) => {
|
|
18
19
|
return omit(classData, ['accountIds', 'key']);
|
|
19
20
|
});
|
|
21
|
+
const projectList = getProjectList(projectState, projectListState);
|
|
22
|
+
const isAccountingProjectsEnabled = getIsAccountingProjectsEnabled(state);
|
|
20
23
|
const accountsHierarchyList = getNestedAccountListHierarchy(accountListState, 'accountList');
|
|
21
24
|
const classHierarchyList = getNestedClassListHierarchy(classListState);
|
|
22
25
|
const currentTenant = getCurrentTenant(state);
|
|
@@ -96,6 +99,8 @@ export function getExpenseAutomationTransactionView(state) {
|
|
|
96
99
|
classList: allClasses,
|
|
97
100
|
accountsHierarchyList,
|
|
98
101
|
classHierarchyList,
|
|
102
|
+
isAccountingProjectsEnabled,
|
|
103
|
+
projectList,
|
|
99
104
|
transactionLocalData,
|
|
100
105
|
uiState,
|
|
101
106
|
refreshStatus,
|
|
@@ -115,6 +115,50 @@ function buildHorizontalClassColumnBalancesSlice(report, classAmounts, totalAmou
|
|
|
115
115
|
},
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Sum balances across descendant accounts (recursive).
|
|
120
|
+
*
|
|
121
|
+
* Parent rows in the horizontal pivot only carry their own direct postings
|
|
122
|
+
* (zero for "container" accounts), so callers rendering "Total {parent}" rows
|
|
123
|
+
* must aggregate from descendants to get the real per-class totals.
|
|
124
|
+
*
|
|
125
|
+
* Returns one `COABalance` per class column + total (same shape as the
|
|
126
|
+
* enriched `balancesInTimeframe.balances`), with `amount` summed and `type`
|
|
127
|
+
* set to `default_with_null_balance` for zeros. Returns `[]` when no
|
|
128
|
+
* descendants have enriched data.
|
|
129
|
+
*/
|
|
130
|
+
export function aggregateHorizontalDescendantBalances(accounts, childIndices, childrenByParent) {
|
|
131
|
+
let template;
|
|
132
|
+
const sums = new Map();
|
|
133
|
+
const walk = (idx) => {
|
|
134
|
+
const childRow = accounts[idx];
|
|
135
|
+
const childBalances = childRow?.accountReportData?.balancesInTimeframe.balances;
|
|
136
|
+
if (childBalances != null) {
|
|
137
|
+
if (template == null) {
|
|
138
|
+
template = childBalances;
|
|
139
|
+
}
|
|
140
|
+
childBalances.forEach((b, i) => {
|
|
141
|
+
sums.set(i, (sums.get(i) ?? 0) + (b.balance.amount ?? 0));
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
// Always recurse — a missing intermediate node should not prune its
|
|
145
|
+
// descendants from the aggregate.
|
|
146
|
+
const grandChildren = childrenByParent[idx] ?? [];
|
|
147
|
+
grandChildren.forEach(walk);
|
|
148
|
+
};
|
|
149
|
+
childIndices.forEach(walk);
|
|
150
|
+
if (template == null) {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
return template.map((tpl, i) => {
|
|
154
|
+
const amount = sums.get(i) ?? 0;
|
|
155
|
+
return {
|
|
156
|
+
...tpl,
|
|
157
|
+
balance: { ...tpl.balance, amount },
|
|
158
|
+
type: amount === 0 ? 'default_with_null_balance' : 'default',
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
}
|
|
118
162
|
/**
|
|
119
163
|
* Parent index per DFS row — O(n). Matches the scan in the former
|
|
120
164
|
* `findHorizontalAccountParentIndex` (depth <= 2 rows have no parent).
|
package/lib/index.d.ts
CHANGED
|
@@ -103,7 +103,7 @@ import { ExternalNotificationData, NotificationActivityType, NotificationGroup,
|
|
|
103
103
|
import { Logo, PaymentAccount, VERIFIED_PAYMENT_ACCOUNT_PROVIDER_VERIFICATION_STATUS } from './entity/paymentAccount/paymentAccountState';
|
|
104
104
|
import { PaymentInstrument } from './entity/paymentInstrument/paymentInstrument';
|
|
105
105
|
import { AccountingSummary, Runway } from './entity/portfolio/accountingSummary/accountingSummaryState';
|
|
106
|
-
import { Project } from './entity/project/projectState';
|
|
106
|
+
import { Project, ProjectBase } from './entity/project/projectState';
|
|
107
107
|
import { MilageReimbursementLine, OutofPocketReimbursementLine, Reimbursement, ReimbursementLine, ReimbursementTypeCode } from './entity/reimbursement/reimbursementState';
|
|
108
108
|
import { SectionAccountsViewReport } from './entity/sectionAccountsView/sectionAccountsViewSelector';
|
|
109
109
|
import { SectionClassesView as SectionClassesViewV2 } from './entity/sectionClassesViewV2/sectionClassesView';
|
|
@@ -357,6 +357,7 @@ import { ProfitAndLossReport, getPandLReportFetchState, getProfitAndLossReport }
|
|
|
357
357
|
import { ProfitAndLossState, ProfitAndLossUIState } from './view/profitAndLoss/profitAndLossState';
|
|
358
358
|
import { getEmptyHorizontalSectionBalancesSlice } from './view/profitAndLossClassesView/horizontalSectionEmptyBalancesSlice';
|
|
359
359
|
import { getProfitAndLossClassesHorizontalView } from './view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector';
|
|
360
|
+
import { aggregateHorizontalDescendantBalances } from './view/profitAndLossClassesView/profitAndLossHorizontalEnrichment';
|
|
360
361
|
import { ClassColumnDefinition, HORIZONTAL_CLASS_TOTAL_BALANCE_ID, HorizontalAccountReportData, HorizontalAccountRow, HorizontalCalculatedSection, HorizontalClassBalance, HorizontalSectionReport, HorizontalSectionTreeLayout, ProfitAndLossByClassHorizontalReport, isHorizontalAccountReportData } from './view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelectorTypes';
|
|
361
362
|
import { fetchProfitAndLossClassesView, resetProfitAndLossClassesNodeCollapseState, updateClassViewLayout, updateAccountViewMode as updateProfitAndLossAccountViewMode, updateClassesToFilterOut as updateProfitAndLossClassesToFilterOut, updateProfitAndLossClassesViewUIState } from './view/profitAndLossClassesView/profitAndLossClassesViewReducer';
|
|
362
363
|
import { getProfitAndLossClassesView } from './view/profitAndLossClassesView/profitAndLossClassesViewSelector';
|
|
@@ -696,8 +697,8 @@ export { fetchDashboard, getDashboard, DashboardReport, updateTreasuryVideoClose
|
|
|
696
697
|
export { updateDashboardLayout };
|
|
697
698
|
export { PandLWithForecastView, getPandLWithForecast, } from './view/profitAndLoss/pAndLWithForecast/pAndLWithForecastSelector';
|
|
698
699
|
export { fetchProfitAndLoss, resetProfitAndLossNodeCollapseState, updateProfitAndLossUIState, ProfitAndLossState, ProfitAndLossUIState, getProfitAndLossReport, ProfitAndLossReport, getPandLReportFetchState, fetchProfitAndLossForTimeframe, };
|
|
699
|
-
export { fetchProfitAndLossClassesView, updateProfitAndLossClassesToFilterOut, resetProfitAndLossClassesNodeCollapseState, updateProfitAndLossClassesViewUIState, updateProfitAndLossAccountViewMode, updateClassViewLayout, ProfitAndLossClassesViewUIState, ProfitAndLossClassesViewReport, getProfitAndLossClassesView, getEmptyHorizontalSectionBalancesSlice, getProfitAndLossClassesHorizontalView, PandLReportViewSectionID, PandLReportViewCalculatedSectionID, isPandLReportViewCalculatedSectionID, isPandLReportViewSectionID, ClassViewLayout, ProfitAndLossByClassHorizontalReport, ClassColumnDefinition, HORIZONTAL_CLASS_TOTAL_BALANCE_ID, HorizontalAccountReportData, HorizontalAccountRow, HorizontalClassBalance, HorizontalSectionReport, HorizontalSectionTreeLayout, HorizontalCalculatedSection, isHorizontalAccountReportData, };
|
|
700
|
-
export { clearProfitAndLossProjectView, fetchProfitAndLossForTimeframeProjectView, fetchProfitAndLossProjectView, resetProfitAndLossProjectNodeCollapseState, updateProjectViewAccountViewMode, updateProjectsToFilterOut, updateProfitAndLossProjectViewUIState, ProfitAndLossProjectViewUIState, getProfitAndLossProjectView, ProfitAndLossProjectViewReport, Project, ProjectReportWithBalances, SectionProjectViewReport, };
|
|
700
|
+
export { fetchProfitAndLossClassesView, updateProfitAndLossClassesToFilterOut, resetProfitAndLossClassesNodeCollapseState, updateProfitAndLossClassesViewUIState, updateProfitAndLossAccountViewMode, updateClassViewLayout, ProfitAndLossClassesViewUIState, ProfitAndLossClassesViewReport, getProfitAndLossClassesView, aggregateHorizontalDescendantBalances, getEmptyHorizontalSectionBalancesSlice, getProfitAndLossClassesHorizontalView, PandLReportViewSectionID, PandLReportViewCalculatedSectionID, isPandLReportViewCalculatedSectionID, isPandLReportViewSectionID, ClassViewLayout, ProfitAndLossByClassHorizontalReport, ClassColumnDefinition, HORIZONTAL_CLASS_TOTAL_BALANCE_ID, HorizontalAccountReportData, HorizontalAccountRow, HorizontalClassBalance, HorizontalSectionReport, HorizontalSectionTreeLayout, HorizontalCalculatedSection, isHorizontalAccountReportData, };
|
|
701
|
+
export { clearProfitAndLossProjectView, fetchProfitAndLossForTimeframeProjectView, fetchProfitAndLossProjectView, resetProfitAndLossProjectNodeCollapseState, updateProjectViewAccountViewMode, updateProjectsToFilterOut, updateProfitAndLossProjectViewUIState, ProfitAndLossProjectViewUIState, getProfitAndLossProjectView, ProfitAndLossProjectViewReport, Project, ProjectBase, ProjectReportWithBalances, SectionProjectViewReport, };
|
|
701
702
|
export { AccountingProviderAttachmentSelector, getAccountingProviderAttachment };
|
|
702
703
|
export { fetchUserListByType, getUserList, UserListSelectorView };
|
|
703
704
|
export { fetchAllPeopleRequiredViews, fetchPeoplePage, fetchPeople, deletePerson, resendInvite, changeZeniPersonRoles, invitePeople, inviteZeniPeople, updatePeopleUIState, peopleSaveUpdates, resetUpdateErrorMessage, peopleSaveDataInLocalStore, peopleClearDataInLocalStore, PeopleSelectorView, PeopleLocalDataView, Person, getPeople, getPeopleLocalData, PeopleState, PersonUpdateType, PeoplePageMode, ZeniPersonUserRoleType, PeopleUIState, PeopleLocalData, initializeEditPerson, };
|