@zeniai/client-epic-state 5.2.10-betaAK4 → 5.2.10-betaAK5

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.
@@ -95,52 +95,37 @@ export declare function toInvoicingQboCustomerMatchingMode(value: string): Invoi
95
95
  * plus hydrated fields the client only displays.
96
96
  */
97
97
  /**
98
- * Account mappings arrive in two shapes. The original sends a bare id per slot
99
- * (`deposit_account_id`); the newer one sends the account itself
100
- * (`deposit_account`). Both are declared because a tenant can be served either
101
- * depending on when its backend rolled, and a page that understands only one
102
- * renders an empty picker over data that exists. `Partial` because a read may
103
- * carry only the ids, while a write sends a whole `AccountBasePayload`.
104
- */
105
- export interface InvoicingQboAccountMappingsPayload {
106
- default_income_account?: Partial<AccountBasePayload> | null;
107
- default_income_account_id?: string | null;
108
- deposit_account?: Partial<AccountBasePayload> | null;
109
- deposit_account_id?: string | null;
110
- discounts_account?: Partial<AccountBasePayload> | null;
111
- discounts_account_id?: string | null;
112
- rounding_account?: Partial<AccountBasePayload> | null;
113
- rounding_account_id?: string | null;
114
- write_off_account?: Partial<AccountBasePayload> | null;
115
- write_off_account_id?: string | null;
116
- }
117
- /**
118
- * The id for one mapping slot, from whichever shape the backend sent. The object
119
- * form wins when present; a blank id in either is treated as unset so the picker
120
- * shows its placeholder rather than selecting nothing.
98
+ * Account mappings as the service stores them: one entry per mapping kind the
99
+ * config API defines, keyed by that kind's code (`deposit_account`,
100
+ * `default_income_account`, …). The value is the whole account, or just its id
101
+ * for a tenant whose backend has not rolled — both are read, and a save writes
102
+ * the record.
103
+ *
104
+ * An index signature rather than a field list, because the config owns which
105
+ * kinds exist: enumerating them here means a kind it adds gets dropped.
121
106
  */
107
+ export type InvoicingQboAccountMappingsPayload = Record<string, Partial<AccountBasePayload> | string | null>;
122
108
  /**
123
109
  * The first id a picker can actually select. Callers list the QuickBooks id
124
- * first: the options are keyed on it, so an internal id selects nothing even
125
- * when it names the same record. Blank counts as absent, so a cleared field
126
- * falls through to the next candidate rather than selecting an empty option.
110
+ * first: the options are keyed on it, so a platform id selects nothing even when
111
+ * it names the same record. Blank counts as absent.
127
112
  */
128
113
  export declare const firstSelectableInvoicingId: (candidates: readonly (string | null | undefined)[]) => string | undefined;
129
114
  /**
130
- * Read one account-mapping dropdown value by its config `code` (the codes in
131
- * `qbo_account_mapping_kind_options` match the `account_mappings` field names).
115
+ * The slot a mapping code names. Codes arrive from the config API as the slot
116
+ * itself (`deposit_account`); a trailing `_id` is tolerated so a tenant sending
117
+ * the older `deposit_account_id` resolves to the same mapping.
132
118
  */
119
+ export declare const toInvoicingQboAccountMappingSlot: (code: string) => string;
120
+ /** Read one account-mapping dropdown value by its config `code`. */
133
121
  export declare function getInvoicingQboAccountMappingId(accountMappings: InvoicingQboAccountMappingsPayload | undefined, code: string): string | undefined;
134
122
  /**
135
- * Expand each mapping from the id the picker submitted to the whole account, the
136
- * shape the service stores: `deposit_account_id: '35'` becomes
137
- * `deposit_account: {qbo_id: '35', account_id, account_name, …}`. Written at the
138
- * request boundary rather than in the page's draft, so the UI keeps working in
139
- * ids. An id that resolves to no account keeps its bare form rather than being
140
- * dropped.
123
+ * Build the `account_mappings` request body from the ids the pickers submitted.
124
+ * Each kind is sent as the whole account, which is what the service stores; an
125
+ * id that resolves to no account is sent as it stands rather than dropped, so a
126
+ * save never silently unsets a mapping it could not expand.
141
127
  */
