@zeniai/client-epic-state 5.2.89 → 5.2.90-betaRD0
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/invoicing/invoicingCommonPayload.d.ts +0 -9
- package/lib/entity/invoicing/invoicingCommonPayload.js +1 -8
- package/lib/esm/entity/invoicing/invoicingCommonPayload.js +0 -7
- package/lib/esm/view/financeStatement/financeStatementCompareBalances.js +23 -0
- package/lib/esm/view/financeStatement/financeStatementReducer.js +6 -11
- package/lib/esm/view/financeStatement/financeStatementSelector.js +23 -12
- package/lib/esm/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +12 -20
- package/lib/esm/view/invoicing/settingsView/invoicingQboSettingsFormConfig.js +1 -6
- package/lib/esm/view/invoicing/settingsView/settingsViewReducer.js +1 -19
- package/lib/esm/view/invoicing/settingsView/settingsViewSelector.js +1 -15
- package/lib/esm/view/reportUIOptions/updateReportUIOptionCompareModeEpic.js +2 -5
- package/lib/esm/view/reportUIOptions/updateReportUIOptionIsCompareModeOnEpic.js +6 -10
- package/lib/view/financeStatement/financeStatementCompareBalances.d.ts +3 -0
- package/lib/view/financeStatement/financeStatementCompareBalances.js +27 -0
- package/lib/view/financeStatement/financeStatementReducer.js +6 -11
- package/lib/view/financeStatement/financeStatementSelector.js +22 -11
- package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.d.ts +1 -6
- package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +12 -20
- package/lib/view/invoicing/settingsView/invoicingQboSettingsFormConfig.js +0 -5
- package/lib/view/invoicing/settingsView/settingsViewReducer.js +1 -19
- package/lib/view/invoicing/settingsView/settingsViewSelector.d.ts +1 -3
- package/lib/view/invoicing/settingsView/settingsViewSelector.js +1 -15
- package/lib/view/reportUIOptions/updateReportUIOptionCompareModeEpic.js +2 -5
- package/lib/view/reportUIOptions/updateReportUIOptionIsCompareModeOnEpic.js +6 -10
- package/package.json +26 -1
|
@@ -120,12 +120,6 @@ export declare const firstSelectableInvoicingId: (candidates: readonly (string |
|
|
|
120
120
|
export declare const toInvoicingQboAccountMappingSlot: (code: string) => string;
|
|
121
121
|
/** Read one account-mapping dropdown value by its config `code`. */
|
|
122
122
|
export declare function getInvoicingQboAccountMappingId(accountMappings: InvoicingQboAccountMappingsPayload | undefined, code: string): string | undefined;
|
|
123
|
-
/**
|
|
124
|
-
* The Zeni-owned account the service posts sales tax to. It arrives with the
|
|
125
|
-
* settings for display only: it is not a mapping the user picks, and the
|
|
126
|
-
* service ignores it on save.
|
|
127
|
-
*/
|
|
128
|
-
export declare const INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT = "sales_tax_posting_account";
|
|
129
123
|
/**
|
|
130
124
|
* Build the `account_mappings` request body from the ids the pickers submitted.
|
|
131
125
|
* Each kind is sent as the whole account, which is what the service stores; an
|
|
@@ -146,14 +140,11 @@ export interface InvoicingQboAccountMappingKindOptionWire {
|
|
|
146
140
|
account_types: string;
|
|
147
141
|
code: string;
|
|
148
142
|
name: string;
|
|
149
|
-
/** Comma-separated QuickBooks account sub types this kind must not offer. */
|
|
150
|
-
excluded_account_sub_types?: string | null;
|
|
151
143
|
}
|
|
152
144
|
export interface InvoicingQboAccountMappingKindOption {
|
|
153
145
|
accountTypes: string;
|
|
154
146
|
code: string;
|
|
155
147
|
name: string;
|
|
156
|
-
excludedAccountSubTypes?: string;
|
|
157
148
|
}
|
|
158
149
|
export declare function toInvoicingQboAccountMappingKindOption(option: InvoicingQboAccountMappingKindOptionWire): InvoicingQboAccountMappingKindOption;
|
|
159
150
|
export interface InvoicingSettingsPayload {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ALL_INVOICING_FIELD_HELP_GROUPS = exports.toInvoicingQboAccountMappingsPayload = exports.
|
|
3
|
+
exports.ALL_INVOICING_FIELD_HELP_GROUPS = exports.toInvoicingQboAccountMappingsPayload = exports.toInvoicingQboAccountMappingSlot = exports.firstSelectableInvoicingId = exports.INVOICING_QBO_CUSTOMER_MATCHING_MODES = exports.INVOICING_DISCOUNT_LIFECYCLE_ACTIONS = exports.INVOICING_CATALOG_PRODUCT_LIFECYCLE_ACTIONS = exports.INVOICING_CATALOG_ITEM_LIFECYCLE_ACTIONS = exports.INVOICING_SUBSCRIPTION_LIFECYCLE_ACTIONS = exports.INVOICING_DUNNING_ACTIONS = void 0;
|
|
4
4
|
exports.toInvoicingDunningAction = toInvoicingDunningAction;
|
|
5
5
|
exports.toInvoicingSubscriptionLifecycleAction = toInvoicingSubscriptionLifecycleAction;
|
|
6
6
|
exports.toInvoicingSubscriptionLifecycleActionOption = toInvoicingSubscriptionLifecycleActionOption;
|
|
@@ -178,12 +178,6 @@ function getInvoicingQboAccountMappingId(accountMappings, code) {
|
|
|
178
178
|
}
|
|
179
179
|
return (0, exports.firstSelectableInvoicingId)([slot?.qbo_id, slot?.account_id]);
|
|
180
180
|
}
|
|
181
|
-
/**
|
|
182
|
-
* The Zeni-owned account the service posts sales tax to. It arrives with the
|
|
183
|
-
* settings for display only: it is not a mapping the user picks, and the
|
|
184
|
-
* service ignores it on save.
|
|
185
|
-
*/
|
|
186
|
-
exports.INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT = 'sales_tax_posting_account';
|
|
187
181
|
/**
|
|
188
182
|
* Build the `account_mappings` request body from the ids the pickers submitted.
|
|
189
183
|
* Each kind is sent as the whole account, which is what the service stores; an
|
|
@@ -207,7 +201,6 @@ function toInvoicingQboAccountMappingKindOption(option) {
|
|
|
207
201
|
return {
|
|
208
202
|
accountTypes: option.account_types,
|
|
209
203
|
code: option.code,
|
|
210
|
-
excludedAccountSubTypes: option.excluded_account_sub_types ?? undefined,
|
|
211
204
|
name: option.name,
|
|
212
205
|
};
|
|
213
206
|
}
|
|
@@ -152,12 +152,6 @@ export function getInvoicingQboAccountMappingId(accountMappings, code) {
|
|
|
152
152
|
}
|
|
153
153
|
return firstSelectableInvoicingId([slot?.qbo_id, slot?.account_id]);
|
|
154
154
|
}
|
|
155
|
-
/**
|
|
156
|
-
* The Zeni-owned account the service posts sales tax to. It arrives with the
|
|
157
|
-
* settings for display only: it is not a mapping the user picks, and the
|
|
158
|
-
* service ignores it on save.
|
|
159
|
-
*/
|
|
160
|
-
export const INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT = 'sales_tax_posting_account';
|
|
161
155
|
/**
|
|
162
156
|
* Build the `account_mappings` request body from the ids the pickers submitted.
|
|
163
157
|
* Each kind is sent as the whole account, which is what the service stores; an
|
|
@@ -180,7 +174,6 @@ export function toInvoicingQboAccountMappingKindOption(option) {
|
|
|
180
174
|
return {
|
|
181
175
|
accountTypes: option.account_types,
|
|
182
176
|
code: option.code,
|
|
183
|
-
excludedAccountSubTypes: option.excluded_account_sub_types ?? undefined,
|
|
184
177
|
name: option.name,
|
|
185
178
|
};
|
|
186
179
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { stripCompareAdditionalBalanceTypes, } from '../../commonStateTypes/coaBalance/coaBalanceType';
|
|
2
|
+
// Reports can show the $ and % change columns together, so every requested compare type is kept;
|
|
3
|
+
// compareMode picks one only when none was requested.
|
|
4
|
+
export function getFinanceStatementAdditionalBalances(requestedBalances, isCompareModeOn, compareBalanceFromMode) {
|
|
5
|
+
const otherBalances = stripCompareAdditionalBalanceTypes(requestedBalances);
|
|
6
|
+
if (!isCompareModeOn) {
|
|
7
|
+
return otherBalances;
|
|
8
|
+
}
|
|
9
|
+
const requestedCompareBalances = requestedBalances.filter((balance) => !otherBalances.includes(balance));
|
|
10
|
+
return [
|
|
11
|
+
...otherBalances,
|
|
12
|
+
...(requestedCompareBalances.length > 0
|
|
13
|
+
? requestedCompareBalances
|
|
14
|
+
: [compareBalanceFromMode]),
|
|
15
|
+
];
|
|
16
|
+
}
|
|
17
|
+
// The table has one change column with a $/% picker, so only the first selected compare type is
|
|
18
|
+
// shown; the rest of the selection still reaches the download.
|
|
19
|
+
export function getDisplayedAdditionalBalances(selectedBalances) {
|
|
20
|
+
const otherBalances = stripCompareAdditionalBalanceTypes(selectedBalances);
|
|
21
|
+
const displayedCompareBalance = selectedBalances.find((balance) => !otherBalances.includes(balance));
|
|
22
|
+
return selectedBalances.filter((balance) => otherBalances.includes(balance) || balance === displayedCompareBalance);
|
|
23
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createSlice } from '@reduxjs/toolkit';
|
|
2
|
+
import { stripCompareAdditionalBalanceTypes, } from '../../commonStateTypes/coaBalance/coaBalanceType';
|
|
2
3
|
import { extractThisPeriod, getMatchingThisPeriod, } from '../../commonStateTypes/viewAndReport/thisPeriodHelpers';
|
|
3
4
|
import { clearAll } from '../../rootActions';
|
|
4
5
|
import { dateNow } from '../../zeniDayJS';
|
|
@@ -88,17 +89,11 @@ const financeStatement = createSlice({
|
|
|
88
89
|
updateFinanceStatementAdditionalBalancesSelection(draft, action) {
|
|
89
90
|
const { additionalBalances: additionalBalancesPayload, maxNumOfPeriodsToHighlight, } = action.payload;
|
|
90
91
|
const additionalBalances = additionalBalancesPayload ?? [];
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
additionalBalances.includes(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (id > -1) {
|
|
97
|
-
const balance = tempAdditionalBalances.splice(id, 1);
|
|
98
|
-
tempAdditionalBalances = [...balance, ...tempAdditionalBalances];
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
draft.selectedAdditionalBalances = tempAdditionalBalances;
|
|
92
|
+
const otherBalances = stripCompareAdditionalBalanceTypes(additionalBalances);
|
|
93
|
+
draft.selectedAdditionalBalances = [
|
|
94
|
+
...additionalBalances.filter((balance) => !otherBalances.includes(balance)),
|
|
95
|
+
...otherBalances,
|
|
96
|
+
];
|
|
102
97
|
draft.maxNumOfPeriodsToHighlight = maxNumOfPeriodsToHighlight;
|
|
103
98
|
draft.isAdditionalBalancesShown =
|
|
104
99
|
additionalBalances.length != 0 ? true : false;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createSelector } from '@reduxjs/toolkit';
|
|
2
|
-
import { stripCompareAdditionalBalanceTypes } from '../../commonStateTypes/coaBalance/coaBalanceType';
|
|
2
|
+
import { stripCompareAdditionalBalanceTypes, } from '../../commonStateTypes/coaBalance/coaBalanceType';
|
|
3
3
|
import { newBalancesFilter, newBalancesFilterForDateRange, } from '../../commonStateTypes/coaBalance/coaBalancesFilter';
|
|
4
4
|
import { newBalancesFilterClassesView } from '../../commonStateTypes/coaBalance/coaBalancesFilterClassesView';
|
|
5
5
|
import { newBalancesFilterProjectView } from '../../commonStateTypes/coaBalance/coaBalancesFilterProjectView';
|
|
@@ -17,6 +17,17 @@ import { getProfitAndLossReport, } from '../profitAndLoss/profitAndLossSelector'
|
|
|
17
17
|
import { getProfitAndLossClassesHorizontalView } from '../profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector';
|
|
18
18
|
import { getProfitAndLossClassesView } from '../profitAndLossClassesView/profitAndLossClassesViewSelector';
|
|
19
19
|
import { getProfitAndLossProjectView } from '../profitAndLossProjectView/profitAndLossProjectViewSelector';
|
|
20
|
+
import { getDisplayedAdditionalBalances } from './financeStatementCompareBalances';
|
|
21
|
+
/**
|
|
22
|
+
* Reports are built from the displayed columns, but their filter carries the full selection: the
|
|
23
|
+
* download and the Add Columns picker read it from there.
|
|
24
|
+
*/
|
|
25
|
+
function withSelectedAdditionalBalances(report, selectedAdditionalBalances) {
|
|
26
|
+
return {
|
|
27
|
+
...report,
|
|
28
|
+
filter: { ...report.filter, additionalBalances: selectedAdditionalBalances },
|
|
29
|
+
};
|
|
30
|
+
}
|
|
20
31
|
function getDateRangeForFinanceReports(profitAndLossState, balanceSheetState, cashFlowState) {
|
|
21
32
|
const datesSorted = [
|
|
22
33
|
profitAndLossState.dataAvailable?.startDate,
|
|
@@ -71,7 +82,7 @@ export const getFinanceStatement = createSelector((state) => state.accountState,
|
|
|
71
82
|
const additionalBalancesForFilter = useHorizontalClassesLayout
|
|
72
83
|
? stripCompareAdditionalBalanceTypes(rawAdditionalBalances)
|
|
73
84
|
: rawAdditionalBalances;
|
|
74
|
-
const filter = newBalancesFilter(firstMonthOfFY, financeStatementState.timeframe, Math.min(getNumberOfPeriods(firstMonthOfFY, financeStatementState.timeframe, dataAvailable) ?? financeStatementState.selectedCOABalancesRange.numberOfPeriods, financeStatementState.selectedCOABalancesRange.numberOfPeriods), thisPeriod, 'ascending_date', additionalBalancesForFilter);
|
|
85
|
+
const filter = newBalancesFilter(firstMonthOfFY, financeStatementState.timeframe, Math.min(getNumberOfPeriods(firstMonthOfFY, financeStatementState.timeframe, dataAvailable) ?? financeStatementState.selectedCOABalancesRange.numberOfPeriods, financeStatementState.selectedCOABalancesRange.numberOfPeriods), thisPeriod, 'ascending_date', getDisplayedAdditionalBalances(additionalBalancesForFilter));
|
|
75
86
|
const profitAndLossReport = getProfitAndLossReport(filter, undefined, accountState, accountGroupState, sectionAccountsViewState, profitAndLossState, forecastState);
|
|
76
87
|
const classFilter = newBalancesFilterClassesView(filter.firstMonthOfFY, filter.timeframe, filter.balancesRange.numberOfPeriods, filter.balancesRange.thisPeriod, filter.balancesRange.orderBy, filter.additionalBalances, profitAndLossClassesViewState.uiState.classesToFilterOut);
|
|
77
88
|
const profitAndLossByClassReport = getProfitAndLossClassesView(classFilter, accountState, classState, sectionsState, profitAndLossClassesViewState);
|
|
@@ -163,17 +174,17 @@ export const getFinanceStatement = createSelector((state) => state.accountState,
|
|
|
163
174
|
reportFormat: financeStatementState.reportFormat,
|
|
164
175
|
firstMonthOfFY,
|
|
165
176
|
dataAvailable,
|
|
166
|
-
profitAndLossReport,
|
|
167
|
-
profitAndLossByClassReport,
|
|
168
|
-
profitAndLossByProjectReport,
|
|
177
|
+
profitAndLossReport: withSelectedAdditionalBalances(profitAndLossReport, additionalBalancesForFilter),
|
|
178
|
+
profitAndLossByClassReport: withSelectedAdditionalBalances(profitAndLossByClassReport, additionalBalancesForFilter),
|
|
179
|
+
profitAndLossByProjectReport: withSelectedAdditionalBalances(profitAndLossByProjectReport, additionalBalancesForFilter),
|
|
169
180
|
profitAndLossByClassHorizontalReport,
|
|
170
|
-
balanceSheetReport,
|
|
171
|
-
balanceSheetByClassReport,
|
|
172
|
-
cashFlowByClassReport,
|
|
173
|
-
cashFlowByProjectReport,
|
|
174
|
-
balanceSheetByProjectReport,
|
|
175
|
-
cashFlowReport,
|
|
176
|
-
filter,
|
|
181
|
+
balanceSheetReport: withSelectedAdditionalBalances(balanceSheetReport, additionalBalancesForFilter),
|
|
182
|
+
balanceSheetByClassReport: withSelectedAdditionalBalances(balanceSheetByClassReport, additionalBalancesForFilter),
|
|
183
|
+
cashFlowByClassReport: withSelectedAdditionalBalances(cashFlowByClassReport, additionalBalancesForFilter),
|
|
184
|
+
cashFlowByProjectReport: withSelectedAdditionalBalances(cashFlowByProjectReport, additionalBalancesForFilter),
|
|
185
|
+
balanceSheetByProjectReport: withSelectedAdditionalBalances(balanceSheetByProjectReport, additionalBalancesForFilter),
|
|
186
|
+
cashFlowReport: withSelectedAdditionalBalances(cashFlowReport, additionalBalancesForFilter),
|
|
187
|
+
filter: { ...filter, additionalBalances: additionalBalancesForFilter },
|
|
177
188
|
selectedReportId: financeStatementState.selectedReportId,
|
|
178
189
|
downloadState: financeStatementState.downloadState,
|
|
179
190
|
isCompareModeOn: useHorizontalClassesLayout
|
|
@@ -106,7 +106,6 @@ export const getInvoicingQboAccountOptions = createSelector([
|
|
|
106
106
|
{
|
|
107
107
|
id: String(account.qboId),
|
|
108
108
|
name: account.accountName,
|
|
109
|
-
accountSubType: account.accountSubType,
|
|
110
109
|
accountType: account.accountType,
|
|
111
110
|
},
|
|
112
111
|
];
|
|
@@ -130,29 +129,22 @@ const normalizeAccountTypeName = (value) => {
|
|
|
130
129
|
// trailing "s" alone would leave "liabilitie", matching nothing.
|
|
131
130
|
return normalized.replace(/ies$/, 'y').replace(/([^s])s$/, '$1');
|
|
132
131
|
};
|
|
133
|
-
const toAccountTypeNameSet = (names) => new Set((names ?? '')
|
|
134
|
-
.split(',')
|
|
135
|
-
.map(normalizeAccountTypeName)
|
|
136
|
-
.filter((name) => name !== ''));
|
|
137
132
|
/**
|
|
138
133
|
* The accounts valid for one QBO mapping kind, whose `accountTypes` is a
|
|
139
134
|
* comma-separated list of QuickBooks type names. An unrecognised list yields no
|
|
140
135
|
* accounts rather than every account: offering a bank account where income is
|
|
141
136
|
* required would post a mapping QuickBooks rejects.
|
|
142
|
-
*
|
|
143
|
-
* `excludedAccountSubTypes` drops accounts QuickBooks will not post to for that
|
|
144
|
-
* kind even though their type matches, e.g. a tax agency's system payable for
|
|
145
|
-
* the sales tax liability.
|
|
146
137
|
*/
|
|
147
|
-
export const invoicingAccountsForQboAccountTypes = (accounts, accountTypes
|
|
148
|
-
const wanted =
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
138
|
+
export const invoicingAccountsForQboAccountTypes = (accounts, accountTypes) => {
|
|
139
|
+
const wanted = new Set(accountTypes
|
|
140
|
+
.split(',')
|
|
141
|
+
.map(normalizeAccountTypeName)
|
|
142
|
+
.filter((accountType) => accountType !== ''));
|
|
143
|
+
if (wanted.size === 0) {
|
|
144
|
+
return accounts;
|
|
145
|
+
}
|
|
146
|
+
return accounts.filter((account) => account.accountType != null &&
|
|
147
|
+
wanted.has(normalizeAccountTypeName(account.accountType)));
|
|
156
148
|
};
|
|
157
149
|
/**
|
|
158
150
|
* The account-mapping slot a catalog price point's income account shares with
|
|
@@ -180,8 +172,8 @@ export const INVOICING_DEFAULT_INCOME_ACCOUNT_TYPES = 'Income';
|
|
|
180
172
|
* not carry falls back to `defaultAccountTypes` rather than to every account.
|
|
181
173
|
*/
|
|
182
174
|
export const invoicingQboAccountIdsForMappingSlot = (accounts, accountMappingKindOptions, mappingSlot, defaultAccountTypes) => {
|
|
183
|
-
const
|
|
184
|
-
return invoicingAccountsForQboAccountTypes(accounts,
|
|
175
|
+
const accountTypes = accountMappingKindOptions.find((kindOption) => toInvoicingQboAccountMappingSlot(kindOption.code) === mappingSlot)?.accountTypes ?? defaultAccountTypes;
|
|
176
|
+
return invoicingAccountsForQboAccountTypes(accounts, accountTypes).map((account) => account.id);
|
|
185
177
|
};
|
|
186
178
|
/**
|
|
187
179
|
* Stable empties for a tenant with no QBO connection.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getInvoicingQboAccountMappingId, toInvoicingQboAccountMappingSlot, toInvoicingQboAccountMappingsPayload, } from '../../../entity/invoicing/invoicingCommonPayload';
|
|
2
2
|
export const initialInvoicingQboSettingsLocalData = {
|
|
3
3
|
accountIdByMappingCode: {},
|
|
4
4
|
autoCreateItems: false,
|
|
@@ -15,11 +15,6 @@ export const entityToInvoicingQboSettingsLocalData = (qbo) => {
|
|
|
15
15
|
// Keyed by the slot the payload used, which is the config code the page will
|
|
16
16
|
// ask for — the reader tolerates the older `_id` form on the way in.
|
|
17
17
|
Object.keys(accountMappings).forEach((key) => {
|
|
18
|
-
// Display-only, so it is never edited or written back on save.
|
|
19
|
-
if (toInvoicingQboAccountMappingSlot(key) ===
|
|
20
|
-
INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT) {
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
18
|
const accountId = getInvoicingQboAccountMappingId(accountMappings, key);
|
|
24
19
|
if (accountId != null) {
|
|
25
20
|
accountIdByMappingCode[toInvoicingQboAccountMappingSlot(key)] = accountId;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createSlice } from '@reduxjs/toolkit';
|
|
2
|
-
import { INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT, } from '../../../entity/invoicing/invoicingCommonPayload';
|
|
3
2
|
import { clearAll } from '../../../rootActions';
|
|
4
3
|
import { entityToInvoicingQboSettingsLocalData, } from './invoicingQboSettingsFormConfig';
|
|
5
4
|
export const initialState = {
|
|
@@ -41,24 +40,7 @@ const settingsView = createSlice({
|
|
|
41
40
|
saveInvoicingSettings(draft, action) {
|
|
42
41
|
draft.saveFetchState = { fetchState: 'In-Progress', error: undefined };
|
|
43
42
|
if (draft.settings != null) {
|
|
44
|
-
|
|
45
|
-
// so carry it over rather than blank it until the response lands.
|
|
46
|
-
const postingAccount = draft.settings.qbo?.account_mappings?.[INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT];
|
|
47
|
-
const settings = { ...draft.settings, ...action.payload.settings };
|
|
48
|
-
const accountMappings = settings.qbo?.account_mappings;
|
|
49
|
-
draft.settings =
|
|
50
|
-
postingAccount != null && accountMappings != null
|
|
51
|
-
? {
|
|
52
|
-
...settings,
|
|
53
|
-
qbo: {
|
|
54
|
-
...settings.qbo,
|
|
55
|
-
account_mappings: {
|
|
56
|
-
...accountMappings,
|
|
57
|
-
[INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT]: postingAccount,
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
}
|
|
61
|
-
: settings;
|
|
43
|
+
draft.settings = { ...draft.settings, ...action.payload.settings };
|
|
62
44
|
}
|
|
63
45
|
},
|
|
64
46
|
saveInvoicingSettingsSuccess(draft, action) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { getInvoicingQboAccountMappingId, } from '../../../entity/invoicing/invoicingCommonPayload';
|
|
1
2
|
import { getAddressByAddressId } from '../../../entity/address/addressSelector';
|
|
2
3
|
import { getFileByFileId } from '../../../entity/file/fileSelector';
|
|
3
|
-
import { INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT, getInvoicingQboAccountMappingId, } from '../../../entity/invoicing/invoicingCommonPayload';
|
|
4
4
|
import { getFormattedAddress } from '../../addressView/addressViewSelector';
|
|
5
5
|
import { getInvoicingConfigView, invoicingFieldHelpForGroup, } from '../invoicingConfigView/invoicingConfigViewSelector';
|
|
6
6
|
import { getInvoicingQboView, isInvoicingAccountingPickerDataLoading, } from '../invoicingQboView/invoicingQboViewSelector';
|
|
@@ -102,19 +102,6 @@ export const getInvoicingSettingsHubView = (state) => {
|
|
|
102
102
|
status,
|
|
103
103
|
};
|
|
104
104
|
};
|
|
105
|
-
/**
|
|
106
|
-
* The name of the account the service posts sales tax to. Read from the whole
|
|
107
|
-
* account the settings carry, or looked up by id in the account list for a
|
|
108
|
-
* payload that sends only the id.
|
|
109
|
-
*/
|
|
110
|
-
const invoicingSalesTaxPostingAccountName = (accountMappings, accounts) => {
|
|
111
|
-
const slot = accountMappings?.[INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT];
|
|
112
|
-
const name = typeof slot === 'string'
|
|
113
|
-
? accounts.find((account) => account.id === slot)?.name
|
|
114
|
-
: slot?.account_name;
|
|
115
|
-
const trimmed = name?.trim() ?? '';
|
|
116
|
-
return trimmed !== '' ? trimmed : undefined;
|
|
117
|
-
};
|
|
118
105
|
/**
|
|
119
106
|
* Bundled view for the QBO configuration page: the draft it edits, the accounts
|
|
120
107
|
* and option lists its dropdowns render, the connection and sync health it
|
|
@@ -148,7 +135,6 @@ export const getInvoicingQboSettingsFormView = (state) => {
|
|
|
148
135
|
localData: qboDraft ?? initialInvoicingQboSettingsLocalData,
|
|
149
136
|
nestedAccountHierarchy: qboView.nestedAccountHierarchy,
|
|
150
137
|
retryFetchStateByTaskId: qboView.retryFetchStateByTaskId,
|
|
151
|
-
salesTaxPostingAccountName: invoicingSalesTaxPostingAccountName(settings?.qbo?.account_mappings, qboView.accounts),
|
|
152
138
|
saveState: saveFetchState.fetchState === 'Not-Started' && error != null
|
|
153
139
|
? { fetchState, error }
|
|
154
140
|
: saveFetchState,
|
|
@@ -2,6 +2,7 @@ import { of } from 'rxjs';
|
|
|
2
2
|
import { filter, switchMap } from 'rxjs/operators';
|
|
3
3
|
import { updateCashInCashOutAdditionalBalancesSelection } from '../cashInCashOut/cashInCashOutReducer';
|
|
4
4
|
import { updateCashPositionAdditionalBalancesSelection } from '../cashPosition/cashPositionReducer';
|
|
5
|
+
import { getFinanceStatementAdditionalBalances } from '../financeStatement/financeStatementCompareBalances';
|
|
5
6
|
import { updateFinanceStatementAdditionalBalancesSelection } from '../financeStatement/financeStatementReducer';
|
|
6
7
|
import { getFinanceStatement } from '../financeStatement/financeStatementSelector';
|
|
7
8
|
import { updateNetBurnOrIncomeAdditionalBalancesSelection } from '../netBurnOrIncome/netBurnOrIncomeReducer';
|
|
@@ -15,11 +16,7 @@ export const updateReportUIOptionIsCompareModeEpic = (actions$, state) => action
|
|
|
15
16
|
? 'this_period_vs_last_period_percent'
|
|
16
17
|
: 'this_period_vs_last_period';
|
|
17
18
|
const financeStatementState = getFinanceStatement(state.value);
|
|
18
|
-
const
|
|
19
|
-
balance !== 'this_period_vs_last_period_percent');
|
|
20
|
-
const financeAdditionalBalances = isCompareModeOn
|
|
21
|
-
? [...filteredFinanceAdditionalBalances, additionalBalance]
|
|
22
|
-
: filteredFinanceAdditionalBalances;
|
|
19
|
+
const financeAdditionalBalances = getFinanceStatementAdditionalBalances(additionalBalances, isCompareModeOn, additionalBalance);
|
|
23
20
|
return of(updateNetBurnOrIncomeAdditionalBalancesSelection({
|
|
24
21
|
isCompareModeOn,
|
|
25
22
|
additionalBalances: isCompareModeOn ? [additionalBalance] : [],
|
|
@@ -2,6 +2,7 @@ import { from } from 'rxjs';
|
|
|
2
2
|
import { filter, switchMap } from 'rxjs/operators';
|
|
3
3
|
import { updateCashInCashOutAdditionalBalancesSelection } from '../cashInCashOut/cashInCashOutReducer';
|
|
4
4
|
import { updateCashPositionAdditionalBalancesSelection } from '../cashPosition/cashPositionReducer';
|
|
5
|
+
import { getFinanceStatementAdditionalBalances } from '../financeStatement/financeStatementCompareBalances';
|
|
5
6
|
import { updateFinanceStatementAdditionalBalancesSelection } from '../financeStatement/financeStatementReducer';
|
|
6
7
|
import { getFinanceStatement } from '../financeStatement/financeStatementSelector';
|
|
7
8
|
import { updateNetBurnOrIncomeAdditionalBalancesSelection } from '../netBurnOrIncome/netBurnOrIncomeReducer';
|
|
@@ -10,19 +11,14 @@ import { updateRevenueAdditionalBalancesSelection } from '../revenue/revenueRedu
|
|
|
10
11
|
import { toggleReportUIOptionForecastMode, updateReportUIOptionIsCompareModeOn, } from './reportUIOptionsReducer';
|
|
11
12
|
export const updateReportUIOptionIsCompareModeOnEpic = (actions$, state) => actions$.pipe(filter(updateReportUIOptionIsCompareModeOn.match), switchMap((action) => {
|
|
12
13
|
const { additionalBalances } = action.payload;
|
|
14
|
+
const compareBalanceFromMode = state.value.reportUIOptionsState.compareMode === 'compare_mode_percent'
|
|
15
|
+
? 'this_period_vs_last_period_percent'
|
|
16
|
+
: 'this_period_vs_last_period';
|
|
13
17
|
const additionalBalance = action.payload.isCompareModeOn
|
|
14
|
-
?
|
|
15
|
-
'compare_mode_percent'
|
|
16
|
-
? ['this_period_vs_last_period_percent']
|
|
17
|
-
: ['this_period_vs_last_period']
|
|
18
|
+
? [compareBalanceFromMode]
|
|
18
19
|
: [];
|
|
19
20
|
const financeStatementState = getFinanceStatement(state.value);
|
|
20
|
-
const
|
|
21
|
-
balance !== 'this_period_vs_last_period_percent');
|
|
22
|
-
const financeAdditionalBalances = [
|
|
23
|
-
...filteredFinanceAdditionalBalances,
|
|
24
|
-
...additionalBalance,
|
|
25
|
-
];
|
|
21
|
+
const financeAdditionalBalances = getFinanceStatementAdditionalBalances(additionalBalances, action.payload.isCompareModeOn, compareBalanceFromMode);
|
|
26
22
|
const actions = [
|
|
27
23
|
updateNetBurnOrIncomeAdditionalBalancesSelection({
|
|
28
24
|
isCompareModeOn: action.payload.isCompareModeOn,
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { COABalanceType } from '../../commonStateTypes/coaBalance/coaBalanceType';
|
|
2
|
+
export declare function getFinanceStatementAdditionalBalances(requestedBalances: COABalanceType[], isCompareModeOn: boolean, compareBalanceFromMode: COABalanceType): COABalanceType[];
|
|
3
|
+
export declare function getDisplayedAdditionalBalances(selectedBalances: COABalanceType[]): COABalanceType[];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getFinanceStatementAdditionalBalances = getFinanceStatementAdditionalBalances;
|
|
4
|
+
exports.getDisplayedAdditionalBalances = getDisplayedAdditionalBalances;
|
|
5
|
+
const coaBalanceType_1 = require("../../commonStateTypes/coaBalance/coaBalanceType");
|
|
6
|
+
// Reports can show the $ and % change columns together, so every requested compare type is kept;
|
|
7
|
+
// compareMode picks one only when none was requested.
|
|
8
|
+
function getFinanceStatementAdditionalBalances(requestedBalances, isCompareModeOn, compareBalanceFromMode) {
|
|
9
|
+
const otherBalances = (0, coaBalanceType_1.stripCompareAdditionalBalanceTypes)(requestedBalances);
|
|
10
|
+
if (!isCompareModeOn) {
|
|
11
|
+
return otherBalances;
|
|
12
|
+
}
|
|
13
|
+
const requestedCompareBalances = requestedBalances.filter((balance) => !otherBalances.includes(balance));
|
|
14
|
+
return [
|
|
15
|
+
...otherBalances,
|
|
16
|
+
...(requestedCompareBalances.length > 0
|
|
17
|
+
? requestedCompareBalances
|
|
18
|
+
: [compareBalanceFromMode]),
|
|
19
|
+
];
|
|
20
|
+
}
|
|
21
|
+
// The table has one change column with a $/% picker, so only the first selected compare type is
|
|
22
|
+
// shown; the rest of the selection still reaches the download.
|
|
23
|
+
function getDisplayedAdditionalBalances(selectedBalances) {
|
|
24
|
+
const otherBalances = (0, coaBalanceType_1.stripCompareAdditionalBalanceTypes)(selectedBalances);
|
|
25
|
+
const displayedCompareBalance = selectedBalances.find((balance) => !otherBalances.includes(balance));
|
|
26
|
+
return selectedBalances.filter((balance) => otherBalances.includes(balance) || balance === displayedCompareBalance);
|
|
27
|
+
}
|
|
@@ -3,6 +3,7 @@ var _a;
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.updateFinanceStatementFormat = exports.updateFinanceStatementDisplayOrder = exports.updateDownloadState = exports.updateFinanceStatementAdditionalBalancesSelection = exports.updateSelectedReportId = exports.updateCOABalancesRange = exports.updateFinanceStatementThisPeriod = exports.updateFinanceStatementTimeframe = exports.fetchFinanceStatement = exports.initialFinanceStatementState = void 0;
|
|
5
5
|
const toolkit_1 = require("@reduxjs/toolkit");
|
|
6
|
+
const coaBalanceType_1 = require("../../commonStateTypes/coaBalance/coaBalanceType");
|
|
6
7
|
const thisPeriodHelpers_1 = require("../../commonStateTypes/viewAndReport/thisPeriodHelpers");
|
|
7
8
|
const rootActions_1 = require("../../rootActions");
|
|
8
9
|
const zeniDayJS_1 = require("../../zeniDayJS");
|
|
@@ -92,17 +93,11 @@ const financeStatement = (0, toolkit_1.createSlice)({
|
|
|
92
93
|
updateFinanceStatementAdditionalBalancesSelection(draft, action) {
|
|
93
94
|
const { additionalBalances: additionalBalancesPayload, maxNumOfPeriodsToHighlight, } = action.payload;
|
|
94
95
|
const additionalBalances = additionalBalancesPayload ?? [];
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
additionalBalances.includes(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (id > -1) {
|
|
101
|
-
const balance = tempAdditionalBalances.splice(id, 1);
|
|
102
|
-
tempAdditionalBalances = [...balance, ...tempAdditionalBalances];
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
draft.selectedAdditionalBalances = tempAdditionalBalances;
|
|
96
|
+
const otherBalances = (0, coaBalanceType_1.stripCompareAdditionalBalanceTypes)(additionalBalances);
|
|
97
|
+
draft.selectedAdditionalBalances = [
|
|
98
|
+
...additionalBalances.filter((balance) => !otherBalances.includes(balance)),
|
|
99
|
+
...otherBalances,
|
|
100
|
+
];
|
|
106
101
|
draft.maxNumOfPeriodsToHighlight = maxNumOfPeriodsToHighlight;
|
|
107
102
|
draft.isAdditionalBalancesShown =
|
|
108
103
|
additionalBalances.length != 0 ? true : false;
|
|
@@ -20,6 +20,17 @@ const profitAndLossSelector_1 = require("../profitAndLoss/profitAndLossSelector"
|
|
|
20
20
|
const profitAndLossClassesByClassHorizontalSelector_1 = require("../profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector");
|
|
21
21
|
const profitAndLossClassesViewSelector_1 = require("../profitAndLossClassesView/profitAndLossClassesViewSelector");
|
|
22
22
|
const profitAndLossProjectViewSelector_1 = require("../profitAndLossProjectView/profitAndLossProjectViewSelector");
|
|
23
|
+
const financeStatementCompareBalances_1 = require("./financeStatementCompareBalances");
|
|
24
|
+
/**
|
|
25
|
+
* Reports are built from the displayed columns, but their filter carries the full selection: the
|
|
26
|
+
* download and the Add Columns picker read it from there.
|
|
27
|
+
*/
|
|
28
|
+
function withSelectedAdditionalBalances(report, selectedAdditionalBalances) {
|
|
29
|
+
return {
|
|
30
|
+
...report,
|
|
31
|
+
filter: { ...report.filter, additionalBalances: selectedAdditionalBalances },
|
|
32
|
+
};
|
|
33
|
+
}
|
|
23
34
|
function getDateRangeForFinanceReports(profitAndLossState, balanceSheetState, cashFlowState) {
|
|
24
35
|
const datesSorted = [
|
|
25
36
|
profitAndLossState.dataAvailable?.startDate,
|
|
@@ -74,7 +85,7 @@ exports.getFinanceStatement = (0, toolkit_1.createSelector)((state) => state.acc
|
|
|
74
85
|
const additionalBalancesForFilter = useHorizontalClassesLayout
|
|
75
86
|
? (0, coaBalanceType_1.stripCompareAdditionalBalanceTypes)(rawAdditionalBalances)
|
|
76
87
|
: rawAdditionalBalances;
|
|
77
|
-
const filter = (0, coaBalancesFilter_1.newBalancesFilter)(firstMonthOfFY, financeStatementState.timeframe, Math.min((0, dataAvailable_1.getNumberOfPeriods)(firstMonthOfFY, financeStatementState.timeframe, dataAvailable) ?? financeStatementState.selectedCOABalancesRange.numberOfPeriods, financeStatementState.selectedCOABalancesRange.numberOfPeriods), thisPeriod, 'ascending_date', additionalBalancesForFilter);
|
|
88
|
+
const filter = (0, coaBalancesFilter_1.newBalancesFilter)(firstMonthOfFY, financeStatementState.timeframe, Math.min((0, dataAvailable_1.getNumberOfPeriods)(firstMonthOfFY, financeStatementState.timeframe, dataAvailable) ?? financeStatementState.selectedCOABalancesRange.numberOfPeriods, financeStatementState.selectedCOABalancesRange.numberOfPeriods), thisPeriod, 'ascending_date', (0, financeStatementCompareBalances_1.getDisplayedAdditionalBalances)(additionalBalancesForFilter));
|
|
78
89
|
const profitAndLossReport = (0, profitAndLossSelector_1.getProfitAndLossReport)(filter, undefined, accountState, accountGroupState, sectionAccountsViewState, profitAndLossState, forecastState);
|
|
79
90
|
const classFilter = (0, coaBalancesFilterClassesView_1.newBalancesFilterClassesView)(filter.firstMonthOfFY, filter.timeframe, filter.balancesRange.numberOfPeriods, filter.balancesRange.thisPeriod, filter.balancesRange.orderBy, filter.additionalBalances, profitAndLossClassesViewState.uiState.classesToFilterOut);
|
|
80
91
|
const profitAndLossByClassReport = (0, profitAndLossClassesViewSelector_1.getProfitAndLossClassesView)(classFilter, accountState, classState, sectionsState, profitAndLossClassesViewState);
|
|
@@ -166,17 +177,17 @@ exports.getFinanceStatement = (0, toolkit_1.createSelector)((state) => state.acc
|
|
|
166
177
|
reportFormat: financeStatementState.reportFormat,
|
|
167
178
|
firstMonthOfFY,
|
|
168
179
|
dataAvailable,
|
|
169
|
-
profitAndLossReport,
|
|
170
|
-
profitAndLossByClassReport,
|
|
171
|
-
profitAndLossByProjectReport,
|
|
180
|
+
profitAndLossReport: withSelectedAdditionalBalances(profitAndLossReport, additionalBalancesForFilter),
|
|
181
|
+
profitAndLossByClassReport: withSelectedAdditionalBalances(profitAndLossByClassReport, additionalBalancesForFilter),
|
|
182
|
+
profitAndLossByProjectReport: withSelectedAdditionalBalances(profitAndLossByProjectReport, additionalBalancesForFilter),
|
|
172
183
|
profitAndLossByClassHorizontalReport,
|
|
173
|
-
balanceSheetReport,
|
|
174
|
-
balanceSheetByClassReport,
|
|
175
|
-
cashFlowByClassReport,
|
|
176
|
-
cashFlowByProjectReport,
|
|
177
|
-
balanceSheetByProjectReport,
|
|
178
|
-
cashFlowReport,
|
|
179
|
-
filter,
|
|
184
|
+
balanceSheetReport: withSelectedAdditionalBalances(balanceSheetReport, additionalBalancesForFilter),
|
|
185
|
+
balanceSheetByClassReport: withSelectedAdditionalBalances(balanceSheetByClassReport, additionalBalancesForFilter),
|
|
186
|
+
cashFlowByClassReport: withSelectedAdditionalBalances(cashFlowByClassReport, additionalBalancesForFilter),
|
|
187
|
+
cashFlowByProjectReport: withSelectedAdditionalBalances(cashFlowByProjectReport, additionalBalancesForFilter),
|
|
188
|
+
balanceSheetByProjectReport: withSelectedAdditionalBalances(balanceSheetByProjectReport, additionalBalancesForFilter),
|
|
189
|
+
cashFlowReport: withSelectedAdditionalBalances(cashFlowReport, additionalBalancesForFilter),
|
|
190
|
+
filter: { ...filter, additionalBalances: additionalBalancesForFilter },
|
|
180
191
|
selectedReportId: financeStatementState.selectedReportId,
|
|
181
192
|
downloadState: financeStatementState.downloadState,
|
|
182
193
|
isCompareModeOn: useHorizontalClassesLayout
|
|
@@ -41,7 +41,6 @@ export declare const getInvoicingCatalogAccountingLookup: (state: RootState) =>
|
|
|
41
41
|
export interface InvoicingAccountingOption {
|
|
42
42
|
id: string;
|
|
43
43
|
name: string;
|
|
44
|
-
accountSubType?: string;
|
|
45
44
|
accountType?: string;
|
|
46
45
|
}
|
|
47
46
|
export interface InvoicingQboView {
|
|
@@ -78,12 +77,8 @@ export declare const getInvoicingQboAccountOptions: (state: RootState) => Invoic
|
|
|
78
77
|
* comma-separated list of QuickBooks type names. An unrecognised list yields no
|
|
79
78
|
* accounts rather than every account: offering a bank account where income is
|
|
80
79
|
* required would post a mapping QuickBooks rejects.
|
|
81
|
-
*
|
|
82
|
-
* `excludedAccountSubTypes` drops accounts QuickBooks will not post to for that
|
|
83
|
-
* kind even though their type matches, e.g. a tax agency's system payable for
|
|
84
|
-
* the sales tax liability.
|
|
85
80
|
*/
|
|
86
|
-
export declare const invoicingAccountsForQboAccountTypes: (accounts: InvoicingAccountingOption[], accountTypes: string
|
|
81
|
+
export declare const invoicingAccountsForQboAccountTypes: (accounts: InvoicingAccountingOption[], accountTypes: string) => InvoicingAccountingOption[];
|
|
87
82
|
/**
|
|
88
83
|
* The account-mapping slot a catalog price point's income account shares with
|
|
89
84
|
* the settings page, and the one a discount's account shares with it.
|
|
@@ -115,7 +115,6 @@ exports.getInvoicingQboAccountOptions = (0, toolkit_1.createSelector)([
|
|
|
115
115
|
{
|
|
116
116
|
id: String(account.qboId),
|
|
117
117
|
name: account.accountName,
|
|
118
|
-
accountSubType: account.accountSubType,
|
|
119
118
|
accountType: account.accountType,
|
|
120
119
|
},
|
|
121
120
|
];
|
|
@@ -139,29 +138,22 @@ const normalizeAccountTypeName = (value) => {
|
|
|
139
138
|
// trailing "s" alone would leave "liabilitie", matching nothing.
|
|
140
139
|
return normalized.replace(/ies$/, 'y').replace(/([^s])s$/, '$1');
|
|
141
140
|
};
|
|
142
|
-
const toAccountTypeNameSet = (names) => new Set((names ?? '')
|
|
143
|
-
.split(',')
|
|
144
|
-
.map(normalizeAccountTypeName)
|
|
145
|
-
.filter((name) => name !== ''));
|
|
146
141
|
/**
|
|
147
142
|
* The accounts valid for one QBO mapping kind, whose `accountTypes` is a
|
|
148
143
|
* comma-separated list of QuickBooks type names. An unrecognised list yields no
|
|
149
144
|
* accounts rather than every account: offering a bank account where income is
|
|
150
145
|
* required would post a mapping QuickBooks rejects.
|
|
151
|
-
*
|
|
152
|
-
* `excludedAccountSubTypes` drops accounts QuickBooks will not post to for that
|
|
153
|
-
* kind even though their type matches, e.g. a tax agency's system payable for
|
|
154
|
-
* the sales tax liability.
|
|
155
146
|
*/
|
|
156
|
-
const invoicingAccountsForQboAccountTypes = (accounts, accountTypes
|
|
157
|
-
const wanted =
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
(
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
147
|
+
const invoicingAccountsForQboAccountTypes = (accounts, accountTypes) => {
|
|
148
|
+
const wanted = new Set(accountTypes
|
|
149
|
+
.split(',')
|
|
150
|
+
.map(normalizeAccountTypeName)
|
|
151
|
+
.filter((accountType) => accountType !== ''));
|
|
152
|
+
if (wanted.size === 0) {
|
|
153
|
+
return accounts;
|
|
154
|
+
}
|
|
155
|
+
return accounts.filter((account) => account.accountType != null &&
|
|
156
|
+
wanted.has(normalizeAccountTypeName(account.accountType)));
|
|
165
157
|
};
|
|
166
158
|
exports.invoicingAccountsForQboAccountTypes = invoicingAccountsForQboAccountTypes;
|
|
167
159
|
/**
|
|
@@ -190,8 +182,8 @@ exports.INVOICING_DEFAULT_INCOME_ACCOUNT_TYPES = 'Income';
|
|
|
190
182
|
* not carry falls back to `defaultAccountTypes` rather than to every account.
|
|
191
183
|
*/
|
|
192
184
|
const invoicingQboAccountIdsForMappingSlot = (accounts, accountMappingKindOptions, mappingSlot, defaultAccountTypes) => {
|
|
193
|
-
const
|
|
194
|
-
return (0, exports.invoicingAccountsForQboAccountTypes)(accounts,
|
|
185
|
+
const accountTypes = accountMappingKindOptions.find((kindOption) => (0, invoicingCommonPayload_1.toInvoicingQboAccountMappingSlot)(kindOption.code) === mappingSlot)?.accountTypes ?? defaultAccountTypes;
|
|
186
|
+
return (0, exports.invoicingAccountsForQboAccountTypes)(accounts, accountTypes).map((account) => account.id);
|
|
195
187
|
};
|
|
196
188
|
exports.invoicingQboAccountIdsForMappingSlot = invoicingQboAccountIdsForMappingSlot;
|
|
197
189
|
/**
|
|
@@ -18,11 +18,6 @@ const entityToInvoicingQboSettingsLocalData = (qbo) => {
|
|
|
18
18
|
// Keyed by the slot the payload used, which is the config code the page will
|
|
19
19
|
// ask for — the reader tolerates the older `_id` form on the way in.
|
|
20
20
|
Object.keys(accountMappings).forEach((key) => {
|
|
21
|
-
// Display-only, so it is never edited or written back on save.
|
|
22
|
-
if ((0, invoicingCommonPayload_1.toInvoicingQboAccountMappingSlot)(key) ===
|
|
23
|
-
invoicingCommonPayload_1.INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT) {
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
21
|
const accountId = (0, invoicingCommonPayload_1.getInvoicingQboAccountMappingId)(accountMappings, key);
|
|
27
22
|
if (accountId != null) {
|
|
28
23
|
accountIdByMappingCode[(0, invoicingCommonPayload_1.toInvoicingQboAccountMappingSlot)(key)] = accountId;
|
|
@@ -3,7 +3,6 @@ var _a;
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.invoicingStripeConnectionFailure = exports.updateInvoicingStripeConnection = exports.disconnectInvoicingStripe = exports.connectInvoicingStripe = exports.resetInvoicingQboSettingsDraft = exports.submitInvoicingQboSettings = exports.updateInvoicingQboSettingsDraft = exports.resetInvoicingBrandingFormDraft = exports.submitInvoicingBrandingForm = exports.updateInvoicingBrandingFormDraft = exports.resetSaveInvoicingSettings = exports.saveInvoicingSettingsFailure = exports.saveInvoicingSettingsSuccess = exports.saveInvoicingSettings = exports.updateInvoicingSettingsFailure = exports.updateInvoicingSettings = exports.fetchInvoicingSettings = exports.initialState = void 0;
|
|
5
5
|
const toolkit_1 = require("@reduxjs/toolkit");
|
|
6
|
-
const invoicingCommonPayload_1 = require("../../../entity/invoicing/invoicingCommonPayload");
|
|
7
6
|
const rootActions_1 = require("../../../rootActions");
|
|
8
7
|
const invoicingQboSettingsFormConfig_1 = require("./invoicingQboSettingsFormConfig");
|
|
9
8
|
exports.initialState = {
|
|
@@ -45,24 +44,7 @@ const settingsView = (0, toolkit_1.createSlice)({
|
|
|
45
44
|
saveInvoicingSettings(draft, action) {
|
|
46
45
|
draft.saveFetchState = { fetchState: 'In-Progress', error: undefined };
|
|
47
46
|
if (draft.settings != null) {
|
|
48
|
-
|
|
49
|
-
// so carry it over rather than blank it until the response lands.
|
|
50
|
-
const postingAccount = draft.settings.qbo?.account_mappings?.[invoicingCommonPayload_1.INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT];
|
|
51
|
-
const settings = { ...draft.settings, ...action.payload.settings };
|
|
52
|
-
const accountMappings = settings.qbo?.account_mappings;
|
|
53
|
-
draft.settings =
|
|
54
|
-
postingAccount != null && accountMappings != null
|
|
55
|
-
? {
|
|
56
|
-
...settings,
|
|
57
|
-
qbo: {
|
|
58
|
-
...settings.qbo,
|
|
59
|
-
account_mappings: {
|
|
60
|
-
...accountMappings,
|
|
61
|
-
[invoicingCommonPayload_1.INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT]: postingAccount,
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
}
|
|
65
|
-
: settings;
|
|
47
|
+
draft.settings = { ...draft.settings, ...action.payload.settings };
|
|
66
48
|
}
|
|
67
49
|
},
|
|
68
50
|
saveInvoicingSettingsSuccess(draft, action) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NestedAccountHierarchyForReport } from '../../../commonStateTypes/accountView/nestedAccountID';
|
|
2
2
|
import { FetchState, FetchStateAndError } from '../../../commonStateTypes/common';
|
|
3
|
-
import { File } from '../../../entity/file/fileState';
|
|
4
3
|
import { InvoicingDunningActionOption, InvoicingFieldHelpByCode, InvoicingNamedOption, InvoicingQboAccountMappingKindOption, InvoicingSettingsPayload } from '../../../entity/invoicing/invoicingCommonPayload';
|
|
4
|
+
import { File } from '../../../entity/file/fileState';
|
|
5
5
|
import { ZeniAPIStatus } from '../../../responsePayload';
|
|
6
6
|
import { RootState } from '../../../rootStateTypes';
|
|
7
7
|
import { AddressUpdatableInfo } from '../../addressView/addressViewState';
|
|
@@ -120,8 +120,6 @@ export interface InvoicingQboSettingsFormView {
|
|
|
120
120
|
settingsFetchState: FetchState;
|
|
121
121
|
syncStatusOptions: InvoicingNamedOption[];
|
|
122
122
|
connection?: InvoicingQboConnection;
|
|
123
|
-
/** The Zeni-owned account sales tax posts to, shown read-only when present. */
|
|
124
|
-
salesTaxPostingAccountName?: string;
|
|
125
123
|
syncHealth?: InvoicingQboSyncHealth;
|
|
126
124
|
}
|
|
127
125
|
/**
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getInvoicingBrandingFormView = exports.getInvoicingQboSettingsFormView = exports.getInvoicingSettingsHubView = exports.getInvoicingSettingsView = exports.getInvoicingSettingsFetchState = exports.getInvoicingSettings = void 0;
|
|
4
4
|
exports.deriveInvoicingEnablePrerequisites = deriveInvoicingEnablePrerequisites;
|
|
5
|
+
const invoicingCommonPayload_1 = require("../../../entity/invoicing/invoicingCommonPayload");
|
|
5
6
|
const addressSelector_1 = require("../../../entity/address/addressSelector");
|
|
6
7
|
const fileSelector_1 = require("../../../entity/file/fileSelector");
|
|
7
|
-
const invoicingCommonPayload_1 = require("../../../entity/invoicing/invoicingCommonPayload");
|
|
8
8
|
const addressViewSelector_1 = require("../../addressView/addressViewSelector");
|
|
9
9
|
const invoicingConfigViewSelector_1 = require("../invoicingConfigView/invoicingConfigViewSelector");
|
|
10
10
|
const invoicingQboViewSelector_1 = require("../invoicingQboView/invoicingQboViewSelector");
|
|
@@ -110,19 +110,6 @@ const getInvoicingSettingsHubView = (state) => {
|
|
|
110
110
|
};
|
|
111
111
|
};
|
|
112
112
|
exports.getInvoicingSettingsHubView = getInvoicingSettingsHubView;
|
|
113
|
-
/**
|
|
114
|
-
* The name of the account the service posts sales tax to. Read from the whole
|
|
115
|
-
* account the settings carry, or looked up by id in the account list for a
|
|
116
|
-
* payload that sends only the id.
|
|
117
|
-
*/
|
|
118
|
-
const invoicingSalesTaxPostingAccountName = (accountMappings, accounts) => {
|
|
119
|
-
const slot = accountMappings?.[invoicingCommonPayload_1.INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT];
|
|
120
|
-
const name = typeof slot === 'string'
|
|
121
|
-
? accounts.find((account) => account.id === slot)?.name
|
|
122
|
-
: slot?.account_name;
|
|
123
|
-
const trimmed = name?.trim() ?? '';
|
|
124
|
-
return trimmed !== '' ? trimmed : undefined;
|
|
125
|
-
};
|
|
126
113
|
/**
|
|
127
114
|
* Bundled view for the QBO configuration page: the draft it edits, the accounts
|
|
128
115
|
* and option lists its dropdowns render, the connection and sync health it
|
|
@@ -156,7 +143,6 @@ const getInvoicingQboSettingsFormView = (state) => {
|
|
|
156
143
|
localData: qboDraft ?? invoicingQboSettingsFormConfig_1.initialInvoicingQboSettingsLocalData,
|
|
157
144
|
nestedAccountHierarchy: qboView.nestedAccountHierarchy,
|
|
158
145
|
retryFetchStateByTaskId: qboView.retryFetchStateByTaskId,
|
|
159
|
-
salesTaxPostingAccountName: invoicingSalesTaxPostingAccountName(settings?.qbo?.account_mappings, qboView.accounts),
|
|
160
146
|
saveState: saveFetchState.fetchState === 'Not-Started' && error != null
|
|
161
147
|
? { fetchState, error }
|
|
162
148
|
: saveFetchState,
|
|
@@ -5,6 +5,7 @@ const rxjs_1 = require("rxjs");
|
|
|
5
5
|
const operators_1 = require("rxjs/operators");
|
|
6
6
|
const cashInCashOutReducer_1 = require("../cashInCashOut/cashInCashOutReducer");
|
|
7
7
|
const cashPositionReducer_1 = require("../cashPosition/cashPositionReducer");
|
|
8
|
+
const financeStatementCompareBalances_1 = require("../financeStatement/financeStatementCompareBalances");
|
|
8
9
|
const financeStatementReducer_1 = require("../financeStatement/financeStatementReducer");
|
|
9
10
|
const financeStatementSelector_1 = require("../financeStatement/financeStatementSelector");
|
|
10
11
|
const netBurnOrIncomeReducer_1 = require("../netBurnOrIncome/netBurnOrIncomeReducer");
|
|
@@ -18,11 +19,7 @@ const updateReportUIOptionIsCompareModeEpic = (actions$, state) => actions$.pipe
|
|
|
18
19
|
? 'this_period_vs_last_period_percent'
|
|
19
20
|
: 'this_period_vs_last_period';
|
|
20
21
|
const financeStatementState = (0, financeStatementSelector_1.getFinanceStatement)(state.value);
|
|
21
|
-
const
|
|
22
|
-
balance !== 'this_period_vs_last_period_percent');
|
|
23
|
-
const financeAdditionalBalances = isCompareModeOn
|
|
24
|
-
? [...filteredFinanceAdditionalBalances, additionalBalance]
|
|
25
|
-
: filteredFinanceAdditionalBalances;
|
|
22
|
+
const financeAdditionalBalances = (0, financeStatementCompareBalances_1.getFinanceStatementAdditionalBalances)(additionalBalances, isCompareModeOn, additionalBalance);
|
|
26
23
|
return (0, rxjs_1.of)((0, netBurnOrIncomeReducer_1.updateNetBurnOrIncomeAdditionalBalancesSelection)({
|
|
27
24
|
isCompareModeOn,
|
|
28
25
|
additionalBalances: isCompareModeOn ? [additionalBalance] : [],
|
|
@@ -5,6 +5,7 @@ const rxjs_1 = require("rxjs");
|
|
|
5
5
|
const operators_1 = require("rxjs/operators");
|
|
6
6
|
const cashInCashOutReducer_1 = require("../cashInCashOut/cashInCashOutReducer");
|
|
7
7
|
const cashPositionReducer_1 = require("../cashPosition/cashPositionReducer");
|
|
8
|
+
const financeStatementCompareBalances_1 = require("../financeStatement/financeStatementCompareBalances");
|
|
8
9
|
const financeStatementReducer_1 = require("../financeStatement/financeStatementReducer");
|
|
9
10
|
const financeStatementSelector_1 = require("../financeStatement/financeStatementSelector");
|
|
10
11
|
const netBurnOrIncomeReducer_1 = require("../netBurnOrIncome/netBurnOrIncomeReducer");
|
|
@@ -13,19 +14,14 @@ const revenueReducer_1 = require("../revenue/revenueReducer");
|
|
|
13
14
|
const reportUIOptionsReducer_1 = require("./reportUIOptionsReducer");
|
|
14
15
|
const updateReportUIOptionIsCompareModeOnEpic = (actions$, state) => actions$.pipe((0, operators_1.filter)(reportUIOptionsReducer_1.updateReportUIOptionIsCompareModeOn.match), (0, operators_1.switchMap)((action) => {
|
|
15
16
|
const { additionalBalances } = action.payload;
|
|
17
|
+
const compareBalanceFromMode = state.value.reportUIOptionsState.compareMode === 'compare_mode_percent'
|
|
18
|
+
? 'this_period_vs_last_period_percent'
|
|
19
|
+
: 'this_period_vs_last_period';
|
|
16
20
|
const additionalBalance = action.payload.isCompareModeOn
|
|
17
|
-
?
|
|
18
|
-
'compare_mode_percent'
|
|
19
|
-
? ['this_period_vs_last_period_percent']
|
|
20
|
-
: ['this_period_vs_last_period']
|
|
21
|
+
? [compareBalanceFromMode]
|
|
21
22
|
: [];
|
|
22
23
|
const financeStatementState = (0, financeStatementSelector_1.getFinanceStatement)(state.value);
|
|
23
|
-
const
|
|
24
|
-
balance !== 'this_period_vs_last_period_percent');
|
|
25
|
-
const financeAdditionalBalances = [
|
|
26
|
-
...filteredFinanceAdditionalBalances,
|
|
27
|
-
...additionalBalance,
|
|
28
|
-
];
|
|
24
|
+
const financeAdditionalBalances = (0, financeStatementCompareBalances_1.getFinanceStatementAdditionalBalances)(additionalBalances, action.payload.isCompareModeOn, compareBalanceFromMode);
|
|
29
25
|
const actions = [
|
|
30
26
|
(0, netBurnOrIncomeReducer_1.updateNetBurnOrIncomeAdditionalBalancesSelection)({
|
|
31
27
|
isCompareModeOn: action.payload.isCompareModeOn,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeniai/client-epic-state",
|
|
3
|
-
"version": "5.2.
|
|
3
|
+
"version": "5.2.90-betaRD0",
|
|
4
4
|
"description": "Shared module between Web & Mobile containing required abstractions for state management, async network communication. ",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "lib/esm/index.js",
|
|
@@ -53,6 +53,31 @@
|
|
|
53
53
|
"url": "https://github.com/zeni-ai/client-epic-state/issues"
|
|
54
54
|
},
|
|
55
55
|
"homepage": "https://github.com/zeni-ai/client-epic-state#readme",
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@eslint/js": "^9.24.0",
|
|
58
|
+
"@liveblocks/client": "^2.23.0",
|
|
59
|
+
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
|
60
|
+
"@types/big.js": "^6.2.2",
|
|
61
|
+
"@types/lodash": "^4.17.23",
|
|
62
|
+
"@types/node": "^20.19.0",
|
|
63
|
+
"@types/url-parse": "^1.4.11",
|
|
64
|
+
"@typescript-eslint/eslint-plugin": "^8.56.0",
|
|
65
|
+
"@typescript-eslint/parser": "^8.56.0",
|
|
66
|
+
"concurrently": "^9.1.2",
|
|
67
|
+
"eslint": "^9.24.0",
|
|
68
|
+
"eslint-config-prettier": "^10.0.2",
|
|
69
|
+
"eslint-plugin-perfectionist": "^4.15.0",
|
|
70
|
+
"eslint-plugin-unused-imports": "^4.1.4",
|
|
71
|
+
"globals": "^16.0.0",
|
|
72
|
+
"knip": "^6.21.0",
|
|
73
|
+
"madge": "^8.0.0",
|
|
74
|
+
"prettier": "^3.5.3",
|
|
75
|
+
"rimraf": "^6.0.1",
|
|
76
|
+
"semver": "^7.7.1",
|
|
77
|
+
"typescript": "^5.7.3",
|
|
78
|
+
"vite": "^7.3.5",
|
|
79
|
+
"vitest": "^4.1.11"
|
|
80
|
+
},
|
|
56
81
|
"dependencies": {
|
|
57
82
|
"@redux-devtools/extension": "^3.3.0",
|
|
58
83
|
"@reduxjs/toolkit": "^1.6.2",
|