@zeniai/client-epic-state 5.2.10-betaAK0 → 5.2.10-betaAK2
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 +15 -3
- package/lib/entity/invoicing/invoicingCommonPayload.js +18 -2
- package/lib/epic.d.ts +1 -1
- package/lib/epic.js +10 -5
- package/lib/esm/entity/invoicing/invoicingCommonPayload.js +16 -1
- package/lib/esm/epic.js +6 -1
- package/lib/esm/index.js +5 -3
- package/lib/esm/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +18 -4
- package/lib/esm/view/invoicing/settingsView/epics/connectInvoicingStripeEpic.js +27 -0
- package/lib/esm/view/invoicing/settingsView/epics/disconnectInvoicingStripeEpic.js +24 -0
- package/lib/esm/view/invoicing/settingsView/epics/fetchInvoicingSettingsEpic.js +19 -0
- package/lib/esm/view/invoicing/settingsView/epics/saveInvoicingSettingsEpic.js +54 -0
- package/lib/esm/view/invoicing/settingsView/epics/settingsViewActionType.js +1 -0
- package/lib/esm/view/invoicing/settingsView/epics/submitInvoicingQboSettingsEpic.js +22 -0
- package/lib/esm/view/invoicing/settingsView/invoicingQboSettingsFormConfig.js +52 -0
- package/lib/esm/view/invoicing/settingsView/settingsViewReducer.js +21 -1
- package/lib/esm/view/invoicing/settingsView/settingsViewSelector.js +48 -2
- package/lib/index.d.ts +8 -4
- package/lib/index.js +22 -11
- package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +18 -4
- package/lib/view/invoicing/settingsView/epics/connectInvoicingStripeEpic.d.ts +6 -0
- package/lib/view/invoicing/settingsView/epics/connectInvoicingStripeEpic.js +31 -0
- package/lib/view/invoicing/settingsView/epics/disconnectInvoicingStripeEpic.d.ts +11 -0
- package/lib/view/invoicing/settingsView/epics/disconnectInvoicingStripeEpic.js +28 -0
- package/lib/view/invoicing/settingsView/epics/fetchInvoicingSettingsEpic.d.ts +6 -0
- package/lib/view/invoicing/settingsView/epics/fetchInvoicingSettingsEpic.js +23 -0
- package/lib/view/invoicing/settingsView/epics/saveInvoicingSettingsEpic.d.ts +6 -0
- package/lib/view/invoicing/settingsView/epics/saveInvoicingSettingsEpic.js +58 -0
- package/lib/view/invoicing/settingsView/epics/settingsViewActionType.d.ts +5 -0
- package/lib/view/invoicing/settingsView/epics/settingsViewActionType.js +2 -0
- package/lib/view/invoicing/settingsView/epics/submitInvoicingQboSettingsEpic.d.ts +11 -0
- package/lib/view/invoicing/settingsView/epics/submitInvoicingQboSettingsEpic.js +26 -0
- package/lib/view/invoicing/settingsView/invoicingQboSettingsFormConfig.d.ts +28 -0
- package/lib/view/invoicing/settingsView/invoicingQboSettingsFormConfig.js +57 -0
- package/lib/view/invoicing/settingsView/settingsViewReducer.d.ts +4 -1
- package/lib/view/invoicing/settingsView/settingsViewReducer.js +22 -2
- package/lib/view/invoicing/settingsView/settingsViewSelector.d.ts +35 -0
- package/lib/view/invoicing/settingsView/settingsViewSelector.js +49 -2
- package/lib/view/invoicing/settingsView/settingsViewState.d.ts +8 -0
- package/package.json +1 -1
- package/lib/esm/view/invoicing/settingsView/settingsViewEpics.js +0 -107
- package/lib/view/invoicing/settingsView/settingsViewEpics.d.ts +0 -12
- package/lib/view/invoicing/settingsView/settingsViewEpics.js +0 -114
|
@@ -107,7 +107,6 @@ export interface InvoicingQboAccountMappingsPayload {
|
|
|
107
107
|
default_income_account_id?: string | null;
|
|
108
108
|
deposit_account?: Partial<AccountBasePayload> | null;
|
|
109
109
|
deposit_account_id?: string | null;
|
|
110
|
-
deposit_account_overrides?: Record<string, string> | null;
|
|
111
110
|
discounts_account?: Partial<AccountBasePayload> | null;
|
|
112
111
|
discounts_account_id?: string | null;
|
|
113
112
|
rounding_account?: Partial<AccountBasePayload> | null;
|
|
@@ -131,7 +130,20 @@ export declare const firstSelectableInvoicingId: (candidates: readonly (string |
|
|
|
131
130
|
* Read one account-mapping dropdown value by its config `code` (the codes in
|
|
132
131
|
* `qbo_account_mapping_kind_options` match the `account_mappings` field names).
|
|
133
132
|
*/
|
|
134
|
-
export declare function getInvoicingQboAccountMappingId(accountMappings: InvoicingQboAccountMappingsPayload | undefined, code:
|
|
133
|
+
export declare function getInvoicingQboAccountMappingId(accountMappings: InvoicingQboAccountMappingsPayload | undefined, code: InvoicingQboAccountMappingCode): string | undefined;
|
|
134
|
+
/**
|
|
135
|
+
* Return a copy of the account mappings with the dropdown slot named by
|
|
136
|
+
* `code` set to `accountId`. Unknown codes leave the mappings unchanged.
|
|
137
|
+
*/
|
|
138
|
+
/** Every account-mapping slot the page can set, in the order it renders them. */
|
|
139
|
+
export declare const INVOICING_QBO_ACCOUNT_MAPPING_CODES: readonly ["deposit_account_id", "default_income_account_id", "discounts_account_id", "rounding_account_id", "write_off_account_id"];
|
|
140
|
+
export type InvoicingQboAccountMappingCode = (typeof INVOICING_QBO_ACCOUNT_MAPPING_CODES)[number];
|
|
141
|
+
/**
|
|
142
|
+
* Narrows a mapping code that arrived from the config API. Anything the client
|
|
143
|
+
* has no slot for returns undefined rather than being cast through: a code we
|
|
144
|
+
* cannot place is a mapping we cannot read or write.
|
|
145
|
+
*/
|
|
146
|
+
export declare const toInvoicingQboAccountMappingCode: (code: string) => InvoicingQboAccountMappingCode | undefined;
|
|
135
147
|
/**
|
|
136
148
|
* Expand each mapping from the id the picker submitted to the whole account, the
|
|
137
149
|
* shape the service stores. Written at the request boundary rather than in the
|
|
@@ -139,7 +151,7 @@ export declare function getInvoicingQboAccountMappingId(accountMappings: Invoici
|
|
|
139
151
|
* is left out — a mapping the service cannot expand is worse than none.
|
|
140
152
|
*/
|
|
141
153
|
export declare const withInvoicingQboMappedAccounts: (accountMappings: InvoicingQboAccountMappingsPayload | undefined, accountByQboId: (qboId: string) => AccountBase | undefined) => InvoicingQboAccountMappingsPayload | undefined;
|
|
142
|
-
export declare function withInvoicingQboAccountMappingId(accountMappings: InvoicingQboAccountMappingsPayload | undefined, code:
|
|
154
|
+
export declare function withInvoicingQboAccountMappingId(accountMappings: InvoicingQboAccountMappingsPayload | undefined, code: InvoicingQboAccountMappingCode, accountId: string): InvoicingQboAccountMappingsPayload;
|
|
143
155
|
export interface InvoicingQboSettingsPayload {
|
|
144
156
|
account_mappings?: InvoicingQboAccountMappingsPayload | null;
|
|
145
157
|
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.withInvoicingQboMappedAccounts = exports.toInvoicingQboAccountMappingCode = exports.INVOICING_QBO_ACCOUNT_MAPPING_CODES = 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;
|
|
@@ -200,6 +200,21 @@ function getInvoicingQboAccountMappingId(accountMappings, code) {
|
|
|
200
200
|
* Return a copy of the account mappings with the dropdown slot named by
|
|
201
201
|
* `code` set to `accountId`. Unknown codes leave the mappings unchanged.
|
|
202
202
|
*/
|
|
203
|
+
/** Every account-mapping slot the page can set, in the order it renders them. */
|
|
204
|
+
exports.INVOICING_QBO_ACCOUNT_MAPPING_CODES = [
|
|
205
|
+
'deposit_account_id',
|
|
206
|
+
'default_income_account_id',
|
|
207
|
+
'discounts_account_id',
|
|
208
|
+
'rounding_account_id',
|
|
209
|
+
'write_off_account_id',
|
|
210
|
+
];
|
|
211
|
+
/**
|
|
212
|
+
* Narrows a mapping code that arrived from the config API. Anything the client
|
|
213
|
+
* has no slot for returns undefined rather than being cast through: a code we
|
|
214
|
+
* cannot place is a mapping we cannot read or write.
|
|
215
|
+
*/
|
|
216
|
+
const toInvoicingQboAccountMappingCode = (code) => exports.INVOICING_QBO_ACCOUNT_MAPPING_CODES.find((knownCode) => knownCode === code);
|
|
217
|
+
exports.toInvoicingQboAccountMappingCode = toInvoicingQboAccountMappingCode;
|
|
203
218
|
/** The object key holding the mapped account for each `*_account_id` code. */
|
|
204
219
|
const INVOICING_QBO_MAPPED_ACCOUNT_KEY = {
|
|
205
220
|
default_income_account_id: 'default_income_account',
|
|
@@ -219,7 +234,8 @@ const withInvoicingQboMappedAccounts = (accountMappings, accountByQboId) => {
|
|
|
219
234
|
return undefined;
|
|
220
235
|
}
|
|
221
236
|
const expanded = { ...accountMappings };
|
|
222
|
-
|
|
237
|
+
exports.INVOICING_QBO_ACCOUNT_MAPPING_CODES.forEach((code) => {
|
|
238
|
+
const objectKey = INVOICING_QBO_MAPPED_ACCOUNT_KEY[code];
|
|
223
239
|
const accountId = getInvoicingQboAccountMappingId(accountMappings, code);
|
|
224
240
|
if (accountId == null) {
|
|
225
241
|
return;
|
package/lib/epic.d.ts
CHANGED
|
@@ -265,7 +265,7 @@ import { ActionType as InvoicingTransactionListViewActionType } from './view/inv
|
|
|
265
265
|
import { ActionType as InvoicingPaymentActionActionType } from './view/invoicing/paymentAction/paymentActionEpic';
|
|
266
266
|
import { ActionType as InvoicingPromotionalCreditActionActionType } from './view/invoicing/promotionalCreditAction/promotionalCreditActionEpic';
|
|
267
267
|
import { ActionType as RecordPaymentActionType } from './view/invoicing/recordPayment/recordPaymentEpic';
|
|
268
|
-
import {
|
|
268
|
+
import { InvoicingSettingsViewActionType } from './view/invoicing/settingsView/epics/settingsViewActionType';
|
|
269
269
|
import { ActionType as InvoicingSubscriptionActionActionType } from './view/invoicing/subscriptionAction/subscriptionActionEpic';
|
|
270
270
|
import { ActionType as FetchMonthClosePerformanceTrendActionType } from './view/monthEndCloseChecksView/epics/fetchMonthClosePerformanceTrendEpic';
|
|
271
271
|
import { ActionType as FetchMonthEndCloseChecksActionType } from './view/monthEndCloseChecksView/epics/fetchMonthEndCloseChecksEpic';
|
package/lib/epic.js
CHANGED
|
@@ -313,8 +313,12 @@ const issueCreditNoteEpic_1 = require("./view/invoicing/issueCreditNote/issueCre
|
|
|
313
313
|
const paymentActionEpic_1 = require("./view/invoicing/paymentAction/paymentActionEpic");
|
|
314
314
|
const promotionalCreditActionEpic_1 = require("./view/invoicing/promotionalCreditAction/promotionalCreditActionEpic");
|
|
315
315
|
const recordPaymentEpic_1 = require("./view/invoicing/recordPayment/recordPaymentEpic");
|
|
316
|
+
const connectInvoicingStripeEpic_1 = require("./view/invoicing/settingsView/epics/connectInvoicingStripeEpic");
|
|
317
|
+
const disconnectInvoicingStripeEpic_1 = require("./view/invoicing/settingsView/epics/disconnectInvoicingStripeEpic");
|
|
318
|
+
const fetchInvoicingSettingsEpic_1 = require("./view/invoicing/settingsView/epics/fetchInvoicingSettingsEpic");
|
|
319
|
+
const saveInvoicingSettingsEpic_1 = require("./view/invoicing/settingsView/epics/saveInvoicingSettingsEpic");
|
|
320
|
+
const submitInvoicingQboSettingsEpic_1 = require("./view/invoicing/settingsView/epics/submitInvoicingQboSettingsEpic");
|
|
316
321
|
const invoicingBrandingFormEpics_1 = require("./view/invoicing/settingsView/invoicingBrandingFormEpics");
|
|
317
|
-
const settingsViewEpics_1 = require("./view/invoicing/settingsView/settingsViewEpics");
|
|
318
322
|
const subscriptionActionEpic_1 = require("./view/invoicing/subscriptionAction/subscriptionActionEpic");
|
|
319
323
|
const fetchMonthClosePerformanceTrendEpic_1 = require("./view/monthEndCloseChecksView/epics/fetchMonthClosePerformanceTrendEpic");
|
|
320
324
|
const fetchMonthEndCloseChecksEpic_1 = require("./view/monthEndCloseChecksView/epics/fetchMonthEndCloseChecksEpic");
|
|
@@ -762,7 +766,7 @@ const ALL_EPICS = [
|
|
|
762
766
|
confirmBulkUploadMatchEpic_1.confirmBulkUploadMatchEpic,
|
|
763
767
|
confirmCardSetupIntentEpic_1.confirmCardSetupIntentEpic,
|
|
764
768
|
confirmStatementMappingEpic_1.confirmStatementMappingEpic,
|
|
765
|
-
|
|
769
|
+
connectInvoicingStripeEpic_1.connectInvoicingStripeEpic,
|
|
766
770
|
convertAmountToHomeCurrencyEpic_1.convertAmountToHomeCurrencyEpic,
|
|
767
771
|
createAddressEpic_1.createAddressEpic,
|
|
768
772
|
createAutoTransferRuleEpic_1.createAutoTransferRuleEpic,
|
|
@@ -814,7 +818,7 @@ const ALL_EPICS = [
|
|
|
814
818
|
deleteTransactionAttachmentEpic_1.deleteTransactionAttachmentEpic,
|
|
815
819
|
deleteUserBankAccountEpic_1.deleteUserBankAccountEpic,
|
|
816
820
|
disableAccountingProjectsEpic_1.disableAccountingProjectsEpic,
|
|
817
|
-
|
|
821
|
+
disconnectInvoicingStripeEpic_1.disconnectInvoicingStripeEpic,
|
|
818
822
|
dismissCapitalizationOnboardingEpic_1.dismissCapitalizationOnboardingEpic,
|
|
819
823
|
doMagicLinkSignInEpic_1.doMagicLinkSignInEpic,
|
|
820
824
|
signInUserEpic_1.doSignInEpic,
|
|
@@ -1023,7 +1027,7 @@ const ALL_EPICS = [
|
|
|
1023
1027
|
customerPaymentMethodEpic_1.fetchInvoicingPlaidLinkTokenEpic,
|
|
1024
1028
|
fetchInvoicingQboConnectionEpic_1.fetchInvoicingQboConnectionEpic,
|
|
1025
1029
|
fetchInvoicingQboSyncHealthEpic_1.fetchInvoicingQboSyncHealthEpic,
|
|
1026
|
-
|
|
1030
|
+
fetchInvoicingSettingsEpic_1.fetchInvoicingSettingsEpic,
|
|
1027
1031
|
fetchInvoicingSubscriptionCountsEpic_1.fetchInvoicingSubscriptionCountsEpic,
|
|
1028
1032
|
invoicingSubscriptionDetailViewEpics_1.fetchInvoicingSubscriptionDetailEpic,
|
|
1029
1033
|
fetchInvoicingSubscriptionFormPageEpic_1.fetchInvoicingSubscriptionFormPageEpic,
|
|
@@ -1311,7 +1315,7 @@ const ALL_EPICS = [
|
|
|
1311
1315
|
saveInvoicingCouponEpic_1.saveInvoicingCouponEpic,
|
|
1312
1316
|
saveInvoicingCustomerEpic_1.saveInvoicingCustomerEpic,
|
|
1313
1317
|
customerPaymentMethodEpic_1.saveInvoicingPaymentMethodEpic,
|
|
1314
|
-
|
|
1318
|
+
saveInvoicingSettingsEpic_1.saveInvoicingSettingsEpic,
|
|
1315
1319
|
saveInvoicingSubscriptionEpic_1.saveInvoicingSubscriptionEpic,
|
|
1316
1320
|
saveMagicLinkBankAccountEpic_1.saveMagicLinkBankAccountEpic,
|
|
1317
1321
|
saveNewAddressEpic_1.saveNewAddressEpic,
|
|
@@ -1360,6 +1364,7 @@ const ALL_EPICS = [
|
|
|
1360
1364
|
submitFeedbackEpic_1.submitFeedbackEpic,
|
|
1361
1365
|
submitIntlVerificationEpic_1.submitIntlVerificationEpic,
|
|
1362
1366
|
invoicingBrandingFormEpics_1.submitInvoicingBrandingFormEpic,
|
|
1367
|
+
submitInvoicingQboSettingsEpic_1.submitInvoicingQboSettingsEpic,
|
|
1363
1368
|
submitQuestionEpic_1.submitQuestionEpic,
|
|
1364
1369
|
syncInvoicingSubscriptionCouponsEpic_1.syncInvoicingSubscriptionCouponsEpic,
|
|
1365
1370
|
syncTabsAfterAutomatchEpic_1.syncTabsAfterAutomatchEpic,
|
|
@@ -174,6 +174,20 @@ export function getInvoicingQboAccountMappingId(accountMappings, code) {
|
|
|
174
174
|
* Return a copy of the account mappings with the dropdown slot named by
|
|
175
175
|
* `code` set to `accountId`. Unknown codes leave the mappings unchanged.
|
|
176
176
|
*/
|
|
177
|
+
/** Every account-mapping slot the page can set, in the order it renders them. */
|
|
178
|
+
export const INVOICING_QBO_ACCOUNT_MAPPING_CODES = [
|
|
179
|
+
'deposit_account_id',
|
|
180
|
+
'default_income_account_id',
|
|
181
|
+
'discounts_account_id',
|
|
182
|
+
'rounding_account_id',
|
|
183
|
+
'write_off_account_id',
|
|
184
|
+
];
|
|
185
|
+
/**
|
|
186
|
+
* Narrows a mapping code that arrived from the config API. Anything the client
|
|
187
|
+
* has no slot for returns undefined rather than being cast through: a code we
|
|
188
|
+
* cannot place is a mapping we cannot read or write.
|
|
189
|
+
*/
|
|
190
|
+
export const toInvoicingQboAccountMappingCode = (code) => INVOICING_QBO_ACCOUNT_MAPPING_CODES.find((knownCode) => knownCode === code);
|
|
177
191
|
/** The object key holding the mapped account for each `*_account_id` code. */
|
|
178
192
|
const INVOICING_QBO_MAPPED_ACCOUNT_KEY = {
|
|
179
193
|
default_income_account_id: 'default_income_account',
|
|
@@ -193,7 +207,8 @@ export const withInvoicingQboMappedAccounts = (accountMappings, accountByQboId)
|
|
|
193
207
|
return undefined;
|
|
194
208
|
}
|
|
195
209
|
const expanded = { ...accountMappings };
|
|
196
|
-
|
|
210
|
+
INVOICING_QBO_ACCOUNT_MAPPING_CODES.forEach((code) => {
|
|
211
|
+
const objectKey = INVOICING_QBO_MAPPED_ACCOUNT_KEY[code];
|
|
197
212
|
const accountId = getInvoicingQboAccountMappingId(accountMappings, code);
|
|
198
213
|
if (accountId == null) {
|
|
199
214
|
return;
|
package/lib/esm/epic.js
CHANGED
|
@@ -310,8 +310,12 @@ import { issueCreditNoteEpic } from './view/invoicing/issueCreditNote/issueCredi
|
|
|
310
310
|
import { runInvoicingPaymentActionEpic, } from './view/invoicing/paymentAction/paymentActionEpic';
|
|
311
311
|
import { addPromotionalCreditsEpic, } from './view/invoicing/promotionalCreditAction/promotionalCreditActionEpic';
|
|
312
312
|
import { recordPaymentEpic, } from './view/invoicing/recordPayment/recordPaymentEpic';
|
|
313
|
+
import { connectInvoicingStripeEpic } from './view/invoicing/settingsView/epics/connectInvoicingStripeEpic';
|
|
314
|
+
import { disconnectInvoicingStripeEpic } from './view/invoicing/settingsView/epics/disconnectInvoicingStripeEpic';
|
|
315
|
+
import { fetchInvoicingSettingsEpic } from './view/invoicing/settingsView/epics/fetchInvoicingSettingsEpic';
|
|
316
|
+
import { saveInvoicingSettingsEpic } from './view/invoicing/settingsView/epics/saveInvoicingSettingsEpic';
|
|
317
|
+
import { submitInvoicingQboSettingsEpic } from './view/invoicing/settingsView/epics/submitInvoicingQboSettingsEpic';
|
|
313
318
|
import { initializeInvoicingBrandingAddressEpic, submitInvoicingBrandingFormEpic, } from './view/invoicing/settingsView/invoicingBrandingFormEpics';
|
|
314
|
-
import { connectInvoicingStripeEpic, disconnectInvoicingStripeEpic, fetchInvoicingSettingsEpic, saveInvoicingSettingsEpic, } from './view/invoicing/settingsView/settingsViewEpics';
|
|
315
319
|
import { runInvoicingSubscriptionActionEpic, } from './view/invoicing/subscriptionAction/subscriptionActionEpic';
|
|
316
320
|
import { fetchMonthClosePerformanceTrendEpic, } from './view/monthEndCloseChecksView/epics/fetchMonthClosePerformanceTrendEpic';
|
|
317
321
|
import { fetchMonthEndCloseChecksEpic, } from './view/monthEndCloseChecksView/epics/fetchMonthEndCloseChecksEpic';
|
|
@@ -1357,6 +1361,7 @@ const ALL_EPICS = [
|
|
|
1357
1361
|
submitFeedbackEpic,
|
|
1358
1362
|
submitIntlVerificationEpic,
|
|
1359
1363
|
submitInvoicingBrandingFormEpic,
|
|
1364
|
+
submitInvoicingQboSettingsEpic,
|
|
1360
1365
|
submitQuestionEpic,
|
|
1361
1366
|
syncInvoicingSubscriptionCouponsEpic,
|
|
1362
1367
|
syncTabsAfterAutomatchEpic,
|
package/lib/esm/index.js
CHANGED
|
@@ -194,11 +194,11 @@ import { getExpenseAutomationTransactionView, getLastTransferEntryReplacement, }
|
|
|
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';
|
|
198
197
|
import { toJEScheduleMainTab as toExpenseAutomationJEScheduleMainTab, toJEScheduleSortKey as toExpenseAutomationJEScheduleSortKey, } from './view/expenseAutomationView/types/jeSchedulesViewState';
|
|
199
198
|
import { BATCH_FILE_STATUSES, isUnmatchedTabFileStatus, toBatchFileStatus, toBatchStatusValue, toBulkUploadPhase, toBulkUploadResultsTab, toBulkUploadSortKey, toMatchSource, toMissingReceiptsTab, } from './view/expenseAutomationView/types/missingReceiptsViewState';
|
|
200
199
|
import { toMissingReceiptsSortKey as toExpenseAutomationMissingReceiptsSortKey, } from './view/expenseAutomationView/types/missingReceiptsViewState';
|
|
201
200
|
import { toReconciliationTabsType, } from './view/expenseAutomationView/types/reconciliationViewState';
|
|
201
|
+
import { DEFAULT_TRANSACTION_GROUP, TRANSACTION_GROUPS, toTransactionGroup, } from './view/expenseAutomationView/types/transactionGroup';
|
|
202
202
|
import { toTransactionsTabKey as toExpenseAutomationTransactionsTabKey, toTransactionsSortKey, } from './view/expenseAutomationView/types/transactionsViewState';
|
|
203
203
|
import { fetchRegisteredInterests, notifyMeForFeature, } from './view/featureNotificationView/featureNotificationViewReducer';
|
|
204
204
|
import { getFeatureNotificationView, getRegisteredInterests, getRegisteredInterestsByFeature, isFeatureInterestRegistered, } from './view/featureNotificationView/featureNotificationViewSelector';
|
|
@@ -849,8 +849,10 @@ export { getInvoicingCatalogItemDetail } from './view/invoicing/invoicingCatalog
|
|
|
849
849
|
export { getInvoicingPlanById } from './entity/invoicing/plan/planSelector';
|
|
850
850
|
export { getInvoicingProductById } from './entity/invoicing/product/productSelector';
|
|
851
851
|
// ── Invoicing: settings ───────────────────────────────────────────────────────
|
|
852
|
-
export { connectInvoicingStripe, disconnectInvoicingStripe, fetchInvoicingSettings, resetInvoicingBrandingFormDraft, resetSaveInvoicingSettings, saveInvoicingSettings, submitInvoicingBrandingForm, updateInvoicingBrandingFormDraft, updateInvoicingSettings, } from './view/invoicing/settingsView/settingsViewReducer';
|
|
853
|
-
export { getInvoicingBrandingFormView, getInvoicingSettings, getInvoicingSettingsFetchState, getInvoicingSettingsHubView, getInvoicingSettingsView, } from './view/invoicing/settingsView/settingsViewSelector';
|
|
852
|
+
export { connectInvoicingStripe, disconnectInvoicingStripe, fetchInvoicingSettings, resetInvoicingBrandingFormDraft, resetInvoicingQboSettingsDraft, resetSaveInvoicingSettings, saveInvoicingSettings, submitInvoicingBrandingForm, submitInvoicingQboSettings, updateInvoicingBrandingFormDraft, updateInvoicingQboSettingsDraft, updateInvoicingSettings, } from './view/invoicing/settingsView/settingsViewReducer';
|
|
853
|
+
export { getInvoicingBrandingFormView, getInvoicingQboSettingsFormView, getInvoicingSettings, getInvoicingSettingsFetchState, getInvoicingSettingsHubView, getInvoicingSettingsView, } from './view/invoicing/settingsView/settingsViewSelector';
|
|
854
|
+
export { INVOICING_QBO_ACCOUNT_MAPPING_CODES, toInvoicingQboAccountMappingCode, } from './entity/invoicing/invoicingCommonPayload';
|
|
855
|
+
export { entityToInvoicingQboSettingsLocalData, initialInvoicingQboSettingsLocalData, invoicingQboSettingsLocalDataToPayload, } from './view/invoicing/settingsView/invoicingQboSettingsFormConfig';
|
|
854
856
|
export { INVOICING_BUSINESS_ADDRESS_TYPE, entityToInvoicingBrandingFormLocalData, initialInvoicingBrandingFormLocalData, } from './view/invoicing/settingsView/invoicingBrandingFormConfig';
|
|
855
857
|
export { isStripeConnectedFromSettings } from './view/invoicing/settingsView/invoicingSettingsHelpers';
|
|
856
858
|
// ── Invoicing: QBO integration ────────────────────────────────────────────────
|
|
@@ -46,16 +46,30 @@ export const invoicingQboAccountByQboId = (state, qboId) => {
|
|
|
46
46
|
if (qboId == null || qboId === '') {
|
|
47
47
|
return undefined;
|
|
48
48
|
}
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
// `getAccountList` throws when a listed id is missing from the entity slice.
|
|
50
|
+
// That is survivable when looking one account up — the caller falls back to
|
|
51
|
+
// the id it already had — but not when it happens inside an epic, where the
|
|
52
|
+
// throw would take the whole request with it.
|
|
53
|
+
try {
|
|
54
|
+
const { accounts } = getAccountList(state.accountState, state.accountListState, INVOICING_ACCOUNT_LIST_KEY);
|
|
55
|
+
return accounts.find((account) => String(account.qboId) === qboId);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
51
60
|
};
|
|
52
61
|
/** The class behind a picker value, for the same reason as the account above. */
|
|
53
62
|
export const invoicingQboClassByQboId = (state, qboId) => {
|
|
54
63
|
if (qboId == null || qboId === '') {
|
|
55
64
|
return undefined;
|
|
56
65
|
}
|
|
57
|
-
|
|
58
|
-
|
|
66
|
+
try {
|
|
67
|
+
const { classes } = getClassList(state.classState, state.classListState);
|
|
68
|
+
return classes.find((accountingClass) => String(accountingClass.qboId) === qboId);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
59
73
|
};
|
|
60
74
|
/**
|
|
61
75
|
* Accounts and classes as the invoicing pickers need them. Both come from the
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { of } from 'rxjs';
|
|
2
|
+
import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
|
|
3
|
+
import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
|
|
4
|
+
import { connectInvoicingStripe, fetchInvoicingSettings, invoicingStripeConnectionFailure, updateInvoicingStripeConnection, } from '../settingsViewReducer';
|
|
5
|
+
export const connectInvoicingStripeEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(connectInvoicingStripe.match), mergeMap((action) => {
|
|
6
|
+
const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/settings/stripe-connect/setup`;
|
|
7
|
+
const { api_key, publishable_key, webhook_secret } = action.payload;
|
|
8
|
+
const body = {
|
|
9
|
+
api_key,
|
|
10
|
+
publishable_key,
|
|
11
|
+
...(webhook_secret != null && webhook_secret !== ''
|
|
12
|
+
? { webhook_secret }
|
|
13
|
+
: {}),
|
|
14
|
+
};
|
|
15
|
+
return zeniAPI
|
|
16
|
+
.postAndGetJSON(apiUrl, body)
|
|
17
|
+
.pipe(switchMap((response) => {
|
|
18
|
+
if (isSuccessResponse(response) && response.data != null) {
|
|
19
|
+
return of(updateInvoicingStripeConnection({
|
|
20
|
+
stripe_connected: response.data.stripe_connected,
|
|
21
|
+
publishable_key: response.data.publishable_key,
|
|
22
|
+
}), fetchInvoicingSettings());
|
|
23
|
+
}
|
|
24
|
+
return of(invoicingStripeConnectionFailure(response.status));
|
|
25
|
+
}), catchError((error) => of(invoicingStripeConnectionFailure(createZeniAPIStatus('Unexpected Error', 'Stripe connect setup REST API call errored out ' +
|
|
26
|
+
JSON.stringify(error))))));
|
|
27
|
+
}));
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { of } from 'rxjs';
|
|
2
|
+
import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
|
|
3
|
+
import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
|
|
4
|
+
import { disconnectInvoicingStripe, fetchInvoicingSettings, invoicingStripeConnectionFailure, updateInvoicingStripeConnection, } from '../settingsViewReducer';
|
|
5
|
+
export const disconnectInvoicingStripeEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(disconnectInvoicingStripe.match), mergeMap(() => {
|
|
6
|
+
const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/settings/stripe-connect/callback`;
|
|
7
|
+
return zeniAPI
|
|
8
|
+
.deleteAndGetJSON(apiUrl)
|
|
9
|
+
.pipe(switchMap((response) => {
|
|
10
|
+
if (isSuccessResponse(response)) {
|
|
11
|
+
return of(updateInvoicingStripeConnection({
|
|
12
|
+
stripe_connected: false,
|
|
13
|
+
publishable_key: '',
|
|
14
|
+
}), fetchInvoicingSettings());
|
|
15
|
+
}
|
|
16
|
+
return of(invoicingStripeConnectionFailure(response.status));
|
|
17
|
+
}), catchError((error) => of(invoicingStripeConnectionFailure(createZeniAPIStatus('Unexpected Error', 'Stripe disconnect REST API call errored out ' +
|
|
18
|
+
JSON.stringify(error))))));
|
|
19
|
+
}));
|
|
20
|
+
/**
|
|
21
|
+
* Turns the QBO settings draft into a save. The draft lives in the store, so the
|
|
22
|
+
* screen fires one action and this reads what to send — the values do not have
|
|
23
|
+
* to travel back out through the page's props to get here.
|
|
24
|
+
*/
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { of } from 'rxjs';
|
|
2
|
+
import { catchError, filter, switchMap } from 'rxjs/operators';
|
|
3
|
+
import { updateAddresses } from '../../../../entity/address/addressReducer';
|
|
4
|
+
import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
|
|
5
|
+
import { fetchInvoicingSettings, updateInvoicingSettings, updateInvoicingSettingsFailure, } from '../settingsViewReducer';
|
|
6
|
+
export const fetchInvoicingSettingsEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchInvoicingSettings.match), switchMap(() => {
|
|
7
|
+
const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/settings`;
|
|
8
|
+
return zeniAPI.getJSON(apiUrl).pipe(switchMap((response) => {
|
|
9
|
+
if (isSuccessResponse(response) && response.data != null) {
|
|
10
|
+
const address = response.data.address;
|
|
11
|
+
return [
|
|
12
|
+
...(address != null ? [updateAddresses([address])] : []),
|
|
13
|
+
updateInvoicingSettings(response.data),
|
|
14
|
+
];
|
|
15
|
+
}
|
|
16
|
+
return of(updateInvoicingSettingsFailure(response.status));
|
|
17
|
+
}), catchError((error) => of(updateInvoicingSettingsFailure(createZeniAPIStatus('Unexpected Error', 'Fetch settings REST API call errored out ' +
|
|
18
|
+
JSON.stringify(error))))));
|
|
19
|
+
}));
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { from } from 'rxjs';
|
|
2
|
+
import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
|
|
3
|
+
import { withInvoicingQboMappedAccounts, } from '../../../../entity/invoicing/invoicingCommonPayload';
|
|
4
|
+
import { apiErrorSnackbarVariables } from '../../../../entity/snackbar/snackbarApiError';
|
|
5
|
+
import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
|
|
6
|
+
import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
|
|
7
|
+
import { invoicingQboAccountByQboId } from '../../invoicingQboView/invoicingQboViewSelector';
|
|
8
|
+
import { saveInvoicingSettings, saveInvoicingSettingsFailure, saveInvoicingSettingsSuccess, } from '../settingsViewReducer';
|
|
9
|
+
export const saveInvoicingSettingsEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(saveInvoicingSettings.match), mergeMap((action) => {
|
|
10
|
+
const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/settings`;
|
|
11
|
+
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
|
+
/** `status` surfaces the platform's error text; omit it to keep the generic copy. */
|
|
26
|
+
const snackbar = (messageText, status) => snackbarSection == null
|
|
27
|
+
? []
|
|
28
|
+
: [
|
|
29
|
+
openSnackbar({
|
|
30
|
+
messageSection: snackbarSection,
|
|
31
|
+
messageText,
|
|
32
|
+
type: messageText === 'success' ? 'success' : 'error',
|
|
33
|
+
variables: apiErrorSnackbarVariables(status),
|
|
34
|
+
}),
|
|
35
|
+
];
|
|
36
|
+
return zeniAPI
|
|
37
|
+
.putAndGetJSON(apiUrl, { ...body })
|
|
38
|
+
.pipe(switchMap((response) => {
|
|
39
|
+
if (isSuccessResponse(response) && response.data != null) {
|
|
40
|
+
return from([
|
|
41
|
+
saveInvoicingSettingsSuccess(response.data),
|
|
42
|
+
...snackbar('success'),
|
|
43
|
+
]);
|
|
44
|
+
}
|
|
45
|
+
return from([
|
|
46
|
+
saveInvoicingSettingsFailure(response.status),
|
|
47
|
+
...snackbar('failed', response.status),
|
|
48
|
+
]);
|
|
49
|
+
}), catchError((error) => from([
|
|
50
|
+
saveInvoicingSettingsFailure(createZeniAPIStatus('Unexpected Error', 'Save settings REST API call errored out ' +
|
|
51
|
+
JSON.stringify(error))),
|
|
52
|
+
...snackbar('failed'),
|
|
53
|
+
])));
|
|
54
|
+
}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { EMPTY, of } from 'rxjs';
|
|
2
|
+
import { filter, mergeMap, withLatestFrom } from 'rxjs/operators';
|
|
3
|
+
import { invoicingQboSettingsLocalDataToPayload } from '../invoicingQboSettingsFormConfig';
|
|
4
|
+
import { saveInvoicingSettings, submitInvoicingQboSettings, } from '../settingsViewReducer';
|
|
5
|
+
/**
|
|
6
|
+
* Saves the QBO local data. The screen fires this without a payload — the local
|
|
7
|
+
* data is already in the store, so sending it back through the action would make
|
|
8
|
+
* the page the carrier for state it does not own. Converting to the request body
|
|
9
|
+
* happens here too, keeping the wire shape out of the screen.
|
|
10
|
+
*/
|
|
11
|
+
export const submitInvoicingQboSettingsEpic = (actions$, state$) => actions$.pipe(filter(submitInvoicingQboSettings.match), withLatestFrom(state$), mergeMap(([, state]) => {
|
|
12
|
+
const { qboDraft } = state.invoicingSettingsViewState;
|
|
13
|
+
// No local data means the settings response has not landed, so there is
|
|
14
|
+
// nothing the user could have edited yet.
|
|
15
|
+
if (qboDraft == null) {
|
|
16
|
+
return EMPTY;
|
|
17
|
+
}
|
|
18
|
+
return of(saveInvoicingSettings({
|
|
19
|
+
settings: { qbo: invoicingQboSettingsLocalDataToPayload(qboDraft) },
|
|
20
|
+
snackbarSection: 'invoicing_qbo_settings_save',
|
|
21
|
+
}));
|
|
22
|
+
}));
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { INVOICING_QBO_ACCOUNT_MAPPING_CODES, getInvoicingQboAccountMappingId, withInvoicingQboAccountMappingId, } from '../../../entity/invoicing/invoicingCommonPayload';
|
|
2
|
+
export const initialInvoicingQboSettingsLocalData = {
|
|
3
|
+
accountIdByMappingCode: {},
|
|
4
|
+
autoCreateItems: false,
|
|
5
|
+
closedPeriodVoidAsCreditMemo: false,
|
|
6
|
+
customerMatching: '',
|
|
7
|
+
isSyncEnabled: false,
|
|
8
|
+
syncStartDate: '',
|
|
9
|
+
trackingCategoriesEnabled: false,
|
|
10
|
+
};
|
|
11
|
+
/** The settings response as the page edits it. */
|
|
12
|
+
export const entityToInvoicingQboSettingsLocalData = (qbo) => {
|
|
13
|
+
const accountIdByMappingCode = {};
|
|
14
|
+
INVOICING_QBO_ACCOUNT_MAPPING_CODES.forEach((code) => {
|
|
15
|
+
const accountId = getInvoicingQboAccountMappingId(qbo?.account_mappings ?? undefined, code);
|
|
16
|
+
if (accountId != null) {
|
|
17
|
+
accountIdByMappingCode[code] = accountId;
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
return {
|
|
21
|
+
accountIdByMappingCode,
|
|
22
|
+
autoCreateItems: qbo?.auto_create_items === true,
|
|
23
|
+
closedPeriodVoidAsCreditMemo: qbo?.closed_period_void_as_credit_memo === true,
|
|
24
|
+
customerMatching: qbo?.customer_matching ?? '',
|
|
25
|
+
isSyncEnabled: qbo?.enabled === true,
|
|
26
|
+
syncStartDate: qbo?.sync_start_date ?? '',
|
|
27
|
+
trackingCategoriesEnabled: qbo?.tracking_categories_enabled === true,
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* The local data as the request body. Only the mappings the page holds are sent;
|
|
32
|
+
* an empty id means the slot was never set, so it is left out rather than sent
|
|
33
|
+
* blank.
|
|
34
|
+
*/
|
|
35
|
+
export const invoicingQboSettingsLocalDataToPayload = (localData) => {
|
|
36
|
+
let accountMappings = {};
|
|
37
|
+
INVOICING_QBO_ACCOUNT_MAPPING_CODES.forEach((code) => {
|
|
38
|
+
const accountId = localData.accountIdByMappingCode[code] ?? '';
|
|
39
|
+
if (accountId !== '') {
|
|
40
|
+
accountMappings = withInvoicingQboAccountMappingId(accountMappings, code, accountId);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
return {
|
|
44
|
+
account_mappings: accountMappings,
|
|
45
|
+
auto_create_items: localData.autoCreateItems,
|
|
46
|
+
closed_period_void_as_credit_memo: localData.closedPeriodVoidAsCreditMemo,
|
|
47
|
+
customer_matching: localData.customerMatching,
|
|
48
|
+
enabled: localData.isSyncEnabled,
|
|
49
|
+
sync_start_date: localData.syncStartDate !== '' ? localData.syncStartDate : null,
|
|
50
|
+
tracking_categories_enabled: localData.trackingCategoriesEnabled,
|
|
51
|
+
};
|
|
52
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createSlice } from '@reduxjs/toolkit';
|
|
2
2
|
import { clearAll } from '../../../rootActions';
|
|
3
|
+
import { entityToInvoicingQboSettingsLocalData, } from './invoicingQboSettingsFormConfig';
|
|
3
4
|
export const initialState = {
|
|
4
5
|
fetchState: 'Not-Started',
|
|
5
6
|
error: undefined,
|
|
@@ -22,6 +23,11 @@ const settingsView = createSlice({
|
|
|
22
23
|
draft.settings = action.payload;
|
|
23
24
|
draft.fetchState = 'Completed';
|
|
24
25
|
draft.error = undefined;
|
|
26
|
+
// Seed the QBO page's local data from the response. An edit already in
|
|
27
|
+
// progress wins: a refetch behind the user must not discard it.
|
|
28
|
+
if (draft.qboDraft == null) {
|
|
29
|
+
draft.qboDraft = entityToInvoicingQboSettingsLocalData(action.payload.qbo);
|
|
30
|
+
}
|
|
25
31
|
},
|
|
26
32
|
updateInvoicingSettingsFailure(draft, action) {
|
|
27
33
|
draft.fetchState = 'Error';
|
|
@@ -36,6 +42,9 @@ const settingsView = createSlice({
|
|
|
36
42
|
saveInvoicingSettingsSuccess(draft, action) {
|
|
37
43
|
draft.settings = action.payload;
|
|
38
44
|
draft.saveFetchState = { fetchState: 'Completed', error: undefined };
|
|
45
|
+
// Re-seeded from what was just persisted, so the page keeps working off
|
|
46
|
+
// local data while showing the server's version of it.
|
|
47
|
+
draft.qboDraft = entityToInvoicingQboSettingsLocalData(action.payload.qbo);
|
|
39
48
|
},
|
|
40
49
|
saveInvoicingSettingsFailure(draft, action) {
|
|
41
50
|
draft.saveFetchState = { fetchState: 'Error', error: action.payload };
|
|
@@ -54,6 +63,17 @@ const settingsView = createSlice({
|
|
|
54
63
|
resetInvoicingBrandingFormDraft(draft) {
|
|
55
64
|
draft.brandingDraft = undefined;
|
|
56
65
|
},
|
|
66
|
+
updateInvoicingQboSettingsDraft(draft, action) {
|
|
67
|
+
draft.qboDraft = action.payload.localData;
|
|
68
|
+
},
|
|
69
|
+
// Trigger only — handled by submitInvoicingQboSettingsEpic, which reads the
|
|
70
|
+
// local data and converts it to the request body.
|
|
71
|
+
submitInvoicingQboSettings(draft) {
|
|
72
|
+
draft.saveFetchState = { fetchState: 'In-Progress', error: undefined };
|
|
73
|
+
},
|
|
74
|
+
resetInvoicingQboSettingsDraft(draft) {
|
|
75
|
+
draft.qboDraft = undefined;
|
|
76
|
+
},
|
|
57
77
|
connectInvoicingStripe: {
|
|
58
78
|
reducer(draft) {
|
|
59
79
|
draft.stripeFetchState = { fetchState: 'In-Progress', error: undefined };
|
|
@@ -85,5 +105,5 @@ const settingsView = createSlice({
|
|
|
85
105
|
});
|
|
86
106
|
},
|
|
87
107
|
});
|
|
88
|
-
export const { fetchInvoicingSettings, updateInvoicingSettings, updateInvoicingSettingsFailure, saveInvoicingSettings, saveInvoicingSettingsSuccess, saveInvoicingSettingsFailure, resetSaveInvoicingSettings, updateInvoicingBrandingFormDraft, submitInvoicingBrandingForm, resetInvoicingBrandingFormDraft, connectInvoicingStripe, disconnectInvoicingStripe, updateInvoicingStripeConnection, invoicingStripeConnectionFailure, } = settingsView.actions;
|
|
108
|
+
export const { fetchInvoicingSettings, updateInvoicingSettings, updateInvoicingSettingsFailure, saveInvoicingSettings, saveInvoicingSettingsSuccess, saveInvoicingSettingsFailure, resetSaveInvoicingSettings, updateInvoicingBrandingFormDraft, submitInvoicingBrandingForm, resetInvoicingBrandingFormDraft, updateInvoicingQboSettingsDraft, submitInvoicingQboSettings, resetInvoicingQboSettingsDraft, connectInvoicingStripe, disconnectInvoicingStripe, updateInvoicingStripeConnection, invoicingStripeConnectionFailure, } = settingsView.actions;
|
|
89
109
|
export default settingsView.reducer;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { getInvoicingQboAccountMappingId, } from '../../../entity/invoicing/invoicingCommonPayload';
|
|
1
|
+
import { getInvoicingQboAccountMappingId, toInvoicingQboAccountMappingCode, } from '../../../entity/invoicing/invoicingCommonPayload';
|
|
2
2
|
import { getFormattedAddress } from '../../addressView/addressViewSelector';
|
|
3
3
|
import { getInvoicingConfigView } from '../invoicingConfigView/invoicingConfigViewSelector';
|
|
4
|
+
import { getInvoicingQboView, isInvoicingAccountingPickerDataLoading, } from '../invoicingQboView/invoicingQboViewSelector';
|
|
4
5
|
import { INVOICING_BUSINESS_ADDRESS_TYPE, entityToInvoicingBrandingFormLocalData, } from './invoicingBrandingFormConfig';
|
|
6
|
+
import { initialInvoicingQboSettingsLocalData, } from './invoicingQboSettingsFormConfig';
|
|
5
7
|
/**
|
|
6
8
|
* Shared primitive: the raw invoicing settings. Consumed internally by other
|
|
7
9
|
* CES selectors (create-invoice, customer-detail, overview) and by non-settings
|
|
@@ -50,7 +52,12 @@ export function deriveInvoicingEnablePrerequisites(settings, isQboConnected, qbo
|
|
|
50
52
|
// configured would let `every` pass vacuously and unlock the button early.
|
|
51
53
|
const isQboConfigured = isQboConnected &&
|
|
52
54
|
qboAccountMappingKindOptions.length > 0 &&
|
|
53
|
-
qboAccountMappingKindOptions.every((option) =>
|
|
55
|
+
qboAccountMappingKindOptions.every((option) => {
|
|
56
|
+
// A kind the client has no slot for cannot count as configured.
|
|
57
|
+
const code = toInvoicingQboAccountMappingCode(option.code);
|
|
58
|
+
return (code != null &&
|
|
59
|
+
getInvoicingQboAccountMappingId(accountMappings, code) != null);
|
|
60
|
+
});
|
|
54
61
|
return {
|
|
55
62
|
canEnable: isStripeConnected && isQboConfigured,
|
|
56
63
|
isQboConfigured,
|
|
@@ -92,6 +99,45 @@ export const getInvoicingSettingsHubView = (state) => {
|
|
|
92
99
|
status,
|
|
93
100
|
};
|
|
94
101
|
};
|
|
102
|
+
/**
|
|
103
|
+
* Bundled view for the QBO configuration page: the draft it edits, the accounts
|
|
104
|
+
* and option lists its dropdowns render, the connection and sync health it
|
|
105
|
+
* reports, and the fetch states that decide between skeleton, form and error.
|
|
106
|
+
*
|
|
107
|
+
* `localData` is the draft once the user has edited, and the settings response
|
|
108
|
+
* before that — so the page has one field to read and CES owns when it becomes
|
|
109
|
+
* available, rather than the page copying settings into local state.
|
|
110
|
+
*/
|
|
111
|
+
export const getInvoicingQboSettingsFormView = (state) => {
|
|
112
|
+
const { error, fetchState, qboDraft, saveFetchState, settings } = state.invoicingSettingsViewState;
|
|
113
|
+
const { fetchState: configFetchState, qboAccountMappingKindOptions, qboCustomerMatchingOptions, qboSyncStatusOptions, } = getInvoicingConfigView(state);
|
|
114
|
+
const qboView = getInvoicingQboView(state);
|
|
115
|
+
return {
|
|
116
|
+
accountMappingKindOptions: qboAccountMappingKindOptions,
|
|
117
|
+
accounts: qboView.accounts,
|
|
118
|
+
backfillState: qboView.backfillState,
|
|
119
|
+
connection: qboView.connection,
|
|
120
|
+
configFetchState,
|
|
121
|
+
customerMatchingOptions: qboCustomerMatchingOptions,
|
|
122
|
+
// The dropdowns cannot be built before both the settings and the accounts
|
|
123
|
+
// land, so the page waits on either rather than rendering empty pickers.
|
|
124
|
+
isLoading: fetchState === 'Not-Started' ||
|
|
125
|
+
fetchState === 'In-Progress' ||
|
|
126
|
+
isInvoicingAccountingPickerDataLoading(state),
|
|
127
|
+
isSyncEnabledSaved: settings?.qbo?.enabled === true,
|
|
128
|
+
// Seeded in the reducer when the settings response lands, so this is a plain
|
|
129
|
+
// read rather than a second place that decides what the page starts from.
|
|
130
|
+
localData: qboDraft ?? initialInvoicingQboSettingsLocalData,
|
|
131
|
+
nestedAccountHierarchy: qboView.nestedAccountHierarchy,
|
|
132
|
+
retryFetchStateByTaskId: qboView.retryFetchStateByTaskId,
|
|
133
|
+
saveState: saveFetchState.fetchState === 'Not-Started' && error != null
|
|
134
|
+
? { fetchState, error }
|
|
135
|
+
: saveFetchState,
|
|
136
|
+
settingsFetchState: fetchState,
|
|
137
|
+
syncHealth: qboView.syncHealth,
|
|
138
|
+
syncStatusOptions: qboSyncStatusOptions,
|
|
139
|
+
};
|
|
140
|
+
};
|
|
95
141
|
/**
|
|
96
142
|
* Bundled view for the branding settings form: the draft `localData` (seeded
|
|
97
143
|
* from the existing settings until the user edits), the business-address label
|