142
- export declare const withInvoicingQboMappedAccounts: (accountMappings: InvoicingQboAccountMappingsPayload | undefined, accountByQboId: (qboId: string) => AccountBase | undefined) => InvoicingQboAccountMappingsPayload | undefined;
143
- export declare function withInvoicingQboAccountMappingId(accountMappings: InvoicingQboAccountMappingsPayload | undefined, code: string, accountId: string): InvoicingQboAccountMappingsPayload;
128
+ export declare const toInvoicingQboAccountMappingsPayload: (accountIdByMappingCode: Record<string, string>, accountForId: (accountId: string) => AccountBase | undefined) => InvoicingQboAccountMappingsPayload;
144
129
  export interface InvoicingQboSettingsPayload {
145
130
  account_mappings?: InvoicingQboAccountMappingsPayload | null;
146
131
  auto_create_items?: boolean;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ALL_INVOICING_FIELD_HELP_GROUPS = exports.withInvoicingQboMappedAccounts = 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;
@@ -12,7 +12,6 @@ exports.toInvoicingDiscountLifecycleAction = toInvoicingDiscountLifecycleAction;
12
12
  exports.toInvoicingDiscountLifecycleActionOption = toInvoicingDiscountLifecycleActionOption;
13
13
  exports.toInvoicingQboCustomerMatchingMode = toInvoicingQboCustomerMatchingMode;
14
14
  exports.getInvoicingQboAccountMappingId = getInvoicingQboAccountMappingId;
15
- exports.withInvoicingQboAccountMappingId = withInvoicingQboAccountMappingId;
16
15
  exports.toInvoicingQboAccountMappingKindOption = toInvoicingQboAccountMappingKindOption;
17
16
  exports.toInvoicingNamedOption = toInvoicingNamedOption;
18
17
  exports.toInvoicingFormOption = toInvoicingFormOption;
@@ -154,90 +153,50 @@ function toInvoicingQboCustomerMatchingMode(value) {
154
153
  throw new Error(`Unknown InvoicingQboCustomerMatchingMode: ${value}`);
155
154
  }
156
155
  }
157
- /**
158
- * The id for one mapping slot, from whichever shape the backend sent. The object
159
- * form wins when present; a blank id in either is treated as unset so the picker
160
- * shows its placeholder rather than selecting nothing.
161
- */
162
156
  /**
163
157
  * The first id a picker can actually select. Callers list the QuickBooks id
164
- * first: the options are keyed on it, so an internal id selects nothing even
165
- * when it names the same record. Blank counts as absent, so a cleared field
166
- * falls through to the next candidate rather than selecting an empty option.
158
+ * first: the options are keyed on it, so a platform id selects nothing even when
159
+ * it names the same record. Blank counts as absent.
167
160
  */
168
161
  const firstSelectableInvoicingId = (candidates) => candidates.find((candidate) => candidate != null && candidate !== '');
169
162
  exports.firstSelectableInvoicingId = firstSelectableInvoicingId;
170
- function readInvoicingQboMappedAccountId(mappedAccount, legacyAccountId) {
171
- // Both shapes are read: the mapping arrives either way depending on how it
172
- // was saved.
173
- return (0, exports.firstSelectableInvoicingId)([
174
- mappedAccount?.qbo_id,
175
- mappedAccount?.account_id,
176
- legacyAccountId,
177
- ]);
178
- }
179
163
  /**
180
- * The object key holding a mapped account, derived from its `*_account_id` code:
181
- * `deposit_account_id` arrives either as `deposit_account_id` or as a
182
- * `deposit_account` record. Derived rather than listed — the set of slots is the
183
- * config API's to grow, and a hardcoded list silently ignores any code it has
184
- * not heard of, which shows up as a mapping that will not save.
185
- */
186
- const mappedAccountKeyForCode = (code) => code.replace(/_id$/, '');
187
- /** Every mapping code a payload carries, in either shape. */
188
- const mappingCodesIn = (accountMappings) => [
189
- ...new Set(Object.keys(accountMappings).map((key) => key.endsWith('_id') ? key : `${key}_id`)),
190
- ];
191
- /**
192
- * Read one account-mapping dropdown value by its config `code` (the codes in
193
- * `qbo_account_mapping_kind_options` match the `account_mappings` field names).
164
+ * The slot a mapping code names. Codes arrive from the config API as the slot
165
+ * itself (`deposit_account`); a trailing `_id` is tolerated so a tenant sending
166
+ * the older `deposit_account_id` resolves to the same mapping.
194
167
  */
168
+ const toInvoicingQboAccountMappingSlot = (code) => code.replace(/_id$/, '');
169
+ exports.toInvoicingQboAccountMappingSlot = toInvoicingQboAccountMappingSlot;
170
+ /** Read one account-mapping dropdown value by its config `code`. */
195
171
  function getInvoicingQboAccountMappingId(accountMappings, code) {
196
- const slots = accountMappings;
197
- const legacyId = slots?.[code];
198
- return readInvoicingQboMappedAccountId(slots?.[mappedAccountKeyForCode(code)], typeof legacyId === 'string' ? legacyId : undefined);
172
+ const slotKey = (0, exports.toInvoicingQboAccountMappingSlot)(code);
173
+ // Either key: the service sends `deposit_account`, but an older payload put
174
+ // the id under `deposit_account_id`.
175
+ const slot = accountMappings?.[slotKey] ?? accountMappings?.[`${slotKey}_id`];
176
+ if (typeof slot === 'string') {
177
+ return slot !== '' ? slot : undefined;
178
+ }
179
+ return (0, exports.firstSelectableInvoicingId)([slot?.qbo_id, slot?.account_id]);
199
180
  }
200
181
  /**
201
- * Expand each mapping from the id the picker submitted to the whole account, the
202
- * shape the service stores: `deposit_account_id: '35'` becomes
203
- * `deposit_account: {qbo_id: '35', account_id, account_name, …}`. Written at the
204
- * request boundary rather than in the page's draft, so the UI keeps working in
205
- * ids. An id that resolves to no account keeps its bare form rather than being
206
- * dropped.
182
+ * Build the `account_mappings` request body from the ids the pickers submitted.
183
+ * Each kind is sent as the whole account, which is what the service stores; an
184
+ * id that resolves to no account is sent as it stands rather than dropped, so a
185
+ * save never silently unsets a mapping it could not expand.
207
186
  */
208
- const withInvoicingQboMappedAccounts = (accountMappings, accountByQboId) => {
209
- if (accountMappings == null) {
210
- return undefined;
211
- }
212
- const expanded = { ...accountMappings };
213
- mappingCodesIn(accountMappings).forEach((code) => {
214
- const objectKey = mappedAccountKeyForCode(code);
215
- const accountId = getInvoicingQboAccountMappingId(accountMappings, code);
216
- if (accountId == null) {
217
- return;
218
- }
219
- const account = accountByQboId(accountId);
220
- if (account == null) {
221
- // Unresolved: leave the id as it stands rather than dropping the mapping.
222
- // Sending less than we were given would unset a live configuration.
187
+ const toInvoicingQboAccountMappingsPayload = (accountIdByMappingCode, accountForId) => {
188
+ const mappings = {};
189
+ Object.entries(accountIdByMappingCode).forEach(([code, accountId]) => {
190
+ if (accountId === '') {
223
191
  return;
224
192
  }
225
- expanded[objectKey] = (0, accountPayload_1.toAccountPayload)(account);
226
- expanded[code] = undefined;
193
+ const account = accountForId(accountId);
194
+ mappings[(0, exports.toInvoicingQboAccountMappingSlot)(code)] =
195
+ account != null ? (0, accountPayload_1.toAccountPayload)(account) : accountId;
227
196
  });
228
- return expanded;
197
+ return mappings;
229
198
  };
230
- exports.withInvoicingQboMappedAccounts = withInvoicingQboMappedAccounts;
231
- function withInvoicingQboAccountMappingId(accountMappings, code, accountId) {
232
- // Both slots are written together: setting only the bare id would leave the
233
- // record shape behind, and the reader prefers it — so the selection just made
234
- // would read back as whatever was mapped before.
235
- return {
236
- ...(accountMappings ?? {}),
237
- [mappedAccountKeyForCode(code)]: undefined,
238
- [code]: accountId,
239
- };
240
- }
199
+ exports.toInvoicingQboAccountMappingsPayload = toInvoicingQboAccountMappingsPayload;
241
200
  function toInvoicingQboAccountMappingKindOption(option) {
242
201
  return {
243
202
  accountTypes: option.account_types,
@@ -129,88 +129,47 @@ export function toInvoicingQboCustomerMatchingMode(value) {
129
129
  throw new Error(`Unknown InvoicingQboCustomerMatchingMode: ${value}`);
130
130
  }
131
131
  }
132
- /**
133
- * The id for one mapping slot, from whichever shape the backend sent. The object
134
- * form wins when present; a blank id in either is treated as unset so the picker
135
- * shows its placeholder rather than selecting nothing.
136
- */
137
132
  /**
138
133
  * The first id a picker can actually select. Callers list the QuickBooks id
139
- * first: the options are keyed on it, so an internal id selects nothing even
140
- * when it names the same record. Blank counts as absent, so a cleared field
141
- * falls through to the next candidate rather than selecting an empty option.
134
+ * first: the options are keyed on it, so a platform id selects nothing even when
135
+ * it names the same record. Blank counts as absent.
142
136
  */
143
137
  export const firstSelectableInvoicingId = (candidates) => candidates.find((candidate) => candidate != null && candidate !== '');
144
- function readInvoicingQboMappedAccountId(mappedAccount, legacyAccountId) {
145
- // Both shapes are read: the mapping arrives either way depending on how it
146
- // was saved.
147
- return firstSelectableInvoicingId([
148
- mappedAccount?.qbo_id,
149
- mappedAccount?.account_id,
150
- legacyAccountId,
151
- ]);
152
- }
153
138
  /**
154
- * The object key holding a mapped account, derived from its `*_account_id` code:
155
- * `deposit_account_id` arrives either as `deposit_account_id` or as a
156
- * `deposit_account` record. Derived rather than listed — the set of slots is the
157
- * config API's to grow, and a hardcoded list silently ignores any code it has
158
- * not heard of, which shows up as a mapping that will not save.
159
- */
160
- const mappedAccountKeyForCode = (code) => code.replace(/_id$/, '');
161
- /** Every mapping code a payload carries, in either shape. */
162
- const mappingCodesIn = (accountMappings) => [
163
- ...new Set(Object.keys(accountMappings).map((key) => key.endsWith('_id') ? key : `${key}_id`)),
164
- ];
165
- /**
166
- * Read one account-mapping dropdown value by its config `code` (the codes in
167
- * `qbo_account_mapping_kind_options` match the `account_mappings` field names).
139
+ * The slot a mapping code names. Codes arrive from the config API as the slot
140
+ * itself (`deposit_account`); a trailing `_id` is tolerated so a tenant sending
141
+ * the older `deposit_account_id` resolves to the same mapping.
168
142
  */
143
+ export const toInvoicingQboAccountMappingSlot = (code) => code.replace(/_id$/, '');
144
+ /** Read one account-mapping dropdown value by its config `code`. */
169
145
  export function getInvoicingQboAccountMappingId(accountMappings, code) {
170
- const slots = accountMappings;
171
- const legacyId = slots?.[code];
172
- return readInvoicingQboMappedAccountId(slots?.[mappedAccountKeyForCode(code)], typeof legacyId === 'string' ? legacyId : undefined);
146
+ const slotKey = toInvoicingQboAccountMappingSlot(code);
147
+ // Either key: the service sends `deposit_account`, but an older payload put
148
+ // the id under `deposit_account_id`.
149
+ const slot = accountMappings?.[slotKey] ?? accountMappings?.[`${slotKey}_id`];
150
+ if (typeof slot === 'string') {
151
+ return slot !== '' ? slot : undefined;
152
+ }
153
+ return firstSelectableInvoicingId([slot?.qbo_id, slot?.account_id]);
173
154
  }
174
155
  /**
175
- * Expand each mapping from the id the picker submitted to the whole account, the
176
- * shape the service stores: `deposit_account_id: '35'` becomes
177
- * `deposit_account: {qbo_id: '35', account_id, account_name, …}`. Written at the
178
- * request boundary rather than in the page's draft, so the UI keeps working in
179
- * ids. An id that resolves to no account keeps its bare form rather than being
180
- * dropped.
156
+ * Build the `account_mappings` request body from the ids the pickers submitted.
157
+ * Each kind is sent as the whole account, which is what the service stores; an
158
+ * id that resolves to no account is sent as it stands rather than dropped, so a
159
+ * save never silently unsets a mapping it could not expand.
181
160
  */
182
- export const withInvoicingQboMappedAccounts = (accountMappings, accountByQboId) => {
183
- if (accountMappings == null) {
184
- return undefined;
185
- }
186
- const expanded = { ...accountMappings };
187
- mappingCodesIn(accountMappings).forEach((code) => {
188
- const objectKey = mappedAccountKeyForCode(code);
189
- const accountId = getInvoicingQboAccountMappingId(accountMappings, code);
190
- if (accountId == null) {
191
- return;
192
- }
193
- const account = accountByQboId(accountId);
194
- if (account == null) {
195
- // Unresolved: leave the id as it stands rather than dropping the mapping.
196
- // Sending less than we were given would unset a live configuration.
161
+ export const toInvoicingQboAccountMappingsPayload = (accountIdByMappingCode, accountForId) => {
162
+ const mappings = {};
163
+ Object.entries(accountIdByMappingCode).forEach(([code, accountId]) => {
164
+ if (accountId === '') {
197
165
  return;
198
166
  }
199
- expanded[objectKey] = toAccountPayload(account);
200
- expanded[code] = undefined;
167
+ const account = accountForId(accountId);
168
+ mappings[toInvoicingQboAccountMappingSlot(code)] =
169
+ account != null ? toAccountPayload(account) : accountId;
201
170
  });
202
- return expanded;
171
+ return mappings;
203
172
  };
204
- export function withInvoicingQboAccountMappingId(accountMappings, code, accountId) {
205
- // Both slots are written together: setting only the bare id would leave the
206
- // record shape behind, and the reader prefers it — so the selection just made
207
- // would read back as whatever was mapped before.
208
- return {
209
- ...(accountMappings ?? {}),
210
- [mappedAccountKeyForCode(code)]: undefined,
211
- [code]: accountId,
212
- };
213
- }
214
173
  export function toInvoicingQboAccountMappingKindOption(option) {
215
174
  return {
216
175
  accountTypes: option.account_types,
package/lib/esm/index.js CHANGED
@@ -858,7 +858,7 @@ export { isStripeConnectedFromSettings } from './view/invoicing/settingsView/inv
858
858
  export { fetchInvoicingAccountingPickers, fetchInvoicingQboConnection, fetchInvoicingQboSyncHealth, retryInvoicingQboSyncTask, startInvoicingQboBackfill, } from './view/invoicing/invoicingQboView/invoicingQboViewReducer';
859
859
  export { INVOICING_INCOME_ACCOUNT_TYPES, invoicingAccountsForQboAccountTypes, } from './view/invoicing/invoicingQboView/invoicingQboViewSelector';
860
860
  export { getInvoicingQboView } from './view/invoicing/invoicingQboView/invoicingQboViewSelector';
861
- export { INVOICING_QBO_CUSTOMER_MATCHING_MODES, getInvoicingQboAccountMappingId, toInvoicingNamedOptionsByKey, toInvoicingQboCustomerMatchingMode, withInvoicingQboAccountMappingId, } from './entity/invoicing/invoicingCommonPayload';
861
+ export { INVOICING_QBO_CUSTOMER_MATCHING_MODES, getInvoicingQboAccountMappingId, toInvoicingNamedOptionsByKey, toInvoicingQboCustomerMatchingMode, } from './entity/invoicing/invoicingCommonPayload';
862
862
  // ── Invoicing: config ─────────────────────────────────────────────────────────
863
863
  export { acceptInvoicingTerms, enableInvoicing, fetchInvoicingConfig, resetInvoicingConfigActionStates, updateInvoicingConfig, } from './view/invoicing/invoicingConfigView/invoicingConfigViewReducer';
864
864
  export { getInvoicingConfigView } from './view/invoicing/invoicingConfigView/invoicingConfigViewSelector';
@@ -1,27 +1,12 @@
1
1
  import { from } from 'rxjs';
2
2
  import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
- import { withInvoicingQboMappedAccounts, } from '../../../../entity/invoicing/invoicingCommonPayload';
4
3
  import { apiErrorSnackbarVariables } from '../../../../entity/snackbar/snackbarApiError';
5
4
  import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
6
5
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
7
- import { invoicingQboAccountByQboId } from '../../invoicingQboView/invoicingQboViewSelector';
8
6
  import { saveInvoicingSettings, saveInvoicingSettingsFailure, saveInvoicingSettingsSuccess, } from '../settingsViewReducer';
9
- export const saveInvoicingSettingsEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(saveInvoicingSettings.match), mergeMap((action) => {
7
+ export const saveInvoicingSettingsEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(saveInvoicingSettings.match), mergeMap((action) => {
10
8
  const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/settings`;
11
9
  const { settings, snackbarSection } = action.payload;
12
- // The QBO page edits mappings as picker ids; the service stores whole
13
- // accounts. Expanded here, at the request boundary, so the page does not
14
- // have to hold entities it only needs in order to save.
15
- const qboSettings = settings.qbo;
16
- const body = qboSettings?.account_mappings != null
17
- ? {
18
- ...settings,
19
- qbo: {
20
- ...qboSettings,
21
- account_mappings: withInvoicingQboMappedAccounts(qboSettings.account_mappings, (qboId) => invoicingQboAccountByQboId(state$.value, qboId)),
22
- },
23
- }
24
- : settings;
25
10
  /** `status` surfaces the platform's error text; omit it to keep the generic copy. */
26
11
  const snackbar = (messageText, status) => snackbarSection == null
27
12
  ? []
@@ -34,7 +19,7 @@ export const saveInvoicingSettingsEpic = (actions$, state$, zeniAPI) => actions$
34
19
  }),
35
20
  ];
36
21
  return zeniAPI
37
- .putAndGetJSON(apiUrl, { ...body })
22
+ .putAndGetJSON(apiUrl, { ...settings })
38
23
  .pipe(switchMap((response) => {
39
24
  if (isSuccessResponse(response) && response.data != null) {
40
25
  return from([
@@ -1,5 +1,6 @@
1
1
  import { EMPTY, of } from 'rxjs';
2
2
  import { filter, mergeMap, withLatestFrom } from 'rxjs/operators';
3
+ import { invoicingQboAccountByQboId } from '../../invoicingQboView/invoicingQboViewSelector';
3
4
  import { invoicingQboSettingsLocalDataToPayload } from '../invoicingQboSettingsFormConfig';
4
5
  import { saveInvoicingSettings, submitInvoicingQboSettings, } from '../settingsViewReducer';
5
6
  /**
@@ -16,7 +17,9 @@ export const submitInvoicingQboSettingsEpic = (actions$, state$) => actions$.pip
16
17
  return EMPTY;
17
18
  }
18
19
  return of(saveInvoicingSettings({
19
- settings: { qbo: invoicingQboSettingsLocalDataToPayload(qboDraft) },
20
+ settings: {
21
+ qbo: invoicingQboSettingsLocalDataToPayload(qboDraft, (accountId) => invoicingQboAccountByQboId(state, accountId)),
22
+ },
20
23
  snackbarSection: 'invoicing_qbo_settings_save',
21
24
  }));
22
25
  }));
@@ -1,4 +1,4 @@
1
- import { getInvoicingQboAccountMappingId, withInvoicingQboAccountMappingId, } 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,
@@ -12,13 +12,12 @@ export const initialInvoicingQboSettingsLocalData = {
12
12
  export const entityToInvoicingQboSettingsLocalData = (qbo) => {
13
13
  const accountMappings = qbo?.account_mappings ?? {};
14
14
  const accountIdByMappingCode = {};
15
- // Whatever slots the payload carries, in either shape — `deposit_account` and
16
- // `deposit_account_id` are the same mapping under one code.
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
17
  Object.keys(accountMappings).forEach((key) => {
18
- const code = key.endsWith('_id') ? key : `${key}_id`;
19
- const accountId = getInvoicingQboAccountMappingId(accountMappings, code);
18
+ const accountId = getInvoicingQboAccountMappingId(accountMappings, key);
20
19
  if (accountId != null) {
21
- accountIdByMappingCode[code] = accountId;
20
+ accountIdByMappingCode[toInvoicingQboAccountMappingSlot(key)] = accountId;
22
21
  }
23
22
  });
24
23
  return {
@@ -32,19 +31,13 @@ export const entityToInvoicingQboSettingsLocalData = (qbo) => {
32
31
  };
33
32
  };
34
33
  /**
35
- * The local data as the request body. Only the mappings the page holds are sent;
36
- * an empty id means the slot was never set, so it is left out rather than sent
37
- * blank.
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.
38
37
  */
39
- export const invoicingQboSettingsLocalDataToPayload = (localData) => {
40
- let accountMappings = {};
41
- Object.entries(localData.accountIdByMappingCode).forEach(([code, accountId]) => {
42
- if (accountId !== '') {
43
- accountMappings = withInvoicingQboAccountMappingId(accountMappings, code, accountId);
44
- }
45
- });
38
+ export const invoicingQboSettingsLocalDataToPayload = (localData, accountForId) => {
46
39
  return {
47
- account_mappings: accountMappings,
40
+ account_mappings: toInvoicingQboAccountMappingsPayload(localData.accountIdByMappingCode, accountForId),
48
41
  auto_create_items: localData.autoCreateItems,
49
42
  closed_period_void_as_credit_memo: localData.closedPeriodVoidAsCreditMemo,
50
43
  customer_matching: localData.customerMatching,
package/lib/index.d.ts CHANGED
@@ -1249,7 +1249,7 @@ export type { InvoicingAccountingOption } from './view/invoicing/invoicingQboVie
1249
1249
  export { getInvoicingQboView } from './view/invoicing/invoicingQboView/invoicingQboViewSelector';
1250
1250
  export type { InvoicingQboView } from './view/invoicing/invoicingQboView/invoicingQboViewSelector';
1251
1251
  export type { InvoicingQboConnection, InvoicingQboFailedTask, InvoicingQboSyncHealth, } from './view/invoicing/invoicingQboView/invoicingQboViewPayload';
1252
- export { INVOICING_QBO_CUSTOMER_MATCHING_MODES, getInvoicingQboAccountMappingId, toInvoicingNamedOptionsByKey, toInvoicingQboCustomerMatchingMode, withInvoicingQboAccountMappingId, } from './entity/invoicing/invoicingCommonPayload';
1252
+ export { INVOICING_QBO_CUSTOMER_MATCHING_MODES, getInvoicingQboAccountMappingId, toInvoicingNamedOptionsByKey, toInvoicingQboCustomerMatchingMode, } from './entity/invoicing/invoicingCommonPayload';
1253
1253
  export type { InvoicingNamedOption, InvoicingFieldHelp, InvoicingFieldHelpByCode, InvoicingFieldHelpGroup, InvoicingNamedOptionsByKey, InvoicingQboAccountMappingKindOption, InvoicingQboAccountMappingsPayload, InvoicingQboCustomerMatchingMode, InvoicingQboSettingsPayload, } from './entity/invoicing/invoicingCommonPayload';
1254
1254
  export { acceptInvoicingTerms, enableInvoicing, fetchInvoicingConfig, resetInvoicingConfigActionStates, updateInvoicingConfig, } from './view/invoicing/invoicingConfigView/invoicingConfigViewReducer';
1255
1255
  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.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;
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; } });
@@ -3133,7 +3133,6 @@ Object.defineProperty(exports, "INVOICING_QBO_CUSTOMER_MATCHING_MODES", { enumer
3133
3133
  Object.defineProperty(exports, "getInvoicingQboAccountMappingId", { enumerable: true, get: function () { return invoicingCommonPayload_2.getInvoicingQboAccountMappingId; } });
3134
3134
  Object.defineProperty(exports, "toInvoicingNamedOptionsByKey", { enumerable: true, get: function () { return invoicingCommonPayload_2.toInvoicingNamedOptionsByKey; } });
3135
3135
  Object.defineProperty(exports, "toInvoicingQboCustomerMatchingMode", { enumerable: true, get: function () { return invoicingCommonPayload_2.toInvoicingQboCustomerMatchingMode; } });
3136
- Object.defineProperty(exports, "withInvoicingQboAccountMappingId", { enumerable: true, get: function () { return invoicingCommonPayload_2.withInvoicingQboAccountMappingId; } });
3137
3136
  // ── Invoicing: config ─────────────────────────────────────────────────────────
3138
3137
  var invoicingConfigViewReducer_1 = require("./view/invoicing/invoicingConfigView/invoicingConfigViewReducer");
3139
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: {
@@ -3,4 +3,4 @@ import { Observable } from 'rxjs';
3
3
  import { RootState } from '../../../../rootStateTypes';
4
4
  import { ZeniAPI } from '../../../../zeniAPI';
5
5
  import { InvoicingSettingsViewActionType } from './settingsViewActionType';
6
- export declare const saveInvoicingSettingsEpic: (actions$: ActionsObservable<InvoicingSettingsViewActionType>, state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<InvoicingSettingsViewActionType>;
6
+ export declare const saveInvoicingSettingsEpic: (actions$: ActionsObservable<InvoicingSettingsViewActionType>, _state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<InvoicingSettingsViewActionType>;
@@ -3,28 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.saveInvoicingSettingsEpic = void 0;
4
4
  const rxjs_1 = require("rxjs");
5
5
  const operators_1 = require("rxjs/operators");
6
- const invoicingCommonPayload_1 = require("../../../../entity/invoicing/invoicingCommonPayload");
7
6
  const snackbarApiError_1 = require("../../../../entity/snackbar/snackbarApiError");
8
7
  const snackbarReducer_1 = require("../../../../entity/snackbar/snackbarReducer");
9
8
  const responsePayload_1 = require("../../../../responsePayload");
10
- const invoicingQboViewSelector_1 = require("../../invoicingQboView/invoicingQboViewSelector");
11
9
  const settingsViewReducer_1 = require("../settingsViewReducer");
12
- const saveInvoicingSettingsEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(settingsViewReducer_1.saveInvoicingSettings.match), (0, operators_1.mergeMap)((action) => {
10
+ const saveInvoicingSettingsEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(settingsViewReducer_1.saveInvoicingSettings.match), (0, operators_1.mergeMap)((action) => {
13
11
  const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/settings`;
14
12
  const { settings, snackbarSection } = action.payload;
15
- // The QBO page edits mappings as picker ids; the service stores whole
16
- // accounts. Expanded here, at the request boundary, so the page does not
17
- // have to hold entities it only needs in order to save.
18
- const qboSettings = settings.qbo;
19
- const body = qboSettings?.account_mappings != null
20
- ? {
21
- ...settings,
22
- qbo: {
23
- ...qboSettings,
24
- account_mappings: (0, invoicingCommonPayload_1.withInvoicingQboMappedAccounts)(qboSettings.account_mappings, (qboId) => (0, invoicingQboViewSelector_1.invoicingQboAccountByQboId)(state$.value, qboId)),
25
- },
26
- }
27
- : settings;
28
13
  /** `status` surfaces the platform's error text; omit it to keep the generic copy. */
29
14
  const snackbar = (messageText, status) => snackbarSection == null
30
15
  ? []
@@ -37,7 +22,7 @@ const saveInvoicingSettingsEpic = (actions$, state$, zeniAPI) => actions$.pipe((
37
22
  }),
38
23
  ];
39
24
  return zeniAPI
40
- .putAndGetJSON(apiUrl, { ...body })
25
+ .putAndGetJSON(apiUrl, { ...settings })
41
26
  .pipe((0, operators_1.switchMap)((response) => {
42
27
  if ((0, responsePayload_1.isSuccessResponse)(response) && response.data != null) {
43
28
  return (0, rxjs_1.from)([
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.submitInvoicingQboSettingsEpic = void 0;
4
4
  const rxjs_1 = require("rxjs");
5
5
  const operators_1 = require("rxjs/operators");
6
+ const invoicingQboViewSelector_1 = require("../../invoicingQboView/invoicingQboViewSelector");
6
7
  const invoicingQboSettingsFormConfig_1 = require("../invoicingQboSettingsFormConfig");
7
8
  const settingsViewReducer_1 = require("../settingsViewReducer");
8
9
  /**
@@ -19,7 +20,9 @@ const submitInvoicingQboSettingsEpic = (actions$, state$) => actions$.pipe((0, o
19
20
  return rxjs_1.EMPTY;
20
21
  }
21
22
  return (0, rxjs_1.of)((0, settingsViewReducer_1.saveInvoicingSettings)({
22
- settings: { qbo: (0, invoicingQboSettingsFormConfig_1.invoicingQboSettingsLocalDataToPayload)(qboDraft) },
23
+ settings: {
24
+ qbo: (0, invoicingQboSettingsFormConfig_1.invoicingQboSettingsLocalDataToPayload)(qboDraft, (accountId) => (0, invoicingQboViewSelector_1.invoicingQboAccountByQboId)(state, accountId)),
25
+ },
23
26
  snackbarSection: 'invoicing_qbo_settings_save',
24
27
  }));
25
28
  }));
@@ -1,3 +1,4 @@
1
+ import { AccountBase } from '../../../entity/account/accountState';
1
2
  import { InvoicingQboSettingsPayload } from '../../../entity/invoicing/invoicingCommonPayload';
2
3
  /**
3
4
  * The QBO configuration page's local data. A client shape, not the wire one: the
@@ -26,8 +27,8 @@ export declare const initialInvoicingQboSettingsLocalData: InvoicingQboSettingsL
26
27
  /** The settings response as the page edits it. */
27
28
  export declare const entityToInvoicingQboSettingsLocalData: (qbo: InvoicingQboSettingsPayload | null | undefined) => InvoicingQboSettingsLocalData;
28
29
  /**
29
- * The local data as the request body. Only the mappings the page holds are sent;
30
- * an empty id means the slot was never set, so it is left out rather than sent
31
- * blank.
30
+ * The local data as the request body. `accountForId` turns each picked id into
31
+ * the whole account the service stores; it comes from the epic, which is where
32
+ * the account list is in reach.
32
33
  */
33
- export declare const invoicingQboSettingsLocalDataToPayload: (localData: InvoicingQboSettingsLocalData) => InvoicingQboSettingsPayload;
34
+ export declare const invoicingQboSettingsLocalDataToPayload: (localData: InvoicingQboSettingsLocalData, accountForId: (accountId: string) => AccountBase | undefined) => InvoicingQboSettingsPayload;
@@ -15,13 +15,12 @@ exports.initialInvoicingQboSettingsLocalData = {
15
15
  const entityToInvoicingQboSettingsLocalData = (qbo) => {
16
16
  const accountMappings = qbo?.account_mappings ?? {};
17
17
  const accountIdByMappingCode = {};
18
- // Whatever slots the payload carries, in either shape — `deposit_account` and
19
- // `deposit_account_id` are the same mapping under one code.
18
+ // Keyed by the slot the payload used, which is the config code the page will
19
+ // ask for — the reader tolerates the older `_id` form on the way in.
20
20
  Object.keys(accountMappings).forEach((key) => {
21
- const code = key.endsWith('_id') ? key : `${key}_id`;
22
- const accountId = (0, invoicingCommonPayload_1.getInvoicingQboAccountMappingId)(accountMappings, code);
21
+ const accountId = (0, invoicingCommonPayload_1.getInvoicingQboAccountMappingId)(accountMappings, key);
23
22
  if (accountId != null) {
24
- accountIdByMappingCode[code] = accountId;
23
+ accountIdByMappingCode[(0, invoicingCommonPayload_1.toInvoicingQboAccountMappingSlot)(key)] = accountId;
25
24
  }
26
25
  });
27
26
  return {
@@ -36,19 +35,13 @@ const entityToInvoicingQboSettingsLocalData = (qbo) => {
36
35
  };
37
36
  exports.entityToInvoicingQboSettingsLocalData = entityToInvoicingQboSettingsLocalData;
38
37
  /**
39
- * The local data as the request body. Only the mappings the page holds are sent;
40
- * an empty id means the slot was never set, so it is left out rather than sent
41
- * blank.
38
+ * The local data as the request body. `accountForId` turns each picked id into
39
+ * the whole account the service stores; it comes from the epic, which is where
40
+ * the account list is in reach.
42
41
  */
43
- const invoicingQboSettingsLocalDataToPayload = (localData) => {
44
- let accountMappings = {};
45
- Object.entries(localData.accountIdByMappingCode).forEach(([code, accountId]) => {
46
- if (accountId !== '') {
47
- accountMappings = (0, invoicingCommonPayload_1.withInvoicingQboAccountMappingId)(accountMappings, code, accountId);
48
- }
49
- });
42
+ const invoicingQboSettingsLocalDataToPayload = (localData, accountForId) => {
50
43
  return {
51
- account_mappings: accountMappings,
44
+ account_mappings: (0, invoicingCommonPayload_1.toInvoicingQboAccountMappingsPayload)(localData.accountIdByMappingCode, accountForId),
52
45
  auto_create_items: localData.autoCreateItems,
53
46
  closed_period_void_as_credit_memo: localData.closedPeriodVoidAsCreditMemo,
54
47
  customer_matching: localData.customerMatching,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.2.10-betaAK4",
3
+ "version": "5.2.10-betaAK5",
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",