mirror of
https://github.com/temetro/temetro.git
synced 2026-08-31 04:47:57 +00:00
backend: portal wallet link identifies the patient by wallet number
The portal 'link' action no longer asks the device for a name + file number. The wallet is identified solely by its relay-verified wallet number: the clinic attaches that number to the file ahead of time (Import from a patient app / QR pairing), and linkWallet now just resolves the paired file (friendly 404 when it hasn't been paired yet). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -30,7 +30,6 @@ import { getPatient } from "./patients.js";
|
|||||||
|
|
||||||
// Clinical-capable roles that can be a patient's provider (mirrors portal.ts).
|
// Clinical-capable roles that can be a patient's provider (mirrors portal.ts).
|
||||||
const PROVIDER_ROLES = ["owner", "admin", "doctor", "member"] as const;
|
const PROVIDER_ROLES = ["owner", "admin", "doctor", "member"] as const;
|
||||||
const norm = (s: string) => s.trim().toLowerCase();
|
|
||||||
|
|
||||||
export type PortalDoctor = { name: string; specialty: string | null };
|
export type PortalDoctor = { name: string; specialty: string | null };
|
||||||
|
|
||||||
@@ -87,35 +86,31 @@ export async function getAvailability(
|
|||||||
|
|
||||||
// --- wallet linkage ---------------------------------------------------------
|
// --- wallet linkage ---------------------------------------------------------
|
||||||
|
|
||||||
// Save a wallet number onto a patient file after verifying the patient's
|
// Confirm the wallet link for a device. The wallet is identified purely by its
|
||||||
// identity (name + file number, like the kiosk). Once linked, clinic→wallet
|
// relay-verified wallet number — the clinic attaches that number to the patient
|
||||||
// pushes and portal actions resolve to this file directly.
|
// file ahead of time (via "Import from a patient app" / QR pairing, which sets
|
||||||
|
// `patients.walletNumber`), so the device never types a name or file number.
|
||||||
|
// Resolves the linked file, or a friendly 404 when the clinic hasn't paired yet.
|
||||||
export async function linkWallet(
|
export async function linkWallet(
|
||||||
orgId: string,
|
orgId: string,
|
||||||
walletNumber: string,
|
walletNumber: string,
|
||||||
fileNumber: string,
|
|
||||||
name: string,
|
|
||||||
): Promise<{ fileNumber: string; name: string }> {
|
): Promise<{ fileNumber: string; name: string }> {
|
||||||
const patient = await getPatient(orgId, fileNumber);
|
if (!walletNumber) {
|
||||||
if (!patient || norm(patient.name) !== norm(name)) {
|
throw new HttpError(400, "Missing wallet identity.");
|
||||||
|
}
|
||||||
|
const fileNumber = await fileNumberForWallet(orgId, walletNumber);
|
||||||
|
if (!fileNumber) {
|
||||||
throw new HttpError(
|
throw new HttpError(
|
||||||
404,
|
404,
|
||||||
"We couldn't find a record matching that name and file number.",
|
"This wallet isn't paired with a record at this clinic yet. Ask the front desk to add your wallet number, then try again.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await db
|
const patient = await getPatient(orgId, fileNumber);
|
||||||
.update(patients)
|
if (!patient) throw new HttpError(404, "Linked record not found.");
|
||||||
.set({ walletNumber })
|
|
||||||
.where(
|
|
||||||
and(
|
|
||||||
eq(patients.organizationId, orgId),
|
|
||||||
eq(patients.fileNumber, patient.fileNumber),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
await recordActivity({
|
await recordActivity({
|
||||||
orgId,
|
orgId,
|
||||||
actor: { id: "", name: patient.name },
|
actor: { id: "", name: patient.name },
|
||||||
action: `Patient portal — ${patient.name} linked a wallet`,
|
action: `Patient portal — ${patient.name} confirmed their wallet link`,
|
||||||
entityType: "patient",
|
entityType: "patient",
|
||||||
entityId: patient.fileNumber,
|
entityId: patient.fileNumber,
|
||||||
});
|
});
|
||||||
@@ -277,7 +272,7 @@ export async function handlePortalRequest(
|
|||||||
case "availability":
|
case "availability":
|
||||||
return getAvailability(orgId, s("provider"), s("date"));
|
return getAvailability(orgId, s("provider"), s("date"));
|
||||||
case "link":
|
case "link":
|
||||||
return linkWallet(orgId, walletNumber, s("fileNumber"), s("name"));
|
return linkWallet(orgId, walletNumber);
|
||||||
case "book":
|
case "book":
|
||||||
return bookForWallet(orgId, walletNumber, {
|
return bookForWallet(orgId, walletNumber, {
|
||||||
date: s("date"),
|
date: s("date"),
|
||||||
|
|||||||
Reference in New Issue
Block a user