@i4e/invest4edu-access-core 0.39.0 → 0.40.1
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.1",
|
|
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": {
|
|
@@ -72,6 +72,7 @@ export const DEFAULT_CLIENT_PROFILE_SETTINGS = deepFreeze({
|
|
|
72
72
|
linked_visibility: MODES.OFF,
|
|
73
73
|
mf_one_investor: MODES.OFF,
|
|
74
74
|
detail_scope: MODES.OFF,
|
|
75
|
+
ucc_followup: MODES.OFF,
|
|
75
76
|
},
|
|
76
77
|
corporate_distributor_types: [],
|
|
77
78
|
house_distributor_codes: [],
|
|
@@ -124,6 +125,15 @@ export const DEFAULT_CLIENT_PROFILE_SETTINGS = deepFreeze({
|
|
|
124
125
|
change_request: {
|
|
125
126
|
ttl_minutes: 10,
|
|
126
127
|
},
|
|
128
|
+
corrections: {
|
|
129
|
+
// Details a client cannot change in the app; asking sends their RM a WhatsApp and an email.
|
|
130
|
+
fields: ["client_name", "pan_card_no", "primary_mobile"],
|
|
131
|
+
},
|
|
132
|
+
ucc_followup: {
|
|
133
|
+
// A client's app edit to one of this product's UCC fields is never pushed to BSE by the client:
|
|
134
|
+
// the profile is flagged for backoffice / the RM to run the UCC update (uccfieldrules).
|
|
135
|
+
product_code: "mf",
|
|
136
|
+
},
|
|
127
137
|
goals: {
|
|
128
138
|
partner_goal_requires_approval: true,
|
|
129
139
|
corporate_goals_stay_with_corporate: true,
|
|
@@ -138,6 +148,8 @@ export const DEFAULT_CLIENT_PROFILE_SETTINGS = deepFreeze({
|
|
|
138
148
|
goal_rejected: "client.goal.rejected",
|
|
139
149
|
goal_share_requested: "client.goal.share_requested",
|
|
140
150
|
goals_share_requested: "client.goals.share_requested",
|
|
151
|
+
profile_correction_requested: "client.profile.correction_requested",
|
|
152
|
+
ucc_update_pending: "client.profile.ucc_update_pending",
|
|
141
153
|
goal_shared: "client.goal.shared",
|
|
142
154
|
goal_share_revoked: "client.goal.revoked",
|
|
143
155
|
relationship_attached: "client.relationship.attached",
|
|
@@ -174,7 +186,8 @@ export const CLIENT_PROFILE_SETTING_DESCRIPTIONS = Object.freeze({
|
|
|
174
186
|
"modes.goal_approval": "Hold partner-added goals until the client approves them.",
|
|
175
187
|
"modes.resolve_by_pan": "Show an existing client instead of a duplicate error when a PAN is found.",
|
|
176
188
|
"modes.mf_one_investor": "Refuse a second MF investor for a person who already has one (D9).",
|
|
177
|
-
"modes.
|
|
189
|
+
"modes.ucc_followup": "Flag a profile for a UCC update when its client changes UCC details in the app (never pushed by the client).",
|
|
190
|
+
"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).",
|
|
178
191
|
"modes.linked_visibility": "Clients reached only through a secondary mapping or a linked user appear in lists, as the limited card.",
|
|
179
192
|
corporate_distributor_types: "distributors.distributor_type values whose clients get a corporate profile.",
|
|
180
193
|
house_distributor_codes: "Distributor codes treated as direct (InvestValue's own house distributors).",
|
|
@@ -202,6 +215,8 @@ export const CLIENT_PROFILE_SETTING_DESCRIPTIONS = Object.freeze({
|
|
|
202
215
|
"field_lock.lock_requires_kyc_verified": "The personal lock starts only once KYC is verified.",
|
|
203
216
|
"field_lock.lock_requires_ucc": "The personal lock starts only once a UCC exists.",
|
|
204
217
|
"change_request.ttl_minutes": "Minutes a corporate change request waits for the client's OTP.",
|
|
218
|
+
"corrections.fields": "Details a client asks their RM to correct (WhatsApp + email to the RM), never edits in the app.",
|
|
219
|
+
"ucc_followup.product_code": "Product whose UCC field rules (uccfieldrules) decide which client edits flag the profile for a UCC update.",
|
|
205
220
|
"goals.partner_goal_requires_approval": "Goals added by partners or employees wait for the client's approval.",
|
|
206
221
|
"goals.corporate_goals_stay_with_corporate": "Corporate-profile goals are never shown to main-profile partners.",
|
|
207
222
|
"events.profile_change_requested": "Event name: a corporate change was sent to the client.",
|
|
@@ -212,6 +227,8 @@ export const CLIENT_PROFILE_SETTING_DESCRIPTIONS = Object.freeze({
|
|
|
212
227
|
"events.goal_approved": "Event name: the client approved a goal.",
|
|
213
228
|
"events.goal_rejected": "Event name: the client rejected a goal.",
|
|
214
229
|
"events.goal_share_requested": "Event name: a partner asked to see a goal.",
|
|
230
|
+
"events.profile_correction_requested": "Event name: a client asked their RM to correct a detail they cannot edit.",
|
|
231
|
+
"events.ucc_update_pending": "Event name: a client changed UCC details in the app; backoffice / the RM must run the UCC update.",
|
|
215
232
|
"events.goals_share_requested": "Event name: a partner asked the client to share their goals (no goal named — R16).",
|
|
216
233
|
"events.goal_shared": "Event name: the client shared a goal with a partner.",
|
|
217
234
|
"events.goal_share_revoked": "Event name: the client stopped sharing a goal.",
|
|
@@ -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
|
+
}
|