@i4e/invest4edu-access-core 0.38.0 → 0.40.0
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@i4e/invest4edu-access-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
4
4
|
"description": "Shared access-control primitives for NeoFindesk: tenant keystone, role capabilities, reportee tree, entity/role vocabulary, mobile app identity, feature flags, and the unified access engine (registry schema, snapshot resolver, visibleWhen).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -71,6 +71,7 @@ export const DEFAULT_CLIENT_PROFILE_SETTINGS = deepFreeze({
|
|
|
71
71
|
resolve_by_pan: MODES.OFF,
|
|
72
72
|
linked_visibility: MODES.OFF,
|
|
73
73
|
mf_one_investor: MODES.OFF,
|
|
74
|
+
detail_scope: MODES.OFF,
|
|
74
75
|
},
|
|
75
76
|
corporate_distributor_types: [],
|
|
76
77
|
house_distributor_codes: [],
|
|
@@ -123,6 +124,15 @@ export const DEFAULT_CLIENT_PROFILE_SETTINGS = deepFreeze({
|
|
|
123
124
|
change_request: {
|
|
124
125
|
ttl_minutes: 10,
|
|
125
126
|
},
|
|
127
|
+
corrections: {
|
|
128
|
+
// Details a client cannot change in the app; asking sends their RM a WhatsApp and an email.
|
|
129
|
+
fields: ["client_name", "pan_card_no", "primary_mobile"],
|
|
130
|
+
},
|
|
131
|
+
ucc_followup: {
|
|
132
|
+
// A client's app edit to one of this product's UCC fields is never pushed to BSE by the client:
|
|
133
|
+
// the profile is flagged for backoffice / the RM to run the UCC update (uccfieldrules).
|
|
134
|
+
product_code: "mf",
|
|
135
|
+
},
|
|
126
136
|
goals: {
|
|
127
137
|
partner_goal_requires_approval: true,
|
|
128
138
|
corporate_goals_stay_with_corporate: true,
|
|
@@ -136,6 +146,9 @@ export const DEFAULT_CLIENT_PROFILE_SETTINGS = deepFreeze({
|
|
|
136
146
|
goal_approved: "client.goal.approved",
|
|
137
147
|
goal_rejected: "client.goal.rejected",
|
|
138
148
|
goal_share_requested: "client.goal.share_requested",
|
|
149
|
+
goals_share_requested: "client.goals.share_requested",
|
|
150
|
+
profile_correction_requested: "client.profile.correction_requested",
|
|
151
|
+
ucc_update_pending: "client.profile.ucc_update_pending",
|
|
139
152
|
goal_shared: "client.goal.shared",
|
|
140
153
|
goal_share_revoked: "client.goal.revoked",
|
|
141
154
|
relationship_attached: "client.relationship.attached",
|
|
@@ -172,6 +185,7 @@ export const CLIENT_PROFILE_SETTING_DESCRIPTIONS = Object.freeze({
|
|
|
172
185
|
"modes.goal_approval": "Hold partner-added goals until the client approves them.",
|
|
173
186
|
"modes.resolve_by_pan": "Show an existing client instead of a duplicate error when a PAN is found.",
|
|
174
187
|
"modes.mf_one_investor": "Refuse a second MF investor for a person who already has one (D9).",
|
|
188
|
+
"modes.detail_scope": "A client's full record by id only for callers whose client list includes them; everyone else gets the limited card (R15).",
|
|
175
189
|
"modes.linked_visibility": "Clients reached only through a secondary mapping or a linked user appear in lists, as the limited card.",
|
|
176
190
|
corporate_distributor_types: "distributors.distributor_type values whose clients get a corporate profile.",
|
|
177
191
|
house_distributor_codes: "Distributor codes treated as direct (InvestValue's own house distributors).",
|
|
@@ -199,6 +213,8 @@ export const CLIENT_PROFILE_SETTING_DESCRIPTIONS = Object.freeze({
|
|
|
199
213
|
"field_lock.lock_requires_kyc_verified": "The personal lock starts only once KYC is verified.",
|
|
200
214
|
"field_lock.lock_requires_ucc": "The personal lock starts only once a UCC exists.",
|
|
201
215
|
"change_request.ttl_minutes": "Minutes a corporate change request waits for the client's OTP.",
|
|
216
|
+
"corrections.fields": "Details a client asks their RM to correct (WhatsApp + email to the RM), never edits in the app.",
|
|
217
|
+
"ucc_followup.product_code": "Product whose UCC field rules (uccfieldrules) decide which client edits flag the profile for a UCC update.",
|
|
202
218
|
"goals.partner_goal_requires_approval": "Goals added by partners or employees wait for the client's approval.",
|
|
203
219
|
"goals.corporate_goals_stay_with_corporate": "Corporate-profile goals are never shown to main-profile partners.",
|
|
204
220
|
"events.profile_change_requested": "Event name: a corporate change was sent to the client.",
|
|
@@ -209,6 +225,9 @@ export const CLIENT_PROFILE_SETTING_DESCRIPTIONS = Object.freeze({
|
|
|
209
225
|
"events.goal_approved": "Event name: the client approved a goal.",
|
|
210
226
|
"events.goal_rejected": "Event name: the client rejected a goal.",
|
|
211
227
|
"events.goal_share_requested": "Event name: a partner asked to see a goal.",
|
|
228
|
+
"events.profile_correction_requested": "Event name: a client asked their RM to correct a detail they cannot edit.",
|
|
229
|
+
"events.ucc_update_pending": "Event name: a client changed UCC details in the app; backoffice / the RM must run the UCC update.",
|
|
230
|
+
"events.goals_share_requested": "Event name: a partner asked the client to share their goals (no goal named — R16).",
|
|
212
231
|
"events.goal_shared": "Event name: the client shared a goal with a partner.",
|
|
213
232
|
"events.goal_share_revoked": "Event name: the client stopped sharing a goal.",
|
|
214
233
|
"events.relationship_attached": "Event name: a new distributor or employee was linked to a client.",
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UCC follow-up — which of a client's own app edits leave their UCC (BSE) out of date.
|
|
3
|
+
*
|
|
4
|
+
* A client never pushes to BSE from the app. When they change a detail the product's UCC field
|
|
5
|
+
* rules (`uccfieldrules`, one document per product) mark as synced to the UCC, the profile is
|
|
6
|
+
* flagged instead, and backoffice or the RM runs the UCC update from the web.
|
|
7
|
+
*
|
|
8
|
+
* Pure: the backend loads the rules document and applies the result.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The rules name a client's contact lists by their UCC meaning; the client record by its field.
|
|
13
|
+
* A data-model fact, not configuration — the two names never diverge per environment.
|
|
14
|
+
*/
|
|
15
|
+
export const UCC_RULE_NAME_OF_CLIENT_FIELD = Object.freeze({
|
|
16
|
+
phone_list: "mobile",
|
|
17
|
+
email_list: "email",
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
/** Rule names a client edit can leave out of date: warn-locked fields synced to the UCC, and
|
|
21
|
+
* collections (bank, nominees) whose tagged entries warn on edit. */
|
|
22
|
+
export function uccSyncedRuleNames(rules) {
|
|
23
|
+
if (!rules || rules.is_active === false) return [];
|
|
24
|
+
const fields = Object.entries(rules.fields ?? {})
|
|
25
|
+
.filter(([, rule]) => rule && rule.lock === "warn" && rule.syncEndpoint)
|
|
26
|
+
.map(([name]) => name);
|
|
27
|
+
const collections = Object.entries(rules.collections ?? {})
|
|
28
|
+
.filter(([, rule]) => rule && rule.edit_tagged === "warn")
|
|
29
|
+
.map(([name]) => name);
|
|
30
|
+
return [...new Set([...fields, ...collections])];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Of the client fields an edit touched, the ones that leave the UCC out of date — by the rule
|
|
35
|
+
* name backoffice will recognise ("mobile", not "phone_list").
|
|
36
|
+
*/
|
|
37
|
+
export function uccFieldsTouched(touchedClientFields, rules) {
|
|
38
|
+
const synced = new Set(uccSyncedRuleNames(rules));
|
|
39
|
+
const out = new Set();
|
|
40
|
+
for (const field of touchedClientFields ?? []) {
|
|
41
|
+
const ruleName = UCC_RULE_NAME_OF_CLIENT_FIELD[field] ?? field;
|
|
42
|
+
if (synced.has(ruleName)) out.add(ruleName);
|
|
43
|
+
}
|
|
44
|
+
return [...out];
|
|
45
|
+
}
|