@zeniai/client-epic-state 5.2.11 → 5.2.12

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 (53) hide show
  1. package/lib/entity/invoicing/invoicingCommonPayload.d.ts +27 -29
  2. package/lib/entity/invoicing/invoicingCommonPayload.js +37 -75
  3. package/lib/entity/invoicing/product/productPayload.d.ts +10 -0
  4. package/lib/entity/invoicing/product/productPayload.js +14 -2
  5. package/lib/epic.d.ts +1 -1
  6. package/lib/epic.js +10 -5
  7. package/lib/esm/entity/invoicing/invoicingCommonPayload.js +33 -73
  8. package/lib/esm/entity/invoicing/product/productPayload.js +14 -2
  9. package/lib/esm/epic.js +6 -1
  10. package/lib/esm/index.js +5 -4
  11. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.js +25 -9
  12. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +6 -1
  13. package/lib/esm/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +40 -0
  14. package/lib/esm/view/invoicing/settingsView/epics/connectInvoicingStripeEpic.js +27 -0
  15. package/lib/esm/view/invoicing/settingsView/epics/disconnectInvoicingStripeEpic.js +19 -0
  16. package/lib/esm/view/invoicing/settingsView/epics/fetchInvoicingSettingsEpic.js +19 -0
  17. package/lib/esm/view/invoicing/settingsView/epics/saveInvoicingSettingsEpic.js +39 -0
  18. package/lib/esm/view/invoicing/settingsView/epics/settingsViewActionType.js +1 -0
  19. package/lib/esm/view/invoicing/settingsView/epics/submitInvoicingQboSettingsEpic.js +25 -0
  20. package/lib/esm/view/invoicing/settingsView/invoicingQboSettingsFormConfig.js +48 -0
  21. package/lib/esm/view/invoicing/settingsView/settingsViewReducer.js +25 -1
  22. package/lib/esm/view/invoicing/settingsView/settingsViewSelector.js +41 -0
  23. package/lib/index.d.ts +7 -5
  24. package/lib/index.js +13 -6
  25. package/lib/view/expenseAutomationView/types/reconciliationViewState.d.ts +1 -1
  26. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.d.ts +12 -1
  27. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.js +25 -9
  28. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +6 -1
  29. package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.d.ts +10 -1
  30. package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +43 -1
  31. package/lib/view/invoicing/settingsView/epics/connectInvoicingStripeEpic.d.ts +6 -0
  32. package/lib/view/invoicing/settingsView/epics/connectInvoicingStripeEpic.js +31 -0
  33. package/lib/view/invoicing/settingsView/epics/disconnectInvoicingStripeEpic.d.ts +6 -0
  34. package/lib/view/invoicing/settingsView/epics/disconnectInvoicingStripeEpic.js +23 -0
  35. package/lib/view/invoicing/settingsView/epics/fetchInvoicingSettingsEpic.d.ts +6 -0
  36. package/lib/view/invoicing/settingsView/epics/fetchInvoicingSettingsEpic.js +23 -0
  37. package/lib/view/invoicing/settingsView/epics/saveInvoicingSettingsEpic.d.ts +6 -0
  38. package/lib/view/invoicing/settingsView/epics/saveInvoicingSettingsEpic.js +43 -0
  39. package/lib/view/invoicing/settingsView/epics/settingsViewActionType.d.ts +5 -0
  40. package/lib/view/invoicing/settingsView/epics/settingsViewActionType.js +2 -0
  41. package/lib/view/invoicing/settingsView/epics/submitInvoicingQboSettingsEpic.d.ts +11 -0
  42. package/lib/view/invoicing/settingsView/epics/submitInvoicingQboSettingsEpic.js +29 -0
  43. package/lib/view/invoicing/settingsView/invoicingQboSettingsFormConfig.d.ts +34 -0
  44. package/lib/view/invoicing/settingsView/invoicingQboSettingsFormConfig.js +53 -0
  45. package/lib/view/invoicing/settingsView/settingsViewReducer.d.ts +4 -1
  46. package/lib/view/invoicing/settingsView/settingsViewReducer.js +26 -2
  47. package/lib/view/invoicing/settingsView/settingsViewSelector.d.ts +35 -0
  48. package/lib/view/invoicing/settingsView/settingsViewSelector.js +43 -1
  49. package/lib/view/invoicing/settingsView/settingsViewState.d.ts +8 -0
  50. package/package.json +1 -1
  51. package/lib/esm/view/invoicing/settingsView/settingsViewEpics.js +0 -92
  52. package/lib/view/invoicing/settingsView/settingsViewEpics.d.ts +0 -12
  53. package/lib/view/invoicing/settingsView/settingsViewEpics.js +0 -99
@@ -1,3 +1,5 @@
1
+ import { toAccountPayload } from '../../../entity/account/accountPayload';
2
+ import { toClassBasePayload } from '../../../entity/class/classPayload';
1
3
  import { blankInvoicingCatalogSubFamily, isTieredCatalogPricingModel, } from './invoicingCatalogItemFormConfig';
2
4
  const CURRENCY_CODE = 'USD';
3
5
  const trimOrUndefined = (value) => value.trim() !== '' ? value.trim() : undefined;
@@ -7,20 +9,26 @@ const toProductCode = (value) => value
7
9
  .toLowerCase()
8
10
  .replace(/[^a-z0-9]+/g, '-')
9
11
  .replace(/^-|-$/g, '');
10
- const buildAccountingDetail = (subFamily) => {
12
+ const buildAccountingDetail = (subFamily, lookup) => {
11
13
  const detail = {};
12
14
  if (subFamily.sku.trim() !== '') {
13
15
  detail.sku = subFamily.sku.trim();
14
16
  }
15
- if (subFamily.accountingCode.trim() !== '') {
16
- detail.accounting_code = subFamily.accountingCode.trim();
17
+ // The account and class travel as whole objects rather than bare codes, built
18
+ // by the same helpers the transaction and vendor payloads use. An id that no
19
+ // longer resolves is dropped: sending a code the service cannot expand is how
20
+ // a mapping silently lands on the wrong account.
21
+ const account = lookup.accountByQboId(subFamily.accountingCode.trim());
22
+ if (account != null) {
23
+ detail.account = toAccountPayload(account);
17
24
  }
18
- if (subFamily.accountingCategory.trim() !== '') {
19
- detail.accounting_category_1 = subFamily.accountingCategory.trim();
25
+ const accountingClass = lookup.classByQboId(subFamily.accountingCategory.trim());
26
+ if (accountingClass != null) {
27
+ detail.accounting_class = toClassBasePayload(accountingClass);
20
28
  }
21
29
  return Object.keys(detail).length > 0 ? detail : undefined;
22
30
  };
23
- const buildSubFamilyPayload = (localData, subFamily, clearEmptyValues) => {
31
+ const buildSubFamilyPayload = (localData, subFamily, clearEmptyValues, lookup) => {
24
32
  const isAddon = localData.catalogType === 'addon';
25
33
  const tiered = isTieredCatalogPricingModel(subFamily.pricingModel);
26
34
  const name = subFamily.name.trim();
@@ -74,7 +82,7 @@ const buildSubFamilyPayload = (localData, subFamily, clearEmptyValues) => {
74
82
  starting_unit: Number(tier.startingUnit) || 1,
75
83
  }));
76
84
  }
77
- const accountingDetail = buildAccountingDetail(subFamily);
85
+ const accountingDetail = buildAccountingDetail(subFamily, lookup);
78
86
  if (accountingDetail != null) {
79
87
  payload.accounting_detail = accountingDetail;
80
88
  }
@@ -86,7 +94,7 @@ const DEFAULT_CATALOG_ITEM_UNIT = 'USD';
86
94
  * Build the parent catalog-item request body from the form draft. Creates omit
87
95
  * both parent and sub-family IDs; edits retain returned sub-family IDs.
88
96
  */
89
- export const localDataToSaveInvoicingCatalogItemPayload = (localData, isCreate) => {
97
+ export const localDataToSaveInvoicingCatalogItemPayload = (localData, isCreate, lookup) => {
90
98
  const description = trimOrUndefined(localData.description);
91
99
  const productCode = trimOrUndefined(localData.productCode) ?? toProductCode(localData.name);
92
100
  const itemPayload = {
@@ -114,7 +122,7 @@ export const localDataToSaveInvoicingCatalogItemPayload = (localData, isCreate)
114
122
  ? localData.subFamilies
115
123
  : [blankInvoicingCatalogSubFamily()];
116
124
  itemPayload.sub_family_details = isCreate
117
- ? subFamilies.map((subFamily) => buildSubFamilyPayload(localData, subFamily, false))
125
+ ? subFamilies.map((subFamily) => buildSubFamilyPayload(localData, subFamily, false, lookup))
118
126
  : subFamilies.map((subFamily) => {
119
127
  const priceCode = trimOrUndefined(subFamily.priceCode) ?? toProductCode(subFamily.name);
120
128
  const detail = {
@@ -124,6 +132,14 @@ export const localDataToSaveInvoicingCatalogItemPayload = (localData, isCreate)
124
132
  price_code: priceCode,
125
133
  show_description_in_invoices: subFamily.showDescriptionInInvoices,
126
134
  };
135
+ // Edits send a narrower body than creates, but the accounting mapping
136
+ // is editable on this form, so leaving it out dropped the account and
137
+ // class the user had just picked. `null` rather than omitted when it is
138
+ // empty: an absent key on an update reads as "leave it alone", which
139
+ // would make a cleared mapping impossible to save — the same reason
140
+ // `description` and `invoice_notes` go through `trimOrNull`.
141
+ detail.accounting_detail =
142
+ buildAccountingDetail(subFamily, lookup) ?? null;
127
143
  const id = trimOrUndefined(subFamily.id);
128
144
  return id != null ? { ...detail, id } : detail;
129
145
  });
@@ -6,6 +6,7 @@ import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiE
6
6
  import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
7
7
  import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
8
8
  import { addInvoicingCatalogItem } from '../invoicingCatalogListView/invoicingCatalogListViewReducer';
9
+ import { invoicingQboAccountByQboId, invoicingQboClassByQboId, } from '../invoicingQboView/invoicingQboViewSelector';
9
10
  import { localDataToSaveInvoicingCatalogItemPayload, } from './editInvoicingCatalogItemDetailViewPayload';
10
11
  import { saveInvoicingCatalogItem, submitInvoicingCatalogItemForm, submitInvoicingCatalogItemFormFailure, submitInvoicingCatalogItemFormSuccess, } from './editInvoicingCatalogItemDetailViewReducer';
11
12
  export const saveInvoicingCatalogItemEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter((action) => submitInvoicingCatalogItemForm.match(action) ||
@@ -20,7 +21,11 @@ export const saveInvoicingCatalogItemEpic = (actions$, state$, zeniAPI) => actio
20
21
  const viewState = state$.value.editInvoicingCatalogItemDetailViewState;
21
22
  const localData = (id != null ? viewState.editDraftById[id] : viewState.newItemDraft) ??
22
23
  viewState.newItemDraft;
23
- data = localDataToSaveInvoicingCatalogItemPayload(localData, id == null);
24
+ const state = state$.value;
25
+ data = localDataToSaveInvoicingCatalogItemPayload(localData, id == null, {
26
+ accountByQboId: (qboId) => invoicingQboAccountByQboId(state, qboId),
27
+ classByQboId: (qboId) => invoicingQboClassByQboId(state, qboId),
28
+ });
24
29
  }
25
30
  const baseUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0`;
26
31
  const path = id != null
@@ -37,6 +37,46 @@ export const INVOICING_INCOME_ACCOUNT_TYPES = [
37
37
  'income',
38
38
  'other_income',
39
39
  ];
40
+ /**
41
+ * The account behind a picker value. The pickers submit the QuickBooks id, so
42
+ * that is what callers hold; the payload builders need the whole entity to send
43
+ * `toAccountPayload`, which is why the lookup lives here next to the options.
44
+ */
45
+ export const invoicingQboAccountByQboId = (state, qboId) => {
46
+ if (qboId == null || qboId === '') {
47
+ return undefined;
48
+ }
49
+ // `getAccountList` throws when a listed id is missing from the entity slice.
50
+ // That is survivable when looking one account up — the caller falls back to
51
+ // the id it already had — but not when it happens inside an epic, where the
52
+ // throw would take the whole request with it.
53
+ try {
54
+ const { accounts } = getAccountList(state.accountState, state.accountListState, INVOICING_ACCOUNT_LIST_KEY);
55
+ // Matched on any id the account is known by. The pickers submit `qboId`,
56
+ // but a mapping that arrived from the service can name the account by its
57
+ // platform id instead, and an account we cannot resolve is a mapping that
58
+ // cannot be sent as a record.
59
+ return accounts.find((account) => account.qboId === qboId ||
60
+ account.accountId === qboId ||
61
+ account.coaId === qboId);
62
+ }
63
+ catch {
64
+ return undefined;
65
+ }
66
+ };
67
+ /** The class behind a picker value, for the same reason as the account above. */
68
+ export const invoicingQboClassByQboId = (state, qboId) => {
69
+ if (qboId == null || qboId === '') {
70
+ return undefined;
71
+ }
72
+ try {
73
+ const { classes } = getClassList(state.classState, state.classListState);
74
+ return classes.find((accountingClass) => accountingClass.qboId === qboId);
75
+ }
76
+ catch {
77
+ return undefined;
78
+ }
79
+ };
40
80
  /**
41
81
  * Accounts and classes as the invoicing pickers need them. Both come from the
42
82
  * shared accounting-service lists, so whatever another screen already loaded
@@ -0,0 +1,27 @@
1
+ import { of } from 'rxjs';
2
+ import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
+ import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
4
+ import { connectInvoicingStripe, fetchInvoicingSettings, invoicingStripeConnectionFailure, updateInvoicingStripeConnection, } from '../settingsViewReducer';
5
+ export const connectInvoicingStripeEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(connectInvoicingStripe.match), mergeMap((action) => {
6
+ const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/settings/stripe-connect/setup`;
7
+ const { api_key, publishable_key, webhook_secret } = action.payload;
8
+ const body = {
9
+ api_key,
10
+ publishable_key,
11
+ ...(webhook_secret != null && webhook_secret !== ''
12
+ ? { webhook_secret }
13
+ : {}),
14
+ };
15
+ return zeniAPI
16
+ .postAndGetJSON(apiUrl, body)
17
+ .pipe(switchMap((response) => {
18
+ if (isSuccessResponse(response) && response.data != null) {
19
+ return of(updateInvoicingStripeConnection({
20
+ stripe_connected: response.data.stripe_connected,
21
+ publishable_key: response.data.publishable_key,
22
+ }), fetchInvoicingSettings());
23
+ }
24
+ return of(invoicingStripeConnectionFailure(response.status));
25
+ }), catchError((error) => of(invoicingStripeConnectionFailure(createZeniAPIStatus('Unexpected Error', 'Stripe connect setup REST API call errored out ' +
26
+ JSON.stringify(error))))));
27
+ }));
@@ -0,0 +1,19 @@
1
+ import { of } from 'rxjs';
2
+ import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
+ import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
4
+ import { disconnectInvoicingStripe, fetchInvoicingSettings, invoicingStripeConnectionFailure, updateInvoicingStripeConnection, } from '../settingsViewReducer';
5
+ export const disconnectInvoicingStripeEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(disconnectInvoicingStripe.match), mergeMap(() => {
6
+ const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/settings/stripe-connect/callback`;
7
+ return zeniAPI
8
+ .deleteAndGetJSON(apiUrl)
9
+ .pipe(switchMap((response) => {
10
+ if (isSuccessResponse(response)) {
11
+ return of(updateInvoicingStripeConnection({
12
+ stripe_connected: false,
13
+ publishable_key: '',
14
+ }), fetchInvoicingSettings());
15
+ }
16
+ return of(invoicingStripeConnectionFailure(response.status));
17
+ }), catchError((error) => of(invoicingStripeConnectionFailure(createZeniAPIStatus('Unexpected Error', 'Stripe disconnect REST API call errored out ' +
18
+ JSON.stringify(error))))));
19
+ }));
@@ -0,0 +1,19 @@
1
+ import { of } from 'rxjs';
2
+ import { catchError, filter, switchMap } from 'rxjs/operators';
3
+ import { updateAddresses } from '../../../../entity/address/addressReducer';
4
+ import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
5
+ import { fetchInvoicingSettings, updateInvoicingSettings, updateInvoicingSettingsFailure, } from '../settingsViewReducer';
6
+ export const fetchInvoicingSettingsEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchInvoicingSettings.match), switchMap(() => {
7
+ const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/settings`;
8
+ return zeniAPI.getJSON(apiUrl).pipe(switchMap((response) => {
9
+ if (isSuccessResponse(response) && response.data != null) {
10
+ const address = response.data.address;
11
+ return [
12
+ ...(address != null ? [updateAddresses([address])] : []),
13
+ updateInvoicingSettings(response.data),
14
+ ];
15
+ }
16
+ return of(updateInvoicingSettingsFailure(response.status));
17
+ }), catchError((error) => of(updateInvoicingSettingsFailure(createZeniAPIStatus('Unexpected Error', 'Fetch settings REST API call errored out ' +
18
+ JSON.stringify(error))))));
19
+ }));
@@ -0,0 +1,39 @@
1
+ import { from } from 'rxjs';
2
+ import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
+ import { apiErrorSnackbarVariables } from '../../../../entity/snackbar/snackbarApiError';
4
+ import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
5
+ import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
6
+ import { saveInvoicingSettings, saveInvoicingSettingsFailure, saveInvoicingSettingsSuccess, } from '../settingsViewReducer';
7
+ export const saveInvoicingSettingsEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(saveInvoicingSettings.match), mergeMap((action) => {
8
+ const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/settings`;
9
+ const { settings, snackbarSection } = action.payload;
10
+ /** `status` surfaces the platform's error text; omit it to keep the generic copy. */
11
+ const snackbar = (messageText, status) => snackbarSection == null
12
+ ? []
13
+ : [
14
+ openSnackbar({
15
+ messageSection: snackbarSection,
16
+ messageText,
17
+ type: messageText === 'success' ? 'success' : 'error',
18
+ variables: apiErrorSnackbarVariables(status),
19
+ }),
20
+ ];
21
+ return zeniAPI
22
+ .putAndGetJSON(apiUrl, { ...settings })
23
+ .pipe(switchMap((response) => {
24
+ if (isSuccessResponse(response) && response.data != null) {
25
+ return from([
26
+ saveInvoicingSettingsSuccess(response.data),
27
+ ...snackbar('success'),
28
+ ]);
29
+ }
30
+ return from([
31
+ saveInvoicingSettingsFailure(response.status),
32
+ ...snackbar('failed', response.status),
33
+ ]);
34
+ }), catchError((error) => from([
35
+ saveInvoicingSettingsFailure(createZeniAPIStatus('Unexpected Error', 'Save settings REST API call errored out ' +
36
+ JSON.stringify(error))),
37
+ ...snackbar('failed'),
38
+ ])));
39
+ }));
@@ -0,0 +1,25 @@
1
+ import { EMPTY, of } from 'rxjs';
2
+ import { filter, mergeMap, withLatestFrom } from 'rxjs/operators';
3
+ import { invoicingQboAccountByQboId } from '../../invoicingQboView/invoicingQboViewSelector';
4
+ import { invoicingQboSettingsLocalDataToPayload } from '../invoicingQboSettingsFormConfig';
5
+ import { saveInvoicingSettings, submitInvoicingQboSettings, } from '../settingsViewReducer';
6
+ /**
7
+ * Saves the QBO local data. The screen fires this without a payload — the local
8
+ * data is already in the store, so sending it back through the action would make
9
+ * the page the carrier for state it does not own. Converting to the request body
10
+ * happens here too, keeping the wire shape out of the screen.
11
+ */
12
+ export const submitInvoicingQboSettingsEpic = (actions$, state$) => actions$.pipe(filter(submitInvoicingQboSettings.match), withLatestFrom(state$), mergeMap(([, state]) => {
13
+ const { qboDraft } = state.invoicingSettingsViewState;
14
+ // No local data means the settings response has not landed, so there is
15
+ // nothing the user could have edited yet.
16
+ if (qboDraft == null) {
17
+ return EMPTY;
18
+ }
19
+ return of(saveInvoicingSettings({
20
+ settings: {
21
+ qbo: invoicingQboSettingsLocalDataToPayload(qboDraft, (accountId) => invoicingQboAccountByQboId(state, accountId)),
22
+ },
23
+ snackbarSection: 'invoicing_qbo_settings_save',
24
+ }));
25
+ }));
@@ -0,0 +1,48 @@
1
+ import { getInvoicingQboAccountMappingId, toInvoicingQboAccountMappingSlot, toInvoicingQboAccountMappingsPayload, } from '../../../entity/invoicing/invoicingCommonPayload';
2
+ export const initialInvoicingQboSettingsLocalData = {
3
+ accountIdByMappingCode: {},
4
+ autoCreateItems: false,
5
+ closedPeriodVoidAsCreditMemo: false,
6
+ customerMatching: '',
7
+ isSyncEnabled: false,
8
+ syncStartDate: '',
9
+ trackingCategoriesEnabled: false,
10
+ };
11
+ /** The settings response as the page edits it. */
12
+ export const entityToInvoicingQboSettingsLocalData = (qbo) => {
13
+ const accountMappings = qbo?.account_mappings ?? {};
14
+ const accountIdByMappingCode = {};
15
+ // Keyed by the slot the payload used, which is the config code the page will
16
+ // ask for — the reader tolerates the older `_id` form on the way in.
17
+ Object.keys(accountMappings).forEach((key) => {
18
+ const accountId = getInvoicingQboAccountMappingId(accountMappings, key);
19
+ if (accountId != null) {
20
+ accountIdByMappingCode[toInvoicingQboAccountMappingSlot(key)] = accountId;
21
+ }
22
+ });
23
+ return {
24
+ accountIdByMappingCode,
25
+ autoCreateItems: qbo?.auto_create_items === true,
26
+ closedPeriodVoidAsCreditMemo: qbo?.closed_period_void_as_credit_memo === true,
27
+ customerMatching: qbo?.customer_matching ?? '',
28
+ isSyncEnabled: qbo?.enabled === true,
29
+ syncStartDate: qbo?.sync_start_date ?? '',
30
+ trackingCategoriesEnabled: qbo?.tracking_categories_enabled === true,
31
+ };
32
+ };
33
+ /**
34
+ * The local data as the request body. `accountForId` turns each picked id into
35
+ * the whole account the service stores; it comes from the epic, which is where
36
+ * the account list is in reach.
37
+ */
38
+ export const invoicingQboSettingsLocalDataToPayload = (localData, accountForId) => {
39
+ return {
40
+ account_mappings: toInvoicingQboAccountMappingsPayload(localData.accountIdByMappingCode, accountForId),
41
+ auto_create_items: localData.autoCreateItems,
42
+ closed_period_void_as_credit_memo: localData.closedPeriodVoidAsCreditMemo,
43
+ customer_matching: localData.customerMatching,
44
+ enabled: localData.isSyncEnabled,
45
+ sync_start_date: localData.syncStartDate !== '' ? localData.syncStartDate : null,
46
+ tracking_categories_enabled: localData.trackingCategoriesEnabled,
47
+ };
48
+ };
@@ -1,11 +1,16 @@
1
1
  import { createSlice } from '@reduxjs/toolkit';
2
2
  import { clearAll } from '../../../rootActions';
3
+ import { entityToInvoicingQboSettingsLocalData, } from './invoicingQboSettingsFormConfig';
3
4
  export const initialState = {
4
5
  fetchState: 'Not-Started',
5
6
  error: undefined,
6
7
  saveFetchState: { fetchState: 'Not-Started', error: undefined },
7
8
  stripeFetchState: { fetchState: 'Not-Started', error: undefined },
8
9
  settings: undefined,
10
+ // Listed so `clearAll` resets them: an in-progress draft left behind by a
11
+ // tenant switch would be shown and saved against the tenant switched to.
12
+ brandingDraft: undefined,
13
+ qboDraft: undefined,
9
14
  hasValidState() {
10
15
  return this.fetchState === 'Completed' && this.settings != null;
11
16
  },
@@ -22,6 +27,11 @@ const settingsView = createSlice({
22
27
  draft.settings = action.payload;
23
28
  draft.fetchState = 'Completed';
24
29
  draft.error = undefined;
30
+ // Seed the QBO page's local data from the response. An edit already in
31
+ // progress wins: a refetch behind the user must not discard it.
32
+ if (draft.qboDraft == null) {
33
+ draft.qboDraft = entityToInvoicingQboSettingsLocalData(action.payload.qbo);
34
+ }
25
35
  },
26
36
  updateInvoicingSettingsFailure(draft, action) {
27
37
  draft.fetchState = 'Error';
@@ -36,6 +46,9 @@ const settingsView = createSlice({
36
46
  saveInvoicingSettingsSuccess(draft, action) {
37
47
  draft.settings = action.payload;
38
48
  draft.saveFetchState = { fetchState: 'Completed', error: undefined };
49
+ // Re-seeded from what was just persisted, so the page keeps working off
50
+ // local data while showing the server's version of it.
51
+ draft.qboDraft = entityToInvoicingQboSettingsLocalData(action.payload.qbo);
39
52
  },
40
53
  saveInvoicingSettingsFailure(draft, action) {
41
54
  draft.saveFetchState = { fetchState: 'Error', error: action.payload };
@@ -54,6 +67,17 @@ const settingsView = createSlice({
54
67
  resetInvoicingBrandingFormDraft(draft) {
55
68
  draft.brandingDraft = undefined;
56
69
  },
70
+ updateInvoicingQboSettingsDraft(draft, action) {
71
+ draft.qboDraft = action.payload.localData;
72
+ },
73
+ // Trigger only — handled by submitInvoicingQboSettingsEpic, which reads the
74
+ // local data and converts it to the request body.
75
+ submitInvoicingQboSettings(draft) {
76
+ draft.saveFetchState = { fetchState: 'In-Progress', error: undefined };
77
+ },
78
+ resetInvoicingQboSettingsDraft(draft) {
79
+ draft.qboDraft = undefined;
80
+ },
57
81
  connectInvoicingStripe: {
58
82
  reducer(draft) {
59
83
  draft.stripeFetchState = { fetchState: 'In-Progress', error: undefined };
@@ -85,5 +109,5 @@ const settingsView = createSlice({
85
109
  });
86
110
  },
87
111
  });
88
- export const { fetchInvoicingSettings, updateInvoicingSettings, updateInvoicingSettingsFailure, saveInvoicingSettings, saveInvoicingSettingsSuccess, saveInvoicingSettingsFailure, resetSaveInvoicingSettings, updateInvoicingBrandingFormDraft, submitInvoicingBrandingForm, resetInvoicingBrandingFormDraft, connectInvoicingStripe, disconnectInvoicingStripe, updateInvoicingStripeConnection, invoicingStripeConnectionFailure, } = settingsView.actions;
112
+ export const { fetchInvoicingSettings, updateInvoicingSettings, updateInvoicingSettingsFailure, saveInvoicingSettings, saveInvoicingSettingsSuccess, saveInvoicingSettingsFailure, resetSaveInvoicingSettings, updateInvoicingBrandingFormDraft, submitInvoicingBrandingForm, resetInvoicingBrandingFormDraft, updateInvoicingQboSettingsDraft, submitInvoicingQboSettings, resetInvoicingQboSettingsDraft, connectInvoicingStripe, disconnectInvoicingStripe, updateInvoicingStripeConnection, invoicingStripeConnectionFailure, } = settingsView.actions;
89
113
  export default settingsView.reducer;
@@ -1,7 +1,9 @@
1
1
  import { getInvoicingQboAccountMappingId, } from '../../../entity/invoicing/invoicingCommonPayload';
2
2
  import { getFormattedAddress } from '../../addressView/addressViewSelector';
3
3
  import { getInvoicingConfigView } from '../invoicingConfigView/invoicingConfigViewSelector';
4
+ import { getInvoicingQboView, isInvoicingAccountingPickerDataLoading, } from '../invoicingQboView/invoicingQboViewSelector';
4
5
  import { INVOICING_BUSINESS_ADDRESS_TYPE, entityToInvoicingBrandingFormLocalData, } from './invoicingBrandingFormConfig';
6
+ import { initialInvoicingQboSettingsLocalData, } from './invoicingQboSettingsFormConfig';
5
7
  /**
6
8
  * Shared primitive: the raw invoicing settings. Consumed internally by other
7
9
  * CES selectors (create-invoice, customer-detail, overview) and by non-settings
@@ -92,6 +94,45 @@ export const getInvoicingSettingsHubView = (state) => {
92
94
  status,
93
95
  };
94
96
  };
97
+ /**
98
+ * Bundled view for the QBO configuration page: the draft it edits, the accounts
99
+ * and option lists its dropdowns render, the connection and sync health it
100
+ * reports, and the fetch states that decide between skeleton, form and error.
101
+ *
102
+ * `localData` is the draft once the user has edited, and the settings response
103
+ * before that — so the page has one field to read and CES owns when it becomes
104
+ * available, rather than the page copying settings into local state.
105
+ */
106
+ export const getInvoicingQboSettingsFormView = (state) => {
107
+ const { error, fetchState, qboDraft, saveFetchState, settings } = state.invoicingSettingsViewState;
108
+ const { fetchState: configFetchState, qboAccountMappingKindOptions, qboCustomerMatchingOptions, qboSyncStatusOptions, } = getInvoicingConfigView(state);
109
+ const qboView = getInvoicingQboView(state);
110
+ return {
111
+ accountMappingKindOptions: qboAccountMappingKindOptions,
112
+ accounts: qboView.accounts,
113
+ backfillState: qboView.backfillState,
114
+ connection: qboView.connection,
115
+ configFetchState,
116
+ customerMatchingOptions: qboCustomerMatchingOptions,
117
+ // The dropdowns cannot be built before both the settings and the accounts
118
+ // land, so the page waits on either rather than rendering empty pickers.
119
+ isLoading: fetchState === 'Not-Started' ||
120
+ fetchState === 'In-Progress' ||
121
+ isInvoicingAccountingPickerDataLoading(state),
122
+ isSyncEnabledSaved: settings?.qbo?.enabled === true,
123
+ // Seeded in the reducer when the settings response lands, so this is a plain
124
+ // read rather than a second place that decides what the page starts from.
125
+ localData: qboDraft ?? initialInvoicingQboSettingsLocalData,
126
+ nestedAccountHierarchy: qboView.nestedAccountHierarchy,
127
+ retryFetchStateByTaskId: qboView.retryFetchStateByTaskId,
128
+ saveState: saveFetchState.fetchState === 'Not-Started' && error != null
129
+ ? { fetchState, error }
130
+ : saveFetchState,
131
+ settingsFetchState: fetchState,
132
+ syncHealth: qboView.syncHealth,
133
+ syncStatusOptions: qboSyncStatusOptions,
134
+ };
135
+ };
95
136
  /**
96
137
  * Bundled view for the branding settings form: the draft `localData` (seeded
97
138
  * from the existing settings until the user edits), the business-address label
package/lib/index.d.ts CHANGED
@@ -321,12 +321,12 @@ import { getExpenseAutomationTransactionView, getLastTransferEntryReplacement }
321
321
  import { applyTransactionFilters } from './view/expenseAutomationView/transactionFilterHelpers';
322
322
  import { TRANSACTION_FILTER_CATEGORIES, TransactionFilterAmountMatchingOperator, TransactionFilterCategory, TransactionFilterCategoryDropdownOption, TransactionFilterCategoryField, TransactionFilterEntityType, TransactionFilters } from './view/expenseAutomationView/transactionFilterTypes';
323
323
  import { CompletedSubTab, DEFAULT_COMPLETED_SUB_TAB, toCompletedSubTab } from './view/expenseAutomationView/types/completedSubTab';
324
- import { DEFAULT_TRANSACTION_GROUP, TRANSACTION_GROUPS, TransactionGroup, toTransactionGroup } from './view/expenseAutomationView/types/transactionGroup';
325
324
  import { FluxAnalysisActionType, FluxAnalysisReviewStatus, FluxAnalysisSectionType, FluxAnalysisSortKey, FluxAnalysisViewUIState, FluxBalancesByMonth } from './view/expenseAutomationView/types/fluxAnalysisViewState';
326
325
  import { AccountSettingsLocalData, JEScheduleMainTab as ExpenseAutomationJEScheduleMainTab, JEScheduleSortKey as ExpenseAutomationJEScheduleSortKey, JESchedulesViewUIState as ExpenseAutomationJESchedulesViewUIState, JEScheduleLocalData, toJEScheduleMainTab as toExpenseAutomationJEScheduleMainTab, toJEScheduleSortKey as toExpenseAutomationJEScheduleSortKey } from './view/expenseAutomationView/types/jeSchedulesViewState';
327
326
  import { BATCH_FILE_STATUSES, BatchDetails, BatchFile, BatchFileStatus, BatchListItem, BatchStatus, BatchStatusValue, BatchSummary, BulkUploadPhase, BulkUploadResultsTab, BulkUploadSortKey, BulkUploadState, CandidateRef, MatchCandidate, MatchSource, MissingReceiptsTab, isUnmatchedTabFileStatus, toBatchFileStatus, toBatchStatusValue, toBulkUploadPhase, toBulkUploadResultsTab, toBulkUploadSortKey, toMatchSource, toMissingReceiptsTab } from './view/expenseAutomationView/types/missingReceiptsViewState';
328
327
  import { MissingReceiptsSortKey as ExpenseAutomationMissingReceiptsSortKey, MissingReceiptsViewState as ExpenseAutomationMissingReceiptsViewState, MissingReceiptsViewUIState as ExpenseAutomationMissingReceiptsViewUIState, toMissingReceiptsSortKey as toExpenseAutomationMissingReceiptsSortKey } from './view/expenseAutomationView/types/missingReceiptsViewState';
329
328
  import { AccountReconciliationLocalData, CombinedStatementAccount, ExcludeAccountFromReconciliationPayload, ReconciliationViewTabType as ExpenseAutomationReconciliationViewTab, ParsedStatementData, ReconReconcileSortKey, ReconReviewSortKey, ReconciliationReconcileTabLocalData, ReconciliationReviewTabLocalData, SaveReconcileDetailActionPayload as SaveExpenseAutomationReconciliationActionType, StatementAccountScope, StatementDateConflict, StatementMeta, StatementTransaction, StatementTransactionForUpdate, StatementTransactionsUpdate, StatementUpdateLocalData, toReconciliationTabsType } from './view/expenseAutomationView/types/reconciliationViewState';
329
+ import { DEFAULT_TRANSACTION_GROUP, TRANSACTION_GROUPS, TransactionGroup, toTransactionGroup } from './view/expenseAutomationView/types/transactionGroup';
330
330
  import { TransactionsTab as ExpenseAutomationTransactionsTab, TransactionsViewState as ExpenseAutomationTransactionsViewState, TransactionsViewUIState as ExpenseAutomationTransactionsViewUIState, SupportedTransactionCategorization, TransactionCategorizationLineItemData, TransactionReviewLocalData, TransactionsSortKey, TransactionsTab, toTransactionsTabKey as toExpenseAutomationTransactionsTabKey, toTransactionsSortKey } from './view/expenseAutomationView/types/transactionsViewState';
331
331
  import { fetchRegisteredInterests, notifyMeForFeature } from './view/featureNotificationView/featureNotificationViewReducer';
332
332
  import { getFeatureNotificationView, getRegisteredInterests, getRegisteredInterestsByFeature, isFeatureInterestRegistered } from './view/featureNotificationView/featureNotificationViewSelector';
@@ -1236,10 +1236,12 @@ export { getInvoicingCatalogItemDetail } from './view/invoicing/invoicingCatalog
1236
1236
  export { getInvoicingPlanById } from './entity/invoicing/plan/planSelector';
1237
1237
  export { getInvoicingProductById } from './entity/invoicing/product/productSelector';
1238
1238
  export type { InvoicingCatalogItemDetailView } from './view/invoicing/invoicingCatalogItemDetailView/invoicingCatalogItemDetailViewSelector';
1239
- export { connectInvoicingStripe, disconnectInvoicingStripe, fetchInvoicingSettings, resetInvoicingBrandingFormDraft, resetSaveInvoicingSettings, saveInvoicingSettings, submitInvoicingBrandingForm, updateInvoicingBrandingFormDraft, updateInvoicingSettings, } from './view/invoicing/settingsView/settingsViewReducer';
1240
- export { getInvoicingBrandingFormView, getInvoicingSettings, getInvoicingSettingsFetchState, getInvoicingSettingsHubView, getInvoicingSettingsView, } from './view/invoicing/settingsView/settingsViewSelector';
1241
- export type { InvoicingBrandingFormView, InvoicingSettingsHubView, InvoicingSettingsView, } from './view/invoicing/settingsView/settingsViewSelector';
1239
+ export { connectInvoicingStripe, disconnectInvoicingStripe, fetchInvoicingSettings, resetInvoicingBrandingFormDraft, resetInvoicingQboSettingsDraft, resetSaveInvoicingSettings, saveInvoicingSettings, submitInvoicingBrandingForm, submitInvoicingQboSettings, updateInvoicingBrandingFormDraft, updateInvoicingQboSettingsDraft, updateInvoicingSettings, } from './view/invoicing/settingsView/settingsViewReducer';
1240
+ export { getInvoicingBrandingFormView, getInvoicingQboSettingsFormView, getInvoicingSettings, getInvoicingSettingsFetchState, getInvoicingSettingsHubView, getInvoicingSettingsView, } from './view/invoicing/settingsView/settingsViewSelector';
1241
+ export type { InvoicingBrandingFormView, InvoicingQboSettingsFormView, InvoicingSettingsHubView, InvoicingSettingsView, } from './view/invoicing/settingsView/settingsViewSelector';
1242
1242
  export type { InvoicingBrandingFormLocalData } from './view/invoicing/settingsView/invoicingBrandingFormConfig';
1243
+ export type { InvoicingQboSettingsLocalData } from './view/invoicing/settingsView/invoicingQboSettingsFormConfig';
1244
+ export { entityToInvoicingQboSettingsLocalData, initialInvoicingQboSettingsLocalData, invoicingQboSettingsLocalDataToPayload, } from './view/invoicing/settingsView/invoicingQboSettingsFormConfig';
1243
1245
  export { INVOICING_BUSINESS_ADDRESS_TYPE, entityToInvoicingBrandingFormLocalData, initialInvoicingBrandingFormLocalData, } from './view/invoicing/settingsView/invoicingBrandingFormConfig';
1244
1246
  export { isStripeConnectedFromSettings } from './view/invoicing/settingsView/invoicingSettingsHelpers';
1245
1247
  export { fetchInvoicingAccountingPickers, fetchInvoicingQboConnection, fetchInvoicingQboSyncHealth, retryInvoicingQboSyncTask, startInvoicingQboBackfill, } from './view/invoicing/invoicingQboView/invoicingQboViewReducer';
@@ -1248,7 +1250,7 @@ export type { InvoicingAccountingOption } from './view/invoicing/invoicingQboVie
1248
1250
  export { getInvoicingQboView } from './view/invoicing/invoicingQboView/invoicingQboViewSelector';
1249
1251
  export type { InvoicingQboView } from './view/invoicing/invoicingQboView/invoicingQboViewSelector';
1250
1252
  export type { InvoicingQboConnection, InvoicingQboFailedTask, InvoicingQboSyncHealth, } from './view/invoicing/invoicingQboView/invoicingQboViewPayload';
1251
- export { INVOICING_QBO_CUSTOMER_MATCHING_MODES, getInvoicingQboAccountMappingId, toInvoicingNamedOptionsByKey, toInvoicingQboCustomerMatchingMode, withInvoicingQboAccountMappingId, } from './entity/invoicing/invoicingCommonPayload';
1253
+ export { INVOICING_QBO_CUSTOMER_MATCHING_MODES, getInvoicingQboAccountMappingId, toInvoicingNamedOptionsByKey, toInvoicingQboCustomerMatchingMode, } from './entity/invoicing/invoicingCommonPayload';
1252
1254
  export type { InvoicingNamedOption, InvoicingFieldHelp, InvoicingFieldHelpByCode, InvoicingFieldHelpGroup, InvoicingNamedOptionsByKey, InvoicingQboAccountMappingKindOption, InvoicingQboAccountMappingsPayload, InvoicingQboCustomerMatchingMode, InvoicingQboSettingsPayload, } from './entity/invoicing/invoicingCommonPayload';
1253
1255
  export { acceptInvoicingTerms, enableInvoicing, fetchInvoicingConfig, resetInvoicingConfigActionStates, updateInvoicingConfig, } from './view/invoicing/invoicingConfigView/invoicingConfigViewReducer';
1254
1256
  export { getInvoicingConfigView } from './view/invoicing/invoicingConfigView/invoicingConfigViewSelector';
package/lib/index.js CHANGED
@@ -83,7 +83,7 @@ exports.runInvoicingCatalogPlanAction = exports.getInvoicingInvoiceActionState =
83
83
  exports.getInvoicingSetupIntentFetchState = exports.getInvoicingSetupIntent = exports.getInvoicingSentPaymentLinkMagicUrl = exports.getInvoicingSentPaymentLinkEmail = exports.getInvoicingPlaidLinkTokenFetchState = exports.getInvoicingPlaidLinkToken = exports.getInvoicingPaymentMethodSaveState = exports.getInvoicingPaymentLinkSendState = exports.sendInvoicingPaymentLink = exports.saveInvoicingPaymentMethod = exports.resetInvoicingCustomerPaymentMethod = exports.fetchInvoicingPlaidLinkToken = exports.createInvoicingSetupIntent = exports.getInvoicingCustomerDetail = exports.fetchInvoicingCustomerDetailPage = exports.fetchInvoicingCustomerDetail = exports.getInvoicingCustomerListView = exports.INVOICING_CUSTOMER_COLUMNS = exports.ALL_INVOICING_CUSTOMER_TAB_IDS = exports.ALL_INVOICING_CUSTOMER_SORT_KEYS = exports.updateInvoicingCustomerFilters = exports.setInvoicingCustomerActiveTab = exports.fetchInvoicingCustomerListPage = exports.fetchInvoicingCustomerList = exports.fetchInvoicingCustomerCounts = exports.updateInvoicingCustomers = exports.removeInvoicingCustomer = exports.INVOICING_LIST_PAGE_SIZE = exports.getInvoicingDataImportActionState = exports.uploadInvoicingMigrationFiles = exports.startInvoicingMigration = exports.rollbackInvoicingMigration = exports.retryInvoicingMigration = exports.connectInvoicingChargebee = exports.getInvoicingDataImportView = exports.setInvoicingActiveMigrationSessionId = exports.fetchInvoicingMigrationSessions = exports.fetchInvoicingMigrationSession = exports.fetchInvoicingMigrationDiagnostics = exports.fetchInvoicingDataImportStatus = exports.getInvoicingPaymentActionState = exports.runInvoicingPaymentAction = exports.getInvoicingDunningActionState = exports.runInvoicingDunningAction = exports.getInvoicingSubscriptionActionState = exports.runInvoicingSubscriptionAction = exports.getInvoicingDiscountActionState = exports.runInvoicingDiscountAction = exports.getInvoicingCatalogActionState = exports.runInvoicingCatalogProductAction = void 0;
84
84
  exports.updateInvoicingCouponFilters = exports.setInvoicingCouponActiveTab = exports.fetchInvoicingCouponListPage = exports.fetchInvoicingCouponList = exports.fetchInvoicingCouponCounts = exports.updateInvoicingCoupons = exports.removeInvoicingCoupon = exports.getInvoicingCreditNoteDetail = exports.getInvoicingCreditNoteById = exports.fetchInvoicingCreditNoteDetail = exports.getInvoicingCreditNoteListView = exports.INVOICING_CREDIT_NOTE_COLUMNS = exports.ALL_INVOICING_CREDIT_NOTE_TAB_IDS = exports.ALL_INVOICING_CREDIT_NOTE_SORT_KEYS = exports.updateInvoicingCreditNoteFilters = exports.setInvoicingCreditNoteActiveTab = exports.fetchInvoicingCreditNoteListPage = exports.fetchInvoicingCreditNoteList = exports.fetchInvoicingCreditNoteCounts = exports.updateInvoicingCreditNotes = exports.removeInvoicingCreditNote = exports.getInvoicingTransactionDetail = exports.getInvoicingTransactionById = exports.fetchInvoicingTransactionDetail = exports.getInvoicingTransactionListView = exports.INVOICING_TRANSACTION_COLUMNS = exports.ALL_INVOICING_TRANSACTION_TAB_IDS = exports.ALL_INVOICING_TRANSACTION_SORT_KEYS = exports.updateInvoicingTransactionFilters = exports.setInvoicingTransactionActiveTab = exports.fetchInvoicingTransactionListPage = exports.fetchInvoicingTransactionList = exports.updateInvoicingTransactions = exports.removeInvoicingTransaction = exports.getInvoicingSubscriptionDetail = exports.getInvoicingSubscriptionById = exports.fetchInvoicingSubscriptionDetail = exports.getInvoicingSubscriptionListView = exports.INVOICING_SUBSCRIPTION_COLUMNS = exports.ALL_INVOICING_SUBSCRIPTION_TAB_IDS = exports.ALL_INVOICING_SUBSCRIPTION_SORT_KEYS = exports.updateInvoicingSubscriptionFilters = exports.setInvoicingSubscriptionActiveTab = exports.fetchInvoicingSubscriptionListPage = exports.fetchInvoicingSubscriptionList = exports.fetchInvoicingSubscriptionCounts = exports.updateInvoicingSubscriptions = exports.removeInvoicingSubscription = exports.resetPromotionalCreditAction = exports.submitAddPromotionalCredits = void 0;
85
85
  exports.connectInvoicingStripe = exports.getInvoicingProductById = exports.getInvoicingPlanById = exports.getInvoicingCatalogItemDetail = exports.fetchInvoicingCatalogItemDetail = exports.getInvoicingCatalogListView = exports.INVOICING_PRODUCT_COLUMNS = exports.ALL_INVOICING_PRODUCT_SORT_KEYS = exports.updateInvoicingCatalogProductFilters = exports.updateInvoicingCatalogPlanFilters = exports.fetchInvoicingCatalogProductList = exports.fetchInvoicingCatalogPlanList = exports.fetchInvoicingCatalogListPage = exports.fetchInvoicingCatalogCounts = exports.updateInvoicingProducts = exports.removeInvoicingProduct = exports.updateInvoicingPlans = exports.removeInvoicingPlan = exports.mergeInvoicingPlanSubFamily = exports.getInvoicingDunningEmailPreview = exports.getInvoicingDunningCaseDetail = exports.getInvoicingDunningCaseById = exports.invalidateInvoicingDunningEmailPreview = exports.fetchInvoicingDunningEmailPreviewPage = exports.fetchInvoicingDunningCaseDetail = exports.clearInvoicingDunningCaseDetailView = exports.getInvoicingDunningCaseListView = exports.INVOICING_DUNNING_CASE_COLUMNS = exports.ALL_INVOICING_DUNNING_CASE_TAB_IDS = exports.ALL_INVOICING_DUNNING_CASE_SORT_KEYS = exports.updateInvoicingDunningCaseFilters = exports.setInvoicingDunningCaseActiveTab = exports.fetchInvoicingDunningCaseListPage = exports.fetchInvoicingDunningCaseList = exports.fetchInvoicingDunningCaseCounts = exports.updateInvoicingDunningCases = exports.removeInvoicingDunningCase = exports.getInvoicingCouponDetail = exports.fetchInvoicingCouponDetail = exports.getInvoicingCouponById = exports.getInvoicingCouponListView = exports.getInvoicingCouponListNextCursor = exports.getInvoicingCouponListItems = exports.getInvoicingCouponListHasMore = exports.getInvoicingCouponListFilters = exports.getInvoicingCouponListFetchState = exports.getInvoicingCouponCounts = exports.INVOICING_COUPON_COLUMNS = exports.ALL_INVOICING_COUPON_TAB_IDS = exports.ALL_INVOICING_COUPON_SORT_KEYS = void 0;
86
- exports.getInvoicingOnboardingGateView = exports.getInvoicingConfigView = exports.updateInvoicingConfig = exports.resetInvoicingConfigActionStates = exports.fetchInvoicingConfig = exports.enableInvoicing = exports.acceptInvoicingTerms = exports.withInvoicingQboAccountMappingId = exports.toInvoicingQboCustomerMatchingMode = exports.toInvoicingNamedOptionsByKey = exports.getInvoicingQboAccountMappingId = exports.INVOICING_QBO_CUSTOMER_MATCHING_MODES = exports.getInvoicingQboView = exports.invoicingAccountsForQboAccountTypes = exports.INVOICING_INCOME_ACCOUNT_TYPES = exports.startInvoicingQboBackfill = exports.retryInvoicingQboSyncTask = exports.fetchInvoicingQboSyncHealth = exports.fetchInvoicingQboConnection = exports.fetchInvoicingAccountingPickers = exports.isStripeConnectedFromSettings = exports.initialInvoicingBrandingFormLocalData = exports.entityToInvoicingBrandingFormLocalData = exports.INVOICING_BUSINESS_ADDRESS_TYPE = exports.getInvoicingSettingsView = exports.getInvoicingSettingsHubView = exports.getInvoicingSettingsFetchState = exports.getInvoicingSettings = exports.getInvoicingBrandingFormView = exports.updateInvoicingSettings = exports.updateInvoicingBrandingFormDraft = exports.submitInvoicingBrandingForm = exports.saveInvoicingSettings = exports.resetSaveInvoicingSettings = exports.resetInvoicingBrandingFormDraft = exports.fetchInvoicingSettings = exports.disconnectInvoicingStripe = void 0;
86
+ exports.getInvoicingOnboardingGateView = exports.getInvoicingConfigView = exports.updateInvoicingConfig = exports.resetInvoicingConfigActionStates = exports.fetchInvoicingConfig = exports.enableInvoicing = exports.acceptInvoicingTerms = exports.toInvoicingQboCustomerMatchingMode = exports.toInvoicingNamedOptionsByKey = exports.getInvoicingQboAccountMappingId = exports.INVOICING_QBO_CUSTOMER_MATCHING_MODES = exports.getInvoicingQboView = exports.invoicingAccountsForQboAccountTypes = exports.INVOICING_INCOME_ACCOUNT_TYPES = exports.startInvoicingQboBackfill = exports.retryInvoicingQboSyncTask = exports.fetchInvoicingQboSyncHealth = exports.fetchInvoicingQboConnection = exports.fetchInvoicingAccountingPickers = exports.isStripeConnectedFromSettings = exports.initialInvoicingBrandingFormLocalData = exports.entityToInvoicingBrandingFormLocalData = exports.INVOICING_BUSINESS_ADDRESS_TYPE = exports.invoicingQboSettingsLocalDataToPayload = exports.initialInvoicingQboSettingsLocalData = exports.entityToInvoicingQboSettingsLocalData = exports.getInvoicingSettingsView = exports.getInvoicingSettingsHubView = exports.getInvoicingSettingsFetchState = exports.getInvoicingSettings = exports.getInvoicingQboSettingsFormView = exports.getInvoicingBrandingFormView = exports.updateInvoicingSettings = exports.updateInvoicingQboSettingsDraft = exports.updateInvoicingBrandingFormDraft = exports.submitInvoicingQboSettings = exports.submitInvoicingBrandingForm = exports.saveInvoicingSettings = exports.resetSaveInvoicingSettings = exports.resetInvoicingQboSettingsDraft = exports.resetInvoicingBrandingFormDraft = exports.fetchInvoicingSettings = exports.disconnectInvoicingStripe = void 0;
87
87
  const allowedValue_1 = require("./commonStateTypes/allowedValue");
88
88
  Object.defineProperty(exports, "isAllowedValueWithCode", { enumerable: true, get: function () { return allowedValue_1.isAllowedValueWithCode; } });
89
89
  Object.defineProperty(exports, "isAllowedValueWithID", { enumerable: true, get: function () { return allowedValue_1.isAllowedValueWithID; } });
@@ -982,10 +982,6 @@ Object.defineProperty(exports, "TRANSACTION_FILTER_CATEGORIES", { enumerable: tr
982
982
  const completedSubTab_1 = require("./view/expenseAutomationView/types/completedSubTab");
983
983
  Object.defineProperty(exports, "DEFAULT_COMPLETED_SUB_TAB", { enumerable: true, get: function () { return completedSubTab_1.DEFAULT_COMPLETED_SUB_TAB; } });
984
984
  Object.defineProperty(exports, "toCompletedSubTab", { enumerable: true, get: function () { return completedSubTab_1.toCompletedSubTab; } });
985
- const transactionGroup_1 = require("./view/expenseAutomationView/types/transactionGroup");
986
- Object.defineProperty(exports, "DEFAULT_TRANSACTION_GROUP", { enumerable: true, get: function () { return transactionGroup_1.DEFAULT_TRANSACTION_GROUP; } });
987
- Object.defineProperty(exports, "TRANSACTION_GROUPS", { enumerable: true, get: function () { return transactionGroup_1.TRANSACTION_GROUPS; } });
988
- Object.defineProperty(exports, "toTransactionGroup", { enumerable: true, get: function () { return transactionGroup_1.toTransactionGroup; } });
989
985
  const jeSchedulesViewState_1 = require("./view/expenseAutomationView/types/jeSchedulesViewState");
990
986
  Object.defineProperty(exports, "toExpenseAutomationJEScheduleMainTab", { enumerable: true, get: function () { return jeSchedulesViewState_1.toJEScheduleMainTab; } });
991
987
  Object.defineProperty(exports, "toExpenseAutomationJEScheduleSortKey", { enumerable: true, get: function () { return jeSchedulesViewState_1.toJEScheduleSortKey; } });
@@ -1003,6 +999,10 @@ const missingReceiptsViewState_2 = require("./view/expenseAutomationView/types/m
1003
999
  Object.defineProperty(exports, "toExpenseAutomationMissingReceiptsSortKey", { enumerable: true, get: function () { return missingReceiptsViewState_2.toMissingReceiptsSortKey; } });
1004
1000
  const reconciliationViewState_1 = require("./view/expenseAutomationView/types/reconciliationViewState");
1005
1001
  Object.defineProperty(exports, "toReconciliationTabsType", { enumerable: true, get: function () { return reconciliationViewState_1.toReconciliationTabsType; } });
1002
+ const transactionGroup_1 = require("./view/expenseAutomationView/types/transactionGroup");
1003
+ Object.defineProperty(exports, "DEFAULT_TRANSACTION_GROUP", { enumerable: true, get: function () { return transactionGroup_1.DEFAULT_TRANSACTION_GROUP; } });
1004
+ Object.defineProperty(exports, "TRANSACTION_GROUPS", { enumerable: true, get: function () { return transactionGroup_1.TRANSACTION_GROUPS; } });
1005
+ Object.defineProperty(exports, "toTransactionGroup", { enumerable: true, get: function () { return transactionGroup_1.toTransactionGroup; } });
1006
1006
  const transactionsViewState_1 = require("./view/expenseAutomationView/types/transactionsViewState");
1007
1007
  Object.defineProperty(exports, "toExpenseAutomationTransactionsTabKey", { enumerable: true, get: function () { return transactionsViewState_1.toTransactionsTabKey; } });
1008
1008
  Object.defineProperty(exports, "toTransactionsSortKey", { enumerable: true, get: function () { return transactionsViewState_1.toTransactionsSortKey; } });
@@ -3091,17 +3091,25 @@ Object.defineProperty(exports, "connectInvoicingStripe", { enumerable: true, get
3091
3091
  Object.defineProperty(exports, "disconnectInvoicingStripe", { enumerable: true, get: function () { return settingsViewReducer_2.disconnectInvoicingStripe; } });
3092
3092
  Object.defineProperty(exports, "fetchInvoicingSettings", { enumerable: true, get: function () { return settingsViewReducer_2.fetchInvoicingSettings; } });
3093
3093
  Object.defineProperty(exports, "resetInvoicingBrandingFormDraft", { enumerable: true, get: function () { return settingsViewReducer_2.resetInvoicingBrandingFormDraft; } });
3094
+ Object.defineProperty(exports, "resetInvoicingQboSettingsDraft", { enumerable: true, get: function () { return settingsViewReducer_2.resetInvoicingQboSettingsDraft; } });
3094
3095
  Object.defineProperty(exports, "resetSaveInvoicingSettings", { enumerable: true, get: function () { return settingsViewReducer_2.resetSaveInvoicingSettings; } });
3095
3096
  Object.defineProperty(exports, "saveInvoicingSettings", { enumerable: true, get: function () { return settingsViewReducer_2.saveInvoicingSettings; } });
3096
3097
  Object.defineProperty(exports, "submitInvoicingBrandingForm", { enumerable: true, get: function () { return settingsViewReducer_2.submitInvoicingBrandingForm; } });
3098
+ Object.defineProperty(exports, "submitInvoicingQboSettings", { enumerable: true, get: function () { return settingsViewReducer_2.submitInvoicingQboSettings; } });
3097
3099
  Object.defineProperty(exports, "updateInvoicingBrandingFormDraft", { enumerable: true, get: function () { return settingsViewReducer_2.updateInvoicingBrandingFormDraft; } });
3100
+ Object.defineProperty(exports, "updateInvoicingQboSettingsDraft", { enumerable: true, get: function () { return settingsViewReducer_2.updateInvoicingQboSettingsDraft; } });
3098
3101
  Object.defineProperty(exports, "updateInvoicingSettings", { enumerable: true, get: function () { return settingsViewReducer_2.updateInvoicingSettings; } });
3099
3102
  var settingsViewSelector_2 = require("./view/invoicing/settingsView/settingsViewSelector");
3100
3103
  Object.defineProperty(exports, "getInvoicingBrandingFormView", { enumerable: true, get: function () { return settingsViewSelector_2.getInvoicingBrandingFormView; } });
3104
+ Object.defineProperty(exports, "getInvoicingQboSettingsFormView", { enumerable: true, get: function () { return settingsViewSelector_2.getInvoicingQboSettingsFormView; } });
3101
3105
  Object.defineProperty(exports, "getInvoicingSettings", { enumerable: true, get: function () { return settingsViewSelector_2.getInvoicingSettings; } });
3102
3106
  Object.defineProperty(exports, "getInvoicingSettingsFetchState", { enumerable: true, get: function () { return settingsViewSelector_2.getInvoicingSettingsFetchState; } });
3103
3107
  Object.defineProperty(exports, "getInvoicingSettingsHubView", { enumerable: true, get: function () { return settingsViewSelector_2.getInvoicingSettingsHubView; } });
3104
3108
  Object.defineProperty(exports, "getInvoicingSettingsView", { enumerable: true, get: function () { return settingsViewSelector_2.getInvoicingSettingsView; } });
3109
+ var invoicingQboSettingsFormConfig_1 = require("./view/invoicing/settingsView/invoicingQboSettingsFormConfig");
3110
+ Object.defineProperty(exports, "entityToInvoicingQboSettingsLocalData", { enumerable: true, get: function () { return invoicingQboSettingsFormConfig_1.entityToInvoicingQboSettingsLocalData; } });
3111
+ Object.defineProperty(exports, "initialInvoicingQboSettingsLocalData", { enumerable: true, get: function () { return invoicingQboSettingsFormConfig_1.initialInvoicingQboSettingsLocalData; } });
3112
+ Object.defineProperty(exports, "invoicingQboSettingsLocalDataToPayload", { enumerable: true, get: function () { return invoicingQboSettingsFormConfig_1.invoicingQboSettingsLocalDataToPayload; } });
3105
3113
  var invoicingBrandingFormConfig_1 = require("./view/invoicing/settingsView/invoicingBrandingFormConfig");
3106
3114
  Object.defineProperty(exports, "INVOICING_BUSINESS_ADDRESS_TYPE", { enumerable: true, get: function () { return invoicingBrandingFormConfig_1.INVOICING_BUSINESS_ADDRESS_TYPE; } });
3107
3115
  Object.defineProperty(exports, "entityToInvoicingBrandingFormLocalData", { enumerable: true, get: function () { return invoicingBrandingFormConfig_1.entityToInvoicingBrandingFormLocalData; } });
@@ -3125,7 +3133,6 @@ Object.defineProperty(exports, "INVOICING_QBO_CUSTOMER_MATCHING_MODES", { enumer
3125
3133
  Object.defineProperty(exports, "getInvoicingQboAccountMappingId", { enumerable: true, get: function () { return invoicingCommonPayload_2.getInvoicingQboAccountMappingId; } });
3126
3134
  Object.defineProperty(exports, "toInvoicingNamedOptionsByKey", { enumerable: true, get: function () { return invoicingCommonPayload_2.toInvoicingNamedOptionsByKey; } });
3127
3135
  Object.defineProperty(exports, "toInvoicingQboCustomerMatchingMode", { enumerable: true, get: function () { return invoicingCommonPayload_2.toInvoicingQboCustomerMatchingMode; } });
3128
- Object.defineProperty(exports, "withInvoicingQboAccountMappingId", { enumerable: true, get: function () { return invoicingCommonPayload_2.withInvoicingQboAccountMappingId; } });
3129
3136
  // ── Invoicing: config ─────────────────────────────────────────────────────────
3130
3137
  var invoicingConfigViewReducer_1 = require("./view/invoicing/invoicingConfigView/invoicingConfigViewReducer");
3131
3138
  Object.defineProperty(exports, "acceptInvoicingTerms", { enumerable: true, get: function () { return invoicingConfigViewReducer_1.acceptInvoicingTerms; } });
@@ -232,7 +232,7 @@ export interface ReconciliationTabsState {
232
232
  uiState: ReconciliationReviewTabViewUiState;
233
233
  };
234
234
  }
235
- declare const toReconReviewSortKey: (v: string) => "date" | "amount" | "memo" | "payee" | "direction" | "match";
235
+ declare const toReconReviewSortKey: (v: string) => "date" | "amount" | "memo" | "match" | "payee" | "direction";
236
236
  export type ReconReviewSortKey = ReturnType<typeof toReconReviewSortKey>;
237
237
  export interface ReconciliationReviewTabViewUiState {
238
238
  scrollPosition: {