@7365admin1/core 3.72.1-staging.325 → 3.72.1-staging.326

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.d.ts CHANGED
@@ -13592,7 +13592,9 @@ declare function useHidAmicoService(): {
13592
13592
  hasMore: boolean;
13593
13593
  pageRange: string;
13594
13594
  }>;
13595
- createIdentity: (readerId: string, value: Partial<THidAmicoIdentity>) => Promise<{
13595
+ createIdentity: (readerId: string, value: Partial<THidAmicoIdentity> & {
13596
+ orgId?: string;
13597
+ }) => Promise<{
13596
13598
  _id: bson.ObjectId;
13597
13599
  reader: bson.ObjectId;
13598
13600
  site: bson.ObjectId;
package/dist/index.js CHANGED
@@ -44051,6 +44051,15 @@ var schemaHidAmicoIdentity = import_joi66.default.object({
44051
44051
  }).or("hidUserId", "registration", "cardNo");
44052
44052
  var schemaCreateHidAmicoIdentity = import_joi66.default.object({
44053
44053
  site: objectIdSchema2.optional(),
44054
+ /*
44055
+ * Which organisation's HID permission document this enrolment's access grant
44056
+ * belongs in. Optional so existing callers keep working - the service falls
44057
+ * back to the site's own organisation - but a managing agent should send its
44058
+ * own org, because a site reached through `customer.sites` would otherwise
44059
+ * get a second document and the permissions screen would show the person as
44060
+ * unassigned while the door let them through.
44061
+ */
44062
+ orgId: objectIdSchema2.optional(),
44054
44063
  hidUserId: import_joi66.default.alternatives(import_joi66.default.string(), import_joi66.default.number()).optional().allow(null, ""),
44055
44064
  registration: import_joi66.default.string().trim().optional().allow(""),
44056
44065
  cardNo: import_joi66.default.string().optional().allow(null, ""),
@@ -45811,6 +45820,27 @@ function resolveHidPhysicalCardValue(input) {
45811
45820
  return normalizeHidCardValue(input.cardValue);
45812
45821
  }
45813
45822
 
45823
+ // src/utils/hid-assignment.util.ts
45824
+ function permissionSubjectOfIdentity(value) {
45825
+ if (value.person) {
45826
+ return { subjectId: String(value.person), category: "resident" };
45827
+ }
45828
+ if (value.member) {
45829
+ return { subjectId: String(value.member), category: "property_management" };
45830
+ }
45831
+ if (value.serviceProvider) {
45832
+ return { subjectId: String(value.serviceProvider), category: "service_provider" };
45833
+ }
45834
+ return null;
45835
+ }
45836
+ function assignmentNamesSubject(item, subject, scope) {
45837
+ if (String(item.subjectId ?? "") !== subject.subjectId)
45838
+ return false;
45839
+ if (item.category !== subject.category)
45840
+ return false;
45841
+ return item.readerId ? String(item.readerId) === scope.readerId : item.locationKey === scope.locationKey;
45842
+ }
45843
+
45814
45844
  // src/utils/hid-json.util.ts
45815
45845
  var import_node_crypto5 = __toESM(require("crypto"));
45816
45846
  var UINT64_MAX = 2n ** 64n - 1n;
@@ -48584,6 +48614,71 @@ function useHidAmicoService() {
48584
48614
  await client.logout();
48585
48615
  }
48586
48616
  }
48617
+ async function resolvePermissionOrgId(reader, requested) {
48618
+ if (requested)
48619
+ return String(requested);
48620
+ const site = await siteRepo.getSiteById(String(reader.site));
48621
+ const orgId = String(site?.org ?? "");
48622
+ if (!orgId) {
48623
+ throw new import_node_server_utils141.BadRequestError("This site has no organisation, so HID access cannot be granted.");
48624
+ }
48625
+ return orgId;
48626
+ }
48627
+ async function addEnrolmentAssignment(reader, orgId, subject) {
48628
+ const siteId = String(reader.site);
48629
+ const readerId = String(reader._id);
48630
+ const scope = await getPermissionScope(orgId, siteId, readerId);
48631
+ const current = await repo.getSitePermissions(orgId, siteId);
48632
+ const assignments = current.assignments ?? [];
48633
+ if (assignments.some((item) => assignmentNamesSubject(item, subject, scope))) {
48634
+ return null;
48635
+ }
48636
+ const [resolved] = await repo.resolvePermissionAssignments(siteId, [
48637
+ { ...subject, intercom: false }
48638
+ ]);
48639
+ if (!resolved) {
48640
+ throw new import_node_server_utils141.BadRequestError("This person cannot be granted access at this site.");
48641
+ }
48642
+ const next = [...assignments, {
48643
+ ...resolved,
48644
+ readerId: scope.readerId,
48645
+ location: scope.location,
48646
+ locationKey: scope.locationKey
48647
+ }];
48648
+ await repo.saveSitePermissions(orgId, siteId, next);
48649
+ return async () => {
48650
+ await repo.saveSitePermissions(orgId, siteId, assignments);
48651
+ };
48652
+ }
48653
+ async function removeEnrolmentAssignment(reader, subject) {
48654
+ const siteId = String(reader.site);
48655
+ const readerId = String(reader._id);
48656
+ const scope = {
48657
+ readerId,
48658
+ locationKey: normalizePermissionLocation(String(reader.location ?? ""))
48659
+ };
48660
+ const documents = await repo.listSitePermissionDocuments(siteId);
48661
+ const undos = [];
48662
+ for (const document2 of documents) {
48663
+ const assignments = document2.assignments ?? [];
48664
+ const next = assignments.filter((item) => !assignmentNamesSubject(item, subject, scope));
48665
+ if (next.length === assignments.length)
48666
+ continue;
48667
+ const orgId = String(document2.orgId ?? "");
48668
+ if (!orgId)
48669
+ continue;
48670
+ await repo.saveSitePermissions(orgId, siteId, next);
48671
+ undos.push(async () => {
48672
+ await repo.saveSitePermissions(orgId, siteId, assignments);
48673
+ });
48674
+ }
48675
+ if (!undos.length)
48676
+ return null;
48677
+ return async () => {
48678
+ for (const undo of undos)
48679
+ await undo();
48680
+ };
48681
+ }
48587
48682
  async function resolveSubjectUser(site, value) {
48588
48683
  const subject = value.person ? { subjectId: String(value.person), category: "resident" } : value.member ? { subjectId: String(value.member), category: "property_management" } : value.serviceProvider ? { subjectId: String(value.serviceProvider), category: "service_provider" } : null;
48589
48684
  if (!subject) {
@@ -48615,6 +48710,7 @@ function useHidAmicoService() {
48615
48710
  );
48616
48711
  }
48617
48712
  async function createIdentity(readerId, value) {
48713
+ const { orgId: requestedOrgId, ...identityValue } = value;
48618
48714
  const reader = await repo.getById(readerId);
48619
48715
  const linkedSubjects = [value.person, value.member, value.serviceProvider, value.visitor].filter(Boolean);
48620
48716
  if (linkedSubjects.length !== 1) {
@@ -48635,7 +48731,7 @@ function useHidAmicoService() {
48635
48731
  const subjectUser = await resolveSubjectUser(String(reader.site), value);
48636
48732
  await assertSubjectNotEnrolled(readerId, value, subjectUser);
48637
48733
  const identity = await repo.addIdentity({
48638
- ...value,
48734
+ ...identityValue,
48639
48735
  // Stored, not derived on read. It is how this row is recognised as the
48640
48736
  // same human as one written by a different screen, and a `site.people`
48641
48737
  // row can be re-pointed at another account after the fact.
@@ -48643,9 +48739,17 @@ function useHidAmicoService() {
48643
48739
  reader: readerId,
48644
48740
  site: value.site || reader?.site?.toString()
48645
48741
  });
48742
+ let undoAssignment = null;
48646
48743
  try {
48744
+ const subject = permissionSubjectOfIdentity(identityValue);
48745
+ if (subject) {
48746
+ const orgId = await resolvePermissionOrgId(reader, requestedOrgId);
48747
+ undoAssignment = await addEnrolmentAssignment(reader, orgId, subject);
48748
+ }
48647
48749
  await reconcileStoredReaderPermissions(readerId);
48648
48750
  } catch (error) {
48751
+ if (undoAssignment)
48752
+ await undoAssignment();
48649
48753
  await repo.deleteIdentity(identity._id);
48650
48754
  throw error;
48651
48755
  }
@@ -48706,9 +48810,17 @@ function useHidAmicoService() {
48706
48810
  async function deleteIdentity(identityId) {
48707
48811
  const current = await repo.getIdentityById(identityId);
48708
48812
  const identity = await repo.deleteIdentity(identityId);
48813
+ let undoAssignment = null;
48709
48814
  try {
48815
+ const subject = permissionSubjectOfIdentity(current);
48816
+ if (subject) {
48817
+ const reader = await repo.getById(String(current.reader));
48818
+ undoAssignment = await removeEnrolmentAssignment(reader, subject);
48819
+ }
48710
48820
  await reconcileStoredReaderPermissions(String(current.reader));
48711
48821
  } catch (error) {
48822
+ if (undoAssignment)
48823
+ await undoAssignment();
48712
48824
  await repo.updateIdentity(identityId, { status: current.status || "active", deletedAt: "" });
48713
48825
  throw error;
48714
48826
  }