@7365admin1/layer-common 4.97.1-staging.574 → 4.97.1-staging.575

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.
@@ -1214,6 +1214,7 @@ import {
1214
1214
  subjectLinkOf,
1215
1215
  type HidEnrolmentSubject,
1216
1216
  } from "../utils/hid-enrolment-subject";
1217
+ import { nextRegistration } from "../utils/hid-registration";
1217
1218
  /* Mirrors core's audience rules — core is what enforces them; this only decides
1218
1219
  what is worth offering and where an enrolment lands. */
1219
1220
  import {
@@ -2954,8 +2955,8 @@ async function openEnroll() {
2954
2955
  * reader chosen on the screen behind it.
2955
2956
  */
2956
2957
  const [hidUserId, registration] = await Promise.all([
2957
- getNextUid(),
2958
- getNextRegistration(),
2958
+ getNextUid(selectedReaderId.value),
2959
+ getNextRegistration(selectedReaderId.value),
2959
2960
  loadReaders({ resolveSelection: false }),
2960
2961
  ]);
2961
2962
  resetForm();
@@ -4377,26 +4378,26 @@ async function getNextUid(readerId: string) {
4377
4378
  return `UID${String(nextNumber).padStart(6, "0")}`;
4378
4379
  }
4379
4380
 
4380
- async function getNextRegistration() {
4381
- const existingUsers = await getExistingHidReaderUsers();
4382
- const registrations = existingUsers
4383
- .map((user) => String(user.registration || "").trim())
4384
- .filter(Boolean);
4385
- const numericRegistrations = registrations
4386
- .map((registration) => {
4387
- const match = registration.match(/^(\d+)$/);
4388
- return match
4389
- ? { value: Number(match[1]), width: match[1].length }
4390
- : undefined;
4391
- })
4392
- .filter((item): item is { value: number; width: number } => Boolean(item));
4393
- const maxRegistration = numericRegistrations.reduce(
4394
- (max, item) => Math.max(max, item.value),
4395
- 0
4396
- );
4397
- const width = Math.max(6, ...numericRegistrations.map((item) => item.width));
4398
-
4399
- return String(maxRegistration + 1).padStart(width, "0");
4381
+ /**
4382
+ * The next registration number ON THIS READER.
4383
+ *
4384
+ * Every reader numbers its own users (see `enrolOnReader`), so the list has to
4385
+ * come from the reader being allocated for. This used to read it with no
4386
+ * reader at all, which `getExistingHidReaderUsers` answers with an empty list,
4387
+ * so every enrolment proposed 000001 and the second person on a reader was
4388
+ * refused with "HID Amico identity already exists for this reader"
4389
+ * (2026-10-20, Landscape Management on HID 1 after Pest Management took
4390
+ * 000001). The roster on screen is the second opinion for the selected reader,
4391
+ * exactly as `getNextUid` consults it: it catches a number the device holds
4392
+ * but has not reported.
4393
+ */
4394
+ async function getNextRegistration(readerId: string) {
4395
+ const existingUsers = await getExistingHidReaderUsers(readerId, ["id", "registration"]);
4396
+ const onScreen = readerId === selectedReaderId.value ? users.value : [];
4397
+ return nextRegistration([
4398
+ ...existingUsers.map((user) => user.registration),
4399
+ ...onScreen.map((user) => user.registration),
4400
+ ]);
4400
4401
  }
4401
4402
 
4402
4403
  async function getExistingHidReaderIds(readerId: string) {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@7365admin1/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "4.97.1-staging.574",
5
+ "version": "4.97.1-staging.575",
6
6
  "author": "7365admin1",
7
7
  "main": "./nuxt.config.ts",
8
8
  "//files": "What a consumer extending this layer actually loads. Without this npm ships the whole working tree - the changesets, the CI workflows, the render harness in tools/ and any scratch directory that happened to exist at publish time. Nuxt resolves a layer by directory, so every runtime directory below has to stay listed; adding a new top-level runtime directory means adding it here too.",
@@ -0,0 +1,24 @@
1
+ /**
2
+ * THE NEXT REGISTRATION NUMBER ON ONE READER.
3
+ *
4
+ * Each reader numbers its own users, so the input is that reader's existing
5
+ * registration numbers (from the device, plus the roster on screen when it is
6
+ * the selected reader). Numeric ones count; the next is the highest plus one,
7
+ * zero padded to six digits or to the widest number already in use, whichever
8
+ * is wider. Anything that is not a plain number (`USR-...`, blank, undefined)
9
+ * is ignored rather than breaking the allocation.
10
+ *
11
+ * Pure. `HidUserEnrollment.vue` reads the lists and calls this.
12
+ */
13
+ export function nextRegistration(registrations: readonly unknown[]): string {
14
+ let max = 0;
15
+ let width = 6;
16
+ for (const value of registrations) {
17
+ const text = String(value ?? "").trim();
18
+ const match = text.match(/^(\d+)$/);
19
+ if (!match) continue;
20
+ max = Math.max(max, Number(match[1]));
21
+ width = Math.max(width, match[1].length);
22
+ }
23
+ return String(max + 1).padStart(width, "0");
24
+ }