@zeniai/client-epic-state 5.2.7 → 5.2.8-betaAK0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/entity/invoicing/invoicingCommonPayload.d.ts +36 -2
- package/lib/entity/invoicing/invoicingCommonPayload.js +89 -11
- package/lib/entity/invoicing/product/productPayload.d.ts +10 -0
- package/lib/entity/invoicing/product/productPayload.js +14 -2
- package/lib/esm/entity/invoicing/invoicingCommonPayload.js +86 -10
- package/lib/esm/entity/invoicing/product/productPayload.js +14 -2
- package/lib/esm/index.js +3 -2
- package/lib/esm/view/accountList/accountListReducer.js +0 -1
- package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/backgroundRefetchReviewTabEpic.js +1 -0
- package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.js +1 -0
- package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +56 -11
- package/lib/esm/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +2 -1
- package/lib/esm/view/expenseAutomationView/types/transactionGroup.js +21 -0
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.js +24 -9
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.js +2 -1
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +6 -1
- package/lib/esm/view/invoicing/invoicingConfigView/invoicingConfigViewSelector.js +2 -0
- package/lib/esm/view/invoicing/invoicingQboView/epics/fetchInvoicingAccountingPickersEpic.js +10 -5
- package/lib/esm/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +47 -3
- package/lib/esm/view/invoicing/settingsView/settingsViewEpics.js +17 -2
- package/lib/esm/view/invoicing/settingsView/settingsViewSelector.js +7 -3
- package/lib/index.d.ts +3 -2
- package/lib/index.js +52 -47
- package/lib/view/accountList/accountListReducer.js +0 -1
- package/lib/view/accountList/accountListState.d.ts +1 -1
- package/lib/view/expenseAutomationView/epics/transactionCategorization/backgroundRefetchReviewTabEpic.js +1 -0
- package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.js +1 -0
- package/lib/view/expenseAutomationView/payload/transactionCategorizationPayload.d.ts +11 -0
- package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.d.ts +4 -1
- package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +57 -12
- package/lib/view/expenseAutomationView/selectorTypes/transactionsViewSelectorTypes.d.ts +2 -0
- package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +2 -1
- package/lib/view/expenseAutomationView/types/transactionGroup.d.ts +21 -0
- package/lib/view/expenseAutomationView/types/transactionGroup.js +25 -0
- package/lib/view/expenseAutomationView/types/transactionsViewState.d.ts +11 -0
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.d.ts +12 -1
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.js +24 -9
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.d.ts +5 -0
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.js +1 -0
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +6 -1
- package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewSelector.d.ts +1 -0
- package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewSelector.js +2 -0
- package/lib/view/invoicing/invoicingQboView/epics/fetchInvoicingAccountingPickersEpic.js +10 -5
- package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.d.ts +19 -2
- package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +52 -4
- package/lib/view/invoicing/settingsView/settingsViewEpics.d.ts +1 -1
- package/lib/view/invoicing/settingsView/settingsViewEpics.js +17 -2
- package/lib/view/invoicing/settingsView/settingsViewSelector.d.ts +11 -4
- package/lib/view/invoicing/settingsView/settingsViewSelector.js +7 -3
- package/package.json +1 -1
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { AccountBasePayload } from '../account/accountPayload';
|
|
2
|
+
import { AccountBase } from '../account/accountState';
|
|
1
3
|
import { AddressPayload } from '../address/addressPayload';
|
|
2
4
|
export type InvoicingPriceType = 'tax_exclusive' | 'tax_inclusive';
|
|
3
5
|
export declare const INVOICING_DUNNING_ACTIONS: readonly ["email_only", "email_and_retry", "do_nothing", "retry_only"];
|
|
@@ -88,23 +90,55 @@ export interface InvoicingStripeAccountPayload {
|
|
|
88
90
|
export declare const INVOICING_QBO_CUSTOMER_MATCHING_MODES: readonly ["link_existing", "always_create"];
|
|
89
91
|
export type InvoicingQboCustomerMatchingMode = (typeof INVOICING_QBO_CUSTOMER_MATCHING_MODES)[number];
|
|
90
92
|
export declare function toInvoicingQboCustomerMatchingMode(value: string): InvoicingQboCustomerMatchingMode;
|
|
93
|
+
/**
|
|
94
|
+
* A mapped account as the newer settings payload returns it: the platform id,
|
|
95
|
+
* plus hydrated fields the client only displays.
|
|
96
|
+
*/
|
|
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
|
+
*/
|
|
91
105
|
export interface InvoicingQboAccountMappingsPayload {
|
|
106
|
+
default_income_account?: Partial<AccountBasePayload> | null;
|
|
92
107
|
default_income_account_id?: string | null;
|
|
108
|
+
deposit_account?: Partial<AccountBasePayload> | null;
|
|
93
109
|
deposit_account_id?: string | null;
|
|
94
110
|
deposit_account_overrides?: Record<string, string> | null;
|
|
111
|
+
discounts_account?: Partial<AccountBasePayload> | null;
|
|
95
112
|
discounts_account_id?: string | null;
|
|
113
|
+
rounding_account?: Partial<AccountBasePayload> | null;
|
|
96
114
|
rounding_account_id?: string | null;
|
|
115
|
+
write_off_account?: Partial<AccountBasePayload> | null;
|
|
97
116
|
write_off_account_id?: string | null;
|
|
98
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* The id for one mapping slot, from whichever shape the backend sent. The object
|
|
120
|
+
* form wins when present; a blank id in either is treated as unset so the picker
|
|
121
|
+
* shows its placeholder rather than selecting nothing.
|
|
122
|
+
*/
|
|
123
|
+
/**
|
|
124
|
+
* The first id a picker can actually select. Callers list the QuickBooks id
|
|
125
|
+
* first: the options are keyed on it, so an internal id selects nothing even
|
|
126
|
+
* when it names the same record. Blank counts as absent, so a cleared field
|
|
127
|
+
* falls through to the next candidate rather than selecting an empty option.
|
|
128
|
+
*/
|
|
129
|
+
export declare const firstSelectableInvoicingId: (candidates: readonly (string | null | undefined)[]) => string | undefined;
|
|
99
130
|
/**
|
|
100
131
|
* Read one account-mapping dropdown value by its config `code` (the codes in
|
|
101
132
|
* `qbo_account_mapping_kind_options` match the `account_mappings` field names).
|
|
102
133
|
*/
|
|
103
134
|
export declare function getInvoicingQboAccountMappingId(accountMappings: InvoicingQboAccountMappingsPayload | undefined, code: string): string | undefined;
|
|
104
135
|
/**
|
|
105
|
-
*
|
|
106
|
-
*
|
|
136
|
+
* Expand each mapping from the id the picker submitted to the whole account, the
|
|
137
|
+
* shape the service stores. Written at the request boundary rather than in the
|
|
138
|
+
* page's draft so the UI keeps working in ids, and an id that no longer resolves
|
|
139
|
+
* is left out — a mapping the service cannot expand is worse than none.
|
|
107
140
|
*/
|
|
141
|
+
export declare const withInvoicingQboMappedAccounts: (accountMappings: InvoicingQboAccountMappingsPayload | undefined, accountByQboId: (qboId: string) => AccountBase | undefined) => InvoicingQboAccountMappingsPayload | undefined;
|
|
108
142
|
export declare function withInvoicingQboAccountMappingId(accountMappings: InvoicingQboAccountMappingsPayload | undefined, code: string, accountId: string): InvoicingQboAccountMappingsPayload;
|
|
109
143
|
export interface InvoicingQboSettingsPayload {
|
|
110
144
|
account_mappings?: InvoicingQboAccountMappingsPayload | null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ALL_INVOICING_FIELD_HELP_GROUPS = 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.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;
|
|
4
4
|
exports.toInvoicingDunningAction = toInvoicingDunningAction;
|
|
5
5
|
exports.toInvoicingSubscriptionLifecycleAction = toInvoicingSubscriptionLifecycleAction;
|
|
6
6
|
exports.toInvoicingSubscriptionLifecycleActionOption = toInvoicingSubscriptionLifecycleActionOption;
|
|
@@ -23,6 +23,7 @@ exports.toInvoicingSubscriptionEditFieldOption = toInvoicingSubscriptionEditFiel
|
|
|
23
23
|
exports.toAuditLogEntityRouteOption = toAuditLogEntityRouteOption;
|
|
24
24
|
exports.buildInvoicingEntityPath = buildInvoicingEntityPath;
|
|
25
25
|
exports.isInvoicingEntityLinkable = isInvoicingEntityLinkable;
|
|
26
|
+
const accountPayload_1 = require("../account/accountPayload");
|
|
26
27
|
exports.INVOICING_DUNNING_ACTIONS = [
|
|
27
28
|
'email_only',
|
|
28
29
|
'email_and_retry',
|
|
@@ -153,6 +154,28 @@ function toInvoicingQboCustomerMatchingMode(value) {
|
|
|
153
154
|
throw new Error(`Unknown InvoicingQboCustomerMatchingMode: ${value}`);
|
|
154
155
|
}
|
|
155
156
|
}
|
|
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
|
+
/**
|
|
163
|
+
* 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.
|
|
167
|
+
*/
|
|
168
|
+
const firstSelectableInvoicingId = (candidates) => candidates.find((candidate) => candidate != null && candidate !== '');
|
|
169
|
+
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
|
+
}
|
|
156
179
|
/**
|
|
157
180
|
* Read one account-mapping dropdown value by its config `code` (the codes in
|
|
158
181
|
* `qbo_account_mapping_kind_options` match the `account_mappings` field names).
|
|
@@ -160,15 +183,15 @@ function toInvoicingQboCustomerMatchingMode(value) {
|
|
|
160
183
|
function getInvoicingQboAccountMappingId(accountMappings, code) {
|
|
161
184
|
switch (code) {
|
|
162
185
|
case 'deposit_account_id':
|
|
163
|
-
return accountMappings?.deposit_account_id
|
|
186
|
+
return readInvoicingQboMappedAccountId(accountMappings?.deposit_account, accountMappings?.deposit_account_id);
|
|
164
187
|
case 'default_income_account_id':
|
|
165
|
-
return accountMappings?.default_income_account_id
|
|
188
|
+
return readInvoicingQboMappedAccountId(accountMappings?.default_income_account, accountMappings?.default_income_account_id);
|
|
166
189
|
case 'discounts_account_id':
|
|
167
|
-
return accountMappings?.discounts_account_id
|
|
190
|
+
return readInvoicingQboMappedAccountId(accountMappings?.discounts_account, accountMappings?.discounts_account_id);
|
|
168
191
|
case 'rounding_account_id':
|
|
169
|
-
return accountMappings?.rounding_account_id
|
|
192
|
+
return readInvoicingQboMappedAccountId(accountMappings?.rounding_account, accountMappings?.rounding_account_id);
|
|
170
193
|
case 'write_off_account_id':
|
|
171
|
-
return accountMappings?.write_off_account_id
|
|
194
|
+
return readInvoicingQboMappedAccountId(accountMappings?.write_off_account, accountMappings?.write_off_account_id);
|
|
172
195
|
default:
|
|
173
196
|
return undefined;
|
|
174
197
|
}
|
|
@@ -177,19 +200,74 @@ function getInvoicingQboAccountMappingId(accountMappings, code) {
|
|
|
177
200
|
* Return a copy of the account mappings with the dropdown slot named by
|
|
178
201
|
* `code` set to `accountId`. Unknown codes leave the mappings unchanged.
|
|
179
202
|
*/
|
|
203
|
+
/** The object key holding the mapped account for each `*_account_id` code. */
|
|
204
|
+
const INVOICING_QBO_MAPPED_ACCOUNT_KEY = {
|
|
205
|
+
default_income_account_id: 'default_income_account',
|
|
206
|
+
deposit_account_id: 'deposit_account',
|
|
207
|
+
discounts_account_id: 'discounts_account',
|
|
208
|
+
rounding_account_id: 'rounding_account',
|
|
209
|
+
write_off_account_id: 'write_off_account',
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* Expand each mapping from the id the picker submitted to the whole account, the
|
|
213
|
+
* shape the service stores. Written at the request boundary rather than in the
|
|
214
|
+
* page's draft so the UI keeps working in ids, and an id that no longer resolves
|
|
215
|
+
* is left out — a mapping the service cannot expand is worse than none.
|
|
216
|
+
*/
|
|
217
|
+
const withInvoicingQboMappedAccounts = (accountMappings, accountByQboId) => {
|
|
218
|
+
if (accountMappings == null) {
|
|
219
|
+
return undefined;
|
|
220
|
+
}
|
|
221
|
+
const expanded = { ...accountMappings };
|
|
222
|
+
Object.entries(INVOICING_QBO_MAPPED_ACCOUNT_KEY).forEach(([code, objectKey]) => {
|
|
223
|
+
const accountId = getInvoicingQboAccountMappingId(accountMappings, code);
|
|
224
|
+
if (accountId == null) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const account = accountByQboId(accountId);
|
|
228
|
+
expanded[objectKey] =
|
|
229
|
+
account != null ? (0, accountPayload_1.toAccountPayload)(account) : undefined;
|
|
230
|
+
expanded[code] = undefined;
|
|
231
|
+
});
|
|
232
|
+
return expanded;
|
|
233
|
+
};
|
|
234
|
+
exports.withInvoicingQboMappedAccounts = withInvoicingQboMappedAccounts;
|
|
180
235
|
function withInvoicingQboAccountMappingId(accountMappings, code, accountId) {
|
|
236
|
+
// Both slots are written together. Setting only the bare id would leave the
|
|
237
|
+
// object shape behind, and the reader prefers it — so the selection just made
|
|
238
|
+
// would read back as whatever was mapped before.
|
|
181
239
|
const base = accountMappings ?? {};
|
|
182
240
|
switch (code) {
|
|
183
241
|
case 'deposit_account_id':
|
|
184
|
-
return {
|
|
242
|
+
return {
|
|
243
|
+
...base,
|
|
244
|
+
deposit_account: undefined,
|
|
245
|
+
deposit_account_id: accountId,
|
|
246
|
+
};
|
|
185
247
|
case 'default_income_account_id':
|
|
186
|
-
return {
|
|
248
|
+
return {
|
|
249
|
+
...base,
|
|
250
|
+
default_income_account: undefined,
|
|
251
|
+
default_income_account_id: accountId,
|
|
252
|
+
};
|
|
187
253
|
case 'discounts_account_id':
|
|
188
|
-
return {
|
|
254
|
+
return {
|
|
255
|
+
...base,
|
|
256
|
+
discounts_account: undefined,
|
|
257
|
+
discounts_account_id: accountId,
|
|
258
|
+
};
|
|
189
259
|
case 'rounding_account_id':
|
|
190
|
-
return {
|
|
260
|
+
return {
|
|
261
|
+
...base,
|
|
262
|
+
rounding_account: undefined,
|
|
263
|
+
rounding_account_id: accountId,
|
|
264
|
+
};
|
|
191
265
|
case 'write_off_account_id':
|
|
192
|
-
return {
|
|
266
|
+
return {
|
|
267
|
+
...base,
|
|
268
|
+
write_off_account: undefined,
|
|
269
|
+
write_off_account_id: accountId,
|
|
270
|
+
};
|
|
193
271
|
default:
|
|
194
272
|
return base;
|
|
195
273
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { AccountBasePayload } from '../../account/accountPayload';
|
|
2
|
+
import { ClassBasePayload } from '../../class/classPayload';
|
|
1
3
|
import { InvoicingPlanTier, InvoicingProduct } from './productState';
|
|
2
4
|
export interface InvoicingPlanTierPayload {
|
|
3
5
|
price: number;
|
|
@@ -7,10 +9,18 @@ export interface InvoicingPlanTierPayload {
|
|
|
7
9
|
pricing_type?: string;
|
|
8
10
|
}
|
|
9
11
|
export interface InvoicingAccountingDetailPayload {
|
|
12
|
+
/**
|
|
13
|
+
* `account` and `accounting_class` hold whole records — what the service stores
|
|
14
|
+
* and what the catalog payload builder sends. The bare `accounting_code` /
|
|
15
|
+
* `accounting_category_1` are the older shape, still read so a plan saved
|
|
16
|
+
* before the rollover prefills.
|
|
17
|
+
*/
|
|
18
|
+
account?: Partial<AccountBasePayload> | null;
|
|
10
19
|
accounting_category_1?: string;
|
|
11
20
|
accounting_category_2?: string;
|
|
12
21
|
accounting_category_3?: string;
|
|
13
22
|
accounting_category_4?: string;
|
|
23
|
+
accounting_class?: Partial<ClassBasePayload> | null;
|
|
14
24
|
accounting_code?: string;
|
|
15
25
|
sku?: string;
|
|
16
26
|
}
|
|
@@ -4,6 +4,7 @@ exports.mapInvoicingProductPayloadToState = exports.mapInvoicingPlanTierPayloadT
|
|
|
4
4
|
const amount_1 = require("../../../commonStateTypes/amount");
|
|
5
5
|
const currencyHelper_1 = require("../../../commonStateTypes/currencyHelper");
|
|
6
6
|
const zeniDayJS_1 = require("../../../zeniDayJS");
|
|
7
|
+
const invoicingCommonPayload_1 = require("../invoicingCommonPayload");
|
|
7
8
|
// ── Payload → State mappers (snake_case wire shape → camelCase domain shape) ──
|
|
8
9
|
const mapInvoicingPlanTierPayloadToState = (payload, currencyCode, currencySymbol) => ({
|
|
9
10
|
price: (0, amount_1.toAmount)(payload.price, currencyCode, currencySymbol),
|
|
@@ -14,11 +15,22 @@ const mapInvoicingPlanTierPayloadToState = (payload, currencyCode, currencySymbo
|
|
|
14
15
|
});
|
|
15
16
|
exports.mapInvoicingPlanTierPayloadToState = mapInvoicingPlanTierPayloadToState;
|
|
16
17
|
const mapInvoicingAccountingDetailPayloadToState = (payload) => ({
|
|
17
|
-
|
|
18
|
+
// The QuickBooks id first in both, for the reason `firstSelectableInvoicingId`
|
|
19
|
+
// documents: the form's pickers are keyed on it, so reading the internal id
|
|
20
|
+
// would leave the field blank over a mapping that exists.
|
|
21
|
+
accountingCategory1: (0, invoicingCommonPayload_1.firstSelectableInvoicingId)([
|
|
22
|
+
payload.accounting_class?.qbo_id,
|
|
23
|
+
payload.accounting_class?.accounting_class_id,
|
|
24
|
+
payload.accounting_category_1,
|
|
25
|
+
]),
|
|
18
26
|
accountingCategory2: payload.accounting_category_2,
|
|
19
27
|
accountingCategory3: payload.accounting_category_3,
|
|
20
28
|
accountingCategory4: payload.accounting_category_4,
|
|
21
|
-
accountingCode:
|
|
29
|
+
accountingCode: (0, invoicingCommonPayload_1.firstSelectableInvoicingId)([
|
|
30
|
+
payload.account?.qbo_id,
|
|
31
|
+
payload.account?.account_id,
|
|
32
|
+
payload.accounting_code,
|
|
33
|
+
]),
|
|
22
34
|
sku: payload.sku,
|
|
23
35
|
});
|
|
24
36
|
const mapInvoicingProductPayloadToState = (payload) => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { toAccountPayload } from '../account/accountPayload';
|
|
1
2
|
export const INVOICING_DUNNING_ACTIONS = [
|
|
2
3
|
'email_only',
|
|
3
4
|
'email_and_retry',
|
|
@@ -128,6 +129,27 @@ export function toInvoicingQboCustomerMatchingMode(value) {
|
|
|
128
129
|
throw new Error(`Unknown InvoicingQboCustomerMatchingMode: ${value}`);
|
|
129
130
|
}
|
|
130
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
|
+
/**
|
|
138
|
+
* 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.
|
|
142
|
+
*/
|
|
143
|
+
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
|
+
}
|
|
131
153
|
/**
|
|
132
154
|
* Read one account-mapping dropdown value by its config `code` (the codes in
|
|
133
155
|
* `qbo_account_mapping_kind_options` match the `account_mappings` field names).
|
|
@@ -135,15 +157,15 @@ export function toInvoicingQboCustomerMatchingMode(value) {
|
|
|
135
157
|
export function getInvoicingQboAccountMappingId(accountMappings, code) {
|
|
136
158
|
switch (code) {
|
|
137
159
|
case 'deposit_account_id':
|
|
138
|
-
return accountMappings?.deposit_account_id
|
|
160
|
+
return readInvoicingQboMappedAccountId(accountMappings?.deposit_account, accountMappings?.deposit_account_id);
|
|
139
161
|
case 'default_income_account_id':
|
|
140
|
-
return accountMappings?.default_income_account_id
|
|
162
|
+
return readInvoicingQboMappedAccountId(accountMappings?.default_income_account, accountMappings?.default_income_account_id);
|
|
141
163
|
case 'discounts_account_id':
|
|
142
|
-
return accountMappings?.discounts_account_id
|
|
164
|
+
return readInvoicingQboMappedAccountId(accountMappings?.discounts_account, accountMappings?.discounts_account_id);
|
|
143
165
|
case 'rounding_account_id':
|
|
144
|
-
return accountMappings?.rounding_account_id
|
|
166
|
+
return readInvoicingQboMappedAccountId(accountMappings?.rounding_account, accountMappings?.rounding_account_id);
|
|
145
167
|
case 'write_off_account_id':
|
|
146
|
-
return accountMappings?.write_off_account_id
|
|
168
|
+
return readInvoicingQboMappedAccountId(accountMappings?.write_off_account, accountMappings?.write_off_account_id);
|
|
147
169
|
default:
|
|
148
170
|
return undefined;
|
|
149
171
|
}
|
|
@@ -152,19 +174,73 @@ export function getInvoicingQboAccountMappingId(accountMappings, code) {
|
|
|
152
174
|
* Return a copy of the account mappings with the dropdown slot named by
|
|
153
175
|
* `code` set to `accountId`. Unknown codes leave the mappings unchanged.
|
|
154
176
|
*/
|
|
177
|
+
/** The object key holding the mapped account for each `*_account_id` code. */
|
|
178
|
+
const INVOICING_QBO_MAPPED_ACCOUNT_KEY = {
|
|
179
|
+
default_income_account_id: 'default_income_account',
|
|
180
|
+
deposit_account_id: 'deposit_account',
|
|
181
|
+
discounts_account_id: 'discounts_account',
|
|
182
|
+
rounding_account_id: 'rounding_account',
|
|
183
|
+
write_off_account_id: 'write_off_account',
|
|
184
|
+
};
|
|
185
|
+
/**
|
|
186
|
+
* Expand each mapping from the id the picker submitted to the whole account, the
|
|
187
|
+
* shape the service stores. Written at the request boundary rather than in the
|
|
188
|
+
* page's draft so the UI keeps working in ids, and an id that no longer resolves
|
|
189
|
+
* is left out — a mapping the service cannot expand is worse than none.
|
|
190
|
+
*/
|
|
191
|
+
export const withInvoicingQboMappedAccounts = (accountMappings, accountByQboId) => {
|
|
192
|
+
if (accountMappings == null) {
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
const expanded = { ...accountMappings };
|
|
196
|
+
Object.entries(INVOICING_QBO_MAPPED_ACCOUNT_KEY).forEach(([code, objectKey]) => {
|
|
197
|
+
const accountId = getInvoicingQboAccountMappingId(accountMappings, code);
|
|
198
|
+
if (accountId == null) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const account = accountByQboId(accountId);
|
|
202
|
+
expanded[objectKey] =
|
|
203
|
+
account != null ? toAccountPayload(account) : undefined;
|
|
204
|
+
expanded[code] = undefined;
|
|
205
|
+
});
|
|
206
|
+
return expanded;
|
|
207
|
+
};
|
|
155
208
|
export function withInvoicingQboAccountMappingId(accountMappings, code, accountId) {
|
|
209
|
+
// Both slots are written together. Setting only the bare id would leave the
|
|
210
|
+
// object shape behind, and the reader prefers it — so the selection just made
|
|
211
|
+
// would read back as whatever was mapped before.
|
|
156
212
|
const base = accountMappings ?? {};
|
|
157
213
|
switch (code) {
|
|
158
214
|
case 'deposit_account_id':
|
|
159
|
-
return {
|
|
215
|
+
return {
|
|
216
|
+
...base,
|
|
217
|
+
deposit_account: undefined,
|
|
218
|
+
deposit_account_id: accountId,
|
|
219
|
+
};
|
|
160
220
|
case 'default_income_account_id':
|
|
161
|
-
return {
|
|
221
|
+
return {
|
|
222
|
+
...base,
|
|
223
|
+
default_income_account: undefined,
|
|
224
|
+
default_income_account_id: accountId,
|
|
225
|
+
};
|
|
162
226
|
case 'discounts_account_id':
|
|
163
|
-
return {
|
|
227
|
+
return {
|
|
228
|
+
...base,
|
|
229
|
+
discounts_account: undefined,
|
|
230
|
+
discounts_account_id: accountId,
|
|
231
|
+
};
|
|
164
232
|
case 'rounding_account_id':
|
|
165
|
-
return {
|
|
233
|
+
return {
|
|
234
|
+
...base,
|
|
235
|
+
rounding_account: undefined,
|
|
236
|
+
rounding_account_id: accountId,
|
|
237
|
+
};
|
|
166
238
|
case 'write_off_account_id':
|
|
167
|
-
return {
|
|
239
|
+
return {
|
|
240
|
+
...base,
|
|
241
|
+
write_off_account: undefined,
|
|
242
|
+
write_off_account_id: accountId,
|
|
243
|
+
};
|
|
168
244
|
default:
|
|
169
245
|
return base;
|
|
170
246
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { toAmount } from '../../../commonStateTypes/amount';
|
|
2
2
|
import { defaultCustomerCurrency } from '../../../commonStateTypes/currencyHelper';
|
|
3
3
|
import { date } from '../../../zeniDayJS';
|
|
4
|
+
import { firstSelectableInvoicingId } from '../invoicingCommonPayload';
|
|
4
5
|
// ── Payload → State mappers (snake_case wire shape → camelCase domain shape) ──
|
|
5
6
|
export const mapInvoicingPlanTierPayloadToState = (payload, currencyCode, currencySymbol) => ({
|
|
6
7
|
price: toAmount(payload.price, currencyCode, currencySymbol),
|
|
@@ -10,11 +11,22 @@ export const mapInvoicingPlanTierPayloadToState = (payload, currencyCode, curren
|
|
|
10
11
|
pricingType: payload.pricing_type,
|
|
11
12
|
});
|
|
12
13
|
const mapInvoicingAccountingDetailPayloadToState = (payload) => ({
|
|
13
|
-
|
|
14
|
+
// The QuickBooks id first in both, for the reason `firstSelectableInvoicingId`
|
|
15
|
+
// documents: the form's pickers are keyed on it, so reading the internal id
|
|
16
|
+
// would leave the field blank over a mapping that exists.
|
|
17
|
+
accountingCategory1: firstSelectableInvoicingId([
|
|
18
|
+
payload.accounting_class?.qbo_id,
|
|
19
|
+
payload.accounting_class?.accounting_class_id,
|
|
20
|
+
payload.accounting_category_1,
|
|
21
|
+
]),
|
|
14
22
|
accountingCategory2: payload.accounting_category_2,
|
|
15
23
|
accountingCategory3: payload.accounting_category_3,
|
|
16
24
|
accountingCategory4: payload.accounting_category_4,
|
|
17
|
-
accountingCode:
|
|
25
|
+
accountingCode: firstSelectableInvoicingId([
|
|
26
|
+
payload.account?.qbo_id,
|
|
27
|
+
payload.account?.account_id,
|
|
28
|
+
payload.accounting_code,
|
|
29
|
+
]),
|
|
18
30
|
sku: payload.sku,
|
|
19
31
|
});
|
|
20
32
|
export const mapInvoicingProductPayloadToState = (payload) => {
|
package/lib/esm/index.js
CHANGED
|
@@ -187,13 +187,14 @@ import { clearExpenseAutomationFluxAnalysisView, fetchFluxAnalysisView, reviewFl
|
|
|
187
187
|
import { clearJeScheduleLocalData as clearExpenseAutomationJEScheduleLocalData, clearExpenseAutomationJESchedulesView, fetchJeSchedulesPage as fetchExpenseAutomationJESchedulesPage, ignoreRecommendedJeSchedule as ignoreExpenseAutomationJESchedule, initializeAccountSettingsView as initializeJeAccountSettingsView, initializeJeScheduleLocalData, removeJeScheduleTransactionKey, retryJeSchedule as retryExpenseAutomationJESchedule, saveAccountSettings as saveJeAccountSettings, saveAccountSettingsLocalData as saveJeAccountSettingsLocalData, updateJESchedulesUIState as updateExpenseAutomationJESchedulesUIState, updateJeScheduleLocalDataById, updateJeScheduleTransactionKeys, } from './view/expenseAutomationView/reducers/jeSchedulesViewReducer';
|
|
188
188
|
import { acknowledgeBulkUploadConfirmMatchComplete, bulkUploadAutomatchingTimedOut, bulkUploadReceipts, bulkUploadReceiptsFailure, bulkUploadReceiptsSuccess, clearBulkUpload, clearBulkUploadBatchDetailsForScopeChange, clearManualSearchResults, clearMissingReceiptsTabNavigation, confirmBulkUploadMatch, confirmBulkUploadMatchFailure, confirmBulkUploadMatchSuccess, fetchBulkUploadBatchDetails, fetchBulkUploadBatchDetailsFailure, fetchBulkUploadBatchDetailsSuccess, fetchBulkUploadBatches, fetchBulkUploadBatchesFailure, fetchBulkUploadBatchesSuccess, fetchCompletedTransactions, fetchCompletedTransactionsFailure, fetchCompletedTransactionsSuccess, fetchMissingReceipts as fetchExpenseAutomationMissingReceipts, fetchMoreBatchDetails, fetchMoreBatchDetailsComplete, fetchMoreBatchDetailsFailure, markBatchDetailRefreshAttempted, markMissingReceiptAsDone as markExpenseAutomationMissingReceiptAsDone, pusherBatchStatusUpdate, refreshBatchDetailsForBatchId, requestMissingReceiptsTabNavigation, restoreBulkUploadAutomatchingOnMount, restoreBulkUploadMatchingState, searchTransactionsForManualMatch, searchTransactionsForManualMatchFailure, searchTransactionsForManualMatchSuccess, setBulkUploadCompletedSubTab, setBulkUploadResultsTab, setBulkUploadSortConfig, setBulkUploadUploadedFileCount, storeBatchDetails, updateBulkUploadProgress, updateMissingReceiptUploadState as updateExpenseAutomationMissingReceiptUploadState, updateMissingReceiptsUIState as updateExpenseAutomationMissingReceiptsUIState, uploadMissingReceiptSuccess as uploadExpenseAutomationMissingReceiptSuccess, } from './view/expenseAutomationView/reducers/missingReceiptsViewReducer';
|
|
189
189
|
import { clearStatementDateConflict, confirmStatementMapping, deleteAccountStatement, excludeAccountFromReconciliation, fetchReconciliation as fetchReconciliationView, includeAccountInReconciliation, parseCombinedStatement, parseStatement, parseStatementFailure, parseStatementSuccess, reparseStatement, reparseStatementFailure, reparseStatementSuccess, resetReparseStatementStatus, saveReconciliationDetail as saveExpenseAutomationReconciliationDetail, saveReconciliationReview as saveExpenseAutomationReconciliationReview, setConnectionInProgressForAccount as setConnectionInProgressForAccountReconciliation, setStatementParseInProgress, setStatementProcessingDismissedToBackground, submitCombinedStatementUpdate, submitStatementUpdate, updateAccountReconciliationLocalData as updateExpenseAutomationAccountReconciliationLocalData, updateSelectedAccountId as updateExpenseAutomationAccountReconciliationSelectedAccountId, updateSelectedTab as updateExpenseAutomationAccountReconciliationSelectedTab, updateReconListScrollPosition as updateExpenseAutomationReconListScrollPosition, updateReviewTabSortState as updateExpenseAutomationReconReviewTabListSortState, updateReviewTabLocalData as updateExpenseAutomationReconReviewTabLocalData, updateReconcileTabListScrollState as updateExpenseAutomationReconcileTabListScrollState, updateReconcileTabListSortState as updateExpenseAutomationReconcileTabListSortState, updateReconcileTabLocalData as updateExpenseAutomationReconcileTabLocalData, updateSelectedDrawerAccountId as updateExpenseAutomationSelectedDrawerAccountId, updateNodeCollapseState, updateParsedStatementData, updateStatementProcessingBackgroundCompletedSteps, updateStatementProcessingFailed, updateStatementUpdateLocalData, updateStatementUploadChosen, uploadAccountStatement, } from './view/expenseAutomationView/reducers/reconciliationViewReducer';
|
|
190
|
-
import { backgroundRefetchReviewTab, clearExpenseAutomationTransactionsView, fetchTransactionCategorization, fetchTransactionCategorizationFailure, fetchTransactionCategorizationView, initializeTransactionCategorizationViewLocalData, markCategoryClassRecommendationsFailureForCategorization, markTransactionAsNotMiscategorized, removeTransactionFromAllTabs, saveTransactionCategorization, saveTransactionCategorizationLocalData, setAllItemsToCategoryClassInLocalDataForCategorization, setEntityRecommendationForLineIdsForCategorization, syncTransactionCategorizationFromDetailSave, updateCurrentSelectedTransactionCategorizationTab, updateSelectedCheckboxTransactionIds, updateSelectedCustomerForTransaction, updateSelectedTransactionId, updateSelectedVendorForTransaction, updateTransactionCategorization, updateTransactionCategorizationCompletedSubTab, updateTransactionCategorizationSaveStatus, updateTransactionCategorizationUIState, updateTransactionCategorizationUploadReceiptState, updateTransactionFilters, uploadTransactionCategorizationReceiptSuccess, } from './view/expenseAutomationView/reducers/transactionsViewReducer';
|
|
190
|
+
import { backgroundRefetchReviewTab, clearExpenseAutomationTransactionsView, fetchTransactionCategorization, fetchTransactionCategorizationFailure, fetchTransactionCategorizationView, initializeTransactionCategorizationViewLocalData, markCategoryClassRecommendationsFailureForCategorization, markTransactionAsNotMiscategorized, removeTransactionFromAllTabs, saveTransactionCategorization, saveTransactionCategorizationLocalData, setAllItemsToCategoryClassInLocalDataForCategorization, setEntityRecommendationForLineIdsForCategorization, syncTransactionCategorizationFromDetailSave, updateCurrentSelectedTransactionCategorizationTab, updateSelectedCheckboxTransactionIds, updateSelectedCustomerForTransaction, updateSelectedTransactionId, updateSelectedVendorForTransaction, updateTransactionCategorization, updateTransactionCategorizationCompletedSubTab, updateTransactionCategorizationSaveStatus, updateTransactionCategorizationUIState, updateTransactionCategorizationUploadReceiptState, updateTransactionFilters, updateTransactionGroup, uploadTransactionCategorizationReceiptSuccess, } from './view/expenseAutomationView/reducers/transactionsViewReducer';
|
|
191
191
|
import { getExpenseAutomationFluxAnalysisView } from './view/expenseAutomationView/selectors/fluxAnalysisViewSelector';
|
|
192
192
|
import { getExpenseAutomationReconciliationView, getLedgerAccountsForCombinedStatementMapping, getReparseStatementStatusByAccountId, getUnavailableLedgerAccountIds, isAccountReconReport, } from './view/expenseAutomationView/selectors/reconciliationViewSelector';
|
|
193
193
|
import { getExpenseAutomationTransactionView, getLastTransferEntryReplacement, } from './view/expenseAutomationView/selectors/transactionCategorizationSelector';
|
|
194
194
|
import { applyTransactionFilters } from './view/expenseAutomationView/transactionFilterHelpers';
|
|
195
195
|
import { TRANSACTION_FILTER_CATEGORIES, } from './view/expenseAutomationView/transactionFilterTypes';
|
|
196
196
|
import { DEFAULT_COMPLETED_SUB_TAB, toCompletedSubTab, } from './view/expenseAutomationView/types/completedSubTab';
|
|
197
|
+
import { DEFAULT_TRANSACTION_GROUP, TRANSACTION_GROUPS, toTransactionGroup, } from './view/expenseAutomationView/types/transactionGroup';
|
|
197
198
|
import { toJEScheduleMainTab as toExpenseAutomationJEScheduleMainTab, toJEScheduleSortKey as toExpenseAutomationJEScheduleSortKey, } from './view/expenseAutomationView/types/jeSchedulesViewState';
|
|
198
199
|
import { BATCH_FILE_STATUSES, isUnmatchedTabFileStatus, toBatchFileStatus, toBatchStatusValue, toBulkUploadPhase, toBulkUploadResultsTab, toBulkUploadSortKey, toMatchSource, toMissingReceiptsTab, } from './view/expenseAutomationView/types/missingReceiptsViewState';
|
|
199
200
|
import { toMissingReceiptsSortKey as toExpenseAutomationMissingReceiptsSortKey, } from './view/expenseAutomationView/types/missingReceiptsViewState';
|
|
@@ -482,7 +483,7 @@ export { stringToUnion, stringToUnionStrict };
|
|
|
482
483
|
export { fetchMonthEndCloseChecks, fetchMonthClosePerformanceTrend, getMonthEndCloseChecksViewByTenantId, ALL_MONTH_END_CLOSE_CHECKS_FREQUENCY, };
|
|
483
484
|
export {
|
|
484
485
|
// Bulk Upload Types
|
|
485
|
-
BATCH_FILE_STATUSES, isUnmatchedTabFileStatus, DEFAULT_COMPLETED_SUB_TAB, toBatchFileStatus, toBatchStatusValue, toBulkUploadPhase, toBulkUploadResultsTab, toBulkUploadSortKey, toCompletedSubTab, toMatchSource, toMissingReceiptsTab, toExpenseAutomationJEScheduleMainTab, toExpenseAutomationJEScheduleSortKey, getExpenseAutomationView, toExpenseAutomationMissingReceiptsSortKey, toExpenseAutomationTransactionsTabKey, toExpenseAutomationViewType, uploadExpenseAutomationMissingReceiptSuccess, markExpenseAutomationMissingReceiptAsDone, fetchAllExpenseAutomationTabs, refreshExpenseAutomationCurrentTab, updateCurrentSelectedTransactionCategorizationTab, updateTransactionCategorizationCompletedSubTab, fetchExpenseAutomationMissingReceipts,
|
|
486
|
+
BATCH_FILE_STATUSES, isUnmatchedTabFileStatus, DEFAULT_COMPLETED_SUB_TAB, DEFAULT_TRANSACTION_GROUP, TRANSACTION_GROUPS, toTransactionGroup, toBatchFileStatus, toBatchStatusValue, toBulkUploadPhase, toBulkUploadResultsTab, toBulkUploadSortKey, toCompletedSubTab, toMatchSource, toMissingReceiptsTab, toExpenseAutomationJEScheduleMainTab, toExpenseAutomationJEScheduleSortKey, getExpenseAutomationView, toExpenseAutomationMissingReceiptsSortKey, toExpenseAutomationTransactionsTabKey, toExpenseAutomationViewType, uploadExpenseAutomationMissingReceiptSuccess, markExpenseAutomationMissingReceiptAsDone, fetchAllExpenseAutomationTabs, refreshExpenseAutomationCurrentTab, updateCurrentSelectedTransactionCategorizationTab, updateTransactionCategorizationCompletedSubTab, updateTransactionGroup, fetchExpenseAutomationMissingReceipts,
|
|
486
487
|
// Bulk Upload Actions
|
|
487
488
|
bulkUploadReceipts, bulkUploadAutomatchingTimedOut, bulkUploadReceiptsSuccess, bulkUploadReceiptsFailure, restoreBulkUploadAutomatchingOnMount, restoreBulkUploadMatchingState, updateBulkUploadProgress, pusherBatchStatusUpdate, requestMissingReceiptsTabNavigation, clearMissingReceiptsTabNavigation, fetchBulkUploadBatchDetails, fetchBulkUploadBatchDetailsSuccess, fetchBulkUploadBatchDetailsFailure, storeBatchDetails, fetchMoreBatchDetails, fetchMoreBatchDetailsComplete, fetchMoreBatchDetailsFailure, fetchBulkUploadBatches, fetchBulkUploadBatchesSuccess, fetchBulkUploadBatchesFailure, clearBulkUploadBatchDetailsForScopeChange, markBatchDetailRefreshAttempted, refreshBatchDetailsForBatchId, confirmBulkUploadMatch, confirmBulkUploadMatchSuccess, confirmBulkUploadMatchFailure, setBulkUploadResultsTab, setBulkUploadCompletedSubTab, setBulkUploadSortConfig, setBulkUploadUploadedFileCount, clearBulkUpload, searchTransactionsForManualMatch, searchTransactionsForManualMatchSuccess, searchTransactionsForManualMatchFailure, clearManualSearchResults, acknowledgeBulkUploadConfirmMatchComplete, fetchCompletedTransactions, fetchCompletedTransactionsSuccess, fetchCompletedTransactionsFailure, fetchFluxAnalysisView, clearExpenseAutomationFluxAnalysisView, updateOperatingExpensesIdsForReview as updateFluxOperatingExpensesIdsForReview, updateSelectedSectionIdsForReview as updateFluxAnalysisSelectedSectionIdsForReview, reviewFluxAnalysisView, updateExpenseAutomationMissingReceiptUploadState, updateExpenseAutomationMissingReceiptsUIState, updateTransactionCategorizationUploadReceiptState, uploadTransactionCategorizationReceiptSuccess, getExpenseAutomationFluxAnalysisView, updateCurrentSelectedView, updateCurrentSelectedPeriod, getExpenseAutomationTransactionView, getLastTransferEntryReplacement, updateFluxAnalysisViewUIState, updateFluxAnalysisViewPageMetaData, MAX_SELECTION_LIMIT, checkIfAllLineItemsAreCategoryClassFilled, getLineItemsByTransactionIdsFromLocalData, isAnyItemWithUncategorizedExpenseAccount, excludeAccountFromReconciliation, includeAccountInReconciliation, saveExpenseAutomationReconciliationDetail, updateExpenseAutomationReconcileTabListScrollState, updateExpenseAutomationReconReviewTabListSortState, updateExpenseAutomationReconcileTabListSortState, updateExpenseAutomationReconcileTabLocalData, updateExpenseAutomationAccountReconciliationSelectedTab, updateExpenseAutomationAccountReconciliationSelectedAccountId, getExpenseAutomationReconciliationView, getLedgerAccountsForCombinedStatementMapping, getReparseStatementStatusByAccountId, getUnavailableLedgerAccountIds, fetchReconciliationView, uploadAccountStatementIntoDocumentAI, updateExpenseAutomationReconListScrollPosition, setConnectionInProgressForAccountReconciliation, getAccountReconByAccountIdAndSelectedPeriod, toReconciliationTabsType, isAccountReconReport, updateExpenseAutomationReconReviewTabLocalData, updateExpenseAutomationSelectedDrawerAccountId, updateStatementProcessingFailed, saveExpenseAutomationReconciliationReview, updateExpenseAutomationAccountReconciliationLocalData, toReconciliationAccountSource, deleteAccountStatement, uploadAccountStatement, parseCombinedStatement, parseStatement, parseStatementSuccess, parseStatementFailure, reparseStatement, reparseStatementSuccess, reparseStatementFailure, resetReparseStatementStatus, updateParsedStatementData, updateNodeCollapseState, updateStatementUploadChosen, submitCombinedStatementUpdate, submitStatementUpdate, clearStatementDateConflict, confirmStatementMapping, updateStatementUpdateLocalData, isReviewTransactionBankTransferType, isReviewTransactionBillPaymentType, isReviewTransactionCreditCardPaymentType, isReviewTransactionDepositType, isReviewTransactionExpenseType, isReviewTransactionCreditCardCreditType, setStatementParseInProgress, setStatementProcessingDismissedToBackground, updateStatementProcessingBackgroundCompletedSteps, };
|
|
488
489
|
export { fetchTransactionCategorization, fetchTransactionCategorizationView, updateTransactionCategorizationUIState, updateTransactionFilters, updateSelectedCheckboxTransactionIds, markCategoryClassRecommendationsFailureForCategorization, setEntityRecommendationForLineIdsForCategorization, initializeTransactionCategorizationViewLocalData, setAllItemsToCategoryClassInLocalDataForCategorization, saveTransactionCategorizationLocalData, fetchTransactionCategorizationFailure, saveTransactionCategorization, updateTransactionCategorization, updateTransactionCategorizationSaveStatus, markTransactionAsNotMiscategorized, updateSelectedVendorForTransaction, updateSelectedCustomerForTransaction, updateSelectedTransactionId, syncTransactionCategorizationFromDetailSave, backgroundRefetchReviewTab, createTransferEntry, createTransferEntryFailure, createTransferEntryReplacedTransaction, createTransferEntrySuccess, resetCreateTransferEntryStatus, clearTransferEntryRouteReplacement, fetchAccountsForTransferFlow, removeTransactionFromAllTabs, clearExpenseAutomationTransactionsView, toTransactionsSortKey, };
|
|
@@ -27,6 +27,7 @@ export const backgroundRefetchReviewTabEpic = (actions$, state$, zeniAPI) => act
|
|
|
27
27
|
page_token: pageToken,
|
|
28
28
|
page_size: PAGE_SIZE,
|
|
29
29
|
search_text: reviewUiState.searchString,
|
|
30
|
+
transaction_group: state.expenseAutomationTransactionsViewState.selectedTransactionGroup,
|
|
30
31
|
};
|
|
31
32
|
return zeniAPI.getJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/expense-automation/transactions?query=${encodeURIComponent(JSON.stringify(queryParam))}`);
|
|
32
33
|
};
|
|
@@ -36,6 +36,7 @@ groupBy((action) => action.payload.selectedTab), mergeMap((group$) => group$.pip
|
|
|
36
36
|
sub_tab: selectedTab === 'autoCategorized'
|
|
37
37
|
? expenseAutomationTransactionsViewState.selectedTransactionCategorizationCompletedSubTab
|
|
38
38
|
: DEFAULT_COMPLETED_SUB_TAB,
|
|
39
|
+
transaction_group: expenseAutomationTransactionsViewState.selectedTransactionGroup,
|
|
39
40
|
};
|
|
40
41
|
return zeniAPI
|
|
41
42
|
.getJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/expense-automation/transactions?query=${encodeURIComponent(JSON.stringify(queryParam))}`)
|
|
@@ -6,7 +6,18 @@ import { isVendorTransaction } from '../../../entity/transaction/stateTypes/vend
|
|
|
6
6
|
import { clearAll } from '../../../rootActions';
|
|
7
7
|
import { filterAutoTabLineItems, mergeTabSpecificLineItems, removeTransactionFromTabView, setEntityRecommendationForLineIdInLocalData, shouldAutoSelectAndAdd, toSetAllLineItemsToCategoryClass, toTransactionDetailLocalData, } from '../helpers/transactionCategorizationLocalDataHelper';
|
|
8
8
|
import { DEFAULT_COMPLETED_SUB_TAB, } from '../types/completedSubTab';
|
|
9
|
+
import { DEFAULT_TRANSACTION_GROUP, } from '../types/transactionGroup';
|
|
9
10
|
import { TRANSACTIONS_TABS, } from '../types/transactionsViewState';
|
|
11
|
+
export const initialUIState = {
|
|
12
|
+
sortKey: 'date',
|
|
13
|
+
sortOrder: 'descending',
|
|
14
|
+
searchString: '',
|
|
15
|
+
nodeCollapseState: {},
|
|
16
|
+
scrollPosition: { scrollTop: 0, scrollLeft: undefined },
|
|
17
|
+
pageToken: null,
|
|
18
|
+
totalCount: {},
|
|
19
|
+
limit: 10,
|
|
20
|
+
};
|
|
10
21
|
export const initialTransactionTabViewState = {
|
|
11
22
|
saveStatus: {
|
|
12
23
|
fetchState: 'Not-Started',
|
|
@@ -20,16 +31,7 @@ export const initialTransactionTabViewState = {
|
|
|
20
31
|
fetchState: 'Not-Started',
|
|
21
32
|
error: undefined,
|
|
22
33
|
},
|
|
23
|
-
uiState: {
|
|
24
|
-
sortKey: 'date',
|
|
25
|
-
sortOrder: 'descending',
|
|
26
|
-
searchString: '',
|
|
27
|
-
nodeCollapseState: {},
|
|
28
|
-
scrollPosition: { scrollTop: 0, scrollLeft: undefined },
|
|
29
|
-
pageToken: null,
|
|
30
|
-
totalCount: {},
|
|
31
|
-
limit: 10,
|
|
32
|
-
},
|
|
34
|
+
uiState: { ...initialUIState },
|
|
33
35
|
filters: {
|
|
34
36
|
categoryCombinationOperator: 'AND',
|
|
35
37
|
categories: [],
|
|
@@ -55,6 +57,7 @@ export const initialState = {
|
|
|
55
57
|
},
|
|
56
58
|
selectedTransactionCategorizationTab: 'review',
|
|
57
59
|
selectedTransactionCategorizationCompletedSubTab: DEFAULT_COMPLETED_SUB_TAB,
|
|
60
|
+
selectedTransactionGroup: DEFAULT_TRANSACTION_GROUP,
|
|
58
61
|
transactionCategorizationView: {
|
|
59
62
|
review: { ...initialTransactionTabViewState },
|
|
60
63
|
autoCategorized: { ...initialTransactionTabViewState },
|
|
@@ -400,6 +403,48 @@ const expenseAutomationTransactionsView = createSlice({
|
|
|
400
403
|
return { payload: { newSubTab } };
|
|
401
404
|
},
|
|
402
405
|
},
|
|
406
|
+
updateTransactionGroup: {
|
|
407
|
+
reducer(draft, action) {
|
|
408
|
+
const { newGroup } = action.payload;
|
|
409
|
+
if (draft.selectedTransactionGroup === newGroup) {
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
draft.selectedTransactionGroup = newGroup;
|
|
413
|
+
// A group change re-scopes the entire dataset on the backend (by
|
|
414
|
+
// account_type), so every parent tab, sub-tab snapshot and count is now
|
|
415
|
+
// stale. Drop the sub-tab cache and parent counts, and reset each tab's
|
|
416
|
+
// paginated data slots so the view epic's cache check falls through to a
|
|
417
|
+
// fresh page-1 fetch. User preferences (search, sort, limit, collapse
|
|
418
|
+
// state) are intentionally preserved, mirroring the sub-tab switch.
|
|
419
|
+
draft.autoCategorizedSubTabCache = {};
|
|
420
|
+
draft.parentTotalCountByTab = { review: {}, autoCategorized: {} };
|
|
421
|
+
TRANSACTIONS_TABS.forEach((tab) => {
|
|
422
|
+
const view = draft.transactionCategorizationView[tab];
|
|
423
|
+
// Reset uiState to its initial values but keep the user prefs (search,
|
|
424
|
+
// sort, limit, collapse) in place -- mirroring the sub-tab switch. Then
|
|
425
|
+
// drop the loaded rows and any stale selection pointing at a now-absent
|
|
426
|
+
// row to avoid transient invalid state.
|
|
427
|
+
const { searchString, sortKey, sortOrder, nodeCollapseState, limit } = view.uiState;
|
|
428
|
+
view.uiState = {
|
|
429
|
+
...initialUIState,
|
|
430
|
+
searchString,
|
|
431
|
+
sortKey,
|
|
432
|
+
sortOrder,
|
|
433
|
+
nodeCollapseState,
|
|
434
|
+
limit,
|
|
435
|
+
};
|
|
436
|
+
view.transactionIdsBySelectedPeriod = {};
|
|
437
|
+
view.fetchState = 'Not-Started';
|
|
438
|
+
view.error = undefined;
|
|
439
|
+
view.selectedTransactionId = undefined;
|
|
440
|
+
view.selectedTransactionLineId = undefined;
|
|
441
|
+
view.selectedCheckBoxTransactionIds = [];
|
|
442
|
+
});
|
|
443
|
+
},
|
|
444
|
+
prepare(newGroup) {
|
|
445
|
+
return { payload: { newGroup } };
|
|
446
|
+
},
|
|
447
|
+
},
|
|
403
448
|
updateTransactionCategorization: {
|
|
404
449
|
reducer(draft, action) {
|
|
405
450
|
const selectedTab = action.payload.selectedTab;
|
|
@@ -1075,5 +1120,5 @@ const expenseAutomationTransactionsView = createSlice({
|
|
|
1075
1120
|
});
|
|
1076
1121
|
},
|
|
1077
1122
|
});
|
|
1078
|
-
export const { fetchTransactionCategorization, updateTransactionCategorizationUIState, updateTransactionFilters, initializeTransactionCategorizationViewLocalData, saveTransactionCategorizationLocalData, fetchTransactionCategorizationFailure, saveTransactionCategorization, updateTransactionCategorization, updateCurrentSelectedTransactionCategorizationTab, updateTransactionCategorizationCompletedSubTab, updateTransactionCategorizationSaveStatus, markTransactionAsNotMiscategorized, updateStatusForTransactionNotMiscategorizedUpdateForCategorization, updateSelectedVendorForTransaction, updateSelectedCustomerForTransaction, setAllItemsToCategoryClassInLocalDataForCategorization, updateTotalCountForTransactionCategorization, updateParentTotalCountForTab, fetchTransactionCategorizationSuccess, resetOtherTabsFetchState, fetchTransactionCategorizationView, updateSelectedCheckboxTransactionIds, clearExpenseAutomationTransactionsViewPerTabView, clearExpenseAutomationTransactionsView, setEntityRecommendationForLineIdsForCategorization, markCategoryClassRecommendationsInProgressForCategorization, markCategoryClassRecommendationsCompletedForCategorization, markCategoryClassRecommendationsFailureForCategorization, updateSelectedTransactionId, syncTransactionCategorizationFromDetailSave, backgroundRefetchReviewTab, removeTransactionFromAllTabs, updateTransactionCategorizationUploadReceiptState, uploadTransactionCategorizationReceiptSuccess, } = expenseAutomationTransactionsView.actions;
|
|
1123
|
+
export const { fetchTransactionCategorization, updateTransactionCategorizationUIState, updateTransactionFilters, initializeTransactionCategorizationViewLocalData, saveTransactionCategorizationLocalData, fetchTransactionCategorizationFailure, saveTransactionCategorization, updateTransactionCategorization, updateCurrentSelectedTransactionCategorizationTab, updateTransactionCategorizationCompletedSubTab, updateTransactionGroup, updateTransactionCategorizationSaveStatus, markTransactionAsNotMiscategorized, updateStatusForTransactionNotMiscategorizedUpdateForCategorization, updateSelectedVendorForTransaction, updateSelectedCustomerForTransaction, setAllItemsToCategoryClassInLocalDataForCategorization, updateTotalCountForTransactionCategorization, updateParentTotalCountForTab, fetchTransactionCategorizationSuccess, resetOtherTabsFetchState, fetchTransactionCategorizationView, updateSelectedCheckboxTransactionIds, clearExpenseAutomationTransactionsViewPerTabView, clearExpenseAutomationTransactionsView, setEntityRecommendationForLineIdsForCategorization, markCategoryClassRecommendationsInProgressForCategorization, markCategoryClassRecommendationsCompletedForCategorization, markCategoryClassRecommendationsFailureForCategorization, updateSelectedTransactionId, syncTransactionCategorizationFromDetailSave, backgroundRefetchReviewTab, removeTransactionFromAllTabs, updateTransactionCategorizationUploadReceiptState, uploadTransactionCategorizationReceiptSuccess, } = expenseAutomationTransactionsView.actions;
|
|
1079
1124
|
export default expenseAutomationTransactionsView.reducer;
|
|
@@ -86,7 +86,7 @@ export const toTransactionView = (transaction, transactionLocalData) => {
|
|
|
86
86
|
export { getLastTransferEntryReplacement } from '../../createTransferEntry/createTransferEntrySelector';
|
|
87
87
|
export function getExpenseAutomationTransactionView(state) {
|
|
88
88
|
const { accountState, classState, classListState, accountListState, createTransferEntryState, expenseAutomationTransactionsViewState, expenseAutomationViewState, projectState, projectListState, transactionState, monthEndCloseChecksState, } = state;
|
|
89
|
-
const { selectedTransactionCategorizationCompletedSubTab, selectedTransactionCategorizationTab, } = expenseAutomationTransactionsViewState;
|
|
89
|
+
const { selectedTransactionCategorizationCompletedSubTab, selectedTransactionCategorizationTab, selectedTransactionGroup, } = expenseAutomationTransactionsViewState;
|
|
90
90
|
const { uiState, refreshStatus, saveStatus, markAsNotMiscategorizedStatus, transactionIdsBySelectedPeriod, transactionReviewLocalDataById, selectedCheckBoxTransactionIds, transactionIdsWithUnsavedData, uploadReceiptStatusById, selectedTransactionId, selectedTransactionLineId, filters, } = expenseAutomationTransactionsViewState.transactionCategorizationView[selectedTransactionCategorizationTab];
|
|
91
91
|
const uncategorizedIncomeExpense = getUncategorizedAccounts(accountState, accountListState, 'accountList');
|
|
92
92
|
const accountList = getAccountList(accountState, accountListState, 'accountList');
|
|
@@ -240,5 +240,6 @@ export function getExpenseAutomationTransactionView(state) {
|
|
|
240
240
|
creditCardTransferAccountsList,
|
|
241
241
|
transferAccounts: transferAccountsList,
|
|
242
242
|
selectedTransactionCategorizationCompletedSubTab,
|
|
243
|
+
selectedTransactionGroup,
|
|
243
244
|
};
|
|
244
245
|
}
|