@zeniai/client-epic-state 5.1.89 → 5.1.91-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/snackbar/snackbarApiError.d.ts +8 -0
- package/lib/entity/snackbar/snackbarApiError.js +15 -0
- package/lib/entity/tenant/tenantPayload.d.ts +1 -0
- package/lib/entity/tenant/tenantReducer.js +1 -0
- package/lib/entity/tenant/tenantSelector.d.ts +2 -0
- package/lib/entity/tenant/tenantSelector.js +11 -1
- package/lib/entity/tenant/tenantState.d.ts +1 -0
- package/lib/esm/entity/snackbar/snackbarApiError.js +11 -0
- package/lib/esm/entity/tenant/tenantReducer.js +1 -0
- package/lib/esm/entity/tenant/tenantSelector.js +9 -0
- package/lib/esm/index.js +3 -3
- package/lib/esm/view/invoicing/createCreditNote/createCreditNoteEpic.js +2 -0
- package/lib/esm/view/invoicing/createInvoice/createInvoiceEpic.js +2 -0
- package/lib/esm/view/invoicing/discountAction/discountActionEpic.js +6 -3
- package/lib/esm/view/invoicing/dunningAction/dunningActionEpic.js +6 -3
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.js +6 -4
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +3 -0
- package/lib/esm/view/invoicing/editInvoicingCouponDetailView/saveInvoicingCouponEpic.js +2 -0
- package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/saveInvoicingCustomerEpic.js +2 -0
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js +2 -0
- package/lib/esm/view/invoicing/invoiceAction/invoiceActionEpic.js +2 -0
- package/lib/esm/view/invoicing/invoicingQboView/epics/retryInvoicingQboSyncTaskEpic.js +2 -0
- package/lib/esm/view/invoicing/invoicingQboView/epics/startInvoicingQboBackfillEpic.js +2 -0
- package/lib/esm/view/invoicing/issueCreditNote/issueCreditNoteEpic.js +2 -0
- package/lib/esm/view/invoicing/recordPayment/recordPaymentEpic.js +3 -0
- package/lib/esm/view/invoicing/settingsView/settingsViewEpics.js +6 -3
- package/lib/esm/view/invoicing/settingsView/settingsViewSelector.js +52 -0
- package/lib/esm/view/invoicing/subscriptionAction/subscriptionActionEpic.js +6 -3
- package/lib/esm/view/vendorTabView/vendorTabViewReducer.js +1 -1
- package/lib/index.d.ts +4 -4
- package/lib/index.js +46 -43
- package/lib/view/invoicing/createCreditNote/createCreditNoteEpic.js +2 -0
- package/lib/view/invoicing/createInvoice/createInvoiceEpic.js +2 -0
- package/lib/view/invoicing/discountAction/discountActionEpic.js +5 -2
- package/lib/view/invoicing/dunningAction/dunningActionEpic.js +5 -2
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.d.ts +0 -4
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.js +6 -4
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +3 -0
- package/lib/view/invoicing/editInvoicingCouponDetailView/saveInvoicingCouponEpic.js +2 -0
- package/lib/view/invoicing/editInvoicingCustomerDetailView/saveInvoicingCustomerEpic.js +2 -0
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js +2 -0
- package/lib/view/invoicing/invoiceAction/invoiceActionEpic.js +2 -0
- package/lib/view/invoicing/invoicingQboView/epics/retryInvoicingQboSyncTaskEpic.js +2 -0
- package/lib/view/invoicing/invoicingQboView/epics/startInvoicingQboBackfillEpic.js +2 -0
- package/lib/view/invoicing/issueCreditNote/issueCreditNoteEpic.js +2 -0
- package/lib/view/invoicing/recordPayment/recordPaymentEpic.js +3 -0
- package/lib/view/invoicing/settingsView/settingsViewEpics.js +5 -2
- package/lib/view/invoicing/settingsView/settingsViewSelector.d.ts +41 -1
- package/lib/view/invoicing/settingsView/settingsViewSelector.js +55 -1
- package/lib/view/invoicing/subscriptionAction/subscriptionActionEpic.js +5 -2
- package/package.json +2 -2
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ZeniAPIStatus } from '../../responsePayload';
|
|
2
|
+
interface SnackbarMessageVariable {
|
|
3
|
+
variableName: string;
|
|
4
|
+
variableValue: string;
|
|
5
|
+
}
|
|
6
|
+
/** Undefined when the status carries no message, so the snackbar falls back to its generic copy. */
|
|
7
|
+
export declare const apiErrorSnackbarVariables: (status: ZeniAPIStatus | undefined) => SnackbarMessageVariable[] | undefined;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.apiErrorSnackbarVariables = void 0;
|
|
4
|
+
/** Placeholder in every `failed` snackbar string, swapped for the API's message. */
|
|
5
|
+
const API_ERROR_TOKEN = '_api-error_';
|
|
6
|
+
/** Undefined when the status carries no message, so the snackbar falls back to its generic copy. */
|
|
7
|
+
// Pass only platform statuses: a local `createZeniAPIStatus` holds internal debug text.
|
|
8
|
+
const apiErrorSnackbarVariables = (status) => {
|
|
9
|
+
const message = status?.message?.trim() ?? '';
|
|
10
|
+
if (message === '') {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
return [{ variableName: API_ERROR_TOKEN, variableValue: message }];
|
|
14
|
+
};
|
|
15
|
+
exports.apiErrorSnackbarVariables = apiErrorSnackbarVariables;
|
|
@@ -96,6 +96,7 @@ interface TenantPayload {
|
|
|
96
96
|
capitalization_onboarding_shown_for_fixed_asset?: boolean;
|
|
97
97
|
capitalization_onboarding_shown_for_prepaid?: boolean;
|
|
98
98
|
master_tos_info?: MasterTOSInfoPayload;
|
|
99
|
+
receipts_email?: string | null;
|
|
99
100
|
reimbursement_info?: UserReimbursementInfoDataPayload;
|
|
100
101
|
zeni_book_close_date?: string;
|
|
101
102
|
zeni_book_close_month_and_year?: string;
|
|
@@ -1044,6 +1044,7 @@ function mapTenantPayloadToTenant(payload, userTenantPayload) {
|
|
|
1044
1044
|
masterTOSInfo: payload.master_tos_info != null
|
|
1045
1045
|
? (0, userRolePayload_1.toMasterTOSInfo)(payload.master_tos_info)
|
|
1046
1046
|
: undefined,
|
|
1047
|
+
receiptsEmail: payload.receipts_email ?? undefined,
|
|
1047
1048
|
rewardsLastViewedTime: userTenantPayload?.rewards_feature_last_viewed_time != null
|
|
1048
1049
|
? (0, zeniDayJS_1.date)(userTenantPayload.rewards_feature_last_viewed_time)
|
|
1049
1050
|
: undefined,
|
|
@@ -92,4 +92,6 @@ export declare const getIsAccountingProjectsEnabled: (state: RootState) => boole
|
|
|
92
92
|
export declare const isTenantBookkeepingEnabled: (tenant: Tenant | undefined) => boolean;
|
|
93
93
|
export declare const isOtherProductsEnabledForTenant: (tenant: Tenant | undefined) => boolean;
|
|
94
94
|
export declare const isTenantTreasuryEnabled: (tenant: Tenant | undefined) => boolean;
|
|
95
|
+
export declare const DEFAULT_RECEIPTS_EMAIL = "receipts@zeni.ai";
|
|
96
|
+
export declare function getReceiptsEmail(tenantState: TenantState, tenantId: ID | undefined): string;
|
|
95
97
|
export {};
|
|
@@ -3,10 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.isTenantTreasuryEnabled = exports.isOtherProductsEnabledForTenant = exports.isTenantBookkeepingEnabled = exports.getIsAccountingProjectsEnabled = exports.getIsAccountingClassesEnabled = exports.isTenantAccountingServicesEnabled = exports.isTenantCardsOnly = exports.isTenantBankingOnly = exports.toTenantCoreDetailsView = exports.getCurrentTenantExternalConnections = exports.isTenantUsingZeniCOA = exports.isZeniDomainTenant = exports.getTenantBaseViewForTenantView = exports.getTenantsCoreDetailsByCheckInDateSelector = exports.getTenantsBaseByCheckInDateSelector = exports.getTenantBaseById = exports.getTenantsByCheckInDateSelector = exports.getCurrentTenant = exports.getTenantBaseViewForTenantId = void 0;
|
|
6
|
+
exports.DEFAULT_RECEIPTS_EMAIL = exports.isTenantTreasuryEnabled = exports.isOtherProductsEnabledForTenant = exports.isTenantBookkeepingEnabled = exports.getIsAccountingProjectsEnabled = exports.getIsAccountingClassesEnabled = exports.isTenantAccountingServicesEnabled = exports.isTenantCardsOnly = exports.isTenantBankingOnly = exports.toTenantCoreDetailsView = exports.getCurrentTenantExternalConnections = exports.isTenantUsingZeniCOA = exports.isZeniDomainTenant = exports.getTenantBaseViewForTenantView = exports.getTenantsCoreDetailsByCheckInDateSelector = exports.getTenantsBaseByCheckInDateSelector = exports.getTenantBaseById = exports.getTenantsByCheckInDateSelector = exports.getCurrentTenant = exports.getTenantBaseViewForTenantId = void 0;
|
|
7
7
|
exports.getTenant = getTenant;
|
|
8
8
|
exports.getTenantIdByCompanyId = getTenantIdByCompanyId;
|
|
9
9
|
exports.getOnboardingTenantByTenantId = getOnboardingTenantByTenantId;
|
|
10
|
+
exports.getReceiptsEmail = getReceiptsEmail;
|
|
10
11
|
const toolkit_1 = require("@reduxjs/toolkit");
|
|
11
12
|
const orderBy_1 = __importDefault(require("lodash/orderBy"));
|
|
12
13
|
const companySelector_1 = require("../company/companySelector");
|
|
@@ -330,3 +331,12 @@ const isTenantTreasuryEnabled = (tenant) => {
|
|
|
330
331
|
: false;
|
|
331
332
|
};
|
|
332
333
|
exports.isTenantTreasuryEnabled = isTenantTreasuryEnabled;
|
|
334
|
+
exports.DEFAULT_RECEIPTS_EMAIL = 'receipts@zeni.ai';
|
|
335
|
+
function getReceiptsEmail(tenantState, tenantId) {
|
|
336
|
+
const receiptsEmail = tenantId != null
|
|
337
|
+
? tenantState.tenantsById[tenantId]?.receiptsEmail
|
|
338
|
+
: undefined;
|
|
339
|
+
// API can send empty string; treat it as "not configured"
|
|
340
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions -- intentional empty-string fallback
|
|
341
|
+
return receiptsEmail || exports.DEFAULT_RECEIPTS_EMAIL;
|
|
342
|
+
}
|
|
@@ -111,6 +111,7 @@ export interface Tenant {
|
|
|
111
111
|
bookCloseDate?: ZeniDate;
|
|
112
112
|
hubSpotId?: ID;
|
|
113
113
|
masterTOSInfo?: MasterTOSInfo;
|
|
114
|
+
receiptsEmail?: string;
|
|
114
115
|
rewardsLastViewedTime?: ZeniDate;
|
|
115
116
|
userReimbursementInfo?: UserReimbursementInfo;
|
|
116
117
|
zeniBookCloseDate?: ZeniDate;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Placeholder in every `failed` snackbar string, swapped for the API's message. */
|
|
2
|
+
const API_ERROR_TOKEN = '_api-error_';
|
|
3
|
+
/** Undefined when the status carries no message, so the snackbar falls back to its generic copy. */
|
|
4
|
+
// Pass only platform statuses: a local `createZeniAPIStatus` holds internal debug text.
|
|
5
|
+
export const apiErrorSnackbarVariables = (status) => {
|
|
6
|
+
const message = status?.message?.trim() ?? '';
|
|
7
|
+
if (message === '') {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
return [{ variableName: API_ERROR_TOKEN, variableValue: message }];
|
|
11
|
+
};
|
|
@@ -1032,6 +1032,7 @@ function mapTenantPayloadToTenant(payload, userTenantPayload) {
|
|
|
1032
1032
|
masterTOSInfo: payload.master_tos_info != null
|
|
1033
1033
|
? toMasterTOSInfo(payload.master_tos_info)
|
|
1034
1034
|
: undefined,
|
|
1035
|
+
receiptsEmail: payload.receipts_email ?? undefined,
|
|
1035
1036
|
rewardsLastViewedTime: userTenantPayload?.rewards_feature_last_viewed_time != null
|
|
1036
1037
|
? date(userTenantPayload.rewards_feature_last_viewed_time)
|
|
1037
1038
|
: undefined,
|
|
@@ -308,3 +308,12 @@ export const isTenantTreasuryEnabled = (tenant) => {
|
|
|
308
308
|
? tenant.productSettings.isTreasuryEnabled === true
|
|
309
309
|
: false;
|
|
310
310
|
};
|
|
311
|
+
export const DEFAULT_RECEIPTS_EMAIL = 'receipts@zeni.ai';
|
|
312
|
+
export function getReceiptsEmail(tenantState, tenantId) {
|
|
313
|
+
const receiptsEmail = tenantId != null
|
|
314
|
+
? tenantState.tenantsById[tenantId]?.receiptsEmail
|
|
315
|
+
: undefined;
|
|
316
|
+
// API can send empty string; treat it as "not configured"
|
|
317
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions -- intentional empty-string fallback
|
|
318
|
+
return receiptsEmail || DEFAULT_RECEIPTS_EMAIL;
|
|
319
|
+
}
|
package/lib/esm/index.js
CHANGED
|
@@ -71,7 +71,7 @@ import { getSnackbar } from './entity/snackbar/snackbarSelector';
|
|
|
71
71
|
import { ALL_WEEK_DAYS, toDayOfWeek, toPriorityCodeType, toTaskStatusCodeType, } from './entity/task/taskState';
|
|
72
72
|
import { getTaskGroupById } from './entity/taskGroup/taskGroupSelector';
|
|
73
73
|
import { deleteConnection, doMagicLinkSignIn, doSignIn, doSignOut, fetchActiveTenant, fetchAllTenants, fetchExcludedResources as fetchExcludedResourcesForTenant, fetchExternalConnections as fetchExternalConnectionsForTenant, fetchSubscriptionSummaryForTenant, initEmailConnectOAuth, resendVerifyDeviceOTP, resetSignInState, saveAPIKeyConnection, saveConnectorCredentials, saveExternalConnection as saveExternalConnectionForTenant, saveOAuthConnection, sendEmailMagicLinkToUser, sendSessionHeartbeat, toExternalIntegrationType, toExternalSupportedTool, updateCurrentTenant, updateSignInState, verifyDeviceWithTwoFA, } from './entity/tenant/tenantReducer';
|
|
74
|
-
import { getCurrentTenant, getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, getTenantBaseById, getTenantBaseViewForTenantId, getTenantBaseViewForTenantView, getTenantsBaseByCheckInDateSelector, getTenantsByCheckInDateSelector, getTenantsCoreDetailsByCheckInDateSelector, isOtherProductsEnabledForTenant, isTenantBankingOnly, isTenantBookkeepingEnabled, isTenantCardsOnly, isTenantUsingZeniCOA, isZeniDomainTenant, toTenantCoreDetailsView, } from './entity/tenant/tenantSelector';
|
|
74
|
+
import { DEFAULT_RECEIPTS_EMAIL, getCurrentTenant, getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, getReceiptsEmail, getTenantBaseById, getTenantBaseViewForTenantId, getTenantBaseViewForTenantView, getTenantsBaseByCheckInDateSelector, getTenantsByCheckInDateSelector, getTenantsCoreDetailsByCheckInDateSelector, isOtherProductsEnabledForTenant, isTenantBankingOnly, isTenantBookkeepingEnabled, isTenantCardsOnly, isTenantUsingZeniCOA, isZeniDomainTenant, toTenantCoreDetailsView, } from './entity/tenant/tenantSelector';
|
|
75
75
|
import { pushToastNotification } from './entity/toastNotification/toastNotificationReducer';
|
|
76
76
|
import { getLastNotificationTime, getNotifications, } from './entity/toastNotification/toastNotificationSelector';
|
|
77
77
|
import { mapFileEntityToAttachment, toContentType, toContentTypeStrict, } from './entity/transaction/stateTypes/attachment';
|
|
@@ -482,7 +482,7 @@ export { toTimeframeTick, mapTimePeriodtoTimeframeTick, toTimePeriod, toAbsolute
|
|
|
482
482
|
export { toVendorSpendTrendFilterTabsTypeStrict, } from './entity/vendorExpense/vendorExpenseSelector';
|
|
483
483
|
export { getNumberOfPeriods };
|
|
484
484
|
export { SCHEDULE_DAYS_OF_MONTH, toScheduleDaysOfMonth, toMonth, toMonthStrict, toQuarter, toQuarterStrict, };
|
|
485
|
-
export { getTenantBaseById, getCurrentTenant, getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, getTenantsByCheckInDateSelector, getTenantsBaseByCheckInDateSelector, getTenantsCoreDetailsByCheckInDateSelector, toTenantCoreDetailsView, getTenantBaseViewForTenantView, getTenantBaseViewForTenantId, isZeniDomainTenant, isTenantUsingZeniCOA, fetchActiveTenant, fetchAllTenants, updateCurrentTenant, clearAll, doMagicLinkSignIn, verifyDeviceWithTwoFA, resendVerifyDeviceOTP, sendEmailMagicLinkToUser, doSignIn, updateSignInState, doSignOut, sendSessionHeartbeat, resetSignInState, fetchExcludedResourcesForTenant, fetchExternalConnectionsForTenant, saveExternalConnectionForTenant, deleteConnection, saveAPIKeyConnection, saveConnectorCredentials, saveOAuthConnection, initEmailConnectOAuth, toExternalIntegrationType, toExternalSupportedTool, fetchSubscriptionSummaryForTenant, isTenantBankingOnly, isTenantCardsOnly, isTenantBookkeepingEnabled, isOtherProductsEnabledForTenant, };
|
|
485
|
+
export { getTenantBaseById, getCurrentTenant, getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, getTenantsByCheckInDateSelector, getTenantsBaseByCheckInDateSelector, getTenantsCoreDetailsByCheckInDateSelector, toTenantCoreDetailsView, getTenantBaseViewForTenantView, getTenantBaseViewForTenantId, isZeniDomainTenant, isTenantUsingZeniCOA, fetchActiveTenant, fetchAllTenants, updateCurrentTenant, clearAll, doMagicLinkSignIn, verifyDeviceWithTwoFA, resendVerifyDeviceOTP, sendEmailMagicLinkToUser, doSignIn, updateSignInState, doSignOut, sendSessionHeartbeat, resetSignInState, fetchExcludedResourcesForTenant, fetchExternalConnectionsForTenant, saveExternalConnectionForTenant, deleteConnection, saveAPIKeyConnection, saveConnectorCredentials, saveOAuthConnection, initEmailConnectOAuth, toExternalIntegrationType, toExternalSupportedTool, fetchSubscriptionSummaryForTenant, isTenantBankingOnly, isTenantCardsOnly, isTenantBookkeepingEnabled, isOtherProductsEnabledForTenant, DEFAULT_RECEIPTS_EMAIL, getReceiptsEmail, };
|
|
486
486
|
export { toAccountType, toAccountGroupType, getAccountGroupKey, getAllAccounts, getTransactionFilterAccountOptions, };
|
|
487
487
|
export { getAllClasses, getClassById, getClassFilterOptions, } from './entity/class/classSelector';
|
|
488
488
|
export { getForecast };
|
|
@@ -839,7 +839,7 @@ export { getInvoicingPlanById } from './entity/invoicing/plan/planSelector';
|
|
|
839
839
|
export { getInvoicingProductById } from './entity/invoicing/product/productSelector';
|
|
840
840
|
// ── Invoicing: settings ───────────────────────────────────────────────────────
|
|
841
841
|
export { clearInvoicingSettings, connectInvoicingStripe, disconnectInvoicingStripe, fetchInvoicingSettings, resetInvoicingBrandingFormDraft, resetSaveInvoicingSettings, saveInvoicingSettings, submitInvoicingBrandingForm, updateInvoicingBrandingFormDraft, updateInvoicingSettings, } from './view/invoicing/settingsView/settingsViewReducer';
|
|
842
|
-
export { getInvoicingBrandingFormView, getInvoicingSettings, getInvoicingSettingsFetchState, getInvoicingSettingsView, } from './view/invoicing/settingsView/settingsViewSelector';
|
|
842
|
+
export { getInvoicingBrandingFormView, getInvoicingSettings, getInvoicingSettingsFetchState, getInvoicingSettingsHubView, getInvoicingSettingsView, } from './view/invoicing/settingsView/settingsViewSelector';
|
|
843
843
|
export { INVOICING_BUSINESS_ADDRESS_TYPE, entityToInvoicingBrandingFormLocalData, initialInvoicingBrandingFormLocalData, } from './view/invoicing/settingsView/invoicingBrandingFormConfig';
|
|
844
844
|
export { isStripeConnectedFromSettings } from './view/invoicing/settingsView/invoicingSettingsHelpers';
|
|
845
845
|
// ── Invoicing: QBO integration ────────────────────────────────────────────────
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { from } from 'rxjs';
|
|
2
2
|
import { catchError, filter, mergeMap } from 'rxjs/operators';
|
|
3
3
|
import { updateInvoicingCreditNotes } from '../../../entity/invoicing/creditNote/creditNoteReducer';
|
|
4
|
+
import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiError';
|
|
4
5
|
import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
|
|
5
6
|
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
6
7
|
import { localDataToCreateCreditNotePayload, } from './createCreditNotePayload';
|
|
@@ -29,6 +30,7 @@ export const createCreditNoteEpic = (actions$, state$, zeniAPI) => actions$.pipe
|
|
|
29
30
|
messageSection: 'invoicing_credit_note_saved',
|
|
30
31
|
messageText: 'failed',
|
|
31
32
|
type: 'error',
|
|
33
|
+
variables: apiErrorSnackbarVariables(response.status),
|
|
32
34
|
}),
|
|
33
35
|
]);
|
|
34
36
|
}), catchError((error) => from([
|
|
@@ -3,6 +3,7 @@ import { catchError, filter, mergeMap } from 'rxjs/operators';
|
|
|
3
3
|
import { updateAddresses } from '../../../entity/address/addressReducer';
|
|
4
4
|
import { extractInvoicingInvoiceAddresses } from '../../../entity/invoicing/invoice/invoicePayload';
|
|
5
5
|
import { updateInvoices } from '../../../entity/invoicing/invoice/invoiceReducer';
|
|
6
|
+
import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiError';
|
|
6
7
|
import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
|
|
7
8
|
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
8
9
|
import { addInvoiceToList } from '../invoiceList/invoiceListReducer';
|
|
@@ -36,6 +37,7 @@ export const createInvoiceEpic = (actions$, _state$, zeniAPI) => actions$.pipe(f
|
|
|
36
37
|
messageSection: 'invoicing_invoice_saved',
|
|
37
38
|
messageText: 'failed',
|
|
38
39
|
type: 'error',
|
|
40
|
+
variables: apiErrorSnackbarVariables(response.status),
|
|
39
41
|
}),
|
|
40
42
|
]);
|
|
41
43
|
}), catchError((error) => from([
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { from } from 'rxjs';
|
|
2
2
|
import { catchError, filter, mergeMap } from 'rxjs/operators';
|
|
3
3
|
import { updateInvoicingCoupons } from '../../../entity/invoicing/coupon/couponReducer';
|
|
4
|
+
import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiError';
|
|
4
5
|
import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
|
|
5
|
-
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
6
|
+
import { createZeniAPIStatus, isSuccessResponse, } from '../../../responsePayload';
|
|
6
7
|
import { runInvoicingDiscountAction, runInvoicingDiscountActionFailure, runInvoicingDiscountActionSuccess, } from './discountActionReducer';
|
|
7
8
|
/** Snackbar copy per action kind; unlisted kinds run without one. */
|
|
8
9
|
const SNACKBAR_SECTION_BY_KIND = {
|
|
@@ -14,13 +15,15 @@ export const runInvoicingDiscountActionEpic = (actions$, _state$, zeniAPI) => ac
|
|
|
14
15
|
const { action: kind, id } = action.payload;
|
|
15
16
|
const requestBody = { action: kind };
|
|
16
17
|
const snackbarSection = SNACKBAR_SECTION_BY_KIND[kind];
|
|
17
|
-
|
|
18
|
+
/** `status` surfaces the platform's error text; omit it to keep the generic copy. */
|
|
19
|
+
const snackbar = (messageText, status) => snackbarSection == null
|
|
18
20
|
? []
|
|
19
21
|
: [
|
|
20
22
|
openSnackbar({
|
|
21
23
|
messageSection: snackbarSection,
|
|
22
24
|
messageText,
|
|
23
25
|
type: messageText === 'success' ? 'success' : 'error',
|
|
26
|
+
variables: apiErrorSnackbarVariables(status),
|
|
24
27
|
}),
|
|
25
28
|
];
|
|
26
29
|
const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/discounts/${encodeURIComponent(id)}/actions`;
|
|
@@ -39,7 +42,7 @@ export const runInvoicingDiscountActionEpic = (actions$, _state$, zeniAPI) => ac
|
|
|
39
42
|
error: response.status,
|
|
40
43
|
id,
|
|
41
44
|
}),
|
|
42
|
-
...snackbar('failed'),
|
|
45
|
+
...snackbar('failed', response.status),
|
|
43
46
|
]);
|
|
44
47
|
}), catchError((error) => from([
|
|
45
48
|
runInvoicingDiscountActionFailure({
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { from } from 'rxjs';
|
|
2
2
|
import { catchError, filter, mergeMap } from 'rxjs/operators';
|
|
3
3
|
import { updateInvoicingDunningCases } from '../../../entity/invoicing/dunningCase/dunningCaseReducer';
|
|
4
|
+
import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiError';
|
|
4
5
|
import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
|
|
5
|
-
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
6
|
+
import { createZeniAPIStatus, isSuccessResponse, } from '../../../responsePayload';
|
|
6
7
|
import { runInvoicingDunningAction, runInvoicingDunningActionFailure, runInvoicingDunningActionSuccess, } from './dunningActionReducer';
|
|
7
8
|
/**
|
|
8
9
|
* Snackbar copy is per action kind; anything not listed here runs without one.
|
|
@@ -15,13 +16,15 @@ const SNACKBAR_SECTION_BY_KIND = {
|
|
|
15
16
|
export const runInvoicingDunningActionEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(runInvoicingDunningAction.match), mergeMap((action) => {
|
|
16
17
|
const { action: kind, body, id } = action.payload;
|
|
17
18
|
const snackbarSection = SNACKBAR_SECTION_BY_KIND[kind];
|
|
18
|
-
|
|
19
|
+
/** `status` surfaces the platform's error text; omit it to keep the generic copy. */
|
|
20
|
+
const snackbar = (messageText, status) => snackbarSection == null
|
|
19
21
|
? []
|
|
20
22
|
: [
|
|
21
23
|
openSnackbar({
|
|
22
24
|
messageSection: snackbarSection,
|
|
23
25
|
messageText,
|
|
24
26
|
type: messageText === 'success' ? 'success' : 'error',
|
|
27
|
+
variables: apiErrorSnackbarVariables(status),
|
|
25
28
|
}),
|
|
26
29
|
];
|
|
27
30
|
const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/dunning/${encodeURIComponent(id)}/${kind}`;
|
|
@@ -40,7 +43,7 @@ export const runInvoicingDunningActionEpic = (actions$, _state$, zeniAPI) => act
|
|
|
40
43
|
error: response.status,
|
|
41
44
|
id,
|
|
42
45
|
}),
|
|
43
|
-
...snackbar('failed'),
|
|
46
|
+
...snackbar('failed', response.status),
|
|
44
47
|
]);
|
|
45
48
|
}), catchError((error) => from([
|
|
46
49
|
runInvoicingDunningActionFailure({
|
|
@@ -84,6 +84,8 @@ const buildSubFamilyPayload = (localData, subFamily, clearEmptyValues) => {
|
|
|
84
84
|
* Build the parent catalog-item request body from the form draft. Creates omit
|
|
85
85
|
* both parent and sub-family IDs; edits retain returned sub-family IDs.
|
|
86
86
|
*/
|
|
87
|
+
/** Sent for every catalog item now that the form does not collect a unit. */
|
|
88
|
+
const DEFAULT_CATALOG_ITEM_UNIT = 'USD';
|
|
87
89
|
export const localDataToSaveInvoicingCatalogItemPayload = (localData, isCreate) => {
|
|
88
90
|
const description = trimOrUndefined(localData.description);
|
|
89
91
|
const productCode = trimOrUndefined(localData.productCode) ?? toProductCode(localData.name);
|
|
@@ -101,10 +103,10 @@ export const localDataToSaveInvoicingCatalogItemPayload = (localData, isCreate)
|
|
|
101
103
|
if (description != null) {
|
|
102
104
|
itemPayload.description = description;
|
|
103
105
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
// The form no longer collects a unit, so a new item is created with the fixed
|
|
107
|
+
// default; an item that already carries one keeps it when saved from the form.
|
|
108
|
+
itemPayload.unit =
|
|
109
|
+
trimOrUndefined(localData.unit) ?? DEFAULT_CATALOG_ITEM_UNIT;
|
|
108
110
|
if (localData.itemApplicability === 'restricted' &&
|
|
109
111
|
localData.applicableItems.length > 0) {
|
|
110
112
|
itemPayload.applicable_items = localData.applicableItems;
|
package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js
CHANGED
|
@@ -2,6 +2,7 @@ import { forkJoin, from } from 'rxjs';
|
|
|
2
2
|
import { catchError, filter, mergeMap } from 'rxjs/operators';
|
|
3
3
|
import { mergeInvoicingPlanSubFamily, updateInvoicingPlans, } from '../../../entity/invoicing/plan/planReducer';
|
|
4
4
|
import { updateInvoicingProducts } from '../../../entity/invoicing/product/productReducer';
|
|
5
|
+
import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiError';
|
|
5
6
|
import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
|
|
6
7
|
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
7
8
|
import { addInvoicingCatalogItem } from '../invoicingCatalogListView/invoicingCatalogListViewReducer';
|
|
@@ -45,6 +46,7 @@ export const saveInvoicingCatalogItemEpic = (actions$, state$, zeniAPI) => actio
|
|
|
45
46
|
messageSection: 'invoicing_catalog_item_saved',
|
|
46
47
|
messageText: 'failed',
|
|
47
48
|
type: 'error',
|
|
49
|
+
variables: apiErrorSnackbarVariables(failedProductResponse.status),
|
|
48
50
|
}),
|
|
49
51
|
]);
|
|
50
52
|
}
|
|
@@ -94,6 +96,7 @@ export const saveInvoicingCatalogItemEpic = (actions$, state$, zeniAPI) => actio
|
|
|
94
96
|
messageSection: 'invoicing_catalog_item_saved',
|
|
95
97
|
messageText: 'failed',
|
|
96
98
|
type: 'error',
|
|
99
|
+
variables: apiErrorSnackbarVariables(response.status),
|
|
97
100
|
}),
|
|
98
101
|
]);
|
|
99
102
|
}), catchError((error) => from([
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { from } from 'rxjs';
|
|
2
2
|
import { catchError, filter, mergeMap } from 'rxjs/operators';
|
|
3
3
|
import { updateInvoicingCoupons } from '../../../entity/invoicing/coupon/couponReducer';
|
|
4
|
+
import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiError';
|
|
4
5
|
import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
|
|
5
6
|
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
6
7
|
import { addInvoicingCouponToList } from '../couponView/couponViewReducer';
|
|
@@ -57,6 +58,7 @@ export const saveInvoicingCouponEpic = (actions$, state$, zeniAPI) => actions$.p
|
|
|
57
58
|
messageSection: 'invoicing_coupon_saved',
|
|
58
59
|
messageText: 'failed',
|
|
59
60
|
type: 'error',
|
|
61
|
+
variables: apiErrorSnackbarVariables(response.status),
|
|
60
62
|
}),
|
|
61
63
|
]);
|
|
62
64
|
}), catchError((error) => from([
|
|
@@ -3,6 +3,7 @@ import { catchError, filter, mergeMap } from 'rxjs/operators';
|
|
|
3
3
|
import { updateAddresses } from '../../../entity/address/addressReducer';
|
|
4
4
|
import { extractInvoicingCustomerAddresses } from '../../../entity/invoicing/invoicingCustomer/invoicingCustomerPayload';
|
|
5
5
|
import { updateInvoicingCustomers } from '../../../entity/invoicing/invoicingCustomer/invoicingCustomerReducer';
|
|
6
|
+
import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiError';
|
|
6
7
|
import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
|
|
7
8
|
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
8
9
|
import { resetNewAddressDataInLocalStore } from '../../addressView/addressViewReducer';
|
|
@@ -59,6 +60,7 @@ export const saveInvoicingCustomerEpic = (actions$, state$, zeniAPI) => actions$
|
|
|
59
60
|
messageSection: 'invoicing_customer_saved',
|
|
60
61
|
messageText: 'failed',
|
|
61
62
|
type: 'error',
|
|
63
|
+
variables: apiErrorSnackbarVariables(response.status),
|
|
62
64
|
}),
|
|
63
65
|
]);
|
|
64
66
|
}), catchError((error) => from([
|
package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js
CHANGED
|
@@ -3,6 +3,7 @@ import { catchError, filter, mergeMap } from 'rxjs/operators';
|
|
|
3
3
|
import { updateAddresses } from '../../../entity/address/addressReducer';
|
|
4
4
|
import { extractInvoicingSubscriptionAddresses } from '../../../entity/invoicing/invoicingSubscription/invoicingSubscriptionPayload';
|
|
5
5
|
import { updateInvoicingSubscriptions } from '../../../entity/invoicing/invoicingSubscription/invoicingSubscriptionReducer';
|
|
6
|
+
import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiError';
|
|
6
7
|
import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
|
|
7
8
|
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
8
9
|
import { resetNewAddressDataInLocalStore } from '../../addressView/addressViewReducer';
|
|
@@ -45,6 +46,7 @@ export const saveInvoicingSubscriptionEpic = (actions$, state$, zeniAPI) => acti
|
|
|
45
46
|
messageSection: 'invoicing_subscription_saved',
|
|
46
47
|
messageText: 'failed',
|
|
47
48
|
type: 'error',
|
|
49
|
+
variables: apiErrorSnackbarVariables(response.status),
|
|
48
50
|
}),
|
|
49
51
|
];
|
|
50
52
|
}
|
|
@@ -3,6 +3,7 @@ import { catchError, filter, mergeMap } from 'rxjs/operators';
|
|
|
3
3
|
import { updateAddresses } from '../../../entity/address/addressReducer';
|
|
4
4
|
import { extractInvoicingInvoiceAddresses } from '../../../entity/invoicing/invoice/invoicePayload';
|
|
5
5
|
import { updateInvoices } from '../../../entity/invoicing/invoice/invoiceReducer';
|
|
6
|
+
import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiError';
|
|
6
7
|
import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
|
|
7
8
|
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
8
9
|
import { runInvoicingInvoiceAction, runInvoicingInvoiceActionFailure, runInvoicingInvoiceActionSuccess, updateInvoicingInvoice, } from './invoiceActionReducer';
|
|
@@ -80,6 +81,7 @@ export const updateInvoicingInvoiceEpic = (actions$, _state$, zeniAPI) => action
|
|
|
80
81
|
messageSection: 'invoicing_invoice_saved',
|
|
81
82
|
messageText: 'failed',
|
|
82
83
|
type: 'error',
|
|
84
|
+
variables: apiErrorSnackbarVariables(response.status),
|
|
83
85
|
}),
|
|
84
86
|
]);
|
|
85
87
|
}), catchError((error) => from([
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { from, of } from 'rxjs';
|
|
2
2
|
import { catchError, filter, mergeMap } from 'rxjs/operators';
|
|
3
|
+
import { apiErrorSnackbarVariables } from '../../../../entity/snackbar/snackbarApiError';
|
|
3
4
|
import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
|
|
4
5
|
import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
|
|
5
6
|
import { fetchInvoicingQboSyncHealth, retryInvoicingQboSyncTask, retryInvoicingQboSyncTaskFailure, retryInvoicingQboSyncTaskSuccess, } from '../invoicingQboViewReducer';
|
|
@@ -28,6 +29,7 @@ export const retryInvoicingQboSyncTaskEpic = (actions$, _state$, zeniAPI) => act
|
|
|
28
29
|
messageSection: 'invoicing_qbo_sync_health_retry',
|
|
29
30
|
messageText: 'failed',
|
|
30
31
|
type: 'error',
|
|
32
|
+
variables: apiErrorSnackbarVariables(response.status),
|
|
31
33
|
}),
|
|
32
34
|
]);
|
|
33
35
|
}), catchError((error) => from([
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { from } from 'rxjs';
|
|
2
2
|
import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
|
|
3
|
+
import { apiErrorSnackbarVariables } from '../../../../entity/snackbar/snackbarApiError';
|
|
3
4
|
import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
|
|
4
5
|
import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
|
|
5
6
|
import { fetchInvoicingQboSyncHealth, startInvoicingQboBackfill, startInvoicingQboBackfillFailure, startInvoicingQboBackfillSuccess, } from '../invoicingQboViewReducer';
|
|
@@ -27,6 +28,7 @@ export const startInvoicingQboBackfillEpic = (actions$, _state$, zeniAPI) => act
|
|
|
27
28
|
messageSection: 'invoicing_qbo_backfill',
|
|
28
29
|
messageText: 'failed',
|
|
29
30
|
type: 'error',
|
|
31
|
+
variables: apiErrorSnackbarVariables(response.status),
|
|
30
32
|
}),
|
|
31
33
|
]);
|
|
32
34
|
}), catchError((error) => from([
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { from } from 'rxjs';
|
|
2
2
|
import { catchError, filter, mergeMap } from 'rxjs/operators';
|
|
3
3
|
import { updateInvoicingCreditNotes } from '../../../entity/invoicing/creditNote/creditNoteReducer';
|
|
4
|
+
import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiError';
|
|
4
5
|
import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
|
|
5
6
|
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
6
7
|
import { fetchInvoiceDetail } from '../invoiceDetail/invoiceDetailReducer';
|
|
@@ -32,6 +33,7 @@ export const issueCreditNoteEpic = (actions$, state$, zeniAPI) => actions$.pipe(
|
|
|
32
33
|
messageSection: 'invoicing_credit_note_saved',
|
|
33
34
|
messageText: 'failed',
|
|
34
35
|
type: 'error',
|
|
36
|
+
variables: apiErrorSnackbarVariables(response.status),
|
|
35
37
|
}),
|
|
36
38
|
]);
|
|
37
39
|
}), catchError((error) => from([
|
|
@@ -2,6 +2,7 @@ import { from } from 'rxjs';
|
|
|
2
2
|
import { catchError, filter, mergeMap } from 'rxjs/operators';
|
|
3
3
|
import { updateInvoices } from '../../../entity/invoicing/invoice/invoiceReducer';
|
|
4
4
|
import { updateInvoicingTransactions } from '../../../entity/invoicing/invoicingTransaction/invoicingTransactionReducer';
|
|
5
|
+
import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiError';
|
|
5
6
|
import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
|
|
6
7
|
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
7
8
|
import { fetchInvoiceDetail } from '../invoiceDetail/invoiceDetailReducer';
|
|
@@ -40,6 +41,7 @@ export const recordPaymentEpic = (actions$, state$, zeniAPI) => actions$.pipe(fi
|
|
|
40
41
|
messageSection: 'invoicing_payment_recorded',
|
|
41
42
|
messageText: 'failed',
|
|
42
43
|
type: 'error',
|
|
44
|
+
variables: apiErrorSnackbarVariables(response.status),
|
|
43
45
|
}),
|
|
44
46
|
]);
|
|
45
47
|
}), catchError((error) => from([
|
|
@@ -82,6 +84,7 @@ export const recordPaymentEpic = (actions$, state$, zeniAPI) => actions$.pipe(fi
|
|
|
82
84
|
messageSection: 'invoicing_payment_recorded',
|
|
83
85
|
messageText: 'failed',
|
|
84
86
|
type: 'error',
|
|
87
|
+
variables: apiErrorSnackbarVariables(response.status),
|
|
85
88
|
}),
|
|
86
89
|
]);
|
|
87
90
|
}), catchError((error) => from([
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { from, of } from 'rxjs';
|
|
2
2
|
import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
|
|
3
3
|
import { updateAddresses } from '../../../entity/address/addressReducer';
|
|
4
|
+
import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiError';
|
|
4
5
|
import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
|
|
5
|
-
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
6
|
+
import { createZeniAPIStatus, isSuccessResponse, } from '../../../responsePayload';
|
|
6
7
|
import { connectInvoicingStripe, disconnectInvoicingStripe, fetchInvoicingSettings, invoicingStripeConnectionFailure, saveInvoicingSettings, saveInvoicingSettingsFailure, saveInvoicingSettingsSuccess, updateInvoicingSettings, updateInvoicingSettingsFailure, updateInvoicingStripeConnection, } from './settingsViewReducer';
|
|
7
8
|
export const fetchInvoicingSettingsEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchInvoicingSettings.match), switchMap(() => {
|
|
8
9
|
const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/settings`;
|
|
@@ -59,13 +60,15 @@ export const disconnectInvoicingStripeEpic = (actions$, _state$, zeniAPI) => act
|
|
|
59
60
|
export const saveInvoicingSettingsEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(saveInvoicingSettings.match), mergeMap((action) => {
|
|
60
61
|
const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/settings`;
|
|
61
62
|
const { settings, snackbarSection } = action.payload;
|
|
62
|
-
|
|
63
|
+
/** `status` surfaces the platform's error text; omit it to keep the generic copy. */
|
|
64
|
+
const snackbar = (messageText, status) => snackbarSection == null
|
|
63
65
|
? []
|
|
64
66
|
: [
|
|
65
67
|
openSnackbar({
|
|
66
68
|
messageSection: snackbarSection,
|
|
67
69
|
messageText,
|
|
68
70
|
type: messageText === 'success' ? 'success' : 'error',
|
|
71
|
+
variables: apiErrorSnackbarVariables(status),
|
|
69
72
|
}),
|
|
70
73
|
];
|
|
71
74
|
return zeniAPI
|
|
@@ -79,7 +82,7 @@ export const saveInvoicingSettingsEpic = (actions$, _state$, zeniAPI) => actions
|
|
|
79
82
|
}
|
|
80
83
|
return from([
|
|
81
84
|
saveInvoicingSettingsFailure(response.status),
|
|
82
|
-
...snackbar('failed'),
|
|
85
|
+
...snackbar('failed', response.status),
|
|
83
86
|
]);
|
|
84
87
|
}), catchError((error) => from([
|
|
85
88
|
saveInvoicingSettingsFailure(createZeniAPIStatus('Unexpected Error', 'Save settings REST API call errored out ' +
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getInvoicingQboAccountMappingId, } from '../../../entity/invoicing/invoicingCommonPayload';
|
|
1
2
|
import { getFormattedAddress } from '../../addressView/addressViewSelector';
|
|
2
3
|
import { getInvoicingConfigView } from '../invoicingConfigView/invoicingConfigViewSelector';
|
|
3
4
|
import { INVOICING_BUSINESS_ADDRESS_TYPE, entityToInvoicingBrandingFormLocalData, } from './invoicingBrandingFormConfig';
|
|
@@ -30,6 +31,57 @@ export const getInvoicingSettingsView = (state) => {
|
|
|
30
31
|
stripeConnectState: stripeFetchState,
|
|
31
32
|
};
|
|
32
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
* Gates the Enable Invoicing action: payments must be able to land (Stripe
|
|
36
|
+
* connected *and* accepting charges) and the QBO accounting step must be
|
|
37
|
+
* finished, so an enabled tenant can always book what it invoices.
|
|
38
|
+
*
|
|
39
|
+
* Deliberately not part of `deriveInvoicingOnboardingStatus`, which routes the
|
|
40
|
+
* module off the config flags alone. Folding these in there would (a) demote a
|
|
41
|
+
* live tenant to the settings tree the moment an integration broke, stranding
|
|
42
|
+
* its existing invoices, and (b) deadlock the route gate, which fetches only the
|
|
43
|
+
* config — never the settings or QBO connection these read.
|
|
44
|
+
*/
|
|
45
|
+
export function deriveInvoicingEnablePrerequisites(settings, isQboConnected, qboAccountMappingKindOptions) {
|
|
46
|
+
const isStripeConnected = settings?.stripe_connected === true &&
|
|
47
|
+
settings?.stripe_account?.charges_enabled === true;
|
|
48
|
+
const accountMappings = settings?.qbo?.account_mappings ?? undefined;
|
|
49
|
+
// An empty option list means the config has not landed yet — treating that as
|
|
50
|
+
// configured would let `every` pass vacuously and unlock the button early.
|
|
51
|
+
const isQboConfigured = isQboConnected &&
|
|
52
|
+
qboAccountMappingKindOptions.length > 0 &&
|
|
53
|
+
qboAccountMappingKindOptions.every((option) => getInvoicingQboAccountMappingId(accountMappings, option.code) != null);
|
|
54
|
+
return {
|
|
55
|
+
canEnable: isStripeConnected && isQboConfigured,
|
|
56
|
+
isQboConfigured,
|
|
57
|
+
isStripeConnected,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The settings hub's composite of everything it derives from the store: whether
|
|
62
|
+
* to offer Enable Invoicing at all, the in-flight flag for that action, and the
|
|
63
|
+
* two integration prerequisites kept separate so the hub can name the step still
|
|
64
|
+
* outstanding instead of showing a dead button.
|
|
65
|
+
*
|
|
66
|
+
* `canEnableInvoicing` folds in the onboarding step, so the screen does not need
|
|
67
|
+
* to re-check it: the footer is only offered mid-onboarding and only once both
|
|
68
|
+
* integrations are ready.
|
|
69
|
+
*
|
|
70
|
+
* Deliberately not folded into `getInvoicingConfigView`: that selector gates
|
|
71
|
+
* every invoicing route and stays a pure read of the config slice. The settings
|
|
72
|
+
* and QBO slices read here are only fetched once a settings screen is mounted.
|
|
73
|
+
*/
|
|
74
|
+
export const getInvoicingSettingsHubView = (state) => {
|
|
75
|
+
const { enableState, qboAccountMappingKindOptions, status } = getInvoicingConfigView(state);
|
|
76
|
+
const { canEnable, isQboConfigured, isStripeConnected } = deriveInvoicingEnablePrerequisites(state.invoicingSettingsViewState.settings, state.invoicingQboViewState.connection?.connected === true, qboAccountMappingKindOptions);
|
|
77
|
+
return {
|
|
78
|
+
canEnableInvoicing: status === 'settings' && canEnable,
|
|
79
|
+
isEnablingInvoicing: enableState.fetchState === 'In-Progress',
|
|
80
|
+
isQboConfigured,
|
|
81
|
+
isStripeConnected,
|
|
82
|
+
status,
|
|
83
|
+
};
|
|
84
|
+
};
|
|
33
85
|
/**
|
|
34
86
|
* Bundled view for the branding settings form: the draft `localData` (seeded
|
|
35
87
|
* from the existing settings until the user edits), the business-address label
|
|
@@ -3,8 +3,9 @@ import { catchError, filter, mergeMap } from 'rxjs/operators';
|
|
|
3
3
|
import { updateAddresses } from '../../../entity/address/addressReducer';
|
|
4
4
|
import { extractInvoicingSubscriptionAddresses } from '../../../entity/invoicing/invoicingSubscription/invoicingSubscriptionPayload';
|
|
5
5
|
import { updateInvoicingSubscriptions } from '../../../entity/invoicing/invoicingSubscription/invoicingSubscriptionReducer';
|
|
6
|
+
import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiError';
|
|
6
7
|
import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
|
|
7
|
-
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
8
|
+
import { createZeniAPIStatus, isSuccessResponse, } from '../../../responsePayload';
|
|
8
9
|
import { runInvoicingSubscriptionAction, runInvoicingSubscriptionActionFailure, runInvoicingSubscriptionActionSuccess, } from './subscriptionActionReducer';
|
|
9
10
|
/**
|
|
10
11
|
* Each lifecycle action reports with its own copy. Keyed loosely because the
|
|
@@ -24,13 +25,15 @@ export const runInvoicingSubscriptionActionEpic = (actions$, _state$, zeniAPI) =
|
|
|
24
25
|
const { action: kind, body, id } = action.payload;
|
|
25
26
|
const requestBody = { action: kind, ...(body ?? {}) };
|
|
26
27
|
const snackbarSection = SNACKBAR_SECTION_BY_ACTION[kind];
|
|
27
|
-
|
|
28
|
+
/** `status` surfaces the platform's error text; omit it to keep the generic copy. */
|
|
29
|
+
const snackbar = (messageText, status) => snackbarSection == null
|
|
28
30
|
? []
|
|
29
31
|
: [
|
|
30
32
|
openSnackbar({
|
|
31
33
|
messageSection: snackbarSection,
|
|
32
34
|
messageText,
|
|
33
35
|
type: messageText === 'success' ? 'success' : 'error',
|
|
36
|
+
variables: apiErrorSnackbarVariables(status),
|
|
34
37
|
}),
|
|
35
38
|
];
|
|
36
39
|
const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/subscriptions/${encodeURIComponent(id)}/actions`;
|
|
@@ -50,7 +53,7 @@ export const runInvoicingSubscriptionActionEpic = (actions$, _state$, zeniAPI) =
|
|
|
50
53
|
error: response.status,
|
|
51
54
|
id,
|
|
52
55
|
}),
|
|
53
|
-
...snackbar('failed'),
|
|
56
|
+
...snackbar('failed', response.status),
|
|
54
57
|
]);
|
|
55
58
|
}), catchError((error) => from([
|
|
56
59
|
runInvoicingSubscriptionActionFailure({
|
|
@@ -53,5 +53,5 @@ const vendorTabView = createSlice({
|
|
|
53
53
|
});
|
|
54
54
|
},
|
|
55
55
|
});
|
|
56
|
-
export const { clearVendorTabView, fetchVendorTabView, updateVendorTabViewTab
|
|
56
|
+
export const { clearVendorTabView, fetchVendorTabView, updateVendorTabViewTab } = vendorTabView.actions;
|
|
57
57
|
export default vendorTabView.reducer;
|