@zeniai/client-epic-state 5.3.26 → 5.3.28
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/approvalRule/approvalRuleSelector.d.ts +8 -1
- package/lib/entity/approvalRule/approvalRuleSelector.js +12 -0
- package/lib/entity/approvalRule/attributeApprovers.d.ts +26 -0
- package/lib/entity/approvalRule/attributeApprovers.js +39 -0
- package/lib/entity/vendor/vendorPayload.js +1 -1
- package/lib/entity/vendor/vendorState.d.ts +6 -1
- package/lib/esm/entity/approvalRule/approvalRuleSelector.js +11 -0
- package/lib/esm/entity/approvalRule/attributeApprovers.js +35 -0
- package/lib/esm/entity/vendor/vendorPayload.js +1 -1
- package/lib/esm/index.js +2 -1
- package/lib/esm/view/commonVendorView/transactionVendorView/transactionVendorLocalDataHelper.js +1 -1
- package/lib/esm/view/commonVendorView/vendorView/epics/vendorDetailLocalDataHelpers.js +1 -1
- package/lib/esm/view/commonVendorView/vendorsTabVendorView/epics/vendorsTabVendorDetailLocalDataHelpers.js +1 -1
- package/lib/esm/view/spendManagement/billPay/billDetailView/billDetailViewSelector.js +1 -1
- package/lib/esm/view/spendManagement/billPay/billPaySetupApproverView/billPaySetupApproverViewSelector.js +30 -16
- package/lib/esm/view/spendManagement/billPay/editBillView/editBillViewSelector.js +6 -0
- package/lib/esm/view/spendManagement/reimbursement/editRemiView/editRemiViewSelector.js +6 -1
- package/lib/esm/view/spendManagement/reimbursement/editRemiView/epics/fetchEditRemiDetailPageEpic.js +6 -0
- package/lib/esm/view/spendManagement/reimbursement/remiSetupApproverView/epic/fetchRemiApproversListEpic.js +7 -3
- package/lib/esm/view/spendManagement/reimbursement/remiSetupApproverView/remiSetupApproverViewReducer.js +7 -1
- package/lib/esm/view/spendManagement/reimbursement/remiSetupApproverView/remiSetupApproverViewSelector.js +31 -17
- package/lib/index.d.ts +2 -1
- package/lib/index.js +30 -28
- package/lib/view/commonVendorView/transactionVendorView/transactionVendorLocalDataHelper.js +1 -1
- package/lib/view/commonVendorView/vendorView/epics/vendorDetailLocalDataHelpers.js +1 -1
- package/lib/view/commonVendorView/vendorsTabVendorView/epics/vendorsTabVendorDetailLocalDataHelpers.js +1 -1
- package/lib/view/spendManagement/billPay/billDetailView/billDetailViewSelector.js +1 -1
- package/lib/view/spendManagement/billPay/billPaySetupApproverView/billPaySetupApproverViewSelector.js +29 -15
- package/lib/view/spendManagement/billPay/billPaySetupApproverView/billPaySetupApproverViewSelectorTypes.d.ts +1 -0
- package/lib/view/spendManagement/billPay/editBillView/editBillViewSelector.d.ts +7 -0
- package/lib/view/spendManagement/billPay/editBillView/editBillViewSelector.js +6 -0
- package/lib/view/spendManagement/reimbursement/editRemiView/editRemiViewSelector.d.ts +8 -0
- package/lib/view/spendManagement/reimbursement/editRemiView/editRemiViewSelector.js +6 -1
- package/lib/view/spendManagement/reimbursement/editRemiView/epics/fetchEditRemiDetailPageEpic.d.ts +2 -1
- package/lib/view/spendManagement/reimbursement/editRemiView/epics/fetchEditRemiDetailPageEpic.js +6 -0
- package/lib/view/spendManagement/reimbursement/remiSetupApproverView/epic/fetchRemiApproversListEpic.js +7 -3
- package/lib/view/spendManagement/reimbursement/remiSetupApproverView/remiSetupApproverViewReducer.d.ts +2 -2
- package/lib/view/spendManagement/reimbursement/remiSetupApproverView/remiSetupApproverViewReducer.js +7 -1
- package/lib/view/spendManagement/reimbursement/remiSetupApproverView/remiSetupApproverViewSelector.js +30 -16
- package/lib/view/spendManagement/reimbursement/remiSetupApproverView/remiSetupApproverViewSelectorTypes.d.ts +1 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ID } from '../../commonStateTypes/common';
|
|
2
2
|
import { EntityType } from '../entityApprovalStatus/entityApprovalStatusState';
|
|
3
|
-
import { AmountCriteria, ApprovalRule, ApprovalRuleState, Criteria, DepartmentCriteria, VendorCriteria } from './approvalRuleState';
|
|
3
|
+
import { Actor, AmountCriteria, ApprovalRule, ApprovalRuleState, Criteria, DepartmentCriteria, Step, VendorCriteria } from './approvalRuleState';
|
|
4
4
|
export declare function getApprovalRuleById(approvalRuleState: ApprovalRuleState, approvalRuleId: ID): ApprovalRule | undefined;
|
|
5
5
|
export declare function getApprovalRulesByIds(approvalRuleState: ApprovalRuleState, approvalRuleIds: ID[]): ApprovalRule[];
|
|
6
6
|
/**
|
|
@@ -26,3 +26,10 @@ export declare function getApprovalRulesByEntityType(approvalRuleState: Approval
|
|
|
26
26
|
* list page and is used to handle bills/reimbursements that match no other rule.
|
|
27
27
|
*/
|
|
28
28
|
export declare function getFallbackApprovalRule(approvalRuleState: ApprovalRuleState, entityType: EntityType): ApprovalRule | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Non-user approvers to show for a fallback-rule step in the draft approval
|
|
31
|
+
* preview. Implicit actors (e.g. the Admin the platform adds) are shown only
|
|
32
|
+
* when the step has no explicit approver, so the step always has someone to
|
|
33
|
+
* approve it without listing Admin next to an explicit approver.
|
|
34
|
+
*/
|
|
35
|
+
export declare function getFallbackStepNonUserActors(step: Step): Actor[];
|
|
@@ -7,6 +7,7 @@ exports.getVendorCriteria = getVendorCriteria;
|
|
|
7
7
|
exports.getDepartmentCriteria = getDepartmentCriteria;
|
|
8
8
|
exports.getApprovalRulesByEntityType = getApprovalRulesByEntityType;
|
|
9
9
|
exports.getFallbackApprovalRule = getFallbackApprovalRule;
|
|
10
|
+
exports.getFallbackStepNonUserActors = getFallbackStepNonUserActors;
|
|
10
11
|
function getApprovalRuleById(approvalRuleState, approvalRuleId) {
|
|
11
12
|
return approvalRuleState.approvalRuleById[approvalRuleId] ?? undefined;
|
|
12
13
|
}
|
|
@@ -51,3 +52,14 @@ function getApprovalRulesByEntityType(approvalRuleState, entityType) {
|
|
|
51
52
|
function getFallbackApprovalRule(approvalRuleState, entityType) {
|
|
52
53
|
return Object.values(approvalRuleState.approvalRuleById).find((rule) => rule.entityType === entityType && rule.isFallback === true);
|
|
53
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Non-user approvers to show for a fallback-rule step in the draft approval
|
|
57
|
+
* preview. Implicit actors (e.g. the Admin the platform adds) are shown only
|
|
58
|
+
* when the step has no explicit approver, so the step always has someone to
|
|
59
|
+
* approve it without listing Admin next to an explicit approver.
|
|
60
|
+
*/
|
|
61
|
+
function getFallbackStepNonUserActors(step) {
|
|
62
|
+
const hasExplicitApprover = step.actors.some((actor) => actor.isImplicitActor === false);
|
|
63
|
+
return step.actors.filter((actor) => actor.type !== 'user' &&
|
|
64
|
+
(!hasExplicitApprover || actor.isImplicitActor === false));
|
|
65
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ID } from '../../commonStateTypes/common';
|
|
2
|
+
import { User, UserState } from '../user/userState';
|
|
3
|
+
import { AttributeType } from './approvalRuleState';
|
|
4
|
+
/**
|
|
5
|
+
* Who a user-attribute approver role (vendor owner, manager, manager's
|
|
6
|
+
* manager) resolves to, for the draft approval preview.
|
|
7
|
+
*
|
|
8
|
+
* Per role:
|
|
9
|
+
* - `User` → resolves to this person.
|
|
10
|
+
* - `null` → known to resolve to no one; the backend routes the step to
|
|
11
|
+
* Admin, so the preview shows "Admin".
|
|
12
|
+
* - absent → not known yet (e.g. no vendor picked, or the mapping was not
|
|
13
|
+
* fetched); the preview keeps the role label.
|
|
14
|
+
*/
|
|
15
|
+
export type AttributeApprovers = Partial<Record<AttributeType, User | null>>;
|
|
16
|
+
/**
|
|
17
|
+
* @param vendorOwnerId `undefined` = no vendor selected (unknown),
|
|
18
|
+
* `null` = vendor selected but it has no owner, otherwise the owner's id.
|
|
19
|
+
* @param attributeUserIdMapping backend `attribute_sub_type_to_user_id_mapping`
|
|
20
|
+
* for the submitter; `undefined` when it was not fetched (unknown). When
|
|
21
|
+
* present, a missing manager / manager_of_manager key means "no one".
|
|
22
|
+
*/
|
|
23
|
+
export declare const getAttributeApprovers: (userState: UserState, { vendorOwnerId, attributeUserIdMapping, }: {
|
|
24
|
+
attributeUserIdMapping?: Partial<Record<AttributeType, ID>>;
|
|
25
|
+
vendorOwnerId?: ID | null;
|
|
26
|
+
}) => AttributeApprovers;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getAttributeApprovers = void 0;
|
|
4
|
+
const userSelector_1 = require("../user/userSelector");
|
|
5
|
+
const toApprover = (userState, userId) => {
|
|
6
|
+
if (userId === undefined) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
if (userId === null || userId === '') {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
// An id we can't find in user state is treated as unknown rather than
|
|
13
|
+
// "no one" — the backend still routes to that user.
|
|
14
|
+
return (0, userSelector_1.getUserByUserId)(userState, userId) ?? undefined;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* @param vendorOwnerId `undefined` = no vendor selected (unknown),
|
|
18
|
+
* `null` = vendor selected but it has no owner, otherwise the owner's id.
|
|
19
|
+
* @param attributeUserIdMapping backend `attribute_sub_type_to_user_id_mapping`
|
|
20
|
+
* for the submitter; `undefined` when it was not fetched (unknown). When
|
|
21
|
+
* present, a missing manager / manager_of_manager key means "no one".
|
|
22
|
+
*/
|
|
23
|
+
const getAttributeApprovers = (userState, { vendorOwnerId, attributeUserIdMapping, }) => {
|
|
24
|
+
const approvers = {};
|
|
25
|
+
const vendorOwner = toApprover(userState, vendorOwnerId);
|
|
26
|
+
if (vendorOwner !== undefined) {
|
|
27
|
+
approvers.vendor_owner = vendorOwner;
|
|
28
|
+
}
|
|
29
|
+
if (attributeUserIdMapping != null) {
|
|
30
|
+
['manager', 'manager_of_manager'].forEach((attribute) => {
|
|
31
|
+
const approver = toApprover(userState, attributeUserIdMapping[attribute] ?? null);
|
|
32
|
+
if (approver !== undefined) {
|
|
33
|
+
approvers[attribute] = approver;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return approvers;
|
|
38
|
+
};
|
|
39
|
+
exports.getAttributeApprovers = getAttributeApprovers;
|
|
@@ -75,7 +75,7 @@ const mapVendorPayloadToVendor = (payload) => {
|
|
|
75
75
|
: undefined,
|
|
76
76
|
bankAccountIds: payload.bank_accounts?.map((bankAcc) => bankAcc.bank_account_id) ?? [],
|
|
77
77
|
paymentInstrumentIds: payload.payment_instruments?.map((piAcc) => piAcc.zeni_payment_instrument_id) ?? [],
|
|
78
|
-
ownerId: payload.owner?.user_id
|
|
78
|
+
ownerId: payload.owner === null ? null : payload.owner?.user_id,
|
|
79
79
|
balance: payload.balance ?? 0,
|
|
80
80
|
matchScore: payload.match_score?.value != null ? payload.match_score.value : 0,
|
|
81
81
|
isVendor1099: payload.is_vendor_1099 ?? false,
|
|
@@ -67,7 +67,12 @@ export interface Vendor extends VendorBase {
|
|
|
67
67
|
last90DaysSpend?: number;
|
|
68
68
|
lastMonthSpend?: number;
|
|
69
69
|
legalName?: string;
|
|
70
|
-
|
|
70
|
+
/**
|
|
71
|
+
* `null` = the vendor explicitly has no owner; `undefined` = not known (the
|
|
72
|
+
* payload didn't include `owner`). The approval preview only shows Admin for
|
|
73
|
+
* `null`.
|
|
74
|
+
*/
|
|
75
|
+
ownerId?: ID | null;
|
|
71
76
|
preferredInternationalTransferFeeBearerPaymentMethod?: InternationalTransferFeeBearerPaymentMethod;
|
|
72
77
|
previousYearSpend?: number;
|
|
73
78
|
shippingAddressId?: ID;
|
|
@@ -42,3 +42,14 @@ export function getApprovalRulesByEntityType(approvalRuleState, entityType) {
|
|
|
42
42
|
export function getFallbackApprovalRule(approvalRuleState, entityType) {
|
|
43
43
|
return Object.values(approvalRuleState.approvalRuleById).find((rule) => rule.entityType === entityType && rule.isFallback === true);
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Non-user approvers to show for a fallback-rule step in the draft approval
|
|
47
|
+
* preview. Implicit actors (e.g. the Admin the platform adds) are shown only
|
|
48
|
+
* when the step has no explicit approver, so the step always has someone to
|
|
49
|
+
* approve it without listing Admin next to an explicit approver.
|
|
50
|
+
*/
|
|
51
|
+
export function getFallbackStepNonUserActors(step) {
|
|
52
|
+
const hasExplicitApprover = step.actors.some((actor) => actor.isImplicitActor === false);
|
|
53
|
+
return step.actors.filter((actor) => actor.type !== 'user' &&
|
|
54
|
+
(!hasExplicitApprover || actor.isImplicitActor === false));
|
|
55
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { getUserByUserId } from '../user/userSelector';
|
|
2
|
+
const toApprover = (userState, userId) => {
|
|
3
|
+
if (userId === undefined) {
|
|
4
|
+
return undefined;
|
|
5
|
+
}
|
|
6
|
+
if (userId === null || userId === '') {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
// An id we can't find in user state is treated as unknown rather than
|
|
10
|
+
// "no one" — the backend still routes to that user.
|
|
11
|
+
return getUserByUserId(userState, userId) ?? undefined;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* @param vendorOwnerId `undefined` = no vendor selected (unknown),
|
|
15
|
+
* `null` = vendor selected but it has no owner, otherwise the owner's id.
|
|
16
|
+
* @param attributeUserIdMapping backend `attribute_sub_type_to_user_id_mapping`
|
|
17
|
+
* for the submitter; `undefined` when it was not fetched (unknown). When
|
|
18
|
+
* present, a missing manager / manager_of_manager key means "no one".
|
|
19
|
+
*/
|
|
20
|
+
export const getAttributeApprovers = (userState, { vendorOwnerId, attributeUserIdMapping, }) => {
|
|
21
|
+
const approvers = {};
|
|
22
|
+
const vendorOwner = toApprover(userState, vendorOwnerId);
|
|
23
|
+
if (vendorOwner !== undefined) {
|
|
24
|
+
approvers.vendor_owner = vendorOwner;
|
|
25
|
+
}
|
|
26
|
+
if (attributeUserIdMapping != null) {
|
|
27
|
+
['manager', 'manager_of_manager'].forEach((attribute) => {
|
|
28
|
+
const approver = toApprover(userState, attributeUserIdMapping[attribute] ?? null);
|
|
29
|
+
if (approver !== undefined) {
|
|
30
|
+
approvers[attribute] = approver;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return approvers;
|
|
35
|
+
};
|
|
@@ -69,7 +69,7 @@ export const mapVendorPayloadToVendor = (payload) => {
|
|
|
69
69
|
: undefined,
|
|
70
70
|
bankAccountIds: payload.bank_accounts?.map((bankAcc) => bankAcc.bank_account_id) ?? [],
|
|
71
71
|
paymentInstrumentIds: payload.payment_instruments?.map((piAcc) => piAcc.zeni_payment_instrument_id) ?? [],
|
|
72
|
-
ownerId: payload.owner?.user_id
|
|
72
|
+
ownerId: payload.owner === null ? null : payload.owner?.user_id,
|
|
73
73
|
balance: payload.balance ?? 0,
|
|
74
74
|
matchScore: payload.match_score?.value != null ? payload.match_score.value : 0,
|
|
75
75
|
isVendor1099: payload.is_vendor_1099 ?? false,
|
package/lib/esm/index.js
CHANGED
|
@@ -37,6 +37,7 @@ import { getAddressByAddressId } from './entity/address/addressSelector';
|
|
|
37
37
|
import { detectRuleOverlap, } from './entity/approvalRule/approvalRuleConflict';
|
|
38
38
|
import { getAmountCriteria, getApprovalRulesByEntityType, getDepartmentCriteria, getFallbackApprovalRule, getVendorCriteria, } from './entity/approvalRule/approvalRuleSelector';
|
|
39
39
|
import { toAttributeOrRoleTypeStrict, toAttributeTypeStrict, toRoleTypeStrict, } from './entity/approvalRule/approvalRuleState';
|
|
40
|
+
import { getAttributeApprovers, } from './entity/approvalRule/attributeApprovers';
|
|
40
41
|
import { toOutsideZeniPaymentModeType, } from './entity/billPay/billTransaction/billTransactionState';
|
|
41
42
|
import { toRecurringBillFrequency, toRecurringBillFrequencyStrict, } from './entity/billPay/recurringBills/recurringBillsState';
|
|
42
43
|
import { toCardAddressType, toCreditLimitFrequencyCodeType, toShippingAddressType, } from './entity/chargeCard/chargeCard';
|
|
@@ -630,7 +631,7 @@ export { getUserFinancialAccount };
|
|
|
630
631
|
export { fetchRemiSetupView, updateMileageDetails, saveRemiSetupViewDataInLocalStore, acceptRemiTerms, acceptEmployeeRemiTerms, getRemiSetupViewDetails, getRemiBusinessVerificationDetails, };
|
|
631
632
|
export { VERIFIED_PAYMENT_ACCOUNT_PROVIDER_VERIFICATION_STATUS, };
|
|
632
633
|
export { isSuccessResponse, isInvalidSessionError, isAccessDeniedError, };
|
|
633
|
-
export { getAmountCriteria, getApprovalRulesByEntityType, getDepartmentCriteria, getFallbackApprovalRule, getVendorCriteria, toAttributeTypeStrict, toAttributeOrRoleTypeStrict, toRoleTypeStrict, detectRuleOverlap, };
|
|
634
|
+
export { getAmountCriteria, getApprovalRulesByEntityType, getDepartmentCriteria, getAttributeApprovers, getFallbackApprovalRule, getVendorCriteria, toAttributeTypeStrict, toAttributeOrRoleTypeStrict, toRoleTypeStrict, detectRuleOverlap, };
|
|
634
635
|
export { getBillPaySetupApproverView, getBillPaySetupApproverUpdateDataView, fetchBillPaySetupApproverView, fetchBillPayApproversDetails, fetchBillPayApproversList, deleteBillPayApprovalRule, reorderBillPayApprovalRules, saveBillPaySetupApproverViewUpdateData, saveBillPaySetupApproverViewUpdates, setBillsSetupApproverViewListeningToPusherEvent, initializeBillPaySetupApproverViewUpdateData, clearBillPaySetupApproverViewUpdateData, };
|
|
635
636
|
export { getRemiSetupApproverView, getRemiSetupApproverUpdateDataView, fetchRemiSetupApproverView, fetchRemiApproversList, fetchRemiApproversDetails, deleteRemiApprovalRule, reorderRemiApprovalRules, saveRemiSetupApproverViewUpdateData, saveRemiSetupApproverViewUpdates, initializeRemiSetupApproverViewUpdateData, setRemiSetupApproverViewListeningToPusherEvent, clearRemiSetupApproverViewUpdateData, };
|
|
636
637
|
export { getRemiDetailView, checkApproveRejectBtnShowForRemi, };
|
package/lib/esm/view/commonVendorView/transactionVendorView/transactionVendorLocalDataHelper.js
CHANGED
|
@@ -3,7 +3,7 @@ export const getInitializedTransactionVendorLocalData = (vendorData) => {
|
|
|
3
3
|
vendorName: vendorData.name,
|
|
4
4
|
vendorCategory: vendorData.accountId,
|
|
5
5
|
vendorClass: vendorData.classId,
|
|
6
|
-
vendorOwner: vendorData.ownerId,
|
|
6
|
+
vendorOwner: vendorData.ownerId ?? undefined,
|
|
7
7
|
vendorId: vendorData.id,
|
|
8
8
|
};
|
|
9
9
|
};
|
|
@@ -21,7 +21,7 @@ export const getInitializedVendorDetailLocalData = (vendorData, state, vendorPay
|
|
|
21
21
|
vendorCategory: vendorData.accountId,
|
|
22
22
|
vendorClass: vendorData.classId,
|
|
23
23
|
vendorType: vendorData.vendorType,
|
|
24
|
-
vendorOwner: vendorData.ownerId,
|
|
24
|
+
vendorOwner: vendorData.ownerId ?? undefined,
|
|
25
25
|
vendorId: vendorData.id,
|
|
26
26
|
isVendor1099: vendorData.isVendor1099,
|
|
27
27
|
documentIds: vendorData.documentIds,
|
|
@@ -16,7 +16,7 @@ export const getInitializedVendorTabDetailLocalData = (vendorData, state, vendor
|
|
|
16
16
|
: getContactsByIds(contactState, vendorData.contactIds ?? []),
|
|
17
17
|
vendorCategory: vendorData.accountId,
|
|
18
18
|
vendorClass: vendorData.classId,
|
|
19
|
-
vendorOwner: vendorData.ownerId,
|
|
19
|
+
vendorOwner: vendorData.ownerId ?? undefined,
|
|
20
20
|
vendorId: vendorData.id,
|
|
21
21
|
recommendations: vendorData.recommendations,
|
|
22
22
|
recommendedCategory: recommendedAccount,
|
|
@@ -236,7 +236,7 @@ export const getBillDetailView = (state, billId) => {
|
|
|
236
236
|
filteredAttributes,
|
|
237
237
|
approverListFetchState: approverListState.fetchStatus,
|
|
238
238
|
vendorCurrency: currentVendorDetails?.vendorCurrencyCode,
|
|
239
|
-
vendorOwnerId: currentVendorDetails?.ownerId,
|
|
239
|
+
vendorOwnerId: currentVendorDetails?.ownerId ?? undefined,
|
|
240
240
|
billHistory: getActivityHistory(commonHistoryState, billId, 'bill_pay'),
|
|
241
241
|
realTimeApproval: getActivityRealTimeApproval(realTimeApprovalState, billId, 'bill_pay'),
|
|
242
242
|
allAccountingClasses,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createSelector } from '@reduxjs/toolkit';
|
|
2
2
|
import cloneDeep from 'lodash/cloneDeep';
|
|
3
3
|
import { reduceAllFetchState, reduceAnyFetchState, } from '../../../../commonStateTypes/reduceFetchState';
|
|
4
|
-
import { getApprovalRulesByIds } from '../../../../entity/approvalRule/approvalRuleSelector';
|
|
4
|
+
import { getApprovalRulesByIds, getFallbackApprovalRule, getFallbackStepNonUserActors, } from '../../../../entity/approvalRule/approvalRuleSelector';
|
|
5
5
|
import { getClassesByIds } from '../../../../entity/class/classSelector';
|
|
6
6
|
import { getIsAccountingClassesEnabled } from '../../../../entity/tenant/tenantSelector';
|
|
7
7
|
import { getUserByUserId } from '../../../../entity/user/userSelector';
|
|
@@ -10,22 +10,35 @@ export const getBillPaySetupApproverView = createSelector((state) => state.billP
|
|
|
10
10
|
const { approvalRulesDeleteStatusById, approvalRuleIds, approverListState, fetchState, error, listeningToPusherEvent, newApprovalRuleId, } = billPaySetupApproverViewState;
|
|
11
11
|
const deleteStateList = Object.values(approvalRulesDeleteStatusById);
|
|
12
12
|
const approvalRules = getApprovalRulesByIds(approvalRuleState, approvalRuleIds);
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
13
|
+
const toStepWithUser = (step) => ({
|
|
14
|
+
...step,
|
|
15
|
+
actors: step.actors
|
|
16
|
+
.filter((actor) => actor.userId != null)
|
|
17
|
+
.map((actor) => getUserByUserId(userState, actor.userId))
|
|
18
|
+
.filter((user) => user != null),
|
|
19
|
+
nonUserActors: step.actors.filter((actor) => actor.type !== 'user' && actor.isImplicitActor === false),
|
|
20
|
+
});
|
|
21
|
+
const toApprovalRuleWithUser = (approvalRule) => ({
|
|
22
|
+
...approvalRule,
|
|
23
|
+
steps: approvalRule.steps.map(toStepWithUser),
|
|
24
|
+
});
|
|
25
|
+
// Unlike the rules page, a fallback step keeps its implicit Admin when it
|
|
26
|
+
// has no explicit approver (see getFallbackStepNonUserActors).
|
|
27
|
+
const toFallbackStepWithUser = (step) => ({
|
|
28
|
+
...toStepWithUser(step),
|
|
29
|
+
nonUserActors: getFallbackStepNonUserActors(step),
|
|
28
30
|
});
|
|
31
|
+
const allApprovalRules = approvalRules.map(toApprovalRuleWithUser);
|
|
32
|
+
// Exposed separately so the new-bill preview can show it when no other
|
|
33
|
+
// rule matches. (It is also in allApprovalRules; the rules pages filter it
|
|
34
|
+
// out by `isFallback`.)
|
|
35
|
+
const fallbackApprovalRule = getFallbackApprovalRule(approvalRuleState, 'bill_pay');
|
|
36
|
+
const fallbackRule = fallbackApprovalRule != null
|
|
37
|
+
? {
|
|
38
|
+
...fallbackApprovalRule,
|
|
39
|
+
steps: fallbackApprovalRule.steps.map(toFallbackStepWithUser),
|
|
40
|
+
}
|
|
41
|
+
: undefined;
|
|
29
42
|
const approvalRulesStatus = allApprovalRules.length > 0
|
|
30
43
|
? {
|
|
31
44
|
code: 'created',
|
|
@@ -40,6 +53,7 @@ export const getBillPaySetupApproverView = createSelector((state) => state.billP
|
|
|
40
53
|
const inactiveApproverIds = Array.from(referencedApproverIds).filter((userId) => !activeApproverIds.has(userId));
|
|
41
54
|
return {
|
|
42
55
|
allApprovalRules,
|
|
56
|
+
fallbackRule,
|
|
43
57
|
deleteStatus: reduceAnyFetchState(deleteStateList),
|
|
44
58
|
approvalRulesStatus,
|
|
45
59
|
approverListFetchStatus: approverListState.fetchStatus,
|
|
@@ -2,6 +2,7 @@ import recordGet from 'lodash/get';
|
|
|
2
2
|
import has from 'lodash/has';
|
|
3
3
|
import { reduceFetchState } from '../../../../commonStateTypes/reduceFetchState';
|
|
4
4
|
import { getAmountCriteria, getDepartmentCriteria, getVendorCriteria, } from '../../../../entity/approvalRule/approvalRuleSelector';
|
|
5
|
+
import { getAttributeApprovers, } from '../../../../entity/approvalRule/attributeApprovers';
|
|
5
6
|
import { getBankAccountByBankAccountId, getBankAccountsByBankAccountIds, } from '../../../../entity/bankAccount/bankAccountSelector';
|
|
6
7
|
import { getContactById, getContactsByIds, } from '../../../../entity/billPay/contact/contactSelector';
|
|
7
8
|
import { getCompanyByCompanyId } from '../../../../entity/company/companySelector';
|
|
@@ -371,6 +372,11 @@ export const getEditBillDetail = (state, isVendorRecommendationFeatureEnabled, i
|
|
|
371
372
|
qboId,
|
|
372
373
|
payNowCutOffTime,
|
|
373
374
|
approvalRules: getBillPaySetupApproverView(state),
|
|
375
|
+
attributeApprovers: getAttributeApprovers(userState, {
|
|
376
|
+
// Only an explicit "no owner" (null) becomes Admin; an owner that wasn't
|
|
377
|
+
// in the vendor payload stays unknown and keeps the role label.
|
|
378
|
+
vendorOwnerId: vendorDetails?.ownerId,
|
|
379
|
+
}),
|
|
374
380
|
billPayInfo,
|
|
375
381
|
billHistory,
|
|
376
382
|
toAccountVendorEmail,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import recordGet from 'lodash/get';
|
|
2
2
|
import { reduceFetchState } from '../../../../commonStateTypes/reduceFetchState';
|
|
3
|
+
import { getAttributeApprovers, } from '../../../../entity/approvalRule/attributeApprovers';
|
|
3
4
|
import { getBankAccountByBankAccountId } from '../../../../entity/bankAccount/bankAccountSelector';
|
|
4
5
|
import { getAllCurrencies } from '../../../../entity/currency/currencySelector';
|
|
5
6
|
import { getCurrentTenant, getIsAccountingClassesEnabled, } from '../../../../entity/tenant/tenantSelector';
|
|
@@ -16,7 +17,7 @@ import { REIMBURSEMENT_PAYMENT_NUMBER_OF_DAYS } from '../reimbursementConfig/rei
|
|
|
16
17
|
import { getRemiActivities } from '../remiDetailView/remiDetailViewSelector';
|
|
17
18
|
import { getRemiSetupApproverView } from '../remiSetupApproverView/remiSetupApproverViewSelector';
|
|
18
19
|
export const getEditRemiDetail = (state, reimbursementId) => {
|
|
19
|
-
const { accountListState, accountState, remiDetailViewState, reimbursementState, currencyState, classListState, classState, editRemiDetailViewState, reimbursementConfigState, merchantListState, merchantState, userState, bankAccountState, commonHistoryState, } = state;
|
|
20
|
+
const { accountListState, accountState, remiDetailViewState, reimbursementState, currencyState, classListState, classState, editRemiDetailViewState, reimbursementConfigState, merchantListState, merchantState, userState, bankAccountState, commonHistoryState, remiSetupApproverViewState, } = state;
|
|
20
21
|
const achSameDayMaxDays = getReimbursementConfigByKey(reimbursementConfigState, 'reimbursement_ach_same-day')?.maxPeriod.value ?? REIMBURSEMENT_PAYMENT_NUMBER_OF_DAYS;
|
|
21
22
|
const sourceRemiDetail = reimbursementId != null
|
|
22
23
|
? editRemiDetailViewState.editRemiStateById?.[reimbursementId]
|
|
@@ -187,6 +188,10 @@ export const getEditRemiDetail = (state, reimbursementId) => {
|
|
|
187
188
|
classHierarchyList,
|
|
188
189
|
createTime,
|
|
189
190
|
approvalRules: getRemiSetupApproverView(state, false),
|
|
191
|
+
attributeApprovers: getAttributeApprovers(userState, {
|
|
192
|
+
attributeUserIdMapping: remiSetupApproverViewState.approverListState
|
|
193
|
+
.attributeSubTypeToUserIdMapping,
|
|
194
|
+
}),
|
|
190
195
|
reimbursementInfo,
|
|
191
196
|
paymentToBankAccountDetail,
|
|
192
197
|
remiHistory: getActivityHistory(commonHistoryState, reimbursementId ?? '', 'reimbursement'),
|
package/lib/esm/view/spendManagement/reimbursement/editRemiView/epics/fetchEditRemiDetailPageEpic.js
CHANGED
|
@@ -6,6 +6,7 @@ import { fetchUserFinancialAccount } from '../../../../userFinancialAccount/user
|
|
|
6
6
|
import { getLatestCreatedUserBankAccount } from '../../../../userFinancialAccount/userFinancialAccountSelector';
|
|
7
7
|
import { fetchMerchantList } from '../../../merchantList/merchantListReducer';
|
|
8
8
|
import { fetchReimbursementConfig } from '../../reimbursementConfig/reimbursementConfigReducer';
|
|
9
|
+
import { fetchRemiApproversList } from '../../remiSetupApproverView/remiSetupApproverViewReducer';
|
|
9
10
|
import { fetchEditRemiDetailPage, fetchRemiAndInitializeLocalStore, updatesPaymentToInLocalStore, } from '../editRemiViewReducer';
|
|
10
11
|
export const fetchEditRemiDetailPageEpic = (actions$, state$) => actions$.pipe(filter(fetchEditRemiDetailPage.match), mergeMap((action) => {
|
|
11
12
|
const { cacheOverride, isLimitedCategoryEnabled, reimbursementId, isShowDeletedReimbursementEnabled, } = action.payload;
|
|
@@ -59,5 +60,10 @@ export const fetchEditRemiDetailPageEpic = (actions$, state$) => actions$.pipe(f
|
|
|
59
60
|
remiDetailActions.push(fetchRemiAndInitializeLocalStore(reimbursementId, false, isShowDeletedReimbursementEnabled));
|
|
60
61
|
}
|
|
61
62
|
}
|
|
63
|
+
// New reimbursement: resolve the submitter's manager / manager's manager
|
|
64
|
+
// so the approval preview can name them (or show Admin when there is none).
|
|
65
|
+
if (reimbursementId == null && userId !== '') {
|
|
66
|
+
remiDetailActions.push(fetchRemiApproversList(true, false, true, userId));
|
|
67
|
+
}
|
|
62
68
|
return concat(from(remiDetailActions));
|
|
63
69
|
}));
|
|
@@ -8,7 +8,11 @@ export const fetchRemiApproversListEpic = (actions$, state$, zeniAPI) => actions
|
|
|
8
8
|
const { cacheOverride, includeUserRoles, includeAttributesUserIds, lastUpdatedByUserId, } = action.payload;
|
|
9
9
|
const { remiSetupApproverViewState } = state$.value;
|
|
10
10
|
const approverIds = remiSetupApproverViewState.approverListState.approverIds;
|
|
11
|
-
|
|
11
|
+
// The attribute mapping depends on `lastUpdatedByUserId`, so it can't be
|
|
12
|
+
// served from the cache.
|
|
13
|
+
if (cacheOverride === true ||
|
|
14
|
+
approverIds.length === 0 ||
|
|
15
|
+
includeAttributesUserIds === true) {
|
|
12
16
|
return zeniAPI
|
|
13
17
|
.getJSON(`${zeniAPI.apiEndPoints.approvalMicroServiceBaseUrl}/1.0/reimbursements/approvers?query={"is_include_user_roles_info":${includeUserRoles},"is_include_attribute_user_ids":${includeAttributesUserIds},"last_updated_by_user_id":"${lastUpdatedByUserId}"}`)
|
|
14
18
|
.pipe(mergeMap((response) => {
|
|
@@ -30,7 +34,7 @@ export const fetchRemiApproversListEpic = (actions$, state$, zeniAPI) => actions
|
|
|
30
34
|
}
|
|
31
35
|
else {
|
|
32
36
|
const { approverListState } = remiSetupApproverViewState;
|
|
33
|
-
const { approverIds, attributes } = approverListState;
|
|
34
|
-
return of(fetchRemiApproversListSuccess(approverIds, attributes));
|
|
37
|
+
const { approverIds, attributes, attributeSubTypeToUserIdMapping } = approverListState;
|
|
38
|
+
return of(fetchRemiApproversListSuccess(approverIds, attributes, attributeSubTypeToUserIdMapping));
|
|
35
39
|
}
|
|
36
40
|
}));
|
|
@@ -84,11 +84,17 @@ const remiSetupApproverView = createSlice({
|
|
|
84
84
|
},
|
|
85
85
|
},
|
|
86
86
|
fetchRemiApproversList: {
|
|
87
|
-
reducer(draft) {
|
|
87
|
+
reducer(draft, action) {
|
|
88
88
|
draft.approverListState.fetchStatus = {
|
|
89
89
|
fetchState: 'In-Progress',
|
|
90
90
|
error: undefined,
|
|
91
91
|
};
|
|
92
|
+
// The attribute mapping is specific to `lastUpdatedByUserId`; drop the
|
|
93
|
+
// previous user's mapping so it is never shown for someone else while
|
|
94
|
+
// the new one loads.
|
|
95
|
+
if (action.payload.includeAttributesUserIds === true) {
|
|
96
|
+
draft.approverListState.attributeSubTypeToUserIdMapping = undefined;
|
|
97
|
+
}
|
|
92
98
|
},
|
|
93
99
|
prepare(cacheOverride = false, includeUserRoles = true, includeAttributesUserIds = false, lastUpdatedByUserId = undefined) {
|
|
94
100
|
return {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createSelector } from '@reduxjs/toolkit';
|
|
2
2
|
import cloneDeep from 'lodash/cloneDeep';
|
|
3
3
|
import { reduceAllFetchState, reduceAnyFetchState, } from '../../../../commonStateTypes/reduceFetchState';
|
|
4
|
-
import { getApprovalRulesByIds } from '../../../../entity/approvalRule/approvalRuleSelector';
|
|
4
|
+
import { getApprovalRulesByIds, getFallbackApprovalRule, getFallbackStepNonUserActors, } from '../../../../entity/approvalRule/approvalRuleSelector';
|
|
5
5
|
import { getClassesByIds } from '../../../../entity/class/classSelector';
|
|
6
6
|
import { getUserByUserId } from '../../../../entity/user/userSelector';
|
|
7
7
|
import { getUserRoleByUserId } from '../../../../entity/userRole/userRoleSelector';
|
|
@@ -10,23 +10,36 @@ export const getRemiSetupApproverView = (state, filterAdmins = true) => {
|
|
|
10
10
|
const { approvalRulesDeleteStatusById, approvalRuleIds, approverListState, fetchState, error, listeningToPusherEvent, newApprovalRuleId, } = remiSetupApproverViewState;
|
|
11
11
|
const deleteStateList = Object.values(approvalRulesDeleteStatusById);
|
|
12
12
|
const approvalRules = getApprovalRulesByIds(approvalRuleState, approvalRuleIds);
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
};
|
|
26
|
-
}),
|
|
27
|
-
};
|
|
28
|
-
return approvalRuleWithUser;
|
|
13
|
+
const toStepWithUser = (step) => ({
|
|
14
|
+
...step,
|
|
15
|
+
actors: step.actors
|
|
16
|
+
.filter((actor) => actor.userId != null)
|
|
17
|
+
.map((actor) => getUserByUserId(userState, actor.userId))
|
|
18
|
+
.filter((user) => user != null),
|
|
19
|
+
nonUserActors: step.actors.filter((actor) => actor.type !== 'user' &&
|
|
20
|
+
(filterAdmins ? actor.isImplicitActor === false : true)),
|
|
21
|
+
});
|
|
22
|
+
const toApprovalRuleWithUser = (approvalRule) => ({
|
|
23
|
+
...approvalRule,
|
|
24
|
+
steps: approvalRule.steps.map(toStepWithUser),
|
|
29
25
|
});
|
|
26
|
+
const allApprovalRules = approvalRules.map(toApprovalRuleWithUser);
|
|
27
|
+
// Exposed separately so the new-reimbursement preview can show it when no other
|
|
28
|
+
// rule matches. (It is also in allApprovalRules; the rules pages filter it
|
|
29
|
+
// out by `isFallback`.)
|
|
30
|
+
const fallbackApprovalRule = getFallbackApprovalRule(approvalRuleState, 'reimbursement');
|
|
31
|
+
// A fallback step shows its implicit Admin only when it has no explicit
|
|
32
|
+
// approver, regardless of `filterAdmins` (same as bills; see
|
|
33
|
+
// getFallbackStepNonUserActors).
|
|
34
|
+
const fallbackRule = fallbackApprovalRule != null
|
|
35
|
+
? {
|
|
36
|
+
...fallbackApprovalRule,
|
|
37
|
+
steps: fallbackApprovalRule.steps.map((step) => ({
|
|
38
|
+
...toStepWithUser(step),
|
|
39
|
+
nonUserActors: getFallbackStepNonUserActors(step),
|
|
40
|
+
})),
|
|
41
|
+
}
|
|
42
|
+
: undefined;
|
|
30
43
|
const approvalRulesStatus = allApprovalRules.length > 0
|
|
31
44
|
? {
|
|
32
45
|
code: 'created',
|
|
@@ -41,6 +54,7 @@ export const getRemiSetupApproverView = (state, filterAdmins = true) => {
|
|
|
41
54
|
const inactiveApproverIds = Array.from(referencedApproverIds).filter((userId) => !activeApproverIds.has(userId));
|
|
42
55
|
return {
|
|
43
56
|
allApprovalRules,
|
|
57
|
+
fallbackRule,
|
|
44
58
|
deleteStatus: reduceAnyFetchState(deleteStateList),
|
|
45
59
|
approvalRulesStatus,
|
|
46
60
|
approverListFetchStatus: approverListState.fetchStatus,
|
package/lib/index.d.ts
CHANGED
|
@@ -62,6 +62,7 @@ import { DetectRuleOverlapInput, RuleForOverlapCheck, RuleOverlap, detectRuleOve
|
|
|
62
62
|
import { getAmountCriteria, getApprovalRulesByEntityType, getDepartmentCriteria, getFallbackApprovalRule, getVendorCriteria } from './entity/approvalRule/approvalRuleSelector';
|
|
63
63
|
import { Actor, ActorType, AmountComparator, AmountCriteria, ApprovalRule, ApprovalRuleState, Approvers, AttributeType, ConditionType, Criteria, DepartmentCriteria, RoleType, Step, VendorCriteria, toAttributeOrRoleTypeStrict, toAttributeTypeStrict, toRoleTypeStrict } from './entity/approvalRule/approvalRuleState';
|
|
64
64
|
import { ApprovalActionType, StepOperatorType } from './entity/approvalRule/approvalStepTypes';
|
|
65
|
+
import { AttributeApprovers, getAttributeApprovers } from './entity/approvalRule/attributeApprovers';
|
|
65
66
|
import { BankAccount } from './entity/bankAccount/bankAccount';
|
|
66
67
|
import { BillPayInfo, BillPaymentRefundStatus, BillPaymentRefundStatusCodeType, BillPaymentStatus, BillPaymentStatusCodeType, BillStage, BillStageCodeType, BillStatus, BillStatusCodeType, BillTransaction, DeletionSourceType, toOutsideZeniPaymentModeType } from './entity/billPay/billTransaction/billTransactionState';
|
|
67
68
|
import { InternationalTransferFeeBearerPaymentMethod } from './entity/billPay/billTransaction/internationalTransferFeeBearerPaymentMethod';
|
|
@@ -901,7 +902,7 @@ export { RemiSetupViewState, RemiSetupViewLocalData, MileageDetailsLocalData, Mi
|
|
|
901
902
|
export { PaymentAccount, Logo, VERIFIED_PAYMENT_ACCOUNT_PROVIDER_VERIFICATION_STATUS, };
|
|
902
903
|
export { SubscriptionPaymentAccount };
|
|
903
904
|
export { PreviousBillsSelectorView, InvoiceProcessingPayload, InvoiceProcessingResponse, isSuccessResponse, isInvalidSessionError, isAccessDeniedError, };
|
|
904
|
-
export { Step, Actor as ApprovalRuleActor, ActorType, ApprovalActionType, StepOperatorType, ApprovalRuleState, ApprovalRule, AmountComparator, AmountCriteria, ConditionType, Criteria, DepartmentCriteria, VendorCriteria, AttributeType, Approvers, RoleType, EntityApprovalStatusState, EntityApprovalStatus, SpendManagementEntityType, SpendManagementEntityTypeWithOtherConnection, getAmountCriteria, getApprovalRulesByEntityType, getDepartmentCriteria, getFallbackApprovalRule, getVendorCriteria, toAttributeTypeStrict, toAttributeOrRoleTypeStrict, toRoleTypeStrict, DetectRuleOverlapInput, RuleForOverlapCheck, RuleOverlap, detectRuleOverlap, };
|
|
905
|
+
export { Step, Actor as ApprovalRuleActor, ActorType, ApprovalActionType, StepOperatorType, ApprovalRuleState, ApprovalRule, AmountComparator, AmountCriteria, ConditionType, Criteria, DepartmentCriteria, VendorCriteria, AttributeType, Approvers, RoleType, EntityApprovalStatusState, EntityApprovalStatus, SpendManagementEntityType, SpendManagementEntityTypeWithOtherConnection, getAmountCriteria, getApprovalRulesByEntityType, getDepartmentCriteria, AttributeApprovers, getAttributeApprovers, getFallbackApprovalRule, getVendorCriteria, toAttributeTypeStrict, toAttributeOrRoleTypeStrict, toRoleTypeStrict, DetectRuleOverlapInput, RuleForOverlapCheck, RuleOverlap, detectRuleOverlap, };
|
|
905
906
|
export { ApprovalRuleUpdateData, ApprovalRuleCreateData, BillPayApprovalRuleWithUser, ApprovalUpdateActionType, ApproverViewUpdateData, BillPaySetupApproverViewState, BillPaySetupApproverView, BillPaySetupApproverUpdateDataView, getBillPaySetupApproverView, getBillPaySetupApproverUpdateDataView, fetchBillPaySetupApproverView, fetchBillPayApproversDetails, fetchBillPayApproversList, deleteBillPayApprovalRule, reorderBillPayApprovalRules, saveBillPaySetupApproverViewUpdateData, saveBillPaySetupApproverViewUpdates, setBillsSetupApproverViewListeningToPusherEvent, initializeBillPaySetupApproverViewUpdateData, clearBillPaySetupApproverViewUpdateData, };
|
|
906
907
|
export { RemiApprovalRuleWithUser, RemiSetupApproverViewState, RemiSetupApproverView, RemiSetupApproverUpdateDataView, getRemiSetupApproverView, getRemiSetupApproverUpdateDataView, fetchRemiSetupApproverView, fetchRemiApproversList, fetchRemiApproversDetails, deleteRemiApprovalRule, reorderRemiApprovalRules, saveRemiSetupApproverViewUpdateData, saveRemiSetupApproverViewUpdates, initializeRemiSetupApproverViewUpdateData, setRemiSetupApproverViewListeningToPusherEvent, clearRemiSetupApproverViewUpdateData, };
|
|
907
908
|
export { RemiDetailViewSelector, getRemiDetailView, RemiActivity, RemiDetails, checkApproveRejectBtnShowForRemi, };
|