@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.
Files changed (25) hide show
  1. package/lib/entity/invoicing/invoicingCommonPayload.d.ts +0 -9
  2. package/lib/entity/invoicing/invoicingCommonPayload.js +1 -8
  3. package/lib/esm/entity/invoicing/invoicingCommonPayload.js +0 -7
  4. package/lib/esm/view/financeStatement/financeStatementCompareBalances.js +23 -0
  5. package/lib/esm/view/financeStatement/financeStatementReducer.js +6 -11
  6. package/lib/esm/view/financeStatement/financeStatementSelector.js +23 -12
  7. package/lib/esm/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +12 -20
  8. package/lib/esm/view/invoicing/settingsView/invoicingQboSettingsFormConfig.js +1 -6
  9. package/lib/esm/view/invoicing/settingsView/settingsViewReducer.js +1 -19
  10. package/lib/esm/view/invoicing/settingsView/settingsViewSelector.js +1 -15
  11. package/lib/esm/view/reportUIOptions/updateReportUIOptionCompareModeEpic.js +2 -5
  12. package/lib/esm/view/reportUIOptions/updateReportUIOptionIsCompareModeOnEpic.js +6 -10
  13. package/lib/view/financeStatement/financeStatementCompareBalances.d.ts +3 -0
  14. package/lib/view/financeStatement/financeStatementCompareBalances.js +27 -0
  15. package/lib/view/financeStatement/financeStatementReducer.js +6 -11
  16. package/lib/view/financeStatement/financeStatementSelector.js +22 -11
  17. package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.d.ts +1 -6
  18. package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +12 -20
  19. package/lib/view/invoicing/settingsView/invoicingQboSettingsFormConfig.js +0 -5
  20. package/lib/view/invoicing/settingsView/settingsViewReducer.js +1 -19
  21. package/lib/view/invoicing/settingsView/settingsViewSelector.d.ts +1 -3
  22. package/lib/view/invoicing/settingsView/settingsViewSelector.js +1 -15
  23. package/lib/view/reportUIOptions/updateReportUIOptionCompareModeEpic.js +2 -5
  24. package/lib/view/reportUIOptions/updateReportUIOptionIsCompareModeOnEpic.js +6 -10
  25. 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.INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT = 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;
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
- let tempAdditionalBalances = [...additionalBalances];
92
- if (additionalBalances.includes('this_period_vs_last_period') ||
93
- additionalBalances.includes('this_period_vs_last_period_percent')) {
94
- const id = tempAdditionalBalances.findIndex((balance) => balance === 'this_period_vs_last_period' ||
95
- balance === 'this_period_vs_last_period_percent');
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, excludedAccountSubTypes) => {
148
- const wanted = toAccountTypeNameSet(accountTypes);
149
- const excluded = toAccountTypeNameSet(excludedAccountSubTypes);
150
- return accounts.filter((account) => (wanted.size === 0 ||
151
- (account.accountType != null &&
152
- wanted.has(normalizeAccountTypeName(account.accountType)))) &&
153
- (excluded.size === 0 ||
154
- account.accountSubType == null ||
155
- !excluded.has(normalizeAccountTypeName(account.accountSubType))));
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 kindOption = accountMappingKindOptions.find((option) => toInvoicingQboAccountMappingSlot(option.code) === mappingSlot);
184
- return invoicingAccountsForQboAccountTypes(accounts, kindOption?.accountTypes ?? defaultAccountTypes, kindOption?.excludedAccountSubTypes).map((account) => account.id);
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 { INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT, getInvoicingQboAccountMappingId, toInvoicingQboAccountMappingSlot, toInvoicingQboAccountMappingsPayload, } from '../../../entity/invoicing/invoicingCommonPayload';
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
- // The display-only sales tax posting account is never in a save body,
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 filteredFinanceAdditionalBalances = additionalBalances.filter((balance) => balance !== 'this_period_vs_last_period' &&
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
- ? state.value.reportUIOptionsState.compareMode ===
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 filteredFinanceAdditionalBalances = additionalBalances.filter((balance) => balance !== 'this_period_vs_last_period' &&
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
- let tempAdditionalBalances = [...additionalBalances];
96
- if (additionalBalances.includes('this_period_vs_last_period') ||
97
- additionalBalances.includes('this_period_vs_last_period_percent')) {
98
- const id = tempAdditionalBalances.findIndex((balance) => balance === 'this_period_vs_last_period' ||
99
- balance === 'this_period_vs_last_period_percent');
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, excludedAccountSubTypes?: string) => InvoicingAccountingOption[];
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, excludedAccountSubTypes) => {
157
- const wanted = toAccountTypeNameSet(accountTypes);
158
- const excluded = toAccountTypeNameSet(excludedAccountSubTypes);
159
- return accounts.filter((account) => (wanted.size === 0 ||
160
- (account.accountType != null &&
161
- wanted.has(normalizeAccountTypeName(account.accountType)))) &&
162
- (excluded.size === 0 ||
163
- account.accountSubType == null ||
164
- !excluded.has(normalizeAccountTypeName(account.accountSubType))));
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 kindOption = accountMappingKindOptions.find((option) => (0, invoicingCommonPayload_1.toInvoicingQboAccountMappingSlot)(option.code) === mappingSlot);
194
- return (0, exports.invoicingAccountsForQboAccountTypes)(accounts, kindOption?.accountTypes ?? defaultAccountTypes, kindOption?.excludedAccountSubTypes).map((account) => account.id);
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
- // The display-only sales tax posting account is never in a save body,
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 filteredFinanceAdditionalBalances = additionalBalances.filter((balance) => balance !== 'this_period_vs_last_period' &&
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
- ? state.value.reportUIOptionsState.compareMode ===
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 filteredFinanceAdditionalBalances = additionalBalances.filter((balance) => balance !== 'this_period_vs_last_period' &&
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.89",
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",