@7365admin1/core 3.73.1 → 3.75.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/dist/index.mjs CHANGED
@@ -3166,6 +3166,16 @@ import {
3166
3166
  } from "@7365admin1/node-server-utils";
3167
3167
  import { v4 as uuidv4 } from "uuid";
3168
3168
 
3169
+ // src/utils/service-app-type.util.ts
3170
+ var APP_TYPE_ALIASES = Object.freeze({
3171
+ real_estate_developer: "property_management_agency"
3172
+ });
3173
+ function serviceAppType(type) {
3174
+ if (typeof type !== "string")
3175
+ return type;
3176
+ return APP_TYPE_ALIASES[type] ?? type;
3177
+ }
3178
+
3169
3179
  // src/repositories/member.repo.ts
3170
3180
  import {
3171
3181
  AppError as AppError2,
@@ -3328,7 +3338,7 @@ function useMemberRepo() {
3328
3338
  }
3329
3339
  async function add(value, session) {
3330
3340
  try {
3331
- value = MMember(value);
3341
+ value = MMember({ ...value, type: serviceAppType(value.type) });
3332
3342
  await collection.insertOne(value, { session });
3333
3343
  delNamespace().then(() => {
3334
3344
  logger7.info(`Cache cleared for namespace: ${namespace_collection3}`);
@@ -3456,6 +3466,7 @@ function useMemberRepo() {
3456
3466
  }
3457
3467
  }
3458
3468
  async function getByUserIdType(user, type) {
3469
+ type = serviceAppType(type);
3459
3470
  try {
3460
3471
  user = new ObjectId13(user);
3461
3472
  } catch (error) {
@@ -3615,7 +3626,7 @@ function useMemberRepo() {
3615
3626
  search = search || "";
3616
3627
  page = page > 0 ? page - 1 : 0;
3617
3628
  const query2 = {
3618
- type,
3629
+ type: serviceAppType(type),
3619
3630
  status
3620
3631
  };
3621
3632
  const cacheOptions = {
@@ -4137,6 +4148,7 @@ function useMemberRepo() {
4137
4148
  }
4138
4149
  }
4139
4150
  async function getByUserIdTypeOrg(user, type, org) {
4151
+ type = serviceAppType(type);
4140
4152
  try {
4141
4153
  user = new ObjectId13(user);
4142
4154
  } catch {
@@ -11751,7 +11763,7 @@ function useRoleRepo() {
11751
11763
  const { delNamespace, setCache, getCache } = useCache14(namespace_collection3);
11752
11764
  const { delNamespace: _delDashboardNameSpace } = useCache14("dashboard");
11753
11765
  async function addRole(value, session) {
11754
- value = new MRole(value);
11766
+ value = new MRole({ ...value, type: serviceAppType(value.type) });
11755
11767
  try {
11756
11768
  const res = await collection.insertOne(value, { session });
11757
11769
  delNamespace().then(() => {
@@ -11854,6 +11866,7 @@ function useRoleRepo() {
11854
11866
  if (!name) {
11855
11867
  throw new BadRequestError33("Role name is required.");
11856
11868
  }
11869
+ type = serviceAppType(type);
11857
11870
  const query2 = type ? { name, type } : { name };
11858
11871
  try {
11859
11872
  const cacheKey = makeCacheKey11(namespace_collection3, query2);
@@ -11910,8 +11923,8 @@ function useRoleRepo() {
11910
11923
  cacheOptions.search = search;
11911
11924
  }
11912
11925
  if (type) {
11913
- query2.type = type;
11914
- cacheOptions.type = type;
11926
+ query2.type = serviceAppType(type);
11927
+ cacheOptions.type = query2.type;
11915
11928
  }
11916
11929
  const cacheKey = makeCacheKey11(namespace_collection3, cacheOptions);
11917
11930
  const cachedData = await getCache(cacheKey);
@@ -14552,13 +14565,14 @@ function defaultOwnerRoles(org, nature, appOwnerPermissions) {
14552
14565
  { ...owner, type: "organization" },
14553
14566
  { ...administrator, type: "organization" }
14554
14567
  ];
14555
- if (nature && APP_SERVICE_TYPES.has(nature)) {
14568
+ const appType = serviceAppType(nature);
14569
+ if (appType && APP_SERVICE_TYPES.has(appType)) {
14556
14570
  roles.push({
14557
14571
  ...owner,
14558
- type: nature,
14572
+ type: appType,
14559
14573
  ...appOwnerPermissions?.length && { permissions: [...appOwnerPermissions] }
14560
14574
  });
14561
- roles.push({ ...administrator, type: nature });
14575
+ roles.push({ ...administrator, type: appType });
14562
14576
  }
14563
14577
  return roles;
14564
14578
  }
@@ -14643,15 +14657,17 @@ async function moduleCeiling(org, site) {
14643
14657
  }
14644
14658
  async function ownerGrantCeiling(org, type) {
14645
14659
  const orgId = org?.toString() ?? "";
14660
+ type = serviceAppType(type);
14646
14661
  if (!orgId || !type)
14647
14662
  return null;
14648
14663
  try {
14649
14664
  const orgDoc = await useOrgRepo().getById(orgId).catch(() => null);
14650
- if (!orgDoc?.invitedRole || orgDoc.nature !== type)
14665
+ const appType = serviceAppType(orgDoc?.nature);
14666
+ if (!orgDoc?.invitedRole || appType !== type)
14651
14667
  return null;
14652
14668
  const role = await useRoleRepo().getRoleById(String(orgDoc.invitedRole)).catch(() => null);
14653
14669
  const permissions = role?.permissions;
14654
- const chosen = role && String(role.org ?? "") === orgId && role.default === true && role.type === orgDoc.nature && Array.isArray(permissions) && permissions.length > 0 && !permissions.includes("*");
14670
+ const chosen = role && String(role.org ?? "") === orgId && role.default === true && role.type === appType && Array.isArray(permissions) && permissions.length > 0 && !permissions.includes("*");
14655
14671
  return chosen ? permissions.map(String) : null;
14656
14672
  } catch {
14657
14673
  return null;
@@ -23571,6 +23587,44 @@ function useMemberController() {
23571
23587
  // src/controllers/verification.controller.ts
23572
23588
  import { BadRequestError as BadRequestError51, logger as logger38 } from "@7365admin1/node-server-utils";
23573
23589
  import Joi28 from "joi";
23590
+
23591
+ // src/utils/resident-app-modules.util.ts
23592
+ var RESIDENT_APP_NATURES = Object.freeze([
23593
+ "property_management_agency",
23594
+ "real_estate_developer"
23595
+ ]);
23596
+ function effectiveResidentAppModules(orgModules, siteModules) {
23597
+ const org = asModules(orgModules);
23598
+ const site = asModules(siteModules);
23599
+ const effective = {};
23600
+ for (const key of residentAppModuleKeys) {
23601
+ effective[key] = org[key] !== false && site[key] !== false;
23602
+ }
23603
+ return effective;
23604
+ }
23605
+ function residentAppModulesAboveCeiling(orgModules, requested, stored) {
23606
+ const org = asModules(orgModules);
23607
+ const wanted = asModules(requested);
23608
+ const had = asModules(stored);
23609
+ return residentAppModuleKeys.filter(
23610
+ (key) => org[key] === false && wanted[key] === true && had[key] === false
23611
+ );
23612
+ }
23613
+ function residentAppModulesAboveCeilingMessage(rejected) {
23614
+ return `This client has not been given ${rejected.join(", ")} in the resident app, so a site cannot switch it on. Ask Seven365 to add it to the organisation first.`;
23615
+ }
23616
+ function withoutDerivedSiteMetadata(metadata) {
23617
+ if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
23618
+ return metadata;
23619
+ }
23620
+ const { residentAppModulesEffective: _derived, ...stored } = metadata;
23621
+ return stored;
23622
+ }
23623
+ function asModules(value) {
23624
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
23625
+ }
23626
+
23627
+ // src/controllers/verification.controller.ts
23574
23628
  var withoutEmail = (payload) => JSON.stringify({ ...payload, email: payload?.email ? "[redacted]" : void 0 });
23575
23629
  function callerId2(req) {
23576
23630
  const user = req.user;
@@ -23652,7 +23706,11 @@ function useVerificationController() {
23652
23706
  modules: modulesSchema.optional(),
23653
23707
  // The new client owner's permissions in that application, chosen with
23654
23708
  // the modules (owner, 2026-09-15). Only accepted together with modules.
23655
- permissions: permissionsSchema.optional()
23709
+ permissions: permissionsSchema.optional(),
23710
+ // The Service365 resident app switches for a Property Management (or
23711
+ // real estate developer) client, same shape the Client details card saves
23712
+ // to `organizations.residentAppModules`. Only accepted with modules.
23713
+ residentAppModules: residentAppModulesSchema.optional()
23656
23714
  });
23657
23715
  const { error, value } = validation.validate(payload);
23658
23716
  if (error) {
@@ -23672,7 +23730,8 @@ function useVerificationController() {
23672
23730
  siteId,
23673
23731
  siteName,
23674
23732
  modules,
23675
- permissions
23733
+ permissions,
23734
+ residentAppModules
23676
23735
  } = value;
23677
23736
  const newClient = !org && !app.includes(PLATFORM_STAFF_MEMBER_TYPE);
23678
23737
  const role = newClient ? "" : value.role;
@@ -23702,6 +23761,14 @@ function useVerificationController() {
23702
23761
  }
23703
23762
  clientModules.permissions = chosen;
23704
23763
  }
23764
+ if (residentAppModules !== void 0) {
23765
+ if (!clientModules || !RESIDENT_APP_NATURES.includes(app[0])) {
23766
+ throw new BadRequestError51(
23767
+ "Service365 resident app modules can only be chosen with the modules of a new Property Management client."
23768
+ );
23769
+ }
23770
+ clientModules.residentAppModules = residentAppModules;
23771
+ }
23705
23772
  await requireInviteReach(req, org, app, role);
23706
23773
  const verificationIds = await _createSimpleUserInvite({
23707
23774
  email,
@@ -25535,10 +25602,12 @@ function useOrgController() {
25535
25602
  const invitedBy = invite.metadata?.invitedBy;
25536
25603
  if (chosen.length && invitedBy && invite.metadata?.app === body.nature) {
25537
25604
  const permissions = clientInviteOwnerPermissionsOn() ? normalisePermissions(invite.metadata?.permissions) : [];
25605
+ const residentApp = RESIDENT_APP_NATURES.includes(body.nature) ? residentAppModulesSchema.validate(invite.metadata?.residentAppModules) : null;
25538
25606
  invitedModules = {
25539
25607
  modules: chosen,
25540
25608
  invitedBy: String(invitedBy),
25541
- permissions: permissionsOutsideModules(permissions, chosen).length ? [] : permissions
25609
+ permissions: permissionsOutsideModules(permissions, chosen).length ? [] : permissions,
25610
+ ...residentApp?.value && !residentApp.error ? { residentAppModules: residentApp.value } : {}
25542
25611
  };
25543
25612
  }
25544
25613
  } else {
@@ -25561,7 +25630,10 @@ function useOrgController() {
25561
25630
  createdBy,
25562
25631
  invitedFrom,
25563
25632
  invitedRole,
25564
- ...invitedModules && { modules: invitedModules.modules }
25633
+ ...invitedModules && { modules: invitedModules.modules },
25634
+ ...invitedModules?.residentAppModules && {
25635
+ residentAppModules: invitedModules.residentAppModules
25636
+ }
25565
25637
  });
25566
25638
  if (invitedModules) {
25567
25639
  await recordConsoleAction({
@@ -25575,7 +25647,10 @@ function useOrgController() {
25575
25647
  moduleCount: invitedModules.modules.length,
25576
25648
  gateListHash: moduleListHash(invitedModules.modules),
25577
25649
  acknowledged: false,
25578
- impactMemberCount: 0
25650
+ impactMemberCount: 0,
25651
+ ...invitedModules.residentAppModules && {
25652
+ residentAppModulesOff: Object.keys(invitedModules.residentAppModules).filter((key) => invitedModules.residentAppModules[key] === false).join(", ")
25653
+ }
25579
25654
  })
25580
25655
  });
25581
25656
  }
@@ -25598,7 +25673,7 @@ function useOrgController() {
25598
25673
  invitedModules?.permissions
25599
25674
  );
25600
25675
  const appOwnerRole = invitedModules?.permissions.length ? seededRoles.find(
25601
- (role) => role.type === body.nature && role.name === "owner"
25676
+ (role) => role.type === serviceAppType(body.nature) && role.name === "owner"
25602
25677
  ) : void 0;
25603
25678
  if (appOwnerRole) {
25604
25679
  try {
@@ -34301,40 +34376,6 @@ import {
34301
34376
  UnauthorizedError as UnauthorizedError14,
34302
34377
  useAtlas as useAtlas56
34303
34378
  } from "@7365admin1/node-server-utils";
34304
-
34305
- // src/utils/resident-app-modules.util.ts
34306
- function effectiveResidentAppModules(orgModules, siteModules) {
34307
- const org = asModules(orgModules);
34308
- const site = asModules(siteModules);
34309
- const effective = {};
34310
- for (const key of residentAppModuleKeys) {
34311
- effective[key] = org[key] !== false && site[key] !== false;
34312
- }
34313
- return effective;
34314
- }
34315
- function residentAppModulesAboveCeiling(orgModules, requested, stored) {
34316
- const org = asModules(orgModules);
34317
- const wanted = asModules(requested);
34318
- const had = asModules(stored);
34319
- return residentAppModuleKeys.filter(
34320
- (key) => org[key] === false && wanted[key] === true && had[key] === false
34321
- );
34322
- }
34323
- function residentAppModulesAboveCeilingMessage(rejected) {
34324
- return `This client has not been given ${rejected.join(", ")} in the resident app, so a site cannot switch it on. Ask Seven365 to add it to the organisation first.`;
34325
- }
34326
- function withoutDerivedSiteMetadata(metadata) {
34327
- if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
34328
- return metadata;
34329
- }
34330
- const { residentAppModulesEffective: _derived, ...stored } = metadata;
34331
- return stored;
34332
- }
34333
- function asModules(value) {
34334
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
34335
- }
34336
-
34337
- // src/controllers/site.controller.ts
34338
34379
  import Joi51 from "joi";
34339
34380
  import { ObjectId as ObjectId74 } from "mongodb";
34340
34381
  async function requireSiteWrite(req, siteId) {