feat(bulk-upload): add uprn corrections table for pre-finalise confirmation (ADR-0057)

Foundation for the "Addresses" review tab: captures the user's confirmed
UPRN per combiner row, keyed by source_row_id (no property.id exists
pre-finalise). At dispatchFinaliser these are overlaid onto the combiner
CSV so the finaliser builds identity + property_overrides from the
confirmed UPRN in one pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jun-te Kim 2026-07-07 15:46:58 +00:00
parent 1ac9f0fdcf
commit e0d2b9dbef
4 changed files with 11950 additions and 0 deletions

View file

@ -0,0 +1,15 @@
CREATE TABLE "bulk_upload_uprn_corrections" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"upload_id" uuid NOT NULL,
"source_row_id" text NOT NULL,
"uprn" text,
"address" text,
"postcode" text,
"marked_no_uprn" boolean DEFAULT false NOT NULL,
"user_id" text,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
CONSTRAINT "bulk_upload_uprn_corrections_upload_row_unique" UNIQUE("upload_id","source_row_id")
);
--> statement-breakpoint
ALTER TABLE "bulk_upload_uprn_corrections" ADD CONSTRAINT "bulk_upload_uprn_corrections_upload_id_bulk_address_uploads_id_fk" FOREIGN KEY ("upload_id") REFERENCES "public"."bulk_address_uploads"("id") ON DELETE cascade ON UPDATE no action;

File diff suppressed because it is too large Load diff

View file

@ -1821,6 +1821,13 @@
"when": 1783345567793,
"tag": "0260_natural_enchantress",
"breakpoints": true
},
{
"idx": 261,
"version": "7",
"when": 1783439171770,
"tag": "0261_uprn_corrections",
"breakpoints": true
}
]
}

View file

@ -0,0 +1,46 @@
import { pgTable, uuid, text, boolean, timestamp, unique } from "drizzle-orm/pg-core";
import { bulkAddressUploads } from "./bulk_address_uploads";
// User confirmations of a combiner row's UPRN, captured on the pre-finalise
// "Addresses" review tab (ADR-0057). address2uprn withholds ambiguous / no-UPRN
// matches; the user resolves each flagged row via OS Places here, keyed by the
// combiner CSV's `source_row_id` (there is no `property.id` yet — the finaliser
// creates identities). At dispatchFinaliser these corrections are overlaid onto
// the combiner CSV before the finaliser reads it, so the confirmed UPRN drives
// both the identity insert and property_overrides in one pass — no post-finalise
// backfill (which is why the portfolio "Unmatched" tab is retired).
//
// A row is resolved when it has a numeric `uprn` OR `markedNoUprn = true` (the
// user asserts no UPRN exists — an accepted terminal state; the finaliser
// leaves it as a no-UPRN property).
export const bulkUploadUprnCorrections = pgTable(
"bulk_upload_uprn_corrections",
{
id: uuid("id").defaultRandom().primaryKey(),
uploadId: uuid("upload_id")
.notNull()
.references(() => bulkAddressUploads.id, { onDelete: "cascade" }),
// The combiner CSV join key (synthetic UUID minted at start-address-matching).
sourceRowId: text("source_row_id").notNull(),
// The confirmed UPRN; null when markedNoUprn.
uprn: text("uprn"),
address: text("address"),
postcode: text("postcode"),
// The user asserts this address has no UPRN — resolved without one.
markedNoUprn: boolean("marked_no_uprn").notNull().default(false),
userId: text("user_id"),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true })
.notNull()
.defaultNow()
.$onUpdate(() => new Date()),
},
(t) => ({
// One correction per combiner row — the confirm action upserts on this.
uploadRowUnique: unique("bulk_upload_uprn_corrections_upload_row_unique").on(
t.uploadId,
t.sourceRowId,
),
}),
);