@7365admin1/core 3.138.0 → 3.139.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/dist/index.mjs CHANGED
@@ -3200,6 +3200,54 @@ function sameAppType(a, b) {
3200
3200
  return !!a && !!b && serviceAppType(a) === serviceAppType(b);
3201
3201
  }
3202
3202
 
3203
+ // src/utils/member-lookup.util.ts
3204
+ var idOf = (v) => v == null ? "" : String(v?._id ?? v).toLowerCase();
3205
+ function isLiveMembership(m) {
3206
+ return m.status !== "deleted" && !m.deletedAt;
3207
+ }
3208
+ function pickSiteMembership(rows, type, site, org = "") {
3209
+ site = site.toLowerCase();
3210
+ org = org.toLowerCase();
3211
+ if (!site)
3212
+ return null;
3213
+ const live2 = rows.filter(
3214
+ (m) => sameAppType(m.type, type) && isLiveMembership(m) && (!org || idOf(m.org) === org)
3215
+ );
3216
+ const atSite = live2.find((m) => idOf(m.siteId) === site);
3217
+ if (atSite)
3218
+ return atSite;
3219
+ if (!org)
3220
+ return null;
3221
+ return live2.find((m) => !idOf(m.siteId)) ?? live2[0] ?? null;
3222
+ }
3223
+ function pickLookupMembership(rows, type, site = "", org = "") {
3224
+ const list = Array.isArray(rows) ? rows : [];
3225
+ const atSite = site ? pickSiteMembership(list, type, site, org) : null;
3226
+ if (atSite)
3227
+ return atSite;
3228
+ const inOrg = org ? list.find((m) => sameAppType(m.type, type) && idOf(m.org) === org.toLowerCase()) : void 0;
3229
+ if (inOrg && isLiveMembership(inOrg))
3230
+ return inOrg;
3231
+ return preferWorkingMembership(list.filter((m) => sameAppType(m.type, type)));
3232
+ }
3233
+ var WORKING = /* @__PURE__ */ new Set(["active", "approved"]);
3234
+ var TAKEN_AWAY_MEMBER_STATUSES = ["deleted", "inactive"];
3235
+ function preferWorkingMembership(rows, defaultOrg = "") {
3236
+ const list = Array.isArray(rows) ? rows : [];
3237
+ const working = list.filter((m) => WORKING.has(String(m.status ?? "")) && !m.deletedAt);
3238
+ const home = idOf(defaultOrg);
3239
+ return (home ? working.find((m) => idOf(m.org) === home) : void 0) ?? working[0] ?? list.find(isLiveMembership) ?? list[0] ?? null;
3240
+ }
3241
+ function memberListSiteScope(memberships, org) {
3242
+ const inOrg = (memberships ?? []).filter((m) => idOf(m.org) === idOf(org));
3243
+ const here = inOrg.filter((m) => m.type !== "resident");
3244
+ if (!inOrg.length)
3245
+ return null;
3246
+ if (here.some((m) => !idOf(m.siteId) || m.orgLevelRole))
3247
+ return null;
3248
+ return [...new Set(here.map((m) => idOf(m.siteId)))];
3249
+ }
3250
+
3203
3251
  // src/repositories/member.repo.ts
3204
3252
  import {
3205
3253
  AppError as AppError2,
@@ -3332,10 +3380,10 @@ import { ObjectId as ObjectId13 } from "mongodb";
3332
3380
  function crossSiteRoleMode() {
3333
3381
  return (process.env.CROSS_SITE_ROLE_MODE ?? "").trim().toLowerCase() === "enforce" ? "enforce" : "log";
3334
3382
  }
3335
- var idOf = (value) => value === null || value === void 0 ? "" : String(value?.toString?.() ?? value);
3383
+ var idOf2 = (value) => value === null || value === void 0 ? "" : String(value?.toString?.() ?? value);
3336
3384
  function isCrossSiteRole(role, memberSite) {
3337
- const roleSite = idOf(role?.site);
3338
- return !!roleSite && roleSite !== idOf(memberSite);
3385
+ const roleSite = idOf2(role?.site);
3386
+ return !!roleSite && roleSite !== idOf2(memberSite);
3339
3387
  }
3340
3388
  var THROTTLE_MS = 10 * 60 * 1e3;
3341
3389
  var lastLogged = /* @__PURE__ */ new Map();
@@ -3344,7 +3392,7 @@ function crossSiteRoleGrants(input) {
3344
3392
  return true;
3345
3393
  const mode = crossSiteRoleMode();
3346
3394
  const granted = mode !== "enforce";
3347
- const key = [mode, idOf(input.member) || idOf(input.user), idOf(input.role?._id), input.where].join("|");
3395
+ const key = [mode, idOf2(input.member) || idOf2(input.user), idOf2(input.role?._id), input.where].join("|");
3348
3396
  const now = Date.now();
3349
3397
  if ((lastLogged.get(key) ?? 0) + THROTTLE_MS <= now) {
3350
3398
  lastLogged.set(key, now);
@@ -3352,7 +3400,7 @@ function crossSiteRoleGrants(input) {
3352
3400
  lastLogged.clear();
3353
3401
  logger7.log({
3354
3402
  level: "warn",
3355
- message: `cross-site-role mode=${mode} ${granted ? "granted" : "refused"} where=${input.where} member=${idOf(input.member) || "-"} user=${idOf(input.user) || "-"} site=${idOf(input.memberSite) || "-"} role=${idOf(input.role?._id) || "-"} roleSite=${idOf(input.role?.site)}`
3403
+ message: `cross-site-role mode=${mode} ${granted ? "granted" : "refused"} where=${input.where} member=${idOf2(input.member) || "-"} user=${idOf2(input.user) || "-"} site=${idOf2(input.memberSite) || "-"} role=${idOf2(input.role?._id) || "-"} roleSite=${idOf2(input.role?.site)}`
3356
3404
  });
3357
3405
  }
3358
3406
  return granted;
@@ -3363,7 +3411,7 @@ function crossSiteRoleRefusal(roleName, roleSiteName) {
3363
3411
  return `${role} belongs to ${site}. Choose a role for this site, or an org-wide role.`;
3364
3412
  }
3365
3413
  async function requireRoleFitsSite(roleId, siteId) {
3366
- const id = idOf(roleId);
3414
+ const id = idOf2(roleId);
3367
3415
  if (!id || !ObjectId13.isValid(id))
3368
3416
  return;
3369
3417
  const db2 = useAtlas7.getDb();
@@ -3372,7 +3420,7 @@ async function requireRoleFitsSite(roleId, siteId) {
3372
3420
  const role = await db2.collection("roles").findOne({ _id: new ObjectId13(id) }, { projection: { name: 1, site: 1 } });
3373
3421
  if (!isCrossSiteRole(role, siteId))
3374
3422
  return;
3375
- const site = ObjectId13.isValid(idOf(role?.site)) ? await db2.collection("sites").findOne({ _id: new ObjectId13(idOf(role?.site)) }, { projection: { name: 1 } }) : null;
3423
+ const site = ObjectId13.isValid(idOf2(role?.site)) ? await db2.collection("sites").findOne({ _id: new ObjectId13(idOf2(role?.site)) }, { projection: { name: 1 } }) : null;
3376
3424
  throw new BadRequestError13(crossSiteRoleRefusal(role?.name, site?.name));
3377
3425
  }
3378
3426
  async function otherSiteRoleClauses(roles, org) {
@@ -3380,7 +3428,7 @@ async function otherSiteRoleClauses(roles, org) {
3380
3428
  { ...org ? { org } : { org: { $nin: ["", null] } }, site: { $nin: ["", null] }, status: "active" },
3381
3429
  { projection: { site: 1, org: 1 } }
3382
3430
  ).toArray();
3383
- const both3 = (v) => ObjectId13.isValid(idOf(v)) ? [new ObjectId13(idOf(v)), idOf(v)] : [v];
3431
+ const both3 = (v) => ObjectId13.isValid(idOf2(v)) ? [new ObjectId13(idOf2(v)), idOf2(v)] : [v];
3384
3432
  const clauses = (list ?? []).map((r) => ({
3385
3433
  role: r._id,
3386
3434
  org: { $in: both3(r.org) },
@@ -3563,9 +3611,12 @@ function useMemberRepo() {
3563
3611
  } catch (error) {
3564
3612
  throw new BadRequestError14("Invalid user ID format.");
3565
3613
  }
3614
+ const account = await db2?.collection("users").findOne({ _id: user }, { projection: { defaultOrg: 1 } }).catch(() => null);
3615
+ const defaultOrg = String(account?.defaultOrg ?? "");
3566
3616
  const cacheKey = makeCacheKey5(namespace_collection3, {
3567
3617
  user: user.toString(),
3568
- type
3618
+ type,
3619
+ defaultOrg
3569
3620
  });
3570
3621
  const cachedData = await getCache(cacheKey);
3571
3622
  if (cachedData) {
@@ -3573,7 +3624,10 @@ function useMemberRepo() {
3573
3624
  return cachedData;
3574
3625
  }
3575
3626
  try {
3576
- const data = await collection.findOne({ user, type: appTypeQuery(type) });
3627
+ const data = preferWorkingMembership(
3628
+ await collection.find({ user, type: appTypeQuery(type) }).toArray(),
3629
+ defaultOrg
3630
+ );
3577
3631
  if (!data) {
3578
3632
  throw new NotFoundError5("Member not found.");
3579
3633
  }
@@ -4036,7 +4090,12 @@ function useMemberRepo() {
4036
4090
  const query2 = {
4037
4091
  user,
4038
4092
  org: { $nin: [null, ""] },
4039
- type: appTypeQuery(type)
4093
+ type: appTypeQuery(type),
4094
+ // The organisation switcher. A membership that was taken away (Repair
4095
+ // sets it inactive, removal deletes it) must not keep the organisation
4096
+ // on the person's list: latitude.sec@ still saw JLL Singapore beside its
4097
+ // own company after the repair (owner report 2026-09-30).
4098
+ status: { $nin: TAKEN_AWAY_MEMBER_STATUSES }
4040
4099
  };
4041
4100
  const cacheOptions = {
4042
4101
  page: page2,
@@ -8745,7 +8804,7 @@ async function resolveInviteActor(userId) {
8745
8804
  const db2 = useAtlas17.getDb();
8746
8805
  if (!db2)
8747
8806
  return { ...EMPTY, id };
8748
- const members = await db2.collection("members").find({ user: new ObjectId26(id), status: { $ne: "deleted" } }).toArray();
8807
+ const members = await db2.collection("members").find({ user: new ObjectId26(id), status: { $nin: TAKEN_AWAY_MEMBER_STATUSES } }).toArray();
8749
8808
  const adminMember = members.find((m) => m.type === "admin");
8750
8809
  let isSuperAdmin2 = false;
8751
8810
  let staffPermissions;
@@ -9052,7 +9111,7 @@ async function holdsRole(userId, roleId, orgId) {
9052
9111
  const member = await db2.collection("members").findOne({
9053
9112
  user: new ObjectId26(id),
9054
9113
  role: new ObjectId26(role),
9055
- status: { $ne: "deleted" },
9114
+ status: { $nin: TAKEN_AWAY_MEMBER_STATUSES },
9056
9115
  // both shapes, as `hasOrgInvitation` does for `metadata.org`
9057
9116
  ...org ? { org: { $in: [new ObjectId26(org), org] } } : {}
9058
9117
  });
@@ -10541,7 +10600,7 @@ function enforcedModuleList(stored, save) {
10541
10600
  return NO_LIST;
10542
10601
  return { list, since: typeof save?.createdAt === "string" ? save.createdAt : "" };
10543
10602
  }
10544
- var idOf2 = (v) => v == null ? "" : String(v?._id ?? v).toLowerCase();
10603
+ var idOf3 = (v) => v == null ? "" : String(v?._id ?? v).toLowerCase();
10545
10604
  function deniedForMembership({
10546
10605
  membership,
10547
10606
  org,
@@ -10549,12 +10608,12 @@ function deniedForMembership({
10549
10608
  site,
10550
10609
  siteSave
10551
10610
  }) {
10552
- const memberOrg = idOf2(membership?.org);
10611
+ const memberOrg = idOf3(membership?.org);
10553
10612
  if (!membership || membership.type === "resident" || !memberOrg) {
10554
10613
  return { denied: [], since: "" };
10555
10614
  }
10556
10615
  const orgTier = enforcedModuleList(org?.modules, orgSave);
10557
- const siteTier = site && idOf2(site.orgId) === memberOrg ? enforcedModuleList(site.metadata?.modules, siteSave) : NO_LIST;
10616
+ const siteTier = site && idOf3(site.orgId) === memberOrg ? enforcedModuleList(site.metadata?.modules, siteSave) : NO_LIST;
10558
10617
  const denied = deniedModules(orgTier.list, siteTier.list);
10559
10618
  if (!denied.length)
10560
10619
  return { denied: [], since: "" };
@@ -11128,46 +11187,6 @@ function staffMayBypass(actor, grant = CROSS_CLIENT_GRANT) {
11128
11187
  return consoleRoleAllows(actor.staffPermissions, grant.resource, grant.action);
11129
11188
  }
11130
11189
 
11131
- // src/utils/member-lookup.util.ts
11132
- var idOf3 = (v) => v == null ? "" : String(v?._id ?? v).toLowerCase();
11133
- function isLiveMembership(m) {
11134
- return m.status !== "deleted" && !m.deletedAt;
11135
- }
11136
- function pickSiteMembership(rows, type, site, org = "") {
11137
- site = site.toLowerCase();
11138
- org = org.toLowerCase();
11139
- if (!site)
11140
- return null;
11141
- const live2 = rows.filter(
11142
- (m) => sameAppType(m.type, type) && isLiveMembership(m) && (!org || idOf3(m.org) === org)
11143
- );
11144
- const atSite = live2.find((m) => idOf3(m.siteId) === site);
11145
- if (atSite)
11146
- return atSite;
11147
- if (!org)
11148
- return null;
11149
- return live2.find((m) => !idOf3(m.siteId)) ?? live2[0] ?? null;
11150
- }
11151
- function pickLookupMembership(rows, type, site = "", org = "") {
11152
- const list = Array.isArray(rows) ? rows : [];
11153
- const atSite = site ? pickSiteMembership(list, type, site, org) : null;
11154
- if (atSite)
11155
- return atSite;
11156
- const inOrg = org ? list.find((m) => sameAppType(m.type, type) && idOf3(m.org) === org.toLowerCase()) : void 0;
11157
- if (inOrg && isLiveMembership(inOrg))
11158
- return inOrg;
11159
- return list.find((m) => sameAppType(m.type, type)) ?? null;
11160
- }
11161
- function memberListSiteScope(memberships, org) {
11162
- const inOrg = (memberships ?? []).filter((m) => idOf3(m.org) === idOf3(org));
11163
- const here = inOrg.filter((m) => m.type !== "resident");
11164
- if (!inOrg.length)
11165
- return null;
11166
- if (here.some((m) => !idOf3(m.siteId) || m.orgLevelRole))
11167
- return null;
11168
- return [...new Set(here.map((m) => idOf3(m.siteId)))];
11169
- }
11170
-
11171
11190
  // src/services/module-gate.service.ts
11172
11191
  var idOf4 = (v) => v == null ? "" : String(v?._id ?? v).toLowerCase();
11173
11192
  var isId = (v) => /^[0-9a-f]{24}$/i.test(v);
@@ -22224,7 +22243,7 @@ function useCameraViewService() {
22224
22243
  }
22225
22244
  }
22226
22245
  async function resolveSiteAccess(params) {
22227
- const memberships = await params.db.collection("members").find({ user: new ObjectId52(params.userId), status: { $ne: "deleted" } }).project({ siteId: 1, org: 1, role: 1, type: 1 }).toArray();
22246
+ const memberships = await params.db.collection("members").find({ user: new ObjectId52(params.userId), status: { $nin: TAKEN_AWAY_MEMBER_STATUSES } }).project({ siteId: 1, org: 1, role: 1, type: 1 }).toArray();
22228
22247
  const memberOrgIds = [
22229
22248
  ...new Map(
22230
22249
  memberships.filter((membership) => membership.org).map((membership) => [String(membership.org), membership.org])
@@ -24560,7 +24579,7 @@ function useServiceProviderInviteService() {
24560
24579
  );
24561
24580
  }
24562
24581
  const existing = await getByCreatorAndNature(actor.id, nature).catch(() => null);
24563
- if (existing) {
24582
+ if (existing && String(existing.invitedFrom ?? "") !== String(invite._id)) {
24564
24583
  throw new BadRequestError47(
24565
24584
  `You already have a ${serviceTypeLabel(nature)} company on iService365 (${existing.name}). Accept the invitation from that company instead.`
24566
24585
  );
@@ -24591,12 +24610,21 @@ function useServiceProviderInviteService() {
24591
24610
  country
24592
24611
  }) {
24593
24612
  const inviteeId = String(user?._id ?? "");
24594
- const name = sanitizeCompanyName(companyName) || sanitizeCompanyName(invite.metadata?.name) || sanitizeCompanyName(user?.name);
24613
+ let name = sanitizeCompanyName(companyName) || sanitizeCompanyName(invite.metadata?.name) || sanitizeCompanyName(user?.name);
24595
24614
  if (!name)
24596
24615
  throw new BadRequestError47("Enter your company's name.");
24597
24616
  const email = String(invite.email ?? "").trim().toLowerCase();
24598
24617
  const actor = { id: inviteeId };
24599
- const orgId = String(
24618
+ const db2 = useAtlas38.getDb();
24619
+ const actorIds = [actor.id, ...ObjectId56.isValid(actor.id) ? [new ObjectId56(actor.id)] : []];
24620
+ const made = db2 ? await db2.collection("organizations").findOne({
24621
+ invitedFrom: { $in: [String(invite._id), invite._id] },
24622
+ createdBy: { $in: actorIds },
24623
+ status: { $ne: "deleted" }
24624
+ }) : null;
24625
+ if (made)
24626
+ name = String(made.name ?? name);
24627
+ const orgId = made ? String(made._id) : String(
24600
24628
  await addOrg({
24601
24629
  name,
24602
24630
  type: nature,
@@ -24608,8 +24636,17 @@ function useServiceProviderInviteService() {
24608
24636
  invitedFrom: String(invite._id)
24609
24637
  })
24610
24638
  );
24611
- const roles = await seedDefaultOrgRoles(useRoleRepo().addRole, orgId, nature);
24612
24639
  const appType = serviceAppType(nature);
24640
+ const owners = made && db2 ? await db2.collection("roles").find({
24641
+ org: { $in: [new ObjectId56(orgId), orgId] },
24642
+ name: "owner",
24643
+ status: { $ne: "deleted" }
24644
+ }).toArray() : [];
24645
+ const ownerRole = (type) => {
24646
+ const r = owners.find((x) => x.type === type);
24647
+ return r ? { id: String(r._id), type, name: "owner" } : null;
24648
+ };
24649
+ const roles = ownerRole("organization") && ownerRole(appType) ? [ownerRole("organization"), ownerRole(appType)] : await seedDefaultOrgRoles(useRoleRepo().addRole, orgId, nature);
24613
24650
  const orgOwner = roles.find((r) => r.type === "organization" && r.name === "owner");
24614
24651
  const appOwner = roles.find((r) => r.type === appType && r.name === "owner");
24615
24652
  if (!orgOwner || !appOwner) {
@@ -24632,8 +24669,18 @@ function useServiceProviderInviteService() {
24632
24669
  siteName: "",
24633
24670
  onboardingRequired: false
24634
24671
  };
24635
- await addMember({ ...base, role: orgOwner.id, type: "organization" });
24636
- await addMember({ ...base, role: appOwner.id, type: appType });
24672
+ const hasOwnerRow = async (type) => !!made && !!await db2?.collection("members").findOne({
24673
+ user: { $in: actorIds },
24674
+ org: { $in: [new ObjectId56(orgId), orgId] },
24675
+ type,
24676
+ status: "active"
24677
+ });
24678
+ if (!await hasOwnerRow("organization")) {
24679
+ await addMember({ ...base, role: orgOwner.id, type: "organization" });
24680
+ }
24681
+ if (!await hasOwnerRow(appType)) {
24682
+ await addMember({ ...base, role: appOwner.id, type: appType });
24683
+ }
24637
24684
  await useUserRepo().updateUserFieldById({ _id: actor.id, field: "defaultOrg", value: orgId }).catch(() => void 0);
24638
24685
  logger36.log({
24639
24686
  level: "info",
@@ -24689,10 +24736,60 @@ function useServiceProviderInviteService() {
24689
24736
  const last = [...history].reverse().find((e) => ["wrong-join-repaired", "wrong-join-undone"].includes(e?.action));
24690
24737
  return last?.action === "wrong-join-repaired" ? last : null;
24691
24738
  }
24739
+ async function leftoverRoles(invite, senderOrg, user) {
24740
+ const db2 = useAtlas38.getDb();
24741
+ if (!db2 || !user?._id || !ObjectId56.isValid(senderOrg))
24742
+ return [];
24743
+ const since = new Date(String(invite.createdAt ?? 0)).getTime();
24744
+ const rows = await db2.collection("members").find({
24745
+ user: { $in: [user._id, String(user._id)] },
24746
+ org: { $in: [new ObjectId56(senderOrg), senderOrg] }
24747
+ }).project({ role: 1 }).toArray();
24748
+ const roleIds = [
24749
+ ...new Set(
24750
+ rows.map((m) => m.role?.toString?.() ?? "").filter((r) => ObjectId56.isValid(r))
24751
+ )
24752
+ ].map((r) => new ObjectId56(r));
24753
+ if (!roleIds.length)
24754
+ return [];
24755
+ const roles = await db2.collection("roles").find({
24756
+ _id: { $in: roleIds },
24757
+ org: { $in: [new ObjectId56(senderOrg), senderOrg] },
24758
+ status: { $ne: "deleted" }
24759
+ }).project({ name: 1, permissions: 1, createdAt: 1 }).toArray();
24760
+ const out = [];
24761
+ for (const role of roles) {
24762
+ if (!(Array.isArray(role.permissions) && role.permissions.includes("*")))
24763
+ continue;
24764
+ if (role.createdAt && Number.isFinite(since) && new Date(role.createdAt).getTime() < since)
24765
+ continue;
24766
+ const inUse = await db2.collection("members").countDocuments({
24767
+ role: { $in: [role._id, role._id.toString()] },
24768
+ status: "active"
24769
+ });
24770
+ if (inUse)
24771
+ continue;
24772
+ out.push({ _id: role._id.toString(), name: String(role.name ?? "") });
24773
+ }
24774
+ return out;
24775
+ }
24776
+ async function setRoleStatus(ids2, status) {
24777
+ const db2 = useAtlas38.getDb();
24778
+ const valid = ids2.filter((r) => ObjectId56.isValid(r)).map((r) => new ObjectId56(r));
24779
+ if (!db2 || !valid.length)
24780
+ return;
24781
+ const now = (/* @__PURE__ */ new Date()).toISOString();
24782
+ await db2.collection("roles").updateMany(
24783
+ { _id: { $in: valid } },
24784
+ { $set: { status, deletedAt: status === "deleted" ? now : "", updatedAt: now } }
24785
+ );
24786
+ await useCache21("roles").delNamespace().catch(() => void 0);
24787
+ }
24692
24788
  async function wrongJoinPreview({ id, userId }) {
24693
24789
  const { invite, senderOrg, user } = await wrongJoinContext(id, userId);
24694
24790
  const org = await getOrgById(senderOrg).catch(() => null);
24695
24791
  const memberships = await wrongJoinRows(invite, senderOrg, user);
24792
+ const roles = await leftoverRoles(invite, senderOrg, user);
24696
24793
  const repaired = lastRepair(invite);
24697
24794
  return {
24698
24795
  invitationId: id,
@@ -24701,6 +24798,8 @@ function useServiceProviderInviteService() {
24701
24798
  senderOrgId: senderOrg,
24702
24799
  senderOrgName: String(org?.name ?? ""),
24703
24800
  memberships,
24801
+ // Left in the sender's role list by the old sign-up; Repair retires them.
24802
+ leftoverRoles: roles,
24704
24803
  defaultOrgIsSender: String(user?.defaultOrg ?? "") === senderOrg,
24705
24804
  repaired: repaired ? { at: repaired.at, by: repaired.byName, memberIds: repaired.memberIds ?? [] } : null
24706
24805
  };
@@ -24709,15 +24808,17 @@ function useServiceProviderInviteService() {
24709
24808
  const { actor, invite, senderOrg, user } = await wrongJoinContext(id, userId);
24710
24809
  const rows = await wrongJoinRows(invite, senderOrg, user);
24711
24810
  const defaultOrgIsSender = !!user?._id && String(user?.defaultOrg ?? "") === senderOrg;
24712
- if (!rows.length && !defaultOrgIsSender) {
24811
+ const { updateMemberStatus } = useMemberRepo();
24812
+ for (const row of rows)
24813
+ await updateMemberStatus(row._id, { status: "inactive" });
24814
+ const roles = await leftoverRoles(invite, senderOrg, user);
24815
+ if (!rows.length && !defaultOrgIsSender && !roles.length) {
24713
24816
  return {
24714
24817
  message: "Nothing to repair: this account is not in the sender's organisation.",
24715
24818
  changed: 0
24716
24819
  };
24717
24820
  }
24718
- const { updateMemberStatus } = useMemberRepo();
24719
- for (const row of rows)
24720
- await updateMemberStatus(row._id, { status: "inactive" });
24821
+ await setRoleStatus(roles.map((r) => r._id), "deleted");
24721
24822
  if (defaultOrgIsSender)
24722
24823
  await setDefaultOrg(String(user._id), "");
24723
24824
  await _transition({
@@ -24726,6 +24827,7 @@ function useServiceProviderInviteService() {
24726
24827
  event: {
24727
24828
  ...event("wrong-join-repaired", actor),
24728
24829
  memberIds: rows.map((row) => row._id),
24830
+ roleIds: roles.map((r) => r._id),
24729
24831
  ...defaultOrgIsSender ? { defaultOrgWas: senderOrg } : {}
24730
24832
  }
24731
24833
  });
@@ -24734,8 +24836,8 @@ function useServiceProviderInviteService() {
24734
24836
  message: `[sp-invite] wrong-join repaired invite=${id} memberships=${rows.length} by=${actor.id}`
24735
24837
  });
24736
24838
  return {
24737
- message: `Repaired: ${rows.length} membership${rows.length === 1 ? "" : "s"} in the sender's organisation set inactive. Undo is available.`,
24738
- changed: rows.length
24839
+ message: `Repaired: ${rows.length} membership${rows.length === 1 ? "" : "s"} in the sender's organisation set inactive` + (roles.length ? `, ${roles.length} unused full-access role${roles.length === 1 ? "" : "s"} removed from its role list` : "") + ". Undo is available.",
24840
+ changed: rows.length + roles.length
24739
24841
  };
24740
24842
  }
24741
24843
  async function undoWrongJoin({ id, userId }) {
@@ -24747,6 +24849,7 @@ function useServiceProviderInviteService() {
24747
24849
  const ids2 = (repaired.memberIds ?? []).filter((m) => ObjectId56.isValid(m));
24748
24850
  for (const m of ids2)
24749
24851
  await updateMemberStatus(m, { status: "active" }).catch(() => 0);
24852
+ await setRoleStatus(repaired.roleIds ?? [], "active");
24750
24853
  const { user } = await wrongJoinContext(id, userId);
24751
24854
  if (repaired.defaultOrgWas && user?._id && !String(user.defaultOrg ?? "")) {
24752
24855
  await setDefaultOrg(String(user._id), String(repaired.defaultOrgWas));