@zeniai/client-epic-state 5.2.87 → 5.2.89
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 +9 -0
- package/lib/entity/invoicing/invoicingCommonPayload.js +8 -1
- package/lib/entity/tenant/tenantPayload.d.ts +1 -0
- package/lib/entity/tenant/tenantReducer.js +1 -0
- package/lib/entity/tenant/tenantState.d.ts +1 -0
- package/lib/esm/entity/invoicing/invoicingCommonPayload.js +7 -0
- package/lib/esm/entity/tenant/tenantReducer.js +1 -0
- package/lib/esm/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +20 -12
- package/lib/esm/view/invoicing/settingsView/invoicingQboSettingsFormConfig.js +6 -1
- package/lib/esm/view/invoicing/settingsView/settingsViewReducer.js +19 -1
- package/lib/esm/view/invoicing/settingsView/settingsViewSelector.js +15 -1
- package/lib/esm/view/settingsView/epic/saveNotificationSettingsEpic.js +7 -0
- package/lib/esm/view/settingsView/settingsViewHelpers.js +1 -0
- package/lib/esm/view/settingsView/settingsViewReducer.js +16 -1
- package/lib/esm/view/settingsView/settingsViewState.js +1 -0
- package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.d.ts +6 -1
- package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +20 -12
- package/lib/view/invoicing/settingsView/invoicingQboSettingsFormConfig.js +5 -0
- package/lib/view/invoicing/settingsView/settingsViewReducer.js +19 -1
- package/lib/view/invoicing/settingsView/settingsViewSelector.d.ts +3 -1
- package/lib/view/invoicing/settingsView/settingsViewSelector.js +15 -1
- package/lib/view/settingsView/epic/saveNotificationSettingsEpic.js +7 -0
- package/lib/view/settingsView/settingsViewHelpers.js +1 -0
- package/lib/view/settingsView/settingsViewPayload.d.ts +2 -0
- package/lib/view/settingsView/settingsViewReducer.js +16 -1
- package/lib/view/settingsView/settingsViewState.d.ts +2 -2
- package/lib/view/settingsView/settingsViewState.js +1 -0
- package/package.json +1 -1
|
@@ -120,6 +120,12 @@ export declare const firstSelectableInvoicingId: (candidates: readonly (string |
|
|
|
120
120
|
export declare const toInvoicingQboAccountMappingSlot: (code: string) => string;
|
|
121
121
|
/** Read one account-mapping dropdown value by its config `code`. */
|
|
122
122
|
export declare function getInvoicingQboAccountMappingId(accountMappings: InvoicingQboAccountMappingsPayload | undefined, code: string): string | undefined;
|
|
123
|
+
/**
|
|
124
|
+
* The Zeni-owned account the service posts sales tax to. It arrives with the
|
|
125
|
+
* settings for display only: it is not a mapping the user picks, and the
|
|
126
|
+
* service ignores it on save.
|
|
127
|
+
*/
|
|
128
|
+
export declare const INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT = "sales_tax_posting_account";
|
|
123
129
|
/**
|
|
124
130
|
* Build the `account_mappings` request body from the ids the pickers submitted.
|
|
125
131
|
* Each kind is sent as the whole account, which is what the service stores; an
|
|
@@ -140,11 +146,14 @@ export interface InvoicingQboAccountMappingKindOptionWire {
|
|
|
140
146
|
account_types: string;
|
|
141
147
|
code: string;
|
|
142
148
|
name: string;
|
|
149
|
+
/** Comma-separated QuickBooks account sub types this kind must not offer. */
|
|
150
|
+
excluded_account_sub_types?: string | null;
|
|
143
151
|
}
|
|
144
152
|
export interface InvoicingQboAccountMappingKindOption {
|
|
145
153
|
accountTypes: string;
|
|
146
154
|
code: string;
|
|
147
155
|
name: string;
|
|
156
|
+
excludedAccountSubTypes?: string;
|
|
148
157
|
}
|
|
149
158
|
export declare function toInvoicingQboAccountMappingKindOption(option: InvoicingQboAccountMappingKindOptionWire): InvoicingQboAccountMappingKindOption;
|
|
150
159
|
export interface InvoicingSettingsPayload {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ALL_INVOICING_FIELD_HELP_GROUPS = exports.toInvoicingQboAccountMappingsPayload = exports.toInvoicingQboAccountMappingSlot = exports.firstSelectableInvoicingId = exports.INVOICING_QBO_CUSTOMER_MATCHING_MODES = exports.INVOICING_DISCOUNT_LIFECYCLE_ACTIONS = exports.INVOICING_CATALOG_PRODUCT_LIFECYCLE_ACTIONS = exports.INVOICING_CATALOG_ITEM_LIFECYCLE_ACTIONS = exports.INVOICING_SUBSCRIPTION_LIFECYCLE_ACTIONS = exports.INVOICING_DUNNING_ACTIONS = void 0;
|
|
3
|
+
exports.ALL_INVOICING_FIELD_HELP_GROUPS = exports.toInvoicingQboAccountMappingsPayload = exports.INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT = exports.toInvoicingQboAccountMappingSlot = exports.firstSelectableInvoicingId = exports.INVOICING_QBO_CUSTOMER_MATCHING_MODES = exports.INVOICING_DISCOUNT_LIFECYCLE_ACTIONS = exports.INVOICING_CATALOG_PRODUCT_LIFECYCLE_ACTIONS = exports.INVOICING_CATALOG_ITEM_LIFECYCLE_ACTIONS = exports.INVOICING_SUBSCRIPTION_LIFECYCLE_ACTIONS = exports.INVOICING_DUNNING_ACTIONS = void 0;
|
|
4
4
|
exports.toInvoicingDunningAction = toInvoicingDunningAction;
|
|
5
5
|
exports.toInvoicingSubscriptionLifecycleAction = toInvoicingSubscriptionLifecycleAction;
|
|
6
6
|
exports.toInvoicingSubscriptionLifecycleActionOption = toInvoicingSubscriptionLifecycleActionOption;
|
|
@@ -178,6 +178,12 @@ function getInvoicingQboAccountMappingId(accountMappings, code) {
|
|
|
178
178
|
}
|
|
179
179
|
return (0, exports.firstSelectableInvoicingId)([slot?.qbo_id, slot?.account_id]);
|
|
180
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* The Zeni-owned account the service posts sales tax to. It arrives with the
|
|
183
|
+
* settings for display only: it is not a mapping the user picks, and the
|
|
184
|
+
* service ignores it on save.
|
|
185
|
+
*/
|
|
186
|
+
exports.INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT = 'sales_tax_posting_account';
|
|
181
187
|
/**
|
|
182
188
|
* Build the `account_mappings` request body from the ids the pickers submitted.
|
|
183
189
|
* Each kind is sent as the whole account, which is what the service stores; an
|
|
@@ -201,6 +207,7 @@ function toInvoicingQboAccountMappingKindOption(option) {
|
|
|
201
207
|
return {
|
|
202
208
|
accountTypes: option.account_types,
|
|
203
209
|
code: option.code,
|
|
210
|
+
excludedAccountSubTypes: option.excluded_account_sub_types ?? undefined,
|
|
204
211
|
name: option.name,
|
|
205
212
|
};
|
|
206
213
|
}
|
|
@@ -98,6 +98,7 @@ interface TenantPayload {
|
|
|
98
98
|
master_tos_info?: MasterTOSInfoPayload;
|
|
99
99
|
receipts_email?: string | null;
|
|
100
100
|
reimbursement_info?: UserReimbursementInfoDataPayload;
|
|
101
|
+
unit_connected_account_application_id?: ID | null;
|
|
101
102
|
zeni_book_close_date?: string;
|
|
102
103
|
zeni_book_close_month_and_year?: string;
|
|
103
104
|
}
|
|
@@ -1025,6 +1025,7 @@ function mapTenantPayloadToTenant(payload, userTenantPayload) {
|
|
|
1025
1025
|
hubSpotId: payload.hubspot_id ?? undefined,
|
|
1026
1026
|
isOnboardingCompleted: true,
|
|
1027
1027
|
qboRealmId: payload.qbo_realm_id,
|
|
1028
|
+
unitConnectedAccountApplicationId: payload.unit_connected_account_application_id ?? undefined,
|
|
1028
1029
|
isCustomerCreated: payload.is_customer_created,
|
|
1029
1030
|
isEmailGroupCreated: payload.is_email_group_created,
|
|
1030
1031
|
userReimbursementInfo: payload.reimbursement_info != null
|
|
@@ -113,6 +113,7 @@ export interface Tenant {
|
|
|
113
113
|
masterTOSInfo?: MasterTOSInfo;
|
|
114
114
|
receiptsEmail?: string;
|
|
115
115
|
rewardsLastViewedTime?: ZeniDate;
|
|
116
|
+
unitConnectedAccountApplicationId?: ID;
|
|
116
117
|
userReimbursementInfo?: UserReimbursementInfo;
|
|
117
118
|
zeniBookCloseDate?: ZeniDate;
|
|
118
119
|
zeniBookCloseMonthAndYear?: MonthYearPeriod;
|
|
@@ -152,6 +152,12 @@ export function getInvoicingQboAccountMappingId(accountMappings, code) {
|
|
|
152
152
|
}
|
|
153
153
|
return firstSelectableInvoicingId([slot?.qbo_id, slot?.account_id]);
|
|
154
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* The Zeni-owned account the service posts sales tax to. It arrives with the
|
|
157
|
+
* settings for display only: it is not a mapping the user picks, and the
|
|
158
|
+
* service ignores it on save.
|
|
159
|
+
*/
|
|
160
|
+
export const INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT = 'sales_tax_posting_account';
|
|
155
161
|
/**
|
|
156
162
|
* Build the `account_mappings` request body from the ids the pickers submitted.
|
|
157
163
|
* Each kind is sent as the whole account, which is what the service stores; an
|
|
@@ -174,6 +180,7 @@ export function toInvoicingQboAccountMappingKindOption(option) {
|
|
|
174
180
|
return {
|
|
175
181
|
accountTypes: option.account_types,
|
|
176
182
|
code: option.code,
|
|
183
|
+
excludedAccountSubTypes: option.excluded_account_sub_types ?? undefined,
|
|
177
184
|
name: option.name,
|
|
178
185
|
};
|
|
179
186
|
}
|
|
@@ -1014,6 +1014,7 @@ function mapTenantPayloadToTenant(payload, userTenantPayload) {
|
|
|
1014
1014
|
hubSpotId: payload.hubspot_id ?? undefined,
|
|
1015
1015
|
isOnboardingCompleted: true,
|
|
1016
1016
|
qboRealmId: payload.qbo_realm_id,
|
|
1017
|
+
unitConnectedAccountApplicationId: payload.unit_connected_account_application_id ?? undefined,
|
|
1017
1018
|
isCustomerCreated: payload.is_customer_created,
|
|
1018
1019
|
isEmailGroupCreated: payload.is_email_group_created,
|
|
1019
1020
|
userReimbursementInfo: payload.reimbursement_info != null
|
|
@@ -106,6 +106,7 @@ export const getInvoicingQboAccountOptions = createSelector([
|
|
|
106
106
|
{
|
|
107
107
|
id: String(account.qboId),
|
|
108
108
|
name: account.accountName,
|
|
109
|
+
accountSubType: account.accountSubType,
|
|
109
110
|
accountType: account.accountType,
|
|
110
111
|
},
|
|
111
112
|
];
|
|
@@ -129,22 +130,29 @@ const normalizeAccountTypeName = (value) => {
|
|
|
129
130
|
// trailing "s" alone would leave "liabilitie", matching nothing.
|
|
130
131
|
return normalized.replace(/ies$/, 'y').replace(/([^s])s$/, '$1');
|
|
131
132
|
};
|
|
133
|
+
const toAccountTypeNameSet = (names) => new Set((names ?? '')
|
|
134
|
+
.split(',')
|
|
135
|
+
.map(normalizeAccountTypeName)
|
|
136
|
+
.filter((name) => name !== ''));
|
|
132
137
|
/**
|
|
133
138
|
* The accounts valid for one QBO mapping kind, whose `accountTypes` is a
|
|
134
139
|
* comma-separated list of QuickBooks type names. An unrecognised list yields no
|
|
135
140
|
* accounts rather than every account: offering a bank account where income is
|
|
136
141
|
* required would post a mapping QuickBooks rejects.
|
|
142
|
+
*
|
|
143
|
+
* `excludedAccountSubTypes` drops accounts QuickBooks will not post to for that
|
|
144
|
+
* kind even though their type matches, e.g. a tax agency's system payable for
|
|
145
|
+
* the sales tax liability.
|
|
137
146
|
*/
|
|
138
|
-
export const invoicingAccountsForQboAccountTypes = (accounts, accountTypes) => {
|
|
139
|
-
const wanted =
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
.
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
wanted.has(normalizeAccountTypeName(account.accountType)));
|
|
147
|
+
export const invoicingAccountsForQboAccountTypes = (accounts, accountTypes, excludedAccountSubTypes) => {
|
|
148
|
+
const wanted = toAccountTypeNameSet(accountTypes);
|
|
149
|
+
const excluded = toAccountTypeNameSet(excludedAccountSubTypes);
|
|
150
|
+
return accounts.filter((account) => (wanted.size === 0 ||
|
|
151
|
+
(account.accountType != null &&
|
|
152
|
+
wanted.has(normalizeAccountTypeName(account.accountType)))) &&
|
|
153
|
+
(excluded.size === 0 ||
|
|
154
|
+
account.accountSubType == null ||
|
|
155
|
+
!excluded.has(normalizeAccountTypeName(account.accountSubType))));
|
|
148
156
|
};
|
|
149
157
|
/**
|
|
150
158
|
* The account-mapping slot a catalog price point's income account shares with
|
|
@@ -172,8 +180,8 @@ export const INVOICING_DEFAULT_INCOME_ACCOUNT_TYPES = 'Income';
|
|
|
172
180
|
* not carry falls back to `defaultAccountTypes` rather than to every account.
|
|
173
181
|
*/
|
|
174
182
|
export const invoicingQboAccountIdsForMappingSlot = (accounts, accountMappingKindOptions, mappingSlot, defaultAccountTypes) => {
|
|
175
|
-
const
|
|
176
|
-
return invoicingAccountsForQboAccountTypes(accounts, accountTypes).map((account) => account.id);
|
|
183
|
+
const kindOption = accountMappingKindOptions.find((option) => toInvoicingQboAccountMappingSlot(option.code) === mappingSlot);
|
|
184
|
+
return invoicingAccountsForQboAccountTypes(accounts, kindOption?.accountTypes ?? defaultAccountTypes, kindOption?.excludedAccountSubTypes).map((account) => account.id);
|
|
177
185
|
};
|
|
178
186
|
/**
|
|
179
187
|
* Stable empties for a tenant with no QBO connection.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getInvoicingQboAccountMappingId, toInvoicingQboAccountMappingSlot, toInvoicingQboAccountMappingsPayload, } from '../../../entity/invoicing/invoicingCommonPayload';
|
|
1
|
+
import { INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT, getInvoicingQboAccountMappingId, toInvoicingQboAccountMappingSlot, toInvoicingQboAccountMappingsPayload, } from '../../../entity/invoicing/invoicingCommonPayload';
|
|
2
2
|
export const initialInvoicingQboSettingsLocalData = {
|
|
3
3
|
accountIdByMappingCode: {},
|
|
4
4
|
autoCreateItems: false,
|
|
@@ -15,6 +15,11 @@ export const entityToInvoicingQboSettingsLocalData = (qbo) => {
|
|
|
15
15
|
// Keyed by the slot the payload used, which is the config code the page will
|
|
16
16
|
// ask for — the reader tolerates the older `_id` form on the way in.
|
|
17
17
|
Object.keys(accountMappings).forEach((key) => {
|
|
18
|
+
// Display-only, so it is never edited or written back on save.
|
|
19
|
+
if (toInvoicingQboAccountMappingSlot(key) ===
|
|
20
|
+
INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
18
23
|
const accountId = getInvoicingQboAccountMappingId(accountMappings, key);
|
|
19
24
|
if (accountId != null) {
|
|
20
25
|
accountIdByMappingCode[toInvoicingQboAccountMappingSlot(key)] = accountId;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createSlice } from '@reduxjs/toolkit';
|
|
2
|
+
import { INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT, } from '../../../entity/invoicing/invoicingCommonPayload';
|
|
2
3
|
import { clearAll } from '../../../rootActions';
|
|
3
4
|
import { entityToInvoicingQboSettingsLocalData, } from './invoicingQboSettingsFormConfig';
|
|
4
5
|
export const initialState = {
|
|
@@ -40,7 +41,24 @@ const settingsView = createSlice({
|
|
|
40
41
|
saveInvoicingSettings(draft, action) {
|
|
41
42
|
draft.saveFetchState = { fetchState: 'In-Progress', error: undefined };
|
|
42
43
|
if (draft.settings != null) {
|
|
43
|
-
|
|
44
|
+
// The display-only sales tax posting account is never in a save body,
|
|
45
|
+
// so carry it over rather than blank it until the response lands.
|
|
46
|
+
const postingAccount = draft.settings.qbo?.account_mappings?.[INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT];
|
|
47
|
+
const settings = { ...draft.settings, ...action.payload.settings };
|
|
48
|
+
const accountMappings = settings.qbo?.account_mappings;
|
|
49
|
+
draft.settings =
|
|
50
|
+
postingAccount != null && accountMappings != null
|
|
51
|
+
? {
|
|
52
|
+
...settings,
|
|
53
|
+
qbo: {
|
|
54
|
+
...settings.qbo,
|
|
55
|
+
account_mappings: {
|
|
56
|
+
...accountMappings,
|
|
57
|
+
[INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT]: postingAccount,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
: settings;
|
|
44
62
|
}
|
|
45
63
|
},
|
|
46
64
|
saveInvoicingSettingsSuccess(draft, action) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { getInvoicingQboAccountMappingId, } from '../../../entity/invoicing/invoicingCommonPayload';
|
|
2
1
|
import { getAddressByAddressId } from '../../../entity/address/addressSelector';
|
|
3
2
|
import { getFileByFileId } from '../../../entity/file/fileSelector';
|
|
3
|
+
import { INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT, getInvoicingQboAccountMappingId, } from '../../../entity/invoicing/invoicingCommonPayload';
|
|
4
4
|
import { getFormattedAddress } from '../../addressView/addressViewSelector';
|
|
5
5
|
import { getInvoicingConfigView, invoicingFieldHelpForGroup, } from '../invoicingConfigView/invoicingConfigViewSelector';
|
|
6
6
|
import { getInvoicingQboView, isInvoicingAccountingPickerDataLoading, } from '../invoicingQboView/invoicingQboViewSelector';
|
|
@@ -102,6 +102,19 @@ export const getInvoicingSettingsHubView = (state) => {
|
|
|
102
102
|
status,
|
|
103
103
|
};
|
|
104
104
|
};
|
|
105
|
+
/**
|
|
106
|
+
* The name of the account the service posts sales tax to. Read from the whole
|
|
107
|
+
* account the settings carry, or looked up by id in the account list for a
|
|
108
|
+
* payload that sends only the id.
|
|
109
|
+
*/
|
|
110
|
+
const invoicingSalesTaxPostingAccountName = (accountMappings, accounts) => {
|
|
111
|
+
const slot = accountMappings?.[INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT];
|
|
112
|
+
const name = typeof slot === 'string'
|
|
113
|
+
? accounts.find((account) => account.id === slot)?.name
|
|
114
|
+
: slot?.account_name;
|
|
115
|
+
const trimmed = name?.trim() ?? '';
|
|
116
|
+
return trimmed !== '' ? trimmed : undefined;
|
|
117
|
+
};
|
|
105
118
|
/**
|
|
106
119
|
* Bundled view for the QBO configuration page: the draft it edits, the accounts
|
|
107
120
|
* and option lists its dropdowns render, the connection and sync health it
|
|
@@ -135,6 +148,7 @@ export const getInvoicingQboSettingsFormView = (state) => {
|
|
|
135
148
|
localData: qboDraft ?? initialInvoicingQboSettingsLocalData,
|
|
136
149
|
nestedAccountHierarchy: qboView.nestedAccountHierarchy,
|
|
137
150
|
retryFetchStateByTaskId: qboView.retryFetchStateByTaskId,
|
|
151
|
+
salesTaxPostingAccountName: invoicingSalesTaxPostingAccountName(settings?.qbo?.account_mappings, qboView.accounts),
|
|
138
152
|
saveState: saveFetchState.fetchState === 'Not-Started' && error != null
|
|
139
153
|
? { fetchState, error }
|
|
140
154
|
: saveFetchState,
|
|
@@ -50,6 +50,8 @@ function toSaveNotificationSettingsPayload(localData, notificationSettingsData)
|
|
|
50
50
|
pending_bill_approval_notification: null,
|
|
51
51
|
pending_reimbursement_approval_notification: null,
|
|
52
52
|
is_pending_reimbursement_approval_notification_active: null,
|
|
53
|
+
is_pending_task_notification_active: null,
|
|
54
|
+
pending_task_notification: null,
|
|
53
55
|
is_month_end_insights_active: monthEndInsightsNotificationSettings.enabled != null
|
|
54
56
|
? monthEndInsights.notify
|
|
55
57
|
: null,
|
|
@@ -77,5 +79,10 @@ function toSaveNotificationSettingsPayload(localData, notificationSettingsData)
|
|
|
77
79
|
0;
|
|
78
80
|
payload.pending_reimbursement_approval_notification = toRemindersPayload(reminders.pendingReimbursementApprovalNotification);
|
|
79
81
|
}
|
|
82
|
+
if (reminderNotificationSettings.pendingTaskNotification != null) {
|
|
83
|
+
payload.is_pending_task_notification_active =
|
|
84
|
+
reminders.pendingTaskNotification.preferredMode.length > 0;
|
|
85
|
+
payload.pending_task_notification = toRemindersPayload(reminders.pendingTaskNotification);
|
|
86
|
+
}
|
|
80
87
|
return payload;
|
|
81
88
|
}
|
|
@@ -140,6 +140,7 @@ export const isRemindersSectionVisibleOnSettingsPage = (reminderNotificationSett
|
|
|
140
140
|
reminderNotificationSettings.pendingBillApprovalNotification != null ||
|
|
141
141
|
reminderNotificationSettings.pendingReimbursementApprovalNotification !=
|
|
142
142
|
null ||
|
|
143
|
+
reminderNotificationSettings.pendingTaskNotification != null ||
|
|
143
144
|
reminderNotificationSettings.uncategorizedTransactionNotification != null);
|
|
144
145
|
};
|
|
145
146
|
export const isMonthEndNotificationsSectionVisibleOnSettingsPage = (monthEndInsightsNotificationSettings, signedInUseEmail) => {
|
|
@@ -4,6 +4,7 @@ import { toNotificationMode, toRemindersFrequencyType, } from './settingsViewSta
|
|
|
4
4
|
const initialNotificationSettingsData = {
|
|
5
5
|
reminderNotificationSettings: {
|
|
6
6
|
missingReceiptNotification: null,
|
|
7
|
+
pendingTaskNotification: null,
|
|
7
8
|
uncategorizedTransactionNotification: null,
|
|
8
9
|
pendingBillApprovalNotification: null,
|
|
9
10
|
pendingReimbursementApprovalNotification: null,
|
|
@@ -44,6 +45,7 @@ const initialNotificationSettingsLocalData = {
|
|
|
44
45
|
missingReceiptNotification: initialRemindersLocalData,
|
|
45
46
|
pendingBillApprovalNotification: initialRemindersLocalData,
|
|
46
47
|
pendingReimbursementApprovalNotification: initialRemindersLocalData,
|
|
48
|
+
pendingTaskNotification: initialRemindersLocalData,
|
|
47
49
|
uncategorizedTransactionNotification: initialRemindersLocalData,
|
|
48
50
|
},
|
|
49
51
|
};
|
|
@@ -373,7 +375,7 @@ function toRemindersLocalData(reminderNotificationSettingDetails) {
|
|
|
373
375
|
}
|
|
374
376
|
const toNotificationSettingsLocalData = (data, loggedInUserEmail) => {
|
|
375
377
|
const { monthEndInsightsNotificationSettings, reminderNotificationSettings } = data;
|
|
376
|
-
const { missingReceiptNotification, pendingBillApprovalNotification, pendingReimbursementApprovalNotification, uncategorizedTransactionNotification, } = reminderNotificationSettings;
|
|
378
|
+
const { missingReceiptNotification, pendingBillApprovalNotification, pendingReimbursementApprovalNotification, pendingTaskNotification, uncategorizedTransactionNotification, } = reminderNotificationSettings;
|
|
377
379
|
return {
|
|
378
380
|
monthEndInsights: {
|
|
379
381
|
notify: monthEndInsightsNotificationSettings.enabled ??
|
|
@@ -384,10 +386,22 @@ const toNotificationSettingsLocalData = (data, loggedInUserEmail) => {
|
|
|
384
386
|
missingReceiptNotification: toRemindersLocalData(missingReceiptNotification),
|
|
385
387
|
pendingBillApprovalNotification: toRemindersLocalData(pendingBillApprovalNotification),
|
|
386
388
|
pendingReimbursementApprovalNotification: toRemindersLocalData(pendingReimbursementApprovalNotification),
|
|
389
|
+
pendingTaskNotification: toRemindersLocalData(pendingTaskNotification),
|
|
387
390
|
uncategorizedTransactionNotification: toRemindersLocalData(uncategorizedTransactionNotification),
|
|
388
391
|
},
|
|
389
392
|
};
|
|
390
393
|
};
|
|
394
|
+
function toPendingTaskNotificationActive(data) {
|
|
395
|
+
if (data.is_pending_task_notification_active != null) {
|
|
396
|
+
return data.is_pending_task_notification_active;
|
|
397
|
+
}
|
|
398
|
+
// Rows saved before this flag existed store null. It shares the
|
|
399
|
+
// transactions-reminder scope with missing receipts and uncategorized
|
|
400
|
+
// transactions, so a set sibling means the user has access.
|
|
401
|
+
const hasTransactionReminderAccess = data.is_missing_receipt_notification_active != null ||
|
|
402
|
+
data.is_uncategorized_transaction_notification_active != null;
|
|
403
|
+
return hasTransactionReminderAccess ? false : null;
|
|
404
|
+
}
|
|
391
405
|
function toReminderNotificationSettingsDetails(isNotificationActive, remindersPayload) {
|
|
392
406
|
if (isNotificationActive != null) {
|
|
393
407
|
return {
|
|
@@ -408,6 +422,7 @@ const toNotificationSettings = (data) => {
|
|
|
408
422
|
missingReceiptNotification: toReminderNotificationSettingsDetails(data.is_missing_receipt_notification_active, data.missing_receipt_notification),
|
|
409
423
|
pendingBillApprovalNotification: toReminderNotificationSettingsDetails(data.is_pending_bill_approval_notification_active, data.pending_bill_approval_notification),
|
|
410
424
|
pendingReimbursementApprovalNotification: toReminderNotificationSettingsDetails(data.is_pending_reimbursement_approval_notification_active, data.pending_reimbursement_approval_notification),
|
|
425
|
+
pendingTaskNotification: toReminderNotificationSettingsDetails(toPendingTaskNotificationActive(data), data.pending_task_notification),
|
|
411
426
|
uncategorizedTransactionNotification: toReminderNotificationSettingsDetails(data.is_uncategorized_transaction_notification_active, data.uncategorized_transaction_notification),
|
|
412
427
|
},
|
|
413
428
|
monthEndInsightsNotificationSettings: {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { stringToUnion } from '../../commonStateTypes/stringToUnion';
|
|
2
2
|
export const REMINDER_NOTIFICATION_TYPES = [
|
|
3
3
|
'missingReceiptNotification',
|
|
4
|
+
'pendingTaskNotification',
|
|
4
5
|
'uncategorizedTransactionNotification',
|
|
5
6
|
'pendingBillApprovalNotification',
|
|
6
7
|
'pendingReimbursementApprovalNotification',
|
|
@@ -41,6 +41,7 @@ export declare const getInvoicingCatalogAccountingLookup: (state: RootState) =>
|
|
|
41
41
|
export interface InvoicingAccountingOption {
|
|
42
42
|
id: string;
|
|
43
43
|
name: string;
|
|
44
|
+
accountSubType?: string;
|
|
44
45
|
accountType?: string;
|
|
45
46
|
}
|
|
46
47
|
export interface InvoicingQboView {
|
|
@@ -77,8 +78,12 @@ export declare const getInvoicingQboAccountOptions: (state: RootState) => Invoic
|
|
|
77
78
|
* comma-separated list of QuickBooks type names. An unrecognised list yields no
|
|
78
79
|
* accounts rather than every account: offering a bank account where income is
|
|
79
80
|
* required would post a mapping QuickBooks rejects.
|
|
81
|
+
*
|
|
82
|
+
* `excludedAccountSubTypes` drops accounts QuickBooks will not post to for that
|
|
83
|
+
* kind even though their type matches, e.g. a tax agency's system payable for
|
|
84
|
+
* the sales tax liability.
|
|
80
85
|
*/
|
|
81
|
-
export declare const invoicingAccountsForQboAccountTypes: (accounts: InvoicingAccountingOption[], accountTypes: string) => InvoicingAccountingOption[];
|
|
86
|
+
export declare const invoicingAccountsForQboAccountTypes: (accounts: InvoicingAccountingOption[], accountTypes: string, excludedAccountSubTypes?: string) => InvoicingAccountingOption[];
|
|
82
87
|
/**
|
|
83
88
|
* The account-mapping slot a catalog price point's income account shares with
|
|
84
89
|
* the settings page, and the one a discount's account shares with it.
|
|
@@ -115,6 +115,7 @@ exports.getInvoicingQboAccountOptions = (0, toolkit_1.createSelector)([
|
|
|
115
115
|
{
|
|
116
116
|
id: String(account.qboId),
|
|
117
117
|
name: account.accountName,
|
|
118
|
+
accountSubType: account.accountSubType,
|
|
118
119
|
accountType: account.accountType,
|
|
119
120
|
},
|
|
120
121
|
];
|
|
@@ -138,22 +139,29 @@ const normalizeAccountTypeName = (value) => {
|
|
|
138
139
|
// trailing "s" alone would leave "liabilitie", matching nothing.
|
|
139
140
|
return normalized.replace(/ies$/, 'y').replace(/([^s])s$/, '$1');
|
|
140
141
|
};
|
|
142
|
+
const toAccountTypeNameSet = (names) => new Set((names ?? '')
|
|
143
|
+
.split(',')
|
|
144
|
+
.map(normalizeAccountTypeName)
|
|
145
|
+
.filter((name) => name !== ''));
|
|
141
146
|
/**
|
|
142
147
|
* The accounts valid for one QBO mapping kind, whose `accountTypes` is a
|
|
143
148
|
* comma-separated list of QuickBooks type names. An unrecognised list yields no
|
|
144
149
|
* accounts rather than every account: offering a bank account where income is
|
|
145
150
|
* required would post a mapping QuickBooks rejects.
|
|
151
|
+
*
|
|
152
|
+
* `excludedAccountSubTypes` drops accounts QuickBooks will not post to for that
|
|
153
|
+
* kind even though their type matches, e.g. a tax agency's system payable for
|
|
154
|
+
* the sales tax liability.
|
|
146
155
|
*/
|
|
147
|
-
const invoicingAccountsForQboAccountTypes = (accounts, accountTypes) => {
|
|
148
|
-
const wanted =
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
wanted.has(normalizeAccountTypeName(account.accountType)));
|
|
156
|
+
const invoicingAccountsForQboAccountTypes = (accounts, accountTypes, excludedAccountSubTypes) => {
|
|
157
|
+
const wanted = toAccountTypeNameSet(accountTypes);
|
|
158
|
+
const excluded = toAccountTypeNameSet(excludedAccountSubTypes);
|
|
159
|
+
return accounts.filter((account) => (wanted.size === 0 ||
|
|
160
|
+
(account.accountType != null &&
|
|
161
|
+
wanted.has(normalizeAccountTypeName(account.accountType)))) &&
|
|
162
|
+
(excluded.size === 0 ||
|
|
163
|
+
account.accountSubType == null ||
|
|
164
|
+
!excluded.has(normalizeAccountTypeName(account.accountSubType))));
|
|
157
165
|
};
|
|
158
166
|
exports.invoicingAccountsForQboAccountTypes = invoicingAccountsForQboAccountTypes;
|
|
159
167
|
/**
|
|
@@ -182,8 +190,8 @@ exports.INVOICING_DEFAULT_INCOME_ACCOUNT_TYPES = 'Income';
|
|
|
182
190
|
* not carry falls back to `defaultAccountTypes` rather than to every account.
|
|
183
191
|
*/
|
|
184
192
|
const invoicingQboAccountIdsForMappingSlot = (accounts, accountMappingKindOptions, mappingSlot, defaultAccountTypes) => {
|
|
185
|
-
const
|
|
186
|
-
return (0, exports.invoicingAccountsForQboAccountTypes)(accounts, accountTypes).map((account) => account.id);
|
|
193
|
+
const kindOption = accountMappingKindOptions.find((option) => (0, invoicingCommonPayload_1.toInvoicingQboAccountMappingSlot)(option.code) === mappingSlot);
|
|
194
|
+
return (0, exports.invoicingAccountsForQboAccountTypes)(accounts, kindOption?.accountTypes ?? defaultAccountTypes, kindOption?.excludedAccountSubTypes).map((account) => account.id);
|
|
187
195
|
};
|
|
188
196
|
exports.invoicingQboAccountIdsForMappingSlot = invoicingQboAccountIdsForMappingSlot;
|
|
189
197
|
/**
|
|
@@ -18,6 +18,11 @@ const entityToInvoicingQboSettingsLocalData = (qbo) => {
|
|
|
18
18
|
// Keyed by the slot the payload used, which is the config code the page will
|
|
19
19
|
// ask for — the reader tolerates the older `_id` form on the way in.
|
|
20
20
|
Object.keys(accountMappings).forEach((key) => {
|
|
21
|
+
// Display-only, so it is never edited or written back on save.
|
|
22
|
+
if ((0, invoicingCommonPayload_1.toInvoicingQboAccountMappingSlot)(key) ===
|
|
23
|
+
invoicingCommonPayload_1.INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
21
26
|
const accountId = (0, invoicingCommonPayload_1.getInvoicingQboAccountMappingId)(accountMappings, key);
|
|
22
27
|
if (accountId != null) {
|
|
23
28
|
accountIdByMappingCode[(0, invoicingCommonPayload_1.toInvoicingQboAccountMappingSlot)(key)] = accountId;
|
|
@@ -3,6 +3,7 @@ var _a;
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.invoicingStripeConnectionFailure = exports.updateInvoicingStripeConnection = exports.disconnectInvoicingStripe = exports.connectInvoicingStripe = exports.resetInvoicingQboSettingsDraft = exports.submitInvoicingQboSettings = exports.updateInvoicingQboSettingsDraft = exports.resetInvoicingBrandingFormDraft = exports.submitInvoicingBrandingForm = exports.updateInvoicingBrandingFormDraft = exports.resetSaveInvoicingSettings = exports.saveInvoicingSettingsFailure = exports.saveInvoicingSettingsSuccess = exports.saveInvoicingSettings = exports.updateInvoicingSettingsFailure = exports.updateInvoicingSettings = exports.fetchInvoicingSettings = exports.initialState = void 0;
|
|
5
5
|
const toolkit_1 = require("@reduxjs/toolkit");
|
|
6
|
+
const invoicingCommonPayload_1 = require("../../../entity/invoicing/invoicingCommonPayload");
|
|
6
7
|
const rootActions_1 = require("../../../rootActions");
|
|
7
8
|
const invoicingQboSettingsFormConfig_1 = require("./invoicingQboSettingsFormConfig");
|
|
8
9
|
exports.initialState = {
|
|
@@ -44,7 +45,24 @@ const settingsView = (0, toolkit_1.createSlice)({
|
|
|
44
45
|
saveInvoicingSettings(draft, action) {
|
|
45
46
|
draft.saveFetchState = { fetchState: 'In-Progress', error: undefined };
|
|
46
47
|
if (draft.settings != null) {
|
|
47
|
-
|
|
48
|
+
// The display-only sales tax posting account is never in a save body,
|
|
49
|
+
// so carry it over rather than blank it until the response lands.
|
|
50
|
+
const postingAccount = draft.settings.qbo?.account_mappings?.[invoicingCommonPayload_1.INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT];
|
|
51
|
+
const settings = { ...draft.settings, ...action.payload.settings };
|
|
52
|
+
const accountMappings = settings.qbo?.account_mappings;
|
|
53
|
+
draft.settings =
|
|
54
|
+
postingAccount != null && accountMappings != null
|
|
55
|
+
? {
|
|
56
|
+
...settings,
|
|
57
|
+
qbo: {
|
|
58
|
+
...settings.qbo,
|
|
59
|
+
account_mappings: {
|
|
60
|
+
...accountMappings,
|
|
61
|
+
[invoicingCommonPayload_1.INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT]: postingAccount,
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
: settings;
|
|
48
66
|
}
|
|
49
67
|
},
|
|
50
68
|
saveInvoicingSettingsSuccess(draft, action) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NestedAccountHierarchyForReport } from '../../../commonStateTypes/accountView/nestedAccountID';
|
|
2
2
|
import { FetchState, FetchStateAndError } from '../../../commonStateTypes/common';
|
|
3
|
-
import { InvoicingDunningActionOption, InvoicingFieldHelpByCode, InvoicingNamedOption, InvoicingQboAccountMappingKindOption, InvoicingSettingsPayload } from '../../../entity/invoicing/invoicingCommonPayload';
|
|
4
3
|
import { File } from '../../../entity/file/fileState';
|
|
4
|
+
import { InvoicingDunningActionOption, InvoicingFieldHelpByCode, InvoicingNamedOption, InvoicingQboAccountMappingKindOption, InvoicingSettingsPayload } from '../../../entity/invoicing/invoicingCommonPayload';
|
|
5
5
|
import { ZeniAPIStatus } from '../../../responsePayload';
|
|
6
6
|
import { RootState } from '../../../rootStateTypes';
|
|
7
7
|
import { AddressUpdatableInfo } from '../../addressView/addressViewState';
|
|
@@ -120,6 +120,8 @@ export interface InvoicingQboSettingsFormView {
|
|
|
120
120
|
settingsFetchState: FetchState;
|
|
121
121
|
syncStatusOptions: InvoicingNamedOption[];
|
|
122
122
|
connection?: InvoicingQboConnection;
|
|
123
|
+
/** The Zeni-owned account sales tax posts to, shown read-only when present. */
|
|
124
|
+
salesTaxPostingAccountName?: string;
|
|
123
125
|
syncHealth?: InvoicingQboSyncHealth;
|
|
124
126
|
}
|
|
125
127
|
/**
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getInvoicingBrandingFormView = exports.getInvoicingQboSettingsFormView = exports.getInvoicingSettingsHubView = exports.getInvoicingSettingsView = exports.getInvoicingSettingsFetchState = exports.getInvoicingSettings = void 0;
|
|
4
4
|
exports.deriveInvoicingEnablePrerequisites = deriveInvoicingEnablePrerequisites;
|
|
5
|
-
const invoicingCommonPayload_1 = require("../../../entity/invoicing/invoicingCommonPayload");
|
|
6
5
|
const addressSelector_1 = require("../../../entity/address/addressSelector");
|
|
7
6
|
const fileSelector_1 = require("../../../entity/file/fileSelector");
|
|
7
|
+
const invoicingCommonPayload_1 = require("../../../entity/invoicing/invoicingCommonPayload");
|
|
8
8
|
const addressViewSelector_1 = require("../../addressView/addressViewSelector");
|
|
9
9
|
const invoicingConfigViewSelector_1 = require("../invoicingConfigView/invoicingConfigViewSelector");
|
|
10
10
|
const invoicingQboViewSelector_1 = require("../invoicingQboView/invoicingQboViewSelector");
|
|
@@ -110,6 +110,19 @@ const getInvoicingSettingsHubView = (state) => {
|
|
|
110
110
|
};
|
|
111
111
|
};
|
|
112
112
|
exports.getInvoicingSettingsHubView = getInvoicingSettingsHubView;
|
|
113
|
+
/**
|
|
114
|
+
* The name of the account the service posts sales tax to. Read from the whole
|
|
115
|
+
* account the settings carry, or looked up by id in the account list for a
|
|
116
|
+
* payload that sends only the id.
|
|
117
|
+
*/
|
|
118
|
+
const invoicingSalesTaxPostingAccountName = (accountMappings, accounts) => {
|
|
119
|
+
const slot = accountMappings?.[invoicingCommonPayload_1.INVOICING_QBO_SALES_TAX_POSTING_ACCOUNT_SLOT];
|
|
120
|
+
const name = typeof slot === 'string'
|
|
121
|
+
? accounts.find((account) => account.id === slot)?.name
|
|
122
|
+
: slot?.account_name;
|
|
123
|
+
const trimmed = name?.trim() ?? '';
|
|
124
|
+
return trimmed !== '' ? trimmed : undefined;
|
|
125
|
+
};
|
|
113
126
|
/**
|
|
114
127
|
* Bundled view for the QBO configuration page: the draft it edits, the accounts
|
|
115
128
|
* and option lists its dropdowns render, the connection and sync health it
|
|
@@ -143,6 +156,7 @@ const getInvoicingQboSettingsFormView = (state) => {
|
|
|
143
156
|
localData: qboDraft ?? invoicingQboSettingsFormConfig_1.initialInvoicingQboSettingsLocalData,
|
|
144
157
|
nestedAccountHierarchy: qboView.nestedAccountHierarchy,
|
|
145
158
|
retryFetchStateByTaskId: qboView.retryFetchStateByTaskId,
|
|
159
|
+
salesTaxPostingAccountName: invoicingSalesTaxPostingAccountName(settings?.qbo?.account_mappings, qboView.accounts),
|
|
146
160
|
saveState: saveFetchState.fetchState === 'Not-Started' && error != null
|
|
147
161
|
? { fetchState, error }
|
|
148
162
|
: saveFetchState,
|
|
@@ -54,6 +54,8 @@ function toSaveNotificationSettingsPayload(localData, notificationSettingsData)
|
|
|
54
54
|
pending_bill_approval_notification: null,
|
|
55
55
|
pending_reimbursement_approval_notification: null,
|
|
56
56
|
is_pending_reimbursement_approval_notification_active: null,
|
|
57
|
+
is_pending_task_notification_active: null,
|
|
58
|
+
pending_task_notification: null,
|
|
57
59
|
is_month_end_insights_active: monthEndInsightsNotificationSettings.enabled != null
|
|
58
60
|
? monthEndInsights.notify
|
|
59
61
|
: null,
|
|
@@ -81,5 +83,10 @@ function toSaveNotificationSettingsPayload(localData, notificationSettingsData)
|
|
|
81
83
|
0;
|
|
82
84
|
payload.pending_reimbursement_approval_notification = toRemindersPayload(reminders.pendingReimbursementApprovalNotification);
|
|
83
85
|
}
|
|
86
|
+
if (reminderNotificationSettings.pendingTaskNotification != null) {
|
|
87
|
+
payload.is_pending_task_notification_active =
|
|
88
|
+
reminders.pendingTaskNotification.preferredMode.length > 0;
|
|
89
|
+
payload.pending_task_notification = toRemindersPayload(reminders.pendingTaskNotification);
|
|
90
|
+
}
|
|
84
91
|
return payload;
|
|
85
92
|
}
|
|
@@ -153,6 +153,7 @@ const isRemindersSectionVisibleOnSettingsPage = (reminderNotificationSettings) =
|
|
|
153
153
|
reminderNotificationSettings.pendingBillApprovalNotification != null ||
|
|
154
154
|
reminderNotificationSettings.pendingReimbursementApprovalNotification !=
|
|
155
155
|
null ||
|
|
156
|
+
reminderNotificationSettings.pendingTaskNotification != null ||
|
|
156
157
|
reminderNotificationSettings.uncategorizedTransactionNotification != null);
|
|
157
158
|
};
|
|
158
159
|
exports.isRemindersSectionVisibleOnSettingsPage = isRemindersSectionVisibleOnSettingsPage;
|
|
@@ -32,10 +32,12 @@ export interface NotificationSettingsPayload {
|
|
|
32
32
|
is_month_end_insights_active: boolean | null;
|
|
33
33
|
is_pending_bill_approval_notification_active: boolean | null;
|
|
34
34
|
is_pending_reimbursement_approval_notification_active: boolean | null;
|
|
35
|
+
is_pending_task_notification_active: boolean | null;
|
|
35
36
|
is_uncategorized_transaction_notification_active: boolean | null;
|
|
36
37
|
missing_receipt_notification: RemindersPayload | null;
|
|
37
38
|
pending_bill_approval_notification: RemindersPayload | null;
|
|
38
39
|
pending_reimbursement_approval_notification: RemindersPayload | null;
|
|
40
|
+
pending_task_notification: RemindersPayload | null;
|
|
39
41
|
uncategorized_transaction_notification: RemindersPayload | null;
|
|
40
42
|
month_end_email_recipient_email_ids?: string[];
|
|
41
43
|
task_checklist_preferences?: NotificationPreferencesPayload;
|
|
@@ -8,6 +8,7 @@ const settingsViewState_1 = require("./settingsViewState");
|
|
|
8
8
|
const initialNotificationSettingsData = {
|
|
9
9
|
reminderNotificationSettings: {
|
|
10
10
|
missingReceiptNotification: null,
|
|
11
|
+
pendingTaskNotification: null,
|
|
11
12
|
uncategorizedTransactionNotification: null,
|
|
12
13
|
pendingBillApprovalNotification: null,
|
|
13
14
|
pendingReimbursementApprovalNotification: null,
|
|
@@ -48,6 +49,7 @@ const initialNotificationSettingsLocalData = {
|
|
|
48
49
|
missingReceiptNotification: initialRemindersLocalData,
|
|
49
50
|
pendingBillApprovalNotification: initialRemindersLocalData,
|
|
50
51
|
pendingReimbursementApprovalNotification: initialRemindersLocalData,
|
|
52
|
+
pendingTaskNotification: initialRemindersLocalData,
|
|
51
53
|
uncategorizedTransactionNotification: initialRemindersLocalData,
|
|
52
54
|
},
|
|
53
55
|
};
|
|
@@ -377,7 +379,7 @@ function toRemindersLocalData(reminderNotificationSettingDetails) {
|
|
|
377
379
|
}
|
|
378
380
|
const toNotificationSettingsLocalData = (data, loggedInUserEmail) => {
|
|
379
381
|
const { monthEndInsightsNotificationSettings, reminderNotificationSettings } = data;
|
|
380
|
-
const { missingReceiptNotification, pendingBillApprovalNotification, pendingReimbursementApprovalNotification, uncategorizedTransactionNotification, } = reminderNotificationSettings;
|
|
382
|
+
const { missingReceiptNotification, pendingBillApprovalNotification, pendingReimbursementApprovalNotification, pendingTaskNotification, uncategorizedTransactionNotification, } = reminderNotificationSettings;
|
|
381
383
|
return {
|
|
382
384
|
monthEndInsights: {
|
|
383
385
|
notify: monthEndInsightsNotificationSettings.enabled ??
|
|
@@ -388,10 +390,22 @@ const toNotificationSettingsLocalData = (data, loggedInUserEmail) => {
|
|
|
388
390
|
missingReceiptNotification: toRemindersLocalData(missingReceiptNotification),
|
|
389
391
|
pendingBillApprovalNotification: toRemindersLocalData(pendingBillApprovalNotification),
|
|
390
392
|
pendingReimbursementApprovalNotification: toRemindersLocalData(pendingReimbursementApprovalNotification),
|
|
393
|
+
pendingTaskNotification: toRemindersLocalData(pendingTaskNotification),
|
|
391
394
|
uncategorizedTransactionNotification: toRemindersLocalData(uncategorizedTransactionNotification),
|
|
392
395
|
},
|
|
393
396
|
};
|
|
394
397
|
};
|
|
398
|
+
function toPendingTaskNotificationActive(data) {
|
|
399
|
+
if (data.is_pending_task_notification_active != null) {
|
|
400
|
+
return data.is_pending_task_notification_active;
|
|
401
|
+
}
|
|
402
|
+
// Rows saved before this flag existed store null. It shares the
|
|
403
|
+
// transactions-reminder scope with missing receipts and uncategorized
|
|
404
|
+
// transactions, so a set sibling means the user has access.
|
|
405
|
+
const hasTransactionReminderAccess = data.is_missing_receipt_notification_active != null ||
|
|
406
|
+
data.is_uncategorized_transaction_notification_active != null;
|
|
407
|
+
return hasTransactionReminderAccess ? false : null;
|
|
408
|
+
}
|
|
395
409
|
function toReminderNotificationSettingsDetails(isNotificationActive, remindersPayload) {
|
|
396
410
|
if (isNotificationActive != null) {
|
|
397
411
|
return {
|
|
@@ -412,6 +426,7 @@ const toNotificationSettings = (data) => {
|
|
|
412
426
|
missingReceiptNotification: toReminderNotificationSettingsDetails(data.is_missing_receipt_notification_active, data.missing_receipt_notification),
|
|
413
427
|
pendingBillApprovalNotification: toReminderNotificationSettingsDetails(data.is_pending_bill_approval_notification_active, data.pending_bill_approval_notification),
|
|
414
428
|
pendingReimbursementApprovalNotification: toReminderNotificationSettingsDetails(data.is_pending_reimbursement_approval_notification_active, data.pending_reimbursement_approval_notification),
|
|
429
|
+
pendingTaskNotification: toReminderNotificationSettingsDetails(toPendingTaskNotificationActive(data), data.pending_task_notification),
|
|
415
430
|
uncategorizedTransactionNotification: toReminderNotificationSettingsDetails(data.is_uncategorized_transaction_notification_active, data.uncategorized_transaction_notification),
|
|
416
431
|
},
|
|
417
432
|
monthEndInsightsNotificationSettings: {
|
|
@@ -9,8 +9,8 @@ interface BillingAddress extends FetchStateAndError {
|
|
|
9
9
|
export interface IntegrationsSettings {
|
|
10
10
|
refreshQBO: FetchStateAndError;
|
|
11
11
|
}
|
|
12
|
-
export declare const REMINDER_NOTIFICATION_TYPES: readonly ["missingReceiptNotification", "uncategorizedTransactionNotification", "pendingBillApprovalNotification", "pendingReimbursementApprovalNotification"];
|
|
13
|
-
declare const toReminderTypeKey: (v: string) => "missingReceiptNotification" | "uncategorizedTransactionNotification" | "pendingBillApprovalNotification" | "pendingReimbursementApprovalNotification";
|
|
12
|
+
export declare const REMINDER_NOTIFICATION_TYPES: readonly ["missingReceiptNotification", "pendingTaskNotification", "uncategorizedTransactionNotification", "pendingBillApprovalNotification", "pendingReimbursementApprovalNotification"];
|
|
13
|
+
declare const toReminderTypeKey: (v: string) => "missingReceiptNotification" | "pendingTaskNotification" | "uncategorizedTransactionNotification" | "pendingBillApprovalNotification" | "pendingReimbursementApprovalNotification";
|
|
14
14
|
export type ReminderTypeKey = ReturnType<typeof toReminderTypeKey>;
|
|
15
15
|
export declare const toRemindersFrequencyType: (v: string) => "weekly" | "monthly" | "daily" | "bi-weekly";
|
|
16
16
|
export type FrequencyTypeKey = ReturnType<typeof toRemindersFrequencyType>;
|
|
@@ -4,6 +4,7 @@ exports.toNotificationMode = exports.NOTIFICATION_MODES = exports.toRemindersFre
|
|
|
4
4
|
const stringToUnion_1 = require("../../commonStateTypes/stringToUnion");
|
|
5
5
|
exports.REMINDER_NOTIFICATION_TYPES = [
|
|
6
6
|
'missingReceiptNotification',
|
|
7
|
+
'pendingTaskNotification',
|
|
7
8
|
'uncategorizedTransactionNotification',
|
|
8
9
|
'pendingBillApprovalNotification',
|
|
9
10
|
'pendingReimbursementApprovalNotification',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeniai/client-epic-state",
|
|
3
|
-
"version": "5.2.
|
|
3
|
+
"version": "5.2.89",
|
|
4
4
|
"description": "Shared module between Web & Mobile containing required abstractions for state management, async network communication. ",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "lib/esm/index.js",
|