From 251085fb8b248c9b83e0403b1a0e5056f84ca651 Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Fri, 24 Jul 2026 08:54:48 +0000 Subject: [PATCH] =?UTF-8?q?feat(ara-projects):=20work-order=20import=203/3?= =?UTF-8?q?=20=E2=80=94=20validation=20+=20insert-only=20commit=20(#417)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The final step of the work-order import journey: resolve every mapped row against the project's live configuration, report the outcome, and commit the ready set in one transaction. Insert-only (no updates/omissions/priority — those are #425). Validation core (pure, DB-free — `@/lib/projects/import/validation`): - Per-row resolution to one of ready / error / skipped-duplicate, one issue per row in a fixed order (property → workstream → duplicate → contractor → stage → reference). Re-uses the exact `resolveValue` the mapping screen used, so the committed set matches what the user previewed. - Matching rules (ADR-0019): property by landlord property id then UPRN, scoped to the project org's portfolios; contractor required from the file and matched to an assignment on that row's workstream; stage optional, blank falls back to the workstream's first stage, provided-but-unmatched is an error; a (property, workstream) pair already present is skipped, not errored — which is what makes a re-run idempotent. Reference is the mapped column else auto `WO-`; a colliding provided reference is an error so the transaction never fails on the UNIQUE. - 30 unit tests covering every rule, ordering, and idempotency. Persistence (`importCommitRepository`): - `loadImportMatchContext` scopes property matching to the org's portfolios via `portfolio_organisation` (linkage verified — clean), gathers existing (property, workstream) pairs and reference facts. - `commitImport` upserts `project_property` and inserts one `work_order` per ready row in a transaction — all-or-nothing. status/priority are never written (they take DB defaults); the stage FK is v1's only lifecycle mechanism. Routes: POST `/import/validate` (read-only report) and POST `/import/commit` (the journey's only write), sharing one auth+resolve prefix so validate and commit cannot drift. UI: `ImportValidation` — stat tiles (ready/errors/skipped-duplicates), tabbed issues table, optional CSV export, transactional commit, and a post-commit per-workstream summary linking to the work-orders table. Wired in as import step 3. Schema: migration 0281 gives `work_order.status` a DEFAULT '' so the commit can omit it (the AC's "status never written"). This mirrors 0280's priority fix, whose own note anticipated #417 landing rows — a NOT NULL column an insert legitimately omits needs a default or every such insert fails. Cypress: `import-work-orders.cy.js` drives the whole flow (stubbed-API group + gated real-writes end-to-end group), following the create-project.cy.js convention. Co-Authored-By: Claude Opus 4.8 (1M context) --- cypress/e2e/projects/import-work-orders.cy.js | 283 + cypress/fixtures/work-order-import.csv | 4 + .../[projectId]/import/commit/route.ts | 47 + .../[projectId]/import/validate/route.ts | 34 + .../[projectId]/import/validationRequest.ts | 177 + .../0281_work_order_status_default.sql | 15 + src/app/db/migrations/meta/0281_snapshot.json | 13494 ++++++++++++++++ src/app/db/migrations/meta/_journal.json | 7 + src/app/db/schema/projects/projects.ts | 9 +- .../import/components/ImportUpload.tsx | 77 +- .../import/components/ImportValidation.tsx | 505 + src/app/projects/[projectId]/import/page.tsx | 18 +- .../projects/importCommitRepository.ts | 309 + src/lib/projects/import/validation.test.ts | 341 + src/lib/projects/import/validation.ts | 440 + src/lib/projects/import/valueMapping.ts | 2 +- 16 files changed, 15683 insertions(+), 79 deletions(-) create mode 100644 cypress/e2e/projects/import-work-orders.cy.js create mode 100644 cypress/fixtures/work-order-import.csv create mode 100644 src/app/api/projects/[projectId]/import/commit/route.ts create mode 100644 src/app/api/projects/[projectId]/import/validate/route.ts create mode 100644 src/app/api/projects/[projectId]/import/validationRequest.ts create mode 100644 src/app/db/migrations/0281_work_order_status_default.sql create mode 100644 src/app/db/migrations/meta/0281_snapshot.json create mode 100644 src/app/projects/[projectId]/import/components/ImportValidation.tsx create mode 100644 src/app/repositories/projects/importCommitRepository.ts create mode 100644 src/lib/projects/import/validation.test.ts create mode 100644 src/lib/projects/import/validation.ts diff --git a/cypress/e2e/projects/import-work-orders.cy.js b/cypress/e2e/projects/import-work-orders.cy.js new file mode 100644 index 00000000..d643fa89 --- /dev/null +++ b/cypress/e2e/projects/import-work-orders.cy.js @@ -0,0 +1,283 @@ +/** + * Ara Projects — work-order import, validation and insert-only commit (#417) + * + * The acceptance criterion is "Cypress: import a small fixture file end-to-end". + * Two groups, mirroring create-project.cy.js, because one of them writes: + * + * 1. The flow group drives the real client components — drop zone → column + * mapping → validation report → commit → summary — against a real, + * already-set-up project (so the server-rendered, setup-gated page renders + * the drop zone at all), but STUBS the three import APIs with cy.intercept + * so the assertions are deterministic and no row is written. It needs a + * ready project's id in CYPRESS_ARA_PROJECTS_IMPORT_PROJECT_ID. + * + * 2. The end-to-end group performs a real parse/validate/commit against a + * scratch database and is the AC's "import a small fixture file end to + * end". It INSERTs work orders, so it is gated behind + * CYPRESS_ARA_PROJECTS_E2E_WRITES and must only ever point at a + * non-production database seeded with a set-up project whose org portfolios + * contain the fixture's properties (PROP-0001 / PROP-0002). + * + * Like the other project specs this uses `cy.login` (cypress/support/commands.ts) + * to mint a real next-auth JWE, so NEXTAUTH_JWT_SECRET must be set. + * + * NOT YET RUN. The environment this was authored in points DATABASE_URL at + * production, so Cypress was deliberately not executed here (see + * create-project.cy.js). Treat the selectors and assertions as unverified until + * someone runs them against a scratch DB. + */ + +const JWT_SECRET = Cypress.env("NEXTAUTH_JWT_SECRET"); +const PROJECT_ID = Cypress.env("ARA_PROJECTS_IMPORT_PROJECT_ID"); +const E2E_WRITES = Cypress.env("ARA_PROJECTS_E2E_WRITES"); + +const INTERNAL_USER = { + email: "dev@domna.homes", + name: "Internal User", + onboarded: true, + sub: "cypress-internal", +}; + +const FIXTURE = "work-order-import.csv"; + +/** The parse route's shape — the whole file, echoed from the fixture. */ +const PARSED = { + filename: FIXTURE, + headers: [ + "Property identifier", + "Workstream", + "Contractor", + "Stage", + "Work order reference", + "Forecast end date", + ], + rows: [ + ["PROP-0001", "Windows", "Acme Windows Ltd", "Ordered", "WO-1001", "2026-09-30"], + ["PROP-0001", "Doors", "Acme Doors Ltd", "", "WO-1002", "2026-10-15"], + ["PROP-0002", "Roofs", "Sunrise Roofing Ltd", "", "", ""], + ], + rowCount: 3, + preview: [ + ["PROP-0001", "Windows", "Acme Windows Ltd", "Ordered", "WO-1001", "2026-09-30"], + ["PROP-0001", "Doors", "Acme Doors Ltd", "", "WO-1002", "2026-10-15"], + ["PROP-0002", "Roofs", "Sunrise Roofing Ltd", "", "", ""], + ], +}; + +describe("Ara Projects — work-order import (flow, stubbed APIs)", function () { + beforeEach(function () { + if (!JWT_SECRET) { + cy.log("NEXTAUTH_JWT_SECRET not set — skipping"); + this.skip(); + } + if (!PROJECT_ID) { + cy.log( + "ARA_PROJECTS_IMPORT_PROJECT_ID not set — no set-up project to render the drop zone against; skipping", + ); + this.skip(); + } + cy.login(INTERNAL_USER); + }); + + it("walks upload → mapping → validation → commit → summary end to end", function () { + // Stub the three client-side import calls so the flow is deterministic and + // writes nothing. The page itself is real: it only renders the drop zone + // when the project is set up (ADR-0019), which is why a ready project id is + // required above. + cy.intercept("POST", `/api/projects/${PROJECT_ID}/import/parse`, { + statusCode: 200, + body: PARSED, + }).as("parse"); + + cy.intercept("POST", `/api/projects/${PROJECT_ID}/import/validate`, { + statusCode: 200, + body: { + summary: { total: 3, ready: 2, errors: 1, duplicates: 1 }, + errors: [ + { + rowNumber: 3, + identifier: "PROP-0002", + issue: "No property in this project's portfolios matches this identifier.", + field: "Property identifier", + }, + ], + duplicates: [ + { + rowNumber: 2, + identifier: "PROP-0001", + issue: "This property already has a Doors work order in this project.", + field: "Workstream", + }, + ], + createdByWorkstream: [ + { projectWorkstreamId: "10", workstreamName: "Windows", count: 1 }, + { projectWorkstreamId: "11", workstreamName: "Doors", count: 1 }, + ], + }, + }).as("validate"); + + cy.intercept("POST", `/api/projects/${PROJECT_ID}/import/commit`, { + statusCode: 200, + body: { + committed: { workOrdersCreated: 2, propertiesLinked: 2 }, + createdByWorkstream: [ + { projectWorkstreamId: "10", workstreamName: "Windows", count: 1 }, + { projectWorkstreamId: "11", workstreamName: "Doors", count: 1 }, + ], + summary: { total: 3, ready: 2, errors: 1, duplicates: 1 }, + }, + }).as("commit"); + + cy.visit(`/projects/${PROJECT_ID}/import`); + + // Step 1 — upload the fixture. + cy.get("[data-testid=import-upload]").should("be.visible"); + cy.get("[data-testid=import-file-input]").selectFile( + `cypress/fixtures/${FIXTURE}`, + { force: true }, + ); + cy.wait("@parse"); + cy.get("[data-testid=import-preview]").should("be.visible"); + cy.get("[data-testid=import-continue-to-mapping]").click(); + + // Step 2 — mapping. The required columns auto-match from the template + // headers, so Continue is enabled without any manual mapping. + cy.get("[data-testid=import-mapping]").should("be.visible"); + cy.get("[data-testid=mapping-continue]").should("not.be.disabled").click(); + + // Step 3 — validation report. + cy.wait("@validate") + .its("request.body") + .then((body) => { + expect(body.rows).to.have.length(3); + expect(body.session.columnMapping.fields).to.have.property("workstream"); + expect(body.session.columnMapping.fields).to.have.property("contractor"); + }); + + cy.get("[data-testid=stat-ready-value]").should("have.text", "2"); + cy.get("[data-testid=stat-errors-value]").should("have.text", "1"); + cy.get("[data-testid=stat-duplicates-value]").should("have.text", "1"); + + // The errors tab is default; its one row is the unmatched property. + cy.get("[data-testid=issues-table]").within(() => { + cy.contains("No property in this project's portfolios").should("be.visible"); + }); + // The duplicate is reported on its own tab, not as an error. + cy.get("[data-testid=tab-duplicates]").click(); + cy.get("[data-testid=issues-table]").within(() => { + cy.contains("already has a Doors work order").should("be.visible"); + }); + + // Commit — the button names the ready count. + cy.get("[data-testid=validation-commit]") + .should("contain", "Commit 2 work orders") + .click(); + + cy.wait("@commit") + .its("request.body") + .then((body) => { + expect(body.rows).to.have.length(3); + expect(body.session).to.have.property("columnMapping"); + }); + + // Summary — the write is stated plainly, with a link into the table. + cy.get("[data-testid=import-committed]").should("be.visible"); + cy.get("[data-testid=import-committed]").should( + "contain", + "Created 2 work orders", + ); + cy.get("[data-testid=view-work-orders]") + .should("be.visible") + .parent("a") + .should("have.attr", "href", `/projects/${PROJECT_ID}/work-orders`); + }); + + it("disables commit when nothing is ready to import", function () { + cy.intercept("POST", `/api/projects/${PROJECT_ID}/import/parse`, { + statusCode: 200, + body: PARSED, + }).as("parse"); + cy.intercept("POST", `/api/projects/${PROJECT_ID}/import/validate`, { + statusCode: 200, + body: { + summary: { total: 3, ready: 0, errors: 3, duplicates: 0 }, + errors: [], + duplicates: [], + createdByWorkstream: [], + }, + }).as("validate"); + + cy.visit(`/projects/${PROJECT_ID}/import`); + cy.get("[data-testid=import-file-input]").selectFile( + `cypress/fixtures/${FIXTURE}`, + { force: true }, + ); + cy.wait("@parse"); + cy.get("[data-testid=import-continue-to-mapping]").click(); + cy.get("[data-testid=mapping-continue]").click(); + cy.wait("@validate"); + + cy.get("[data-testid=stat-ready-value]").should("have.text", "0"); + cy.get("[data-testid=validation-commit]").should("be.disabled"); + }); +}); + +describe("Ara Projects — work-order import (end to end, writes)", function () { + beforeEach(function () { + if (!JWT_SECRET || !E2E_WRITES || !PROJECT_ID) { + cy.log( + "ARA_PROJECTS_E2E_WRITES / ARA_PROJECTS_IMPORT_PROJECT_ID not set — skipping the writing happy path", + ); + this.skip(); + } + cy.login(INTERNAL_USER); + }); + + it("imports the fixture file and commits work orders", function () { + // Watch the real routes without stubbing them — pass-through intercepts so + // the payloads and status codes can be asserted while the DB actually + // writes. The project named by PROJECT_ID must be fully set up and its org's + // portfolios must contain PROP-0001 and PROP-0002. + cy.intercept("POST", `/api/projects/${PROJECT_ID}/import/validate`).as("validate"); + cy.intercept("POST", `/api/projects/${PROJECT_ID}/import/commit`).as("commit"); + + cy.visit(`/projects/${PROJECT_ID}/import`); + cy.get("[data-testid=import-file-input]").selectFile( + `cypress/fixtures/${FIXTURE}`, + { force: true }, + ); + cy.get("[data-testid=import-preview]").should("be.visible"); + cy.get("[data-testid=import-continue-to-mapping]").click(); + cy.get("[data-testid=mapping-continue]").should("not.be.disabled").click(); + + cy.wait("@validate").its("response.statusCode").should("eq", 200); + // At least one row should be ready to import from a correctly-seeded project. + cy.get("[data-testid=stat-ready-value]") + .invoke("text") + .then((t) => expect(Number(t.replace(/,/g, ""))).to.be.greaterThan(0)); + + cy.get("[data-testid=validation-commit]").click(); + cy.wait("@commit").then(({ response }) => { + expect(response.statusCode).to.eq(200); + expect(response.body.committed.workOrdersCreated).to.be.greaterThan(0); + }); + cy.get("[data-testid=import-committed]").should("be.visible"); + + // Re-running the same file is idempotent: every row that committed is now a + // (property, workstream) duplicate, so it is skipped and nothing is ready to + // import — the commit button disables itself and no second write is offered. + cy.visit(`/projects/${PROJECT_ID}/import`); + cy.get("[data-testid=import-file-input]").selectFile( + `cypress/fixtures/${FIXTURE}`, + { force: true }, + ); + cy.get("[data-testid=import-continue-to-mapping]").click(); + cy.get("[data-testid=mapping-continue]").click(); + cy.wait("@validate"); + cy.get("[data-testid=stat-duplicates-value]") + .invoke("text") + .then((t) => expect(Number(t.replace(/,/g, ""))).to.be.greaterThan(0)); + cy.get("[data-testid=stat-ready-value]").should("have.text", "0"); + cy.get("[data-testid=validation-commit]").should("be.disabled"); + }); +}); diff --git a/cypress/fixtures/work-order-import.csv b/cypress/fixtures/work-order-import.csv new file mode 100644 index 00000000..1496617c --- /dev/null +++ b/cypress/fixtures/work-order-import.csv @@ -0,0 +1,4 @@ +Property identifier,Workstream,Contractor,Stage,Work order reference,Forecast end date +PROP-0001,Windows,Acme Windows Ltd,Ordered,WO-1001,2026-09-30 +PROP-0001,Doors,Acme Doors Ltd,,WO-1002,2026-10-15 +PROP-0002,Roofs,Sunrise Roofing Ltd,,, diff --git a/src/app/api/projects/[projectId]/import/commit/route.ts b/src/app/api/projects/[projectId]/import/commit/route.ts new file mode 100644 index 00000000..b9015518 --- /dev/null +++ b/src/app/api/projects/[projectId]/import/commit/route.ts @@ -0,0 +1,47 @@ +import { NextRequest, NextResponse } from "next/server"; +import { + createdByWorkstream, + prepareImport, +} from "../validationRequest"; +import { commitImport } from "@/app/repositories/projects/importCommitRepository"; + +type Params = { params: Promise<{ projectId: string }> }; + +/** + * POST — commit a validated work-order import (#417, the only write in the + * whole journey). + * + * Re-resolves the mapping server-side (same `prepareImport` the validate route + * runs), then inserts the ready set in one transaction — all valid rows or none + * (the AC's "all-or-nothing per valid row set"). Error rows are excluded and + * duplicate rows are skipped; neither blocks the commit of the rows that are + * fine. Re-running the same file is idempotent: its rows are now + * (property, workstream) duplicates, so nothing new is written. + * + * Every committed `work_order` carries a contractor resolved from the file, and + * `status`/`priority` are never written — see `commitImport`. + */ +export async function POST(req: NextRequest, props: Params) { + const { projectId } = await props.params; + const prepared = await prepareImport(req, projectId); + if (!prepared.ok) return prepared.response; + + const { result } = prepared; + + try { + const committed = await commitImport(BigInt(projectId), result.ready); + return NextResponse.json({ + committed, + createdByWorkstream: createdByWorkstream(result), + summary: result.summary, + }); + } catch { + // The transaction rolled back — nothing was written. A unique-violation + // here would mean a race we did not resolve at validation; the whole set is + // rejected rather than partially applied. + return NextResponse.json( + { error: "Couldn't commit the import — no work orders were created." }, + { status: 500 }, + ); + } +} diff --git a/src/app/api/projects/[projectId]/import/validate/route.ts b/src/app/api/projects/[projectId]/import/validate/route.ts new file mode 100644 index 00000000..141dff5d --- /dev/null +++ b/src/app/api/projects/[projectId]/import/validate/route.ts @@ -0,0 +1,34 @@ +import { NextRequest, NextResponse } from "next/server"; +import { + createdByWorkstream, + prepareImport, +} from "../validationRequest"; + +type Params = { params: Promise<{ projectId: string }> }; + +/** + * POST — validate a mapped work-order import without writing anything (#417). + * + * The validation report's data source: it resolves every row against the + * project's live configuration and returns the counts, the error rows and the + * skipped-duplicate rows the report table renders, plus a preview of what a + * commit would create per workstream. It is a pure read — no `work_order` or + * `project_property` row is written here; that is the commit route's job. + * + * The ready *rows* are deliberately not returned — the report needs only their + * count and their per-workstream grouping, and the commit re-derives the full + * set server-side rather than trusting a client to echo it back. + */ +export async function POST(req: NextRequest, props: Params) { + const { projectId } = await props.params; + const prepared = await prepareImport(req, projectId); + if (!prepared.ok) return prepared.response; + + const { result } = prepared; + return NextResponse.json({ + summary: result.summary, + errors: result.errors, + duplicates: result.duplicates, + createdByWorkstream: createdByWorkstream(result), + }); +} diff --git a/src/app/api/projects/[projectId]/import/validationRequest.ts b/src/app/api/projects/[projectId]/import/validationRequest.ts new file mode 100644 index 00000000..c9005255 --- /dev/null +++ b/src/app/api/projects/[projectId]/import/validationRequest.ts @@ -0,0 +1,177 @@ +/** + * Work-order import — shared request handling for validate and commit (#417). + * + * Both routes do the same first four things: authenticate, authorise (only a + * manager may import), parse the `{ session, rows }` body, and resolve every + * row against the project's live configuration and database facts. They differ + * only in what they do with the result — one reports it, one writes the ready + * set. That shared prefix lives here so the two handlers cannot drift apart on + * auth or resolution, which is exactly where a validate/commit mismatch would + * let an un-previewed row slip in. + * + * The session is re-resolved server-side rather than trusted: the client sends + * the mapping, but the *outcome* (which rows are ready) is computed here from + * the same pure `validateImportRows` the mapping screen used, against + * freshly-loaded targets and match facts. See `@/lib/projects/import/validation`. + */ + +import { NextRequest, NextResponse } from "next/server"; +import { getServerSession } from "next-auth"; +import { z } from "zod"; +import { AuthOptions } from "@/app/api/auth/[...nextauth]/authOptions"; +import { canManageProject } from "@/lib/projects/authz"; +import { + loadAuthzUser, + loadProjectAuthzFacts, +} from "@/app/repositories/projects/authzRepository"; +import { loadMappingTargets } from "@/lib/projects/import/mappingTargets"; +import { cellFor, type ImportFieldKey } from "@/lib/projects/import/columnMapping"; +import { MAX_IMPORT_ROWS } from "@/lib/projects/import/parse"; +import { + validateImportRows, + type ImportValidationResult, +} from "@/lib/projects/import/validation"; +import { loadImportMatchContext } from "@/app/repositories/projects/importCommitRepository"; +import type { ProjectAuthzFacts } from "@/lib/projects/authz"; + +const columnMappingSchema = z.object({ + fields: z.record(z.string(), z.number().int().nonnegative()), + ignoredColumns: z.array(z.number().int().nonnegative()), +}); + +const valueMappingSchema = z.object({ + workstreams: z.record(z.string(), z.string().nullable()), + contractors: z.record(z.string(), z.string().nullable()), + stages: z.record(z.string(), z.string().nullable()), +}); + +const sessionSchema = z.object({ + version: z.number(), + filename: z.string(), + headers: z.array(z.string()), + rowCount: z.number(), + columnMapping: columnMappingSchema, + valueMapping: valueMappingSchema, +}); + +export const importRequestSchema = z.object({ + session: sessionSchema, + rows: z.array(z.array(z.string())).max(MAX_IMPORT_ROWS), +}); + +export type ImportRequestBody = z.infer; + +/** A short-circuit `NextResponse` (auth/parse failure) or the resolved result. */ +export type PreparedImport = + | { ok: false; response: NextResponse } + | { + ok: true; + project: ProjectAuthzFacts; + body: ImportRequestBody; + result: ImportValidationResult; + }; + +function fail(error: string, status: number): { ok: false; response: NextResponse } { + return { ok: false, response: NextResponse.json({ error }, { status }) }; +} + +/** + * Authenticate, authorise, parse and resolve — the shared prefix of both + * routes. Returns the full validation result (ready set included) so the commit + * can write it and the validate route can summarise it. + */ +export async function prepareImport( + req: NextRequest, + projectIdParam: string, +): Promise { + const session = await getServerSession(AuthOptions); + if (!session?.user?.dbId) return fail("Unauthorised", 401); + if (!/^\d+$/.test(projectIdParam)) return fail("Invalid project id", 400); + const projectId = BigInt(projectIdParam); + + const [user, project] = await Promise.all([ + loadAuthzUser(BigInt(session.user.dbId)), + loadProjectAuthzFacts(projectId), + ]); + // Existence is not the caller's to learn — 404, not 403, for a project they + // cannot see (matches the parse route). + if (!user || !project) return fail("Project not found", 404); + if (!canManageProject(user, project)) return fail("Forbidden", 403); + + const parsed = importRequestSchema.safeParse( + await req.json().catch(() => null), + ); + if (!parsed.success) { + return fail(parsed.error.issues[0]?.message ?? "Invalid request body", 400); + } + const body = parsed.data; + const { columnMapping, valueMapping } = body.session; + + // Bound the property/reference lookups by what the file actually carries. + const landlordIds = distinctCells(body.rows, columnMapping, "landlordPropertyId"); + const uprns = distinctCells(body.rows, columnMapping, "uprn"); + const references = distinctCells(body.rows, columnMapping, "workOrderReference"); + + // Targets are re-loaded server-side — the client's mapping references ids, and + // those ids are only trustworthy against the project's own live configuration. + const [targets, context] = await Promise.all([ + loadMappingTargets(projectId), + loadImportMatchContext( + projectId, + project.organisationId, + landlordIds, + uprns, + references, + ), + ]); + + const result = validateImportRows( + body.rows, + columnMapping, + valueMapping, + targets, + context, + ); + + return { ok: true, project, body, result }; +} + +/** The distinct, non-blank values of one mapped field across every row. */ +function distinctCells( + rows: string[][], + columnMapping: ImportRequestBody["session"]["columnMapping"], + field: ImportFieldKey, +): string[] { + const seen = new Set(); + for (const row of rows) { + const cell = cellFor(row, columnMapping, field); + if (cell !== "") seen.add(cell); + } + return [...seen]; +} + +/** + * Ready rows grouped into per-workstream created counts — the post-commit + * summary, and the validate route's preview of what a commit would create. + */ +export function createdByWorkstream( + result: ImportValidationResult, +): { projectWorkstreamId: string; workstreamName: string; count: number }[] { + const byId = new Map< + string, + { projectWorkstreamId: string; workstreamName: string; count: number } + >(); + for (const row of result.ready) { + const existing = byId.get(row.projectWorkstreamId); + if (existing) existing.count += 1; + else + byId.set(row.projectWorkstreamId, { + projectWorkstreamId: row.projectWorkstreamId, + workstreamName: row.workstreamName, + count: 1, + }); + } + return [...byId.values()].sort((a, b) => + a.workstreamName.localeCompare(b.workstreamName, "en-GB"), + ); +} diff --git a/src/app/db/migrations/0281_work_order_status_default.sql b/src/app/db/migrations/0281_work_order_status_default.sql new file mode 100644 index 00000000..eab570cb --- /dev/null +++ b/src/app/db/migrations/0281_work_order_status_default.sql @@ -0,0 +1,15 @@ +-- work_order.status: add DEFAULT '' (#417). +-- +-- work_order.status is NOT NULL with no default. v1 uses the stage FK +-- (project_workstream_stage_id) as the only lifecycle mechanism and leaves +-- status untouched (CONTEXT.md, "Stage"); the programme-import commit (#417) +-- therefore inserts work orders WITHOUT writing status. A NOT NULL column an +-- insert legitimately omits needs a default, or every such insert fails — the +-- same trap 0280 fixed for priority, and 0280's own note anticipated #417 +-- landing rows before its default existed. +-- +-- '' is the "untouched" sentinel: it is not one of the ladder stage names, so +-- it can never be mistaken for a lifecycle value. When the status value set is +-- finally agreed and the column becomes a pg enum, this default is revisited +-- with it. +ALTER TABLE "work_order" ALTER COLUMN "status" SET DEFAULT ''; diff --git a/src/app/db/migrations/meta/0281_snapshot.json b/src/app/db/migrations/meta/0281_snapshot.json new file mode 100644 index 00000000..6ec6cab7 --- /dev/null +++ b/src/app/db/migrations/meta/0281_snapshot.json @@ -0,0 +1,13494 @@ +{ + "id": "ef811747-01b6-46f3-9a2d-b42f9385f1ae", + "prevId": "02e783cd-ab0a-4c15-b82c-dc26a43ffc0b", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.postcode_search": { + "name": "postcode_search", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "postcode": { + "name": "postcode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "result_data": { + "name": "result_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_updated_at": { + "name": "last_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postcode_search_postcode_unique": { + "name": "postcode_search_postcode_unique", + "nullsNotDistinct": false, + "columns": [ + "postcode" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deal_measure_approval_events": { + "name": "deal_measure_approval_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "hubspot_deal_id": { + "name": "hubspot_deal_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "measure_name": { + "name": "measure_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "acted_by": { + "name": "acted_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "acted_at": { + "name": "acted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_deal_measure_events_deal_id": { + "name": "idx_deal_measure_events_deal_id", + "columns": [ + { + "expression": "hubspot_deal_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_deal_measure_events_acted_at": { + "name": "idx_deal_measure_events_acted_at", + "columns": [ + { + "expression": "acted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deal_measure_approval_events_acted_by_user_id_fk": { + "name": "deal_measure_approval_events_acted_by_user_id_fk", + "tableFrom": "deal_measure_approval_events", + "tableTo": "user", + "columnsFrom": [ + "acted_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deal_measure_approvals": { + "name": "deal_measure_approvals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "hubspot_deal_id": { + "name": "hubspot_deal_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "measure_name": { + "name": "measure_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_approved": { + "name": "is_approved", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "approved_by": { + "name": "approved_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "approved_at": { + "name": "approved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_deal_measure_approvals_deal_id": { + "name": "idx_deal_measure_approvals_deal_id", + "columns": [ + { + "expression": "hubspot_deal_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deal_measure_approvals_approved_by_user_id_fk": { + "name": "deal_measure_approvals_approved_by_user_id_fk", + "tableFrom": "deal_measure_approvals", + "tableTo": "user", + "columnsFrom": [ + "approved_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "uq_deal_measure": { + "name": "uq_deal_measure", + "nullsNotDistinct": false, + "columns": [ + "hubspot_deal_id", + "measure_name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bulk_address_uploads": { + "name": "bulk_address_uploads", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "s3_bucket": { + "name": "s3_bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "s3_key": { + "name": "s3_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "filename": { + "name": "filename", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'ready_for_processing'" + }, + "source_headers": { + "name": "source_headers", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "column_mapping": { + "name": "column_mapping", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "multi_entry_summary": { + "name": "multi_entry_summary", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "multi_entry_ordering": { + "name": "multi_entry_ordering", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "verify_ack": { + "name": "verify_ack", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "task_id": { + "name": "task_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "combined_output_s3_uri": { + "name": "combined_output_s3_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bulk_upload_uprn_corrections": { + "name": "bulk_upload_uprn_corrections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "upload_id": { + "name": "upload_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_row_id": { + "name": "source_row_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "uprn": { + "name": "uprn", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address": { + "name": "address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postcode": { + "name": "postcode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "marked_no_uprn": { + "name": "marked_no_uprn", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "bulk_upload_uprn_corrections_upload_id_bulk_address_uploads_id_fk": { + "name": "bulk_upload_uprn_corrections_upload_id_bulk_address_uploads_id_fk", + "tableFrom": "bulk_upload_uprn_corrections", + "tableTo": "bulk_address_uploads", + "columnsFrom": [ + "upload_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "bulk_upload_uprn_corrections_upload_row_unique": { + "name": "bulk_upload_uprn_corrections_upload_row_unique", + "nullsNotDistinct": false, + "columns": [ + "upload_id", + "source_row_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.aspect_condition": { + "name": "aspect_condition", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "element_id": { + "name": "element_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "aspect_type": { + "name": "aspect_type", + "type": "aspect_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "aspect_instance": { + "name": "aspect_instance", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "install_date": { + "name": "install_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "renewal_year": { + "name": "renewal_year", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "comments": { + "name": "comments", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "aspect_condition_element_id_element_id_fk": { + "name": "aspect_condition_element_id_element_id_fk", + "tableFrom": "aspect_condition", + "tableTo": "element", + "columnsFrom": [ + "element_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.element": { + "name": "element", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "survey_id": { + "name": "survey_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "element_type": { + "name": "element_type", + "type": "element_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "element_instance": { + "name": "element_instance", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "element_survey_id_property_condition_survey_id_fk": { + "name": "element_survey_id_property_condition_survey_id_fk", + "tableFrom": "element", + "tableTo": "property_condition_survey", + "columnsFrom": [ + "survey_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.property_condition_survey": { + "name": "property_condition_survey", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "uprn": { + "name": "uprn", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.hubspot_company_data": { + "name": "hubspot_company_data", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "company_name": { + "name": "company_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_id": { + "name": "group_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.hubspot_deal_data": { + "name": "hubspot_deal_data", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "deal_id": { + "name": "deal_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dealname": { + "name": "dealname", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dealstage": { + "name": "dealstage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "company_id": { + "name": "company_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_code": { + "name": "project_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "landlord_property_id": { + "name": "landlord_property_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "listing_id": { + "name": "listing_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uprn": { + "name": "uprn", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "outcome_notes": { + "name": "outcome_notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "major_condition_issue_description": { + "name": "major_condition_issue_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "major_condition_issue_photos": { + "name": "major_condition_issue_photos", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "major_condition_issue_evidence_s3_url": { + "name": "major_condition_issue_evidence_s3_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "coordination_status": { + "name": "coordination_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "design_status": { + "name": "design_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "booking_status": { + "name": "booking_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pashub_link": { + "name": "pashub_link", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sharepoint_link": { + "name": "sharepoint_link", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dampmould_growth": { + "name": "dampmould_growth", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pre_sap": { + "name": "pre_sap", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "coordinator": { + "name": "coordinator", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mtp_completion_date": { + "name": "mtp_completion_date", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "mtp_re_model_completion_date": { + "name": "mtp_re_model_completion_date", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "ioe_v3_completion_date": { + "name": "ioe_v3_completion_date", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "proposed_measures": { + "name": "proposed_measures", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "approved_package": { + "name": "approved_package", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "designer": { + "name": "designer", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "design_type": { + "name": "design_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "design_completion_date": { + "name": "design_completion_date", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "actual_measures_installed": { + "name": "actual_measures_installed", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "installer": { + "name": "installer", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "installer_handover": { + "name": "installer_handover", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lodgement_status": { + "name": "lodgement_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "measures_lodgement_date": { + "name": "measures_lodgement_date", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "lodgement_date": { + "name": "lodgement_date", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "expected_commencement_date": { + "name": "expected_commencement_date", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "coordination_comments": { + "name": "coordination_comments", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "surveyor": { + "name": "surveyor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "damp_mould_and_repairs_comments": { + "name": "damp_mould_and_repairs_comments", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "batch": { + "name": "batch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "batch_description": { + "name": "batch_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "block_reference": { + "name": "block_reference", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "nonfunded_measures": { + "name": "nonfunded_measures", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "epc_prn": { + "name": "epc_prn", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "potential_post_sap_score_dropdown": { + "name": "potential_post_sap_score_dropdown", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ei_score": { + "name": "ei_score", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ei_score__potential_": { + "name": "ei_score__potential_", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "epc_sap_score": { + "name": "epc_sap_score", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "epc_sap_score__potential_": { + "name": "epc_sap_score__potential_", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmed_survey_date": { + "name": "confirmed_survey_date", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "confirmed_survey_time": { + "name": "confirmed_survey_time", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "surveyed_date": { + "name": "surveyed_date", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "survey_type": { + "name": "survey_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "measures_for_pibi_ordered": { + "name": "measures_for_pibi_ordered", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pibi_order_date": { + "name": "pibi_order_date", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "pibi_completed_date": { + "name": "pibi_completed_date", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "property_halted_date": { + "name": "property_halted_date", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "property_halted_reason": { + "name": "property_halted_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "technical_approved_measures_for_install": { + "name": "technical_approved_measures_for_install", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sent_to_installer_for_pricing": { + "name": "sent_to_installer_for_pricing", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "domna_survey_required": { + "name": "domna_survey_required", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "domna_survey_type": { + "name": "domna_survey_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domna_survey_date": { + "name": "domna_survey_date", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "date_booking_made": { + "name": "date_booking_made", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "last_contact_date": { + "name": "last_contact_date", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "last_outbound_call": { + "name": "last_outbound_call", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "last_outbound_email": { + "name": "last_outbound_email", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "last_submission_date": { + "name": "last_submission_date", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "number_of_attempts": { + "name": "number_of_attempts", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_booking_reference": { + "name": "client_booking_reference", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "third_party_surveyor_identifier": { + "name": "third_party_surveyor_identifier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "design_constraints": { + "name": "design_constraints", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "planning_comments": { + "name": "planning_comments", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "planning_status": { + "name": "planning_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "planning_suggested_approach": { + "name": "planning_suggested_approach", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "planning_authority": { + "name": "planning_authority", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "designated_area": { + "name": "designated_area", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "article_4_pd_rights": { + "name": "article_4_pd_rights", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "listed_building": { + "name": "listed_building", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.hubspot_projects_data": { + "name": "hubspot_projects_data", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "hubspot_projects_data_project_id_unique": { + "name": "hubspot_projects_data_project_id_unique", + "nullsNotDistinct": false, + "columns": [ + "project_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.hubspot_users": { + "name": "hubspot_users", + "schema": "", + "columns": { + "hubspot_owner_id": { + "name": "hubspot_owner_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "first_name": { + "name": "first_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_name": { + "name": "last_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.property_status_tracker": { + "name": "property_status_tracker", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "hubspot_deal_id": { + "name": "hubspot_deal_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "property_status_tracker_property_id_property_id_fk": { + "name": "property_status_tracker_property_id_property_id_fk", + "tableFrom": "property_status_tracker", + "tableTo": "property", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "property_status_tracker_portfolio_id_portfolio_id_fk": { + "name": "property_status_tracker_portfolio_id_portfolio_id_fk", + "tableFrom": "property_status_tracker", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.energy_assessments": { + "name": "energy_assessments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "uprn": { + "name": "uprn", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "uprn_source": { + "name": "uprn_source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "property_type": { + "name": "property_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "building_reference_number": { + "name": "building_reference_number", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "current_energy_efficiency": { + "name": "current_energy_efficiency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "current_energy_rating": { + "name": "current_energy_rating", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "address1": { + "name": "address1", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "address2": { + "name": "address2", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "address3": { + "name": "address3", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "posttown": { + "name": "posttown", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "postcode": { + "name": "postcode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "address": { + "name": "address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "county": { + "name": "county", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "constituency": { + "name": "constituency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "constituency_label": { + "name": "constituency_label", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "low_energy_fixed_light_count": { + "name": "low_energy_fixed_light_count", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "construction_age_band": { + "name": "construction_age_band", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mainheat_energy_eff": { + "name": "mainheat_energy_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "windows_env_eff": { + "name": "windows_env_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lighting_energy_eff": { + "name": "lighting_energy_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environment_impact_potential": { + "name": "environment_impact_potential", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mainheatcont_description": { + "name": "mainheatcont_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sheating_energy_eff": { + "name": "sheating_energy_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "local_authority": { + "name": "local_authority", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "local_authority_label": { + "name": "local_authority_label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fixed_lighting_outlets_count": { + "name": "fixed_lighting_outlets_count", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "energy_tariff": { + "name": "energy_tariff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mechanical_ventilation": { + "name": "mechanical_ventilation", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "solar_water_heating_flag": { + "name": "solar_water_heating_flag", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "co2_emissions_potential": { + "name": "co2_emissions_potential", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "number_heated_rooms": { + "name": "number_heated_rooms", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "floor_description": { + "name": "floor_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "energy_consumption_potential": { + "name": "energy_consumption_potential", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "built_form": { + "name": "built_form", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "number_open_fireplaces": { + "name": "number_open_fireplaces", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "windows_description": { + "name": "windows_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "glazed_area": { + "name": "glazed_area", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "inspection_date": { + "name": "inspection_date", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true + }, + "mains_gas_flag": { + "name": "mains_gas_flag", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "co2_emiss_curr_per_floor_area": { + "name": "co2_emiss_curr_per_floor_area", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "heat_loss_corridor": { + "name": "heat_loss_corridor", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "unheated_corridor_length": { + "name": "unheated_corridor_length", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "flat_storey_count": { + "name": "flat_storey_count", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "roof_energy_eff": { + "name": "roof_energy_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "total_floor_area": { + "name": "total_floor_area", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environment_impact_current": { + "name": "environment_impact_current", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "roof_description": { + "name": "roof_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "floor_energy_eff": { + "name": "floor_energy_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "number_habitable_rooms": { + "name": "number_habitable_rooms", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hot_water_env_eff": { + "name": "hot_water_env_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mainheatc_energy_eff": { + "name": "mainheatc_energy_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "main_fuel": { + "name": "main_fuel", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lighting_env_eff": { + "name": "lighting_env_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "windows_energy_eff": { + "name": "windows_energy_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "floor_env_eff": { + "name": "floor_env_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sheating_env_eff": { + "name": "sheating_env_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lighting_description": { + "name": "lighting_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "roof_env_eff": { + "name": "roof_env_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "walls_energy_eff": { + "name": "walls_energy_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "photo_supply": { + "name": "photo_supply", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lighting_cost_potential": { + "name": "lighting_cost_potential", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mainheat_env_eff": { + "name": "mainheat_env_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "multi_glaze_proportion": { + "name": "multi_glaze_proportion", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "main_heating_controls": { + "name": "main_heating_controls", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "flat_top_storey": { + "name": "flat_top_storey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secondheat_description": { + "name": "secondheat_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "walls_env_eff": { + "name": "walls_env_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "transaction_type": { + "name": "transaction_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "extension_count": { + "name": "extension_count", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mainheatc_env_eff": { + "name": "mainheatc_env_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lmk_key": { + "name": "lmk_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "wind_turbine_count": { + "name": "wind_turbine_count", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tenure": { + "name": "tenure", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "floor_level": { + "name": "floor_level", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "potential_energy_efficiency": { + "name": "potential_energy_efficiency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "potential_energy_rating": { + "name": "potential_energy_rating", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hot_water_energy_eff": { + "name": "hot_water_energy_eff", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "low_energy_lighting": { + "name": "low_energy_lighting", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "walls_description": { + "name": "walls_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hotwater_description": { + "name": "hotwater_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "co2_emissions_current": { + "name": "co2_emissions_current", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "heating_cost_current": { + "name": "heating_cost_current", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "heating_cost_potential": { + "name": "heating_cost_potential", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hot_water_cost_current": { + "name": "hot_water_cost_current", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hot_water_cost_potential": { + "name": "hot_water_cost_potential", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lighting_cost_current": { + "name": "lighting_cost_current", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "energy_consumption_current": { + "name": "energy_consumption_current", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lodgement_date": { + "name": "lodgement_date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "lodgement_datetime": { + "name": "lodgement_datetime", + "type": "timestamp (6)", + "primaryKey": false, + "notNull": true + }, + "mainheat_description": { + "name": "mainheat_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "floor_height": { + "name": "floor_height", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "glazed_type": { + "name": "glazed_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_location": { + "name": "file_location", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "surveyor_name": { + "name": "surveyor_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "surveyor_company": { + "name": "surveyor_company", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "space_heating_kwh": { + "name": "space_heating_kwh", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "water_heating_kwh": { + "name": "water_heating_kwh", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "number_of_doors": { + "name": "number_of_doors", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "number_of_insulated_doors": { + "name": "number_of_insulated_doors", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "number_of_floors": { + "name": "number_of_floors", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "insulation_wall_area": { + "name": "insulation_wall_area", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "heat_loss_perimeter": { + "name": "heat_loss_perimeter", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "party_wall_length": { + "name": "party_wall_length", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "perimeter": { + "name": "perimeter", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "rooms_with_bath_and_or_shower": { + "name": "rooms_with_bath_and_or_shower", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "rooms_with_mixer_shower_no_bath": { + "name": "rooms_with_mixer_shower_no_bath", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "room_with_bath_and_mixer_shower": { + "name": "room_with_bath_and_mixer_shower", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "percent_draftproofed": { + "name": "percent_draftproofed", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "has_hot_water_cylinder": { + "name": "has_hot_water_cylinder", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "cylinder_insulation_type": { + "name": "cylinder_insulation_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cylinder_insulation_thickness": { + "name": "cylinder_insulation_thickness", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cylinder_thermostat": { + "name": "cylinder_thermostat", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "main_dwelling_ground_floor_area": { + "name": "main_dwelling_ground_floor_area", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "number_of_windows": { + "name": "number_of_windows", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "windows_area": { + "name": "windows_area", + "type": "real", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.energy_assessment_documents": { + "name": "energy_assessment_documents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "uprn": { + "name": "uprn", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "energy_assessment_id": { + "name": "energy_assessment_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "document_type": { + "name": "document_type", + "type": "document_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "document_location": { + "name": "document_location", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "scenario_id": { + "name": "scenario_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "energy_assessment_documents_energy_assessment_id_energy_assessments_id_fk": { + "name": "energy_assessment_documents_energy_assessment_id_energy_assessments_id_fk", + "tableFrom": "energy_assessment_documents", + "tableTo": "energy_assessments", + "columnsFrom": [ + "energy_assessment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "energy_assessment_documents_scenario_id_energy_assessment_scenarios_id_fk": { + "name": "energy_assessment_documents_scenario_id_energy_assessment_scenarios_id_fk", + "tableFrom": "energy_assessment_documents", + "tableTo": "energy_assessment_scenarios", + "columnsFrom": [ + "scenario_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.energy_assessment_scenarios": { + "name": "energy_assessment_scenarios", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "scenario_name": { + "name": "scenario_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "energy_assessment_id": { + "name": "energy_assessment_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "energy_assessment_scenarios_energy_assessment_id_energy_assessments_id_fk": { + "name": "energy_assessment_scenarios_energy_assessment_id_energy_assessments_id_fk", + "tableFrom": "energy_assessment_scenarios", + "tableTo": "energy_assessments", + "columnsFrom": [ + "energy_assessment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.epc_store": { + "name": "epc_store", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "uprn": { + "name": "uprn", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "epc_api_created_at": { + "name": "epc_api_created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "epc_api": { + "name": "epc_api", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "epc_page_created_at": { + "name": "epc_page_created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "epc_page": { + "name": "epc_page", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "epc_page_rrn": { + "name": "epc_page_rrn", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "uq_epc_store_uprn": { + "name": "uq_epc_store_uprn", + "columns": [ + { + "expression": "uprn", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.files_from_surveyor": { + "name": "files_from_surveyor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "s3_json_url": { + "name": "s3_json_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "files_from_surveyor_portfolio_id_portfolio_id_fk": { + "name": "files_from_surveyor_portfolio_id_portfolio_id_fk", + "tableFrom": "files_from_surveyor", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_from_surveyor_property_id_property_id_fk": { + "name": "files_from_surveyor_property_id_property_id_fk", + "tableFrom": "files_from_surveyor", + "tableTo": "property", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.funding_package": { + "name": "funding_package", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "plan_id": { + "name": "plan_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "scheme": { + "name": "scheme", + "type": "scheme", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "project_funding": { + "name": "project_funding", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "total_uplift": { + "name": "total_uplift", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "full_project_score": { + "name": "full_project_score", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "partial_project_score": { + "name": "partial_project_score", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "uplift_project_score": { + "name": "uplift_project_score", + "type": "real", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "funding_package_plan_id_plan_id_fk": { + "name": "funding_package_plan_id_plan_id_fk", + "tableFrom": "funding_package", + "tableTo": "plan", + "columnsFrom": [ + "plan_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.funding_package_measures": { + "name": "funding_package_measures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "funding_package_id": { + "name": "funding_package_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "measure": { + "name": "measure", + "type": "type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "material_id": { + "name": "material_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "innovation_uplift": { + "name": "innovation_uplift", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "partial_project_score": { + "name": "partial_project_score", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "uplift_project_score": { + "name": "uplift_project_score", + "type": "real", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "funding_package_measures_funding_package_id_funding_package_id_fk": { + "name": "funding_package_measures_funding_package_id_funding_package_id_fk", + "tableFrom": "funding_package_measures", + "tableTo": "funding_package", + "columnsFrom": [ + "funding_package_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "funding_package_measures_material_id_material_id_fk": { + "name": "funding_package_measures_material_id_material_id_fk", + "tableFrom": "funding_package_measures", + "tableTo": "material", + "columnsFrom": [ + "material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.inspections": { + "name": "inspections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "archetype": { + "name": "archetype", + "type": "inspection_archetype", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "archetype_2": { + "name": "archetype_2", + "type": "inspection_archetype_2", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "wall_construction": { + "name": "wall_construction", + "type": "inspections_wall_construction", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "insulation": { + "name": "insulation", + "type": "inspections_wall_insulation", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "insulation_material": { + "name": "insulation_material", + "type": "inspections_insulation_material", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "borescoped": { + "name": "borescoped", + "type": "inspection_borescoped", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "roof_orientation": { + "name": "roof_orientation", + "type": "inspections_roof_orientation", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "tile_hung": { + "name": "tile_hung", + "type": "inspections_tile_hung", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "rendered": { + "name": "rendered", + "type": "inspections_rendered", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "cladding": { + "name": "cladding", + "type": "inspections_cladding", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "access_issues": { + "name": "access_issues", + "type": "inspections_access_issues", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "surveyor_name": { + "name": "surveyor_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "inspections_property_id_property_id_fk": { + "name": "inspections_property_id_property_id_fk", + "tableFrom": "inspections", + "tableTo": "property", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landlord_built_form_type_overrides": { + "name": "landlord_built_form_type_overrides", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "built_form_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "override_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "landlord_built_form_type_overrides_portfolio_id_portfolio_id_fk": { + "name": "landlord_built_form_type_overrides_portfolio_id_portfolio_id_fk", + "tableFrom": "landlord_built_form_type_overrides", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "landlord_built_form_type_overrides_portfolio_description_unique": { + "name": "landlord_built_form_type_overrides_portfolio_description_unique", + "nullsNotDistinct": false, + "columns": [ + "portfolio_id", + "description" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landlord_construction_age_band_overrides": { + "name": "landlord_construction_age_band_overrides", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "construction_age_band", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "override_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "landlord_construction_age_band_overrides_portfolio_fk": { + "name": "landlord_construction_age_band_overrides_portfolio_fk", + "tableFrom": "landlord_construction_age_band_overrides", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "landlord_construction_age_band_portfolio_description_unique": { + "name": "landlord_construction_age_band_portfolio_description_unique", + "nullsNotDistinct": false, + "columns": [ + "portfolio_id", + "description" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landlord_glazing_overrides": { + "name": "landlord_glazing_overrides", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "glazing", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "override_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "landlord_glazing_overrides_portfolio_id_portfolio_id_fk": { + "name": "landlord_glazing_overrides_portfolio_id_portfolio_id_fk", + "tableFrom": "landlord_glazing_overrides", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "landlord_glazing_overrides_portfolio_description_unique": { + "name": "landlord_glazing_overrides_portfolio_description_unique", + "nullsNotDistinct": false, + "columns": [ + "portfolio_id", + "description" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landlord_main_fuel_overrides": { + "name": "landlord_main_fuel_overrides", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "main_fuel", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "override_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "landlord_main_fuel_overrides_portfolio_id_portfolio_id_fk": { + "name": "landlord_main_fuel_overrides_portfolio_id_portfolio_id_fk", + "tableFrom": "landlord_main_fuel_overrides", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "landlord_main_fuel_overrides_portfolio_description_unique": { + "name": "landlord_main_fuel_overrides_portfolio_description_unique", + "nullsNotDistinct": false, + "columns": [ + "portfolio_id", + "description" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landlord_main_heating_system_overrides": { + "name": "landlord_main_heating_system_overrides", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "main_heating_system", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "override_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "landlord_main_heating_system_overrides_portfolio_fk": { + "name": "landlord_main_heating_system_overrides_portfolio_fk", + "tableFrom": "landlord_main_heating_system_overrides", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "landlord_main_heating_system_portfolio_description_unique": { + "name": "landlord_main_heating_system_portfolio_description_unique", + "nullsNotDistinct": false, + "columns": [ + "portfolio_id", + "description" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landlord_property_type_overrides": { + "name": "landlord_property_type_overrides", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "property_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "override_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "landlord_property_type_overrides_portfolio_id_portfolio_id_fk": { + "name": "landlord_property_type_overrides_portfolio_id_portfolio_id_fk", + "tableFrom": "landlord_property_type_overrides", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "landlord_property_type_overrides_portfolio_description_unique": { + "name": "landlord_property_type_overrides_portfolio_description_unique", + "nullsNotDistinct": false, + "columns": [ + "portfolio_id", + "description" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landlord_roof_type_overrides": { + "name": "landlord_roof_type_overrides", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "roof_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "override_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "landlord_roof_type_overrides_portfolio_id_portfolio_id_fk": { + "name": "landlord_roof_type_overrides_portfolio_id_portfolio_id_fk", + "tableFrom": "landlord_roof_type_overrides", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "landlord_roof_type_overrides_portfolio_description_unique": { + "name": "landlord_roof_type_overrides_portfolio_description_unique", + "nullsNotDistinct": false, + "columns": [ + "portfolio_id", + "description" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landlord_wall_type_overrides": { + "name": "landlord_wall_type_overrides", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "wall_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "override_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "landlord_wall_type_overrides_portfolio_id_portfolio_id_fk": { + "name": "landlord_wall_type_overrides_portfolio_id_portfolio_id_fk", + "tableFrom": "landlord_wall_type_overrides", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "landlord_wall_type_overrides_portfolio_description_unique": { + "name": "landlord_wall_type_overrides_portfolio_description_unique", + "nullsNotDistinct": false, + "columns": [ + "portfolio_id", + "description" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landlord_water_heating_overrides": { + "name": "landlord_water_heating_overrides", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "water_heating", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "override_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "landlord_water_heating_overrides_portfolio_id_portfolio_id_fk": { + "name": "landlord_water_heating_overrides_portfolio_id_portfolio_id_fk", + "tableFrom": "landlord_water_heating_overrides", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "landlord_water_heating_overrides_portfolio_description_unique": { + "name": "landlord_water_heating_overrides_portfolio_description_unique", + "nullsNotDistinct": false, + "columns": [ + "portfolio_id", + "description" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.magic_plan_door_ventilation": { + "name": "magic_plan_door_ventilation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "magic_plan_door_id": { + "name": "magic_plan_door_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "undercut_mm": { + "name": "undercut_mm", + "type": "real", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "magic_plan_door_ventilation_magic_plan_door_id_magic_plan_door_id_fk": { + "name": "magic_plan_door_ventilation_magic_plan_door_id_magic_plan_door_id_fk", + "tableFrom": "magic_plan_door_ventilation", + "tableTo": "magic_plan_door", + "columnsFrom": [ + "magic_plan_door_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "magic_plan_door_ventilation_magic_plan_door_id_unique": { + "name": "magic_plan_door_ventilation_magic_plan_door_id_unique", + "nullsNotDistinct": false, + "columns": [ + "magic_plan_door_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.magic_plan_door": { + "name": "magic_plan_door", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "magic_plan_room_id": { + "name": "magic_plan_room_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "width_mm": { + "name": "width_mm", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "height_mm": { + "name": "height_mm", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "magic_plan_door_magic_plan_room_id_magic_plan_room_id_fk": { + "name": "magic_plan_door_magic_plan_room_id_magic_plan_room_id_fk", + "tableFrom": "magic_plan_door", + "tableTo": "magic_plan_room", + "columnsFrom": [ + "magic_plan_room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.magic_plan_floor": { + "name": "magic_plan_floor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "magic_plan_plan_id": { + "name": "magic_plan_plan_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "level": { + "name": "level", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "magic_plan_floor_magic_plan_plan_id_magic_plan_plan_id_fk": { + "name": "magic_plan_floor_magic_plan_plan_id_magic_plan_plan_id_fk", + "tableFrom": "magic_plan_floor", + "tableTo": "magic_plan_plan", + "columnsFrom": [ + "magic_plan_plan_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.magic_plan_plan": { + "name": "magic_plan_plan", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address": { + "name": "address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postcode": { + "name": "postcode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "magic_plan_uid": { + "name": "magic_plan_uid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploaded_file_id": { + "name": "uploaded_file_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "magic_plan_plan_uploaded_file_id_uploaded_files_id_fk": { + "name": "magic_plan_plan_uploaded_file_id_uploaded_files_id_fk", + "tableFrom": "magic_plan_plan", + "tableTo": "uploaded_files", + "columnsFrom": [ + "uploaded_file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "magic_plan_plan_magic_plan_uid_unique": { + "name": "magic_plan_plan_magic_plan_uid_unique", + "nullsNotDistinct": false, + "columns": [ + "magic_plan_uid" + ] + }, + "magic_plan_plan_uploaded_file_id_unique": { + "name": "magic_plan_plan_uploaded_file_id_unique", + "nullsNotDistinct": false, + "columns": [ + "uploaded_file_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.magic_plan_room": { + "name": "magic_plan_room", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "magic_plan_floor_id": { + "name": "magic_plan_floor_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "width_m": { + "name": "width_m", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "length_m": { + "name": "length_m", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "area_m2": { + "name": "area_m2", + "type": "real", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "magic_plan_room_magic_plan_floor_id_magic_plan_floor_id_fk": { + "name": "magic_plan_room_magic_plan_floor_id_magic_plan_floor_id_fk", + "tableFrom": "magic_plan_room", + "tableTo": "magic_plan_floor", + "columnsFrom": [ + "magic_plan_floor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.magic_plan_window_ventilation": { + "name": "magic_plan_window_ventilation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "magic_plan_window_id": { + "name": "magic_plan_window_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "opening_type": { + "name": "opening_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "num_openings": { + "name": "num_openings", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "pct_openable": { + "name": "pct_openable", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "trickle_vent_area_mm2": { + "name": "trickle_vent_area_mm2", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "num_trickle_vents": { + "name": "num_trickle_vents", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "magic_plan_window_ventilation_magic_plan_window_id_magic_plan_window_id_fk": { + "name": "magic_plan_window_ventilation_magic_plan_window_id_magic_plan_window_id_fk", + "tableFrom": "magic_plan_window_ventilation", + "tableTo": "magic_plan_window", + "columnsFrom": [ + "magic_plan_window_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "magic_plan_window_ventilation_magic_plan_window_id_unique": { + "name": "magic_plan_window_ventilation_magic_plan_window_id_unique", + "nullsNotDistinct": false, + "columns": [ + "magic_plan_window_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.magic_plan_window": { + "name": "magic_plan_window", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "magic_plan_room_id": { + "name": "magic_plan_room_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "width_m": { + "name": "width_m", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "height_m": { + "name": "height_m", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "area_m2": { + "name": "area_m2", + "type": "real", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "magic_plan_window_magic_plan_room_id_magic_plan_room_id_fk": { + "name": "magic_plan_window_magic_plan_room_id_magic_plan_room_id_fk", + "tableFrom": "magic_plan_window", + "tableTo": "magic_plan_room", + "columnsFrom": [ + "magic_plan_room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.material": { + "name": "material", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "depth": { + "name": "depth", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "depth_unit": { + "name": "depth_unit", + "type": "depth_unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "cost_unit": { + "name": "cost_unit", + "type": "cost_unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "r_value_per_mm": { + "name": "r_value_per_mm", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "r_value_unit": { + "name": "r_value_unit", + "type": "r_value_unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "thermal_conductivity": { + "name": "thermal_conductivity", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "thermal_conductivity_unit": { + "name": "thermal_conductivity_unit", + "type": "thermal_conductivity_unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "link": { + "name": "link", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "prime_material_cost": { + "name": "prime_material_cost", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "material_cost": { + "name": "material_cost", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "labour_cost": { + "name": "labour_cost", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "labour_hours_per_unit": { + "name": "labour_hours_per_unit", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "plant_cost": { + "name": "plant_cost", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "total_cost": { + "name": "total_cost", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "cost": { + "name": "cost", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_installer_quote": { + "name": "is_installer_quote", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "innovation_rate": { + "name": "innovation_rate", + "type": "real", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "size": { + "name": "size", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "size_unit": { + "name": "size_unit", + "type": "size_unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "includes_scaffolding": { + "name": "includes_scaffolding", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "includes_battery": { + "name": "includes_battery", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "battery_size": { + "name": "battery_size", + "type": "real", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_material_active": { + "name": "idx_material_active", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"material\".\"is_active\"", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organisation": { + "name": "organisation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "hubspot_company_id": { + "name": "hubspot_company_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pibi_requests": { + "name": "pibi_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "hubspot_deal_id": { + "name": "hubspot_deal_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "measure_name": { + "name": "measure_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ordered_at": { + "name": "ordered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "pushed_at": { + "name": "pushed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_pibi_requests_deal_id": { + "name": "idx_pibi_requests_deal_id", + "columns": [ + { + "expression": "hubspot_deal_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_pibi_requests_portfolio_id": { + "name": "idx_pibi_requests_portfolio_id", + "columns": [ + { + "expression": "portfolio_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pibi_requests_portfolio_id_portfolio_id_fk": { + "name": "pibi_requests_portfolio_id_portfolio_id_fk", + "tableFrom": "pibi_requests", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "pibi_requests_created_by_user_id_user_id_fk": { + "name": "pibi_requests_created_by_user_id_user_id_fk", + "tableFrom": "pibi_requests", + "tableTo": "user", + "columnsFrom": [ + "created_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.portfolio_organisation": { + "name": "portfolio_organisation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "portfolio_organisation_portfolio_id_portfolio_id_fk": { + "name": "portfolio_organisation_portfolio_id_portfolio_id_fk", + "tableFrom": "portfolio_organisation", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "portfolio_organisation_organisation_id_organisation_id_fk": { + "name": "portfolio_organisation_organisation_id_organisation_id_fk", + "tableFrom": "portfolio_organisation", + "tableTo": "organisation", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "portfolio_organisation_portfolio_id_organisation_id_unique": { + "name": "portfolio_organisation_portfolio_id_organisation_id_unique", + "nullsNotDistinct": false, + "columns": [ + "portfolio_id", + "organisation_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.portfolio": { + "name": "portfolio", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "budget": { + "name": "budget", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "goal": { + "name": "goal", + "type": "goal", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "cost": { + "name": "cost", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "number_of_properties": { + "name": "number_of_properties", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "co2_equivalent_savings": { + "name": "co2_equivalent_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "energy_savings": { + "name": "energy_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "energy_cost_savings": { + "name": "energy_cost_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "property_valuation_increase": { + "name": "property_valuation_increase", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "rental_yield_increase": { + "name": "rental_yield_increase", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "total_work_hours": { + "name": "total_work_hours", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "labour_days": { + "name": "labour_days", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "epc_breakdown_pre_retrofit": { + "name": "epc_breakdown_pre_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "epc_breakdown_post_retrofit": { + "name": "epc_breakdown_post_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "n_units_to_retrofit": { + "name": "n_units_to_retrofit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "co2_per_unit_pre_retrofit": { + "name": "co2_per_unit_pre_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "co2_per_unit_post_retrofit": { + "name": "co2_per_unit_post_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "energy_bill_per_unit_pre_retrofit": { + "name": "energy_bill_per_unit_pre_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "energy_bill_per_unit_post_retrofit": { + "name": "energy_bill_per_unit_post_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "energy_consumption_per_unit_pre_retrofit": { + "name": "energy_consumption_per_unit_pre_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "energy_consumption_per_unit_post_retrofit": { + "name": "energy_consumption_per_unit_post_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "valuation_improvement_per_unit": { + "name": "valuation_improvement_per_unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cost_per_unit": { + "name": "cost_per_unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cost_per_co2_saved": { + "name": "cost_per_co2_saved", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cost_per_sap_point": { + "name": "cost_per_sap_point", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "valuation_return_on_investment": { + "name": "valuation_return_on_investment", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.portfolio_capabilities": { + "name": "portfolio_capabilities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "capability": { + "name": "capability", + "type": "portfolio_capability", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "portfolio_capabilities_user_id_user_id_fk": { + "name": "portfolio_capabilities_user_id_user_id_fk", + "tableFrom": "portfolio_capabilities", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "portfolio_capabilities_portfolio_id_portfolio_id_fk": { + "name": "portfolio_capabilities_portfolio_id_portfolio_id_fk", + "tableFrom": "portfolio_capabilities", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "portfolio_capabilities_user_id_portfolio_id_capability_unique": { + "name": "portfolio_capabilities_user_id_portfolio_id_capability_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id", + "portfolio_id", + "capability" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.portfolioInvitations": { + "name": "portfolioInvitations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "invited_by_user_id": { + "name": "invited_by_user_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "portfolioInvitations_portfolio_id_portfolio_id_fk": { + "name": "portfolioInvitations_portfolio_id_portfolio_id_fk", + "tableFrom": "portfolioInvitations", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "portfolioInvitations_invited_by_user_id_user_id_fk": { + "name": "portfolioInvitations_invited_by_user_id_user_id_fk", + "tableFrom": "portfolioInvitations", + "tableTo": "user", + "columnsFrom": [ + "invited_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "portfolio_invitations_portfolio_email_unique": { + "name": "portfolio_invitations_portfolio_email_unique", + "nullsNotDistinct": false, + "columns": [ + "portfolio_id", + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.portfolioUsers": { + "name": "portfolioUsers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "portfolioUsers_user_id_user_id_fk": { + "name": "portfolioUsers_user_id_user_id_fk", + "tableFrom": "portfolioUsers", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "portfolioUsers_portfolio_id_portfolio_id_fk": { + "name": "portfolioUsers_portfolio_id_portfolio_id_fk", + "tableFrom": "portfolioUsers", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "project_type_id": { + "name": "project_type_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "start_date": { + "name": "start_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "end_date": { + "name": "end_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "domna_admin_access": { + "name": "domna_admin_access", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sharepoint_base_url": { + "name": "sharepoint_base_url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "sharepoint_sub_folder_template": { + "name": "sharepoint_sub_folder_template", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "project_organisation_id_organisation_id_fk": { + "name": "project_organisation_id_organisation_id_fk", + "tableFrom": "project", + "tableTo": "organisation", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "project_project_type_id_project_type_id_fk": { + "name": "project_project_type_id_project_type_id_fk", + "tableFrom": "project", + "tableTo": "project_type", + "columnsFrom": [ + "project_type_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project_property": { + "name": "project_property", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "project_property_project_id_project_id_fk": { + "name": "project_property_project_id_project_id_fk", + "tableFrom": "project_property", + "tableTo": "project", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "project_property_property_id_property_id_fk": { + "name": "project_property_property_id_property_id_fk", + "tableFrom": "project_property", + "tableTo": "property", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "project_property_project_id_property_id_unique": { + "name": "project_property_project_id_property_id_unique", + "nullsNotDistinct": false, + "columns": [ + "project_id", + "property_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project_type": { + "name": "project_type", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project_workstream": { + "name": "project_workstream", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "workstream_id": { + "name": "workstream_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "current_stage_id": { + "name": "current_stage_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "project_workstream_project_id_project_id_fk": { + "name": "project_workstream_project_id_project_id_fk", + "tableFrom": "project_workstream", + "tableTo": "project", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "project_workstream_workstream_id_workstream_id_fk": { + "name": "project_workstream_workstream_id_workstream_id_fk", + "tableFrom": "project_workstream", + "tableTo": "workstream", + "columnsFrom": [ + "workstream_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "project_workstream_current_stage_id_project_workstream_stage_id_fk": { + "name": "project_workstream_current_stage_id_project_workstream_stage_id_fk", + "tableFrom": "project_workstream", + "tableTo": "project_workstream_stage", + "columnsFrom": [ + "current_stage_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project_workstream_contractor": { + "name": "project_workstream_contractor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "project_workstream_id": { + "name": "project_workstream_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "update_stages_permission": { + "name": "update_stages_permission", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "upload_documents_permission": { + "name": "upload_documents_permission", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "assigned_at": { + "name": "assigned_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "project_workstream_contractor_project_workstream_id_project_workstream_id_fk": { + "name": "project_workstream_contractor_project_workstream_id_project_workstream_id_fk", + "tableFrom": "project_workstream_contractor", + "tableTo": "project_workstream", + "columnsFrom": [ + "project_workstream_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "project_workstream_contractor_organisation_id_organisation_id_fk": { + "name": "project_workstream_contractor_organisation_id_organisation_id_fk", + "tableFrom": "project_workstream_contractor", + "tableTo": "organisation", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project_workstream_evidence_requirement": { + "name": "project_workstream_evidence_requirement", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "project_workstream_id": { + "name": "project_workstream_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "file_type": { + "name": "file_type", + "type": "file_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "project_workstream_stage_id": { + "name": "project_workstream_stage_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "naming_rule": { + "name": "naming_rule", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "project_workstream_evidence_requirement_project_workstream_id_project_workstream_id_fk": { + "name": "project_workstream_evidence_requirement_project_workstream_id_project_workstream_id_fk", + "tableFrom": "project_workstream_evidence_requirement", + "tableTo": "project_workstream", + "columnsFrom": [ + "project_workstream_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "project_workstream_evidence_requirement_project_workstream_stage_id_project_workstream_stage_id_fk": { + "name": "project_workstream_evidence_requirement_project_workstream_stage_id_project_workstream_stage_id_fk", + "tableFrom": "project_workstream_evidence_requirement", + "tableTo": "project_workstream_stage", + "columnsFrom": [ + "project_workstream_stage_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project_workstream_stage": { + "name": "project_workstream_stage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "project_workstream_id": { + "name": "project_workstream_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "start_date": { + "name": "start_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "due_date": { + "name": "due_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "last_updated": { + "name": "last_updated", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status_description": { + "name": "status_description", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "project_workstream_stage_project_workstream_id_project_workstream_id_fk": { + "name": "project_workstream_stage_project_workstream_id_project_workstream_id_fk", + "tableFrom": "project_workstream_stage", + "tableTo": "project_workstream", + "columnsFrom": [ + "project_workstream_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.work_order": { + "name": "work_order", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "reference": { + "name": "reference", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "project_workstream_contractor_id": { + "name": "project_workstream_contractor_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "project_workstream_stage_id": { + "name": "project_workstream_stage_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "forecast_end": { + "name": "forecast_end", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "priority": { + "name": "priority", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "work_order_project_workstream_contractor_id_project_workstream_contractor_id_fk": { + "name": "work_order_project_workstream_contractor_id_project_workstream_contractor_id_fk", + "tableFrom": "work_order", + "tableTo": "project_workstream_contractor", + "columnsFrom": [ + "project_workstream_contractor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "work_order_property_id_property_id_fk": { + "name": "work_order_property_id_property_id_fk", + "tableFrom": "work_order", + "tableTo": "property", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "work_order_project_workstream_stage_id_project_workstream_stage_id_fk": { + "name": "work_order_project_workstream_stage_id_project_workstream_stage_id_fk", + "tableFrom": "work_order", + "tableTo": "project_workstream_stage", + "columnsFrom": [ + "project_workstream_stage_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "work_order_reference_unique": { + "name": "work_order_reference_unique", + "nullsNotDistinct": false, + "columns": [ + "reference" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workstream": { + "name": "workstream", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.property_overrides": { + "name": "property_overrides", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "property_id": { + "name": "property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "building_part": { + "name": "building_part", + "type": "smallint", + "primaryKey": false, + "notNull": true + }, + "override_component": { + "name": "override_component", + "type": "override_component", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "override_value": { + "name": "override_value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "original_spreadsheet_description": { + "name": "original_spreadsheet_description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "property_overrides_property_id_property_id_fk": { + "name": "property_overrides_property_id_property_id_fk", + "tableFrom": "property_overrides", + "tableTo": "property", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "property_overrides_portfolio_id_portfolio_id_fk": { + "name": "property_overrides_portfolio_id_portfolio_id_fk", + "tableFrom": "property_overrides", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "property_overrides_property_component_part_unique": { + "name": "property_overrides_property_component_part_unique", + "nullsNotDistinct": false, + "columns": [ + "property_id", + "override_component", + "building_part" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.epc_building_part": { + "name": "epc_building_part", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "epc_property_id": { + "name": "epc_property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "construction_age_band": { + "name": "construction_age_band", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "wall_construction": { + "name": "wall_construction", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "wall_insulation_type": { + "name": "wall_insulation_type", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "wall_thickness_measured": { + "name": "wall_thickness_measured", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "party_wall_construction": { + "name": "party_wall_construction", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "building_part_number": { + "name": "building_part_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "wall_dry_lined": { + "name": "wall_dry_lined", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "wall_thickness_mm": { + "name": "wall_thickness_mm", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "wall_insulation_thickness": { + "name": "wall_insulation_thickness", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "wall_insulation_thermal_conductivity": { + "name": "wall_insulation_thermal_conductivity", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "floor_heat_loss": { + "name": "floor_heat_loss", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "floor_insulation_thickness": { + "name": "floor_insulation_thickness", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "flat_roof_insulation_thickness": { + "name": "flat_roof_insulation_thickness", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "floor_type": { + "name": "floor_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "floor_construction_type": { + "name": "floor_construction_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "floor_insulation_type_str": { + "name": "floor_insulation_type_str", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "floor_u_value_known": { + "name": "floor_u_value_known", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "roof_construction": { + "name": "roof_construction", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "roof_insulation_location": { + "name": "roof_insulation_location", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "roof_insulation_thickness": { + "name": "roof_insulation_thickness", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "rafter_insulation_thickness": { + "name": "rafter_insulation_thickness", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "roof_construction_type": { + "name": "roof_construction_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "curtain_wall_age": { + "name": "curtain_wall_age", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "wall_is_basement": { + "name": "wall_is_basement", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "wall_u_value": { + "name": "wall_u_value", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "alt_wall_1_area": { + "name": "alt_wall_1_area", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "alt_wall_1_dry_lined": { + "name": "alt_wall_1_dry_lined", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "alt_wall_1_construction": { + "name": "alt_wall_1_construction", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "alt_wall_1_insulation_type": { + "name": "alt_wall_1_insulation_type", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "alt_wall_1_thickness_measured": { + "name": "alt_wall_1_thickness_measured", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "alt_wall_1_insulation_thickness": { + "name": "alt_wall_1_insulation_thickness", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "alt_wall_1_is_sheltered": { + "name": "alt_wall_1_is_sheltered", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "alt_wall_1_u_value": { + "name": "alt_wall_1_u_value", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "alt_wall_1_thickness_mm": { + "name": "alt_wall_1_thickness_mm", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "alt_wall_1_is_basement": { + "name": "alt_wall_1_is_basement", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "alt_wall_2_area": { + "name": "alt_wall_2_area", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "alt_wall_2_dry_lined": { + "name": "alt_wall_2_dry_lined", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "alt_wall_2_construction": { + "name": "alt_wall_2_construction", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "alt_wall_2_insulation_type": { + "name": "alt_wall_2_insulation_type", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "alt_wall_2_thickness_measured": { + "name": "alt_wall_2_thickness_measured", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "alt_wall_2_insulation_thickness": { + "name": "alt_wall_2_insulation_thickness", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "alt_wall_2_is_sheltered": { + "name": "alt_wall_2_is_sheltered", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "alt_wall_2_u_value": { + "name": "alt_wall_2_u_value", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "alt_wall_2_thickness_mm": { + "name": "alt_wall_2_thickness_mm", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "alt_wall_2_is_basement": { + "name": "alt_wall_2_is_basement", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "ix_epc_building_part_epc_property": { + "name": "ix_epc_building_part_epc_property", + "columns": [ + { + "expression": "epc_property_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "epc_building_part_epc_property_id_epc_property_id_fk": { + "name": "epc_building_part_epc_property_id_epc_property_id_fk", + "tableFrom": "epc_building_part", + "tableTo": "epc_property", + "columnsFrom": [ + "epc_property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.epc_energy_element": { + "name": "epc_energy_element", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "epc_property_id": { + "name": "epc_property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "element_type": { + "name": "element_type", + "type": "energy_element_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "energy_efficiency_rating": { + "name": "energy_efficiency_rating", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "environmental_efficiency_rating": { + "name": "environmental_efficiency_rating", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "ix_epc_energy_element_epc_property": { + "name": "ix_epc_energy_element_epc_property", + "columns": [ + { + "expression": "epc_property_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "epc_energy_element_epc_property_id_epc_property_id_fk": { + "name": "epc_energy_element_epc_property_id_epc_property_id_fk", + "tableFrom": "epc_energy_element", + "tableTo": "epc_property", + "columnsFrom": [ + "epc_property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.epc_flat_details": { + "name": "epc_flat_details", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "epc_property_id": { + "name": "epc_property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "level": { + "name": "level", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "top_storey": { + "name": "top_storey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "flat_location": { + "name": "flat_location", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "heat_loss_corridor": { + "name": "heat_loss_corridor", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "storey_count": { + "name": "storey_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "unheated_corridor_length_m": { + "name": "unheated_corridor_length_m", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "epc_flat_details_epc_property_id_epc_property_id_fk": { + "name": "epc_flat_details_epc_property_id_epc_property_id_fk", + "tableFrom": "epc_flat_details", + "tableTo": "epc_property", + "columnsFrom": [ + "epc_property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "epc_flat_details_epc_property_id_unique": { + "name": "epc_flat_details_epc_property_id_unique", + "nullsNotDistinct": false, + "columns": [ + "epc_property_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.epc_floor_dimension": { + "name": "epc_floor_dimension", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "epc_building_part_id": { + "name": "epc_building_part_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "floor": { + "name": "floor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "room_height_m": { + "name": "room_height_m", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "total_floor_area_m2": { + "name": "total_floor_area_m2", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "party_wall_length_m": { + "name": "party_wall_length_m", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "heat_loss_perimeter_m": { + "name": "heat_loss_perimeter_m", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "floor_insulation": { + "name": "floor_insulation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "floor_construction": { + "name": "floor_construction", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "is_exposed_floor": { + "name": "is_exposed_floor", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_above_partially_heated_space": { + "name": "is_above_partially_heated_space", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "ix_epc_floor_dimension_building_part": { + "name": "ix_epc_floor_dimension_building_part", + "columns": [ + { + "expression": "epc_building_part_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "epc_floor_dimension_epc_building_part_id_epc_building_part_id_fk": { + "name": "epc_floor_dimension_epc_building_part_id_epc_building_part_id_fk", + "tableFrom": "epc_floor_dimension", + "tableTo": "epc_building_part", + "columnsFrom": [ + "epc_building_part_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.epc_main_heating_detail": { + "name": "epc_main_heating_detail", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "epc_property_id": { + "name": "epc_property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "has_fghrs": { + "name": "has_fghrs", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "main_fuel_type": { + "name": "main_fuel_type", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "heat_emitter_type": { + "name": "heat_emitter_type", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "emitter_temperature": { + "name": "emitter_temperature", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "main_heating_control": { + "name": "main_heating_control", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "fan_flue_present": { + "name": "fan_flue_present", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "boiler_flue_type": { + "name": "boiler_flue_type", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "boiler_ignition_type": { + "name": "boiler_ignition_type", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "central_heating_pump_age": { + "name": "central_heating_pump_age", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "central_heating_pump_age_str": { + "name": "central_heating_pump_age_str", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "main_heating_index_number": { + "name": "main_heating_index_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sap_main_heating_code": { + "name": "sap_main_heating_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "main_heating_number": { + "name": "main_heating_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "main_heating_category": { + "name": "main_heating_category", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "main_heating_fraction": { + "name": "main_heating_fraction", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "main_heating_data_source": { + "name": "main_heating_data_source", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "condensing": { + "name": "condensing", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "weather_compensator": { + "name": "weather_compensator", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "community_heating_boiler_fuel_type": { + "name": "community_heating_boiler_fuel_type", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "community_heating_chp_fraction": { + "name": "community_heating_chp_fraction", + "type": "real", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "ix_epc_main_heating_detail_epc_property": { + "name": "ix_epc_main_heating_detail_epc_property", + "columns": [ + { + "expression": "epc_property_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "epc_main_heating_detail_epc_property_id_epc_property_id_fk": { + "name": "epc_main_heating_detail_epc_property_id_epc_property_id_fk", + "tableFrom": "epc_main_heating_detail", + "tableTo": "epc_property", + "columnsFrom": [ + "epc_property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.epc_photovoltaic_array": { + "name": "epc_photovoltaic_array", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "epc_property_id": { + "name": "epc_property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "array_index": { + "name": "array_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "peak_power": { + "name": "peak_power", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "pitch": { + "name": "pitch", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "overshading": { + "name": "overshading", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "orientation": { + "name": "orientation", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "epc_photovoltaic_array_epc_property_id_epc_property_id_fk": { + "name": "epc_photovoltaic_array_epc_property_id_epc_property_id_fk", + "tableFrom": "epc_photovoltaic_array", + "tableTo": "epc_property", + "columnsFrom": [ + "epc_property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.epc_property": { + "name": "epc_property", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "uploaded_file_id": { + "name": "uploaded_file_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'lodged'" + }, + "uprn": { + "name": "uprn", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "uprn_source": { + "name": "uprn_source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "report_reference": { + "name": "report_reference", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "report_type": { + "name": "report_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assessment_type": { + "name": "assessment_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sap_version": { + "name": "sap_version", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "schema_type": { + "name": "schema_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "schema_versions_original": { + "name": "schema_versions_original", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "calculation_software_version": { + "name": "calculation_software_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_line_1": { + "name": "address_line_1", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "address_line_2": { + "name": "address_line_2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "post_town": { + "name": "post_town", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "postcode": { + "name": "postcode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region_code": { + "name": "region_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "country_code": { + "name": "country_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "language_code": { + "name": "language_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dwelling_type": { + "name": "dwelling_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "property_type": { + "name": "property_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "built_form": { + "name": "built_form", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenure": { + "name": "tenure", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "transaction_type": { + "name": "transaction_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "inspection_date": { + "name": "inspection_date", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "completion_date": { + "name": "completion_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "registration_date": { + "name": "registration_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "total_floor_area_m2": { + "name": "total_floor_area_m2", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "measurement_type": { + "name": "measurement_type", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "solar_water_heating": { + "name": "solar_water_heating", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "has_hot_water_cylinder": { + "name": "has_hot_water_cylinder", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "has_fixed_air_conditioning": { + "name": "has_fixed_air_conditioning", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "has_conservatory": { + "name": "has_conservatory", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "has_heated_separate_conservatory": { + "name": "has_heated_separate_conservatory", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "conservatory_type": { + "name": "conservatory_type", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "conservatory_floor_area_m2": { + "name": "conservatory_floor_area_m2", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "conservatory_glazed_perimeter_m": { + "name": "conservatory_glazed_perimeter_m", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "conservatory_double_glazed": { + "name": "conservatory_double_glazed", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "conservatory_thermally_separated": { + "name": "conservatory_thermally_separated", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "conservatory_room_height_storeys": { + "name": "conservatory_room_height_storeys", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "door_count": { + "name": "door_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "wet_rooms_count": { + "name": "wet_rooms_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "extensions_count": { + "name": "extensions_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "heated_rooms_count": { + "name": "heated_rooms_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "open_chimneys_count": { + "name": "open_chimneys_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "habitable_rooms_count": { + "name": "habitable_rooms_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "insulated_door_count": { + "name": "insulated_door_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "cfl_fixed_lighting_bulbs_count": { + "name": "cfl_fixed_lighting_bulbs_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "led_fixed_lighting_bulbs_count": { + "name": "led_fixed_lighting_bulbs_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "incandescent_fixed_lighting_bulbs_count": { + "name": "incandescent_fixed_lighting_bulbs_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "blocked_chimneys_count": { + "name": "blocked_chimneys_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "draughtproofed_door_count": { + "name": "draughtproofed_door_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "energy_rating_average": { + "name": "energy_rating_average", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "low_energy_fixed_lighting_bulbs_count": { + "name": "low_energy_fixed_lighting_bulbs_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "fixed_lighting_outlets_count": { + "name": "fixed_lighting_outlets_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "low_energy_fixed_lighting_outlets_count": { + "name": "low_energy_fixed_lighting_outlets_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "number_of_storeys": { + "name": "number_of_storeys", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "any_unheated_rooms": { + "name": "any_unheated_rooms", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "hydro": { + "name": "hydro", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "photovoltaic_array": { + "name": "photovoltaic_array", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "waste_water_heat_recovery": { + "name": "waste_water_heat_recovery", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pressure_test": { + "name": "pressure_test", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "pressure_test_certificate_number": { + "name": "pressure_test_certificate_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "percent_draughtproofed": { + "name": "percent_draughtproofed", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "insulated_door_u_value": { + "name": "insulated_door_u_value", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "multiple_glazed_proportion": { + "name": "multiple_glazed_proportion", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "windows_transmission_u_value": { + "name": "windows_transmission_u_value", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "windows_transmission_data_source": { + "name": "windows_transmission_data_source", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "windows_transmission_solar_transmittance": { + "name": "windows_transmission_solar_transmittance", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "energy_gas_connection_available": { + "name": "energy_gas_connection_available", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "energy_meter_type": { + "name": "energy_meter_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "energy_pv_battery_count": { + "name": "energy_pv_battery_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "energy_wind_turbines_count": { + "name": "energy_wind_turbines_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "energy_gas_smart_meter_present": { + "name": "energy_gas_smart_meter_present", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "energy_is_dwelling_export_capable": { + "name": "energy_is_dwelling_export_capable", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "energy_wind_turbines_terrain_type": { + "name": "energy_wind_turbines_terrain_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "energy_electricity_smart_meter_present": { + "name": "energy_electricity_smart_meter_present", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "energy_pv_connection": { + "name": "energy_pv_connection", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "energy_pv_percent_roof_area": { + "name": "energy_pv_percent_roof_area", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "energy_pv_battery_capacity": { + "name": "energy_pv_battery_capacity", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "energy_wind_turbine_hub_height": { + "name": "energy_wind_turbine_hub_height", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "energy_wind_turbine_rotor_diameter": { + "name": "energy_wind_turbine_rotor_diameter", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "energy_pv_diverter_present": { + "name": "energy_pv_diverter_present", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "heating_cylinder_size": { + "name": "heating_cylinder_size", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "heating_water_heating_code": { + "name": "heating_water_heating_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "heating_water_heating_fuel": { + "name": "heating_water_heating_fuel", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "heating_immersion_heating_type": { + "name": "heating_immersion_heating_type", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "heating_cylinder_insulation_type": { + "name": "heating_cylinder_insulation_type", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "heating_cylinder_thermostat": { + "name": "heating_cylinder_thermostat", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "heating_secondary_fuel_type": { + "name": "heating_secondary_fuel_type", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "heating_secondary_heating_type": { + "name": "heating_secondary_heating_type", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "heating_cylinder_insulation_thickness_mm": { + "name": "heating_cylinder_insulation_thickness_mm", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "heating_cylinder_volume_measured_l": { + "name": "heating_cylinder_volume_measured_l", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "heating_cylinder_heat_loss": { + "name": "heating_cylinder_heat_loss", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "heating_wwhrs_index_number_1": { + "name": "heating_wwhrs_index_number_1", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "heating_wwhrs_index_number_2": { + "name": "heating_wwhrs_index_number_2", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "heating_shower_outlet_type": { + "name": "heating_shower_outlet_type", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "heating_shower_wwhrs": { + "name": "heating_shower_wwhrs", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ventilation_type": { + "name": "ventilation_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ventilation_draught_lobby": { + "name": "ventilation_draught_lobby", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ventilation_pressure_test": { + "name": "ventilation_pressure_test", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ventilation_open_flues_count": { + "name": "ventilation_open_flues_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ventilation_closed_flues_count": { + "name": "ventilation_closed_flues_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ventilation_boiler_flues_count": { + "name": "ventilation_boiler_flues_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ventilation_other_flues_count": { + "name": "ventilation_other_flues_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ventilation_extract_fans_count": { + "name": "ventilation_extract_fans_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ventilation_passive_vents_count": { + "name": "ventilation_passive_vents_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ventilation_flueless_gas_fires_count": { + "name": "ventilation_flueless_gas_fires_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ventilation_in_pcdf_database": { + "name": "ventilation_in_pcdf_database", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "mechanical_ventilation": { + "name": "mechanical_ventilation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "mechanical_vent_duct_type": { + "name": "mechanical_vent_duct_type", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "mechanical_vent_duct_placement": { + "name": "mechanical_vent_duct_placement", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "mechanical_vent_duct_insulation": { + "name": "mechanical_vent_duct_insulation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "mechanical_ventilation_index_number": { + "name": "mechanical_ventilation_index_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "mechanical_vent_measured_installation": { + "name": "mechanical_vent_measured_installation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mechanical_vent_duct_insulation_level": { + "name": "mechanical_vent_duct_insulation_level", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "addendum_stone_walls": { + "name": "addendum_stone_walls", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "addendum_system_build": { + "name": "addendum_system_build", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "addendum_numbers": { + "name": "addendum_numbers", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "heating_number_baths": { + "name": "heating_number_baths", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "heating_number_baths_wwhrs": { + "name": "heating_number_baths_wwhrs", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "heating_electric_shower_count": { + "name": "heating_electric_shower_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "heating_mixer_shower_count": { + "name": "heating_mixer_shower_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ventilation_present": { + "name": "ventilation_present", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "ventilation_sheltered_sides": { + "name": "ventilation_sheltered_sides", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ventilation_has_suspended_timber_floor": { + "name": "ventilation_has_suspended_timber_floor", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ventilation_suspended_timber_floor_sealed": { + "name": "ventilation_suspended_timber_floor_sealed", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ventilation_has_draught_lobby": { + "name": "ventilation_has_draught_lobby", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ventilation_air_permeability_ap4_m3_h_m2": { + "name": "ventilation_air_permeability_ap4_m3_h_m2", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "ventilation_air_permeability_ap50_m3_h_m2": { + "name": "ventilation_air_permeability_ap50_m3_h_m2", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "ventilation_mechanical_ventilation_kind": { + "name": "ventilation_mechanical_ventilation_kind", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "uq_epc_property_property_portfolio": { + "name": "uq_epc_property_property_portfolio", + "columns": [ + { + "expression": "property_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "portfolio_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ix_epc_property_property_source": { + "name": "ix_epc_property_property_source", + "columns": [ + { + "expression": "property_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "epc_property_property_id_property_id_fk": { + "name": "epc_property_property_id_property_id_fk", + "tableFrom": "epc_property", + "tableTo": "property", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "epc_property_portfolio_id_portfolio_id_fk": { + "name": "epc_property_portfolio_id_portfolio_id_fk", + "tableFrom": "epc_property", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "epc_property_uploaded_file_id_uploaded_files_id_fk": { + "name": "epc_property_uploaded_file_id_uploaded_files_id_fk", + "tableFrom": "epc_property", + "tableTo": "uploaded_files", + "columnsFrom": [ + "uploaded_file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "epc_property_uploaded_file_id_unique": { + "name": "epc_property_uploaded_file_id_unique", + "nullsNotDistinct": false, + "columns": [ + "uploaded_file_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.epc_property_energy_performance": { + "name": "epc_property_energy_performance", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "epc_property_id": { + "name": "epc_property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "energy_rating_current": { + "name": "energy_rating_current", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "energy_consumption_current": { + "name": "energy_consumption_current", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "environmental_impact_current": { + "name": "environmental_impact_current", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "heating_cost_current": { + "name": "heating_cost_current", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "lighting_cost_current": { + "name": "lighting_cost_current", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "hot_water_cost_current": { + "name": "hot_water_cost_current", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "co2_emissions_current": { + "name": "co2_emissions_current", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "co2_emissions_current_per_floor_area": { + "name": "co2_emissions_current_per_floor_area", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "current_energy_efficiency_band": { + "name": "current_energy_efficiency_band", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "energy_rating_potential": { + "name": "energy_rating_potential", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "energy_consumption_potential": { + "name": "energy_consumption_potential", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "environmental_impact_potential": { + "name": "environmental_impact_potential", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "heating_cost_potential": { + "name": "heating_cost_potential", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "lighting_cost_potential": { + "name": "lighting_cost_potential", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "hot_water_cost_potential": { + "name": "hot_water_cost_potential", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "co2_emissions_potential": { + "name": "co2_emissions_potential", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "potential_energy_efficiency_band": { + "name": "potential_energy_efficiency_band", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "epc_property_energy_performance_epc_property_id_epc_property_id_fk": { + "name": "epc_property_energy_performance_epc_property_id_epc_property_id_fk", + "tableFrom": "epc_property_energy_performance", + "tableTo": "epc_property", + "columnsFrom": [ + "epc_property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "epc_property_energy_performance_epc_property_id_unique": { + "name": "epc_property_energy_performance_epc_property_id_unique", + "nullsNotDistinct": false, + "columns": [ + "epc_property_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.epc_renewable_heat_incentive": { + "name": "epc_renewable_heat_incentive", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "epc_property_id": { + "name": "epc_property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "space_heating_kwh": { + "name": "space_heating_kwh", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "water_heating_kwh": { + "name": "water_heating_kwh", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "impact_of_loft_insulation_kwh": { + "name": "impact_of_loft_insulation_kwh", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "impact_of_cavity_insulation_kwh": { + "name": "impact_of_cavity_insulation_kwh", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "impact_of_solid_wall_insulation_kwh": { + "name": "impact_of_solid_wall_insulation_kwh", + "type": "real", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "epc_renewable_heat_incentive_epc_property_id_epc_property_id_fk": { + "name": "epc_renewable_heat_incentive_epc_property_id_epc_property_id_fk", + "tableFrom": "epc_renewable_heat_incentive", + "tableTo": "epc_property", + "columnsFrom": [ + "epc_property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "epc_renewable_heat_incentive_epc_property_id_unique": { + "name": "epc_renewable_heat_incentive_epc_property_id_unique", + "nullsNotDistinct": false, + "columns": [ + "epc_property_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.epc_roof_window": { + "name": "epc_roof_window", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "epc_property_id": { + "name": "epc_property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "roof_window_index": { + "name": "roof_window_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "area_m2": { + "name": "area_m2", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "u_value_raw": { + "name": "u_value_raw", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "orientation": { + "name": "orientation", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "pitch_deg": { + "name": "pitch_deg", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "g_perpendicular": { + "name": "g_perpendicular", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "frame_factor": { + "name": "frame_factor", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "glazing_type": { + "name": "glazing_type", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "window_location": { + "name": "window_location", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "uq_epc_roof_window_property_index": { + "name": "uq_epc_roof_window_property_index", + "columns": [ + { + "expression": "epc_property_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "roof_window_index", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "epc_roof_window_epc_property_id_epc_property_id_fk": { + "name": "epc_roof_window_epc_property_id_epc_property_id_fk", + "tableFrom": "epc_roof_window", + "tableTo": "epc_property", + "columnsFrom": [ + "epc_property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.epc_room_in_roof": { + "name": "epc_room_in_roof", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "epc_building_part_id": { + "name": "epc_building_part_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "floor_area": { + "name": "floor_area", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "construction_age_band": { + "name": "construction_age_band", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "common_wall_length_m": { + "name": "common_wall_length_m", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "common_wall_height_m": { + "name": "common_wall_height_m", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "gable_1_length_m": { + "name": "gable_1_length_m", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "gable_1_height_m": { + "name": "gable_1_height_m", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "gable_2_length_m": { + "name": "gable_2_length_m", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "gable_2_height_m": { + "name": "gable_2_height_m", + "type": "double precision", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "epc_room_in_roof_epc_building_part_id_epc_building_part_id_fk": { + "name": "epc_room_in_roof_epc_building_part_id_epc_building_part_id_fk", + "tableFrom": "epc_room_in_roof", + "tableTo": "epc_building_part", + "columnsFrom": [ + "epc_building_part_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "epc_room_in_roof_epc_building_part_id_unique": { + "name": "epc_room_in_roof_epc_building_part_id_unique", + "nullsNotDistinct": false, + "columns": [ + "epc_building_part_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.epc_room_in_roof_surface": { + "name": "epc_room_in_roof_surface", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "epc_room_in_roof_id": { + "name": "epc_room_in_roof_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "surface_index": { + "name": "surface_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "area_m2": { + "name": "area_m2", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "insulation_thickness_mm": { + "name": "insulation_thickness_mm", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "insulation_type": { + "name": "insulation_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "u_value": { + "name": "u_value", + "type": "double precision", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "uq_epc_room_in_roof_surface_index": { + "name": "uq_epc_room_in_roof_surface_index", + "columns": [ + { + "expression": "epc_room_in_roof_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "surface_index", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "epc_room_in_roof_surface_epc_room_in_roof_id_epc_room_in_roof_id_fk": { + "name": "epc_room_in_roof_surface_epc_room_in_roof_id_epc_room_in_roof_id_fk", + "tableFrom": "epc_room_in_roof_surface", + "tableTo": "epc_room_in_roof", + "columnsFrom": [ + "epc_room_in_roof_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.epc_window": { + "name": "epc_window", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "epc_property_id": { + "name": "epc_property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "glazing_gap": { + "name": "glazing_gap", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "orientation": { + "name": "orientation", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "window_type": { + "name": "window_type", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "glazing_type": { + "name": "glazing_type", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "window_width": { + "name": "window_width", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "window_height": { + "name": "window_height", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "draught_proofed": { + "name": "draught_proofed", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "window_location": { + "name": "window_location", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "window_wall_type": { + "name": "window_wall_type", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "permanent_shutters_present": { + "name": "permanent_shutters_present", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "frame_material": { + "name": "frame_material", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "frame_factor": { + "name": "frame_factor", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "permanent_shutters_insulated": { + "name": "permanent_shutters_insulated", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "transmission_u_value": { + "name": "transmission_u_value", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "transmission_data_source": { + "name": "transmission_data_source", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "transmission_solar_transmittance": { + "name": "transmission_solar_transmittance", + "type": "real", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "epc_window_epc_property_id_epc_property_id_fk": { + "name": "epc_window_epc_property_id_epc_property_id_fk", + "tableFrom": "epc_window", + "tableTo": "epc_property", + "columnsFrom": [ + "epc_property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.non_intrusive_survey": { + "name": "non_intrusive_survey", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "uprn": { + "name": "uprn", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "survey_date": { + "name": "survey_date", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "surveyor": { + "name": "surveyor", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.non_intrusive_survey_notes": { + "name": "non_intrusive_survey_notes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "survey_id": { + "name": "survey_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "non_intrusive_survey_notes_survey_id_non_intrusive_survey_id_fk": { + "name": "non_intrusive_survey_notes_survey_id_non_intrusive_survey_id_fk", + "tableFrom": "non_intrusive_survey_notes", + "tableTo": "non_intrusive_survey", + "columnsFrom": [ + "survey_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.property": { + "name": "property", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "creation_status": { + "name": "creation_status", + "type": "creation_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "uprn": { + "name": "uprn", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "certificate_number": { + "name": "certificate_number", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "landlord_property_id": { + "name": "landlord_property_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "building_reference_number": { + "name": "building_reference_number", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "address": { + "name": "address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postcode": { + "name": "postcode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_inputted_address": { + "name": "user_inputted_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_inputted_postcode": { + "name": "user_inputted_postcode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lexiscore": { + "name": "lexiscore", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "has_pre_condition_report": { + "name": "has_pre_condition_report", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "has_recommendations": { + "name": "has_recommendations", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "property_type": { + "name": "property_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "built_form": { + "name": "built_form", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "local_authority": { + "name": "local_authority", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "constituency": { + "name": "constituency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "number_of_rooms": { + "name": "number_of_rooms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "year_built": { + "name": "year_built", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenure": { + "name": "tenure", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "current_epc_rating": { + "name": "current_epc_rating", + "type": "epc", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "current_sap_points": { + "name": "current_sap_points", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "current_valuation": { + "name": "current_valuation", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "installed_measures_sap_point_adjustment": { + "name": "installed_measures_sap_point_adjustment", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "is_sap_points_adjusted_for_installed_measures": { + "name": "is_sap_points_adjusted_for_installed_measures", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "original_sap_points": { + "name": "original_sap_points", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "lodged_sap_points": { + "name": "lodged_sap_points", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "lodged_epc_rating": { + "name": "lodged_epc_rating", + "type": "epc", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "marked_for_deletion": { + "name": "marked_for_deletion", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "uq_property_portfolio_uprn": { + "name": "uq_property_portfolio_uprn", + "columns": [ + { + "expression": "portfolio_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "uprn", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"property\".\"uprn\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "ix_property_portfolio": { + "name": "ix_property_portfolio", + "columns": [ + { + "expression": "portfolio_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "property_portfolio_id_portfolio_id_fk": { + "name": "property_portfolio_id_portfolio_id_fk", + "tableFrom": "property", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.property_baseline_performance": { + "name": "property_baseline_performance", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "lodged_sap_score": { + "name": "lodged_sap_score", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "lodged_epc_band": { + "name": "lodged_epc_band", + "type": "epc", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "lodged_co2_emissions_t_per_yr": { + "name": "lodged_co2_emissions_t_per_yr", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "lodged_primary_energy_intensity_kwh_per_m2_yr": { + "name": "lodged_primary_energy_intensity_kwh_per_m2_yr", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "effective_sap_score": { + "name": "effective_sap_score", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "effective_epc_band": { + "name": "effective_epc_band", + "type": "epc", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "effective_co2_emissions_t_per_yr": { + "name": "effective_co2_emissions_t_per_yr", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "effective_primary_energy_intensity_kwh_per_m2_yr": { + "name": "effective_primary_energy_intensity_kwh_per_m2_yr", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "rebaseline_reason": { + "name": "rebaseline_reason", + "type": "rebaseline_reason", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "space_heating_kwh": { + "name": "space_heating_kwh", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "water_heating_kwh": { + "name": "water_heating_kwh", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "fuel_rates_period": { + "name": "fuel_rates_period", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "heating_kwh": { + "name": "heating_kwh", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "heating_cost_gbp": { + "name": "heating_cost_gbp", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "hot_water_kwh": { + "name": "hot_water_kwh", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "hot_water_cost_gbp": { + "name": "hot_water_cost_gbp", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "lighting_kwh": { + "name": "lighting_kwh", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "lighting_cost_gbp": { + "name": "lighting_cost_gbp", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "appliances_kwh": { + "name": "appliances_kwh", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "appliances_cost_gbp": { + "name": "appliances_cost_gbp", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "cooking_kwh": { + "name": "cooking_kwh", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "cooking_cost_gbp": { + "name": "cooking_cost_gbp", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "pumps_fans_kwh": { + "name": "pumps_fans_kwh", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "pumps_fans_cost_gbp": { + "name": "pumps_fans_cost_gbp", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "cooling_kwh": { + "name": "cooling_kwh", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "cooling_cost_gbp": { + "name": "cooling_cost_gbp", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "standing_charges_gbp": { + "name": "standing_charges_gbp", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "seg_credit_gbp": { + "name": "seg_credit_gbp", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "total_annual_bill_gbp": { + "name": "total_annual_bill_gbp", + "type": "real", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "property_baseline_performance_property_id_property_id_fk": { + "name": "property_baseline_performance_property_id_property_id_fk", + "tableFrom": "property_baseline_performance", + "tableTo": "property", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "property_baseline_performance_property_id_unique": { + "name": "property_baseline_performance_property_id_unique", + "nullsNotDistinct": false, + "columns": [ + "property_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.property_details_epc": { + "name": "property_details_epc", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "full_address": { + "name": "full_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lodgement_date": { + "name": "lodgement_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "is_expired": { + "name": "is_expired", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "total_floor_area": { + "name": "total_floor_area", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "walls": { + "name": "walls", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "walls_rating": { + "name": "walls_rating", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "roof": { + "name": "roof", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "roof_rating": { + "name": "roof_rating", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "floor": { + "name": "floor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "floor_rating": { + "name": "floor_rating", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "windows": { + "name": "windows", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "windows_rating": { + "name": "windows_rating", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "heating": { + "name": "heating", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "heating_rating": { + "name": "heating_rating", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "heating_controls": { + "name": "heating_controls", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "heating_controls_rating": { + "name": "heating_controls_rating", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "hot_water": { + "name": "hot_water", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "hot_water_rating": { + "name": "hot_water_rating", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "lighting": { + "name": "lighting", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lighting_rating": { + "name": "lighting_rating", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "mainfuel": { + "name": "mainfuel", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ventilation": { + "name": "ventilation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "solar_pv": { + "name": "solar_pv", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "solar_hot_water": { + "name": "solar_hot_water", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "wind_turbine": { + "name": "wind_turbine", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "floor_height": { + "name": "floor_height", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "number_heated_rooms": { + "name": "number_heated_rooms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "heat_loss_corridor": { + "name": "heat_loss_corridor", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "unheated_corridor_length": { + "name": "unheated_corridor_length", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "number_of_open_fireplaces": { + "name": "number_of_open_fireplaces", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "number_of_extensions": { + "name": "number_of_extensions", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "number_of_storeys": { + "name": "number_of_storeys", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "mains_gas": { + "name": "mains_gas", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "energy_tariff": { + "name": "energy_tariff", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "primary_energy_consumption": { + "name": "primary_energy_consumption", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "co2_emissions": { + "name": "co2_emissions", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "current_energy_demand": { + "name": "current_energy_demand", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "current_energy_demand_heating_hotwater": { + "name": "current_energy_demand_heating_hotwater", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "estimated": { + "name": "estimated", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "sap_05_overwritten": { + "name": "sap_05_overwritten", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "sap_05_score": { + "name": "sap_05_score", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "sap_05_epc_rating": { + "name": "sap_05_epc_rating", + "type": "epc", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "heating_cost_current": { + "name": "heating_cost_current", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "hot_water_cost_current": { + "name": "hot_water_cost_current", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "lighting_cost_current": { + "name": "lighting_cost_current", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "appliances_cost_current": { + "name": "appliances_cost_current", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "gas_standing_charge": { + "name": "gas_standing_charge", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "electricity_standing_charge": { + "name": "electricity_standing_charge", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "original_co2_emissions": { + "name": "original_co2_emissions", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "original_primary_energy_consumption": { + "name": "original_primary_energy_consumption", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "original_current_energy_demand": { + "name": "original_current_energy_demand", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "original_current_energy_demand_heating_hotwater": { + "name": "original_current_energy_demand_heating_hotwater", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "installed_measures_co2_adjustment": { + "name": "installed_measures_co2_adjustment", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "installed_measures_energy_demand_adjustment": { + "name": "installed_measures_energy_demand_adjustment", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "installed_measures_total_energy_bill_adjustment": { + "name": "installed_measures_total_energy_bill_adjustment", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "installed_measures_heat_demand_adjustment": { + "name": "installed_measures_heat_demand_adjustment", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "is_epc_adjusted_for_installed_measures": { + "name": "is_epc_adjusted_for_installed_measures", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "lodged_co2_emissions": { + "name": "lodged_co2_emissions", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "lodged_heat_demand": { + "name": "lodged_heat_demand", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "has_been_remodelled": { + "name": "has_been_remodelled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "environment_impact_current": { + "name": "environment_impact_current", + "type": "real", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "uq_property_details_epc_property_portfolio": { + "name": "uq_property_details_epc_property_portfolio", + "columns": [ + { + "expression": "property_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "portfolio_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "property_details_epc_property_id_property_id_fk": { + "name": "property_details_epc_property_id_property_id_fk", + "tableFrom": "property_details_epc", + "tableTo": "property", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "property_details_epc_portfolio_id_portfolio_id_fk": { + "name": "property_details_epc_portfolio_id_portfolio_id_fk", + "tableFrom": "property_details_epc", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.property_details_meter": { + "name": "property_details_meter", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "uprn": { + "name": "uprn", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "energy_supplier": { + "name": "energy_supplier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gas_supplier": { + "name": "gas_supplier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "meter_reading_total": { + "name": "meter_reading_total", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "meter_reading_electricity": { + "name": "meter_reading_electricity", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "meter_reading_gas": { + "name": "meter_reading_gas", + "type": "real", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.property_details_spatial": { + "name": "property_details_spatial", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "uprn": { + "name": "uprn", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "x_coordinate": { + "name": "x_coordinate", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "y_coordinate": { + "name": "y_coordinate", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "latitude": { + "name": "latitude", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "longitude": { + "name": "longitude", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "conservation_status": { + "name": "conservation_status", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "is_listed_building": { + "name": "is_listed_building", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "is_heritage_building": { + "name": "is_heritage_building", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "uq_property_details_spatial_uprn": { + "name": "uq_property_details_spatial_uprn", + "columns": [ + { + "expression": "uprn", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.property_targets": { + "name": "property_targets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "epc": { + "name": "epc", + "type": "epc", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "heat_demand": { + "name": "heat_demand", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "property_targets_property_id_property_id_fk": { + "name": "property_targets_property_id_property_id_fk", + "tableFrom": "property_targets", + "tableTo": "property", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "property_targets_portfolio_id_portfolio_id_fk": { + "name": "property_targets_portfolio_id_portfolio_id_fk", + "tableFrom": "property_targets", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.installed_measure": { + "name": "installed_measure", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "uprn": { + "name": "uprn", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "measure_type": { + "name": "measure_type", + "type": "measure_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "installed_at": { + "name": "installed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "sap_points": { + "name": "sap_points", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "carbon_savings": { + "name": "carbon_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "kwh_savings": { + "name": "kwh_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "bill_savings": { + "name": "bill_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "heat_demand_savings": { + "name": "heat_demand_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + } + }, + "indexes": { + "idx_installed_measure_uprn": { + "name": "idx_installed_measure_uprn", + "columns": [ + { + "expression": "uprn", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_installed_measure_uprn_active": { + "name": "idx_installed_measure_uprn_active", + "columns": [ + { + "expression": "uprn", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"installed_measure\".\"is_active\" = true", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_installed_measure_measure_type": { + "name": "idx_installed_measure_measure_type", + "columns": [ + { + "expression": "measure_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_installed_measure_uprn_measure": { + "name": "idx_installed_measure_uprn_measure", + "columns": [ + { + "expression": "uprn", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "measure_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"installed_measure\".\"is_active\" = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.plan": { + "name": "plan", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "scenario_id": { + "name": "scenario_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "valuation_increase_lower_bound": { + "name": "valuation_increase_lower_bound", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "valuation_increase_upper_bound": { + "name": "valuation_increase_upper_bound", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "valuation_increase_average": { + "name": "valuation_increase_average", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "post_sap_points": { + "name": "post_sap_points", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "post_epc_rating": { + "name": "post_epc_rating", + "type": "epc", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "post_co2_emissions": { + "name": "post_co2_emissions", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "co2_savings": { + "name": "co2_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "post_energy_bill": { + "name": "post_energy_bill", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "energy_bill_savings": { + "name": "energy_bill_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "post_energy_consumption": { + "name": "post_energy_consumption", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "energy_consumption_savings": { + "name": "energy_consumption_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "valuation_post_retrofit": { + "name": "valuation_post_retrofit", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "valuation_increase": { + "name": "valuation_increase", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "cost_of_works": { + "name": "cost_of_works", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "contingency_cost": { + "name": "contingency_cost", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "plan_type": { + "name": "plan_type", + "type": "plan_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_plan_portfolio_scenario": { + "name": "idx_plan_portfolio_scenario", + "columns": [ + { + "expression": "portfolio_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scenario_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_plan_latest_per_property": { + "name": "idx_plan_latest_per_property", + "columns": [ + { + "expression": "portfolio_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scenario_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "property_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_plan_property_default_latest": { + "name": "idx_plan_property_default_latest", + "columns": [ + { + "expression": "property_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "portfolio_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "where": "is_default = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "plan_portfolio_id_portfolio_id_fk": { + "name": "plan_portfolio_id_portfolio_id_fk", + "tableFrom": "plan", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "plan_property_id_property_id_fk": { + "name": "plan_property_id_property_id_fk", + "tableFrom": "plan", + "tableTo": "property", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "plan_scenario_id_scenario_id_fk": { + "name": "plan_scenario_id_scenario_id_fk", + "tableFrom": "plan", + "tableTo": "scenario", + "columnsFrom": [ + "scenario_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.plan_recommendations": { + "name": "plan_recommendations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "plan_id": { + "name": "plan_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "recommendation_id": { + "name": "recommendation_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "idx_plan_recommendations_plan_id": { + "name": "idx_plan_recommendations_plan_id", + "columns": [ + { + "expression": "plan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_plan_recommendations_plan_rec": { + "name": "idx_plan_recommendations_plan_rec", + "columns": [ + { + "expression": "plan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "recommendation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_plan_recommendations_recommendation_id": { + "name": "idx_plan_recommendations_recommendation_id", + "columns": [ + { + "expression": "recommendation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "plan_recommendations_plan_id_plan_id_fk": { + "name": "plan_recommendations_plan_id_plan_id_fk", + "tableFrom": "plan_recommendations", + "tableTo": "plan", + "columnsFrom": [ + "plan_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "plan_recommendations_recommendation_id_recommendation_id_fk": { + "name": "plan_recommendations_recommendation_id_recommendation_id_fk", + "tableFrom": "plan_recommendations", + "tableTo": "recommendation", + "columnsFrom": [ + "recommendation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recommendation": { + "name": "recommendation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "property_id": { + "name": "property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "plan_id": { + "name": "plan_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "material_id": { + "name": "material_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "material_quantity": { + "name": "material_quantity", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "material_quantity_unit": { + "name": "material_quantity_unit", + "type": "unit_quantity", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "material_depth": { + "name": "material_depth", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "measure_type": { + "name": "measure_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "estimated_cost": { + "name": "estimated_cost", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "contingency_cost": { + "name": "contingency_cost", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "default": { + "name": "default", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "starting_u_value": { + "name": "starting_u_value", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "new_u_value": { + "name": "new_u_value", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "sap_points": { + "name": "sap_points", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "heat_demand": { + "name": "heat_demand", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "kwh_savings": { + "name": "kwh_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "co2_equivalent_savings": { + "name": "co2_equivalent_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "energy_savings": { + "name": "energy_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "energy_cost_savings": { + "name": "energy_cost_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "property_valuation_increase": { + "name": "property_valuation_increase", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "rental_yield_increase": { + "name": "rental_yield_increase", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "total_work_hours": { + "name": "total_work_hours", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "labour_days": { + "name": "labour_days", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "already_installed": { + "name": "already_installed", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": { + "recommendation_property_id_idx": { + "name": "recommendation_property_id_idx", + "columns": [ + { + "expression": "property_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_recommendation_plan_id": { + "name": "idx_recommendation_plan_id", + "columns": [ + { + "expression": "plan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_recommendation_active_defaults": { + "name": "idx_recommendation_active_defaults", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"recommendation\".\"default\" = true AND \"recommendation\".\"already_installed\" = false", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_recommendation_active_id_property": { + "name": "idx_recommendation_active_id_property", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "property_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"recommendation\".\"default\" = true AND \"recommendation\".\"already_installed\" = false", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_recommendation_plan_default": { + "name": "idx_recommendation_plan_default", + "columns": [ + { + "expression": "plan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "measure_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "property_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "estimated_cost", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"recommendation\".\"default\" = true AND \"recommendation\".\"already_installed\" = false", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_recommendation_material_id": { + "name": "idx_recommendation_material_id", + "columns": [ + { + "expression": "material_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recommendation_property_id_property_id_fk": { + "name": "recommendation_property_id_property_id_fk", + "tableFrom": "recommendation", + "tableTo": "property", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "recommendation_plan_id_plan_id_fk": { + "name": "recommendation_plan_id_plan_id_fk", + "tableFrom": "recommendation", + "tableTo": "plan", + "columnsFrom": [ + "plan_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "recommendation_material_id_material_id_fk": { + "name": "recommendation_material_id_material_id_fk", + "tableFrom": "recommendation", + "tableTo": "material", + "columnsFrom": [ + "material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recommendation_materials": { + "name": "recommendation_materials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "recommendation_id": { + "name": "recommendation_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "material_id": { + "name": "material_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "depth": { + "name": "depth", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "quantity": { + "name": "quantity", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "quantity_unit": { + "name": "quantity_unit", + "type": "unit_quantity", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "estimated_cost": { + "name": "estimated_cost", + "type": "real", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "recommendation_materials_recommendation_id_idx": { + "name": "recommendation_materials_recommendation_id_idx", + "columns": [ + { + "expression": "recommendation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recommendation_materials_recommendation_id_recommendation_id_fk": { + "name": "recommendation_materials_recommendation_id_recommendation_id_fk", + "tableFrom": "recommendation_materials", + "tableTo": "recommendation", + "columnsFrom": [ + "recommendation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "recommendation_materials_material_id_material_id_fk": { + "name": "recommendation_materials_material_id_material_id_fk", + "tableFrom": "recommendation_materials", + "tableTo": "material", + "columnsFrom": [ + "material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scenario": { + "name": "scenario", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "budget": { + "name": "budget", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "housing_type": { + "name": "housing_type", + "type": "housing_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "goal": { + "name": "goal", + "type": "goal", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "goal_value": { + "name": "goal_value", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ashp_cop": { + "name": "ashp_cop", + "type": "real", + "primaryKey": false, + "notNull": false, + "default": 2.8 + }, + "trigger_file_path": { + "name": "trigger_file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "already_installed_file_path": { + "name": "already_installed_file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "patches_file_path": { + "name": "patches_file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "non_invasive_recommendations_file_path": { + "name": "non_invasive_recommendations_file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "exclusions": { + "name": "exclusions", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "multi_plan": { + "name": "multi_plan", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "fabric_first": { + "name": "fabric_first", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cost": { + "name": "cost", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "contingency": { + "name": "contingency", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "funding": { + "name": "funding", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "total_work_hours": { + "name": "total_work_hours", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "energy_savings": { + "name": "energy_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "co2_equivalent_savings": { + "name": "co2_equivalent_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "energy_cost_savings": { + "name": "energy_cost_savings", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "property_valuation_increase": { + "name": "property_valuation_increase", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "labour_days": { + "name": "labour_days", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "epc_breakdown_pre_retrofit": { + "name": "epc_breakdown_pre_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "epc_breakdown_post_retrofit": { + "name": "epc_breakdown_post_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "number_of_properties": { + "name": "number_of_properties", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "n_units_to_retrofit": { + "name": "n_units_to_retrofit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "co2_per_unit_pre_retrofit": { + "name": "co2_per_unit_pre_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "co2_per_unit_post_retrofit": { + "name": "co2_per_unit_post_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "energy_bill_per_unit_pre_retrofit": { + "name": "energy_bill_per_unit_pre_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "energy_bill_per_unit_post_retrofit": { + "name": "energy_bill_per_unit_post_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "energy_consumption_per_unit_pre_retrofit": { + "name": "energy_consumption_per_unit_pre_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "energy_consumption_per_unit_post_retrofit": { + "name": "energy_consumption_per_unit_post_retrofit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "valuation_improvement_per_unit": { + "name": "valuation_improvement_per_unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cost_per_unit": { + "name": "cost_per_unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cost_per_co2_saved": { + "name": "cost_per_co2_saved", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cost_per_sap_point": { + "name": "cost_per_sap_point", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "valuation_return_on_investment": { + "name": "valuation_return_on_investment", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "scenario_portfolio_id_portfolio_id_fk": { + "name": "scenario_portfolio_id_portfolio_id_fk", + "tableFrom": "scenario", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.property_removal_requests": { + "name": "property_removal_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "hubspot_deal_id": { + "name": "hubspot_deal_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'removal'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "requested_by": { + "name": "requested_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "requested_at": { + "name": "requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "reviewed_by": { + "name": "reviewed_by", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "reviewed_at": { + "name": "reviewed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "original_batch": { + "name": "original_batch", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_removal_requests_deal_id": { + "name": "idx_removal_requests_deal_id", + "columns": [ + { + "expression": "hubspot_deal_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_removal_requests_portfolio_id": { + "name": "idx_removal_requests_portfolio_id", + "columns": [ + { + "expression": "portfolio_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "property_removal_requests_portfolio_id_portfolio_id_fk": { + "name": "property_removal_requests_portfolio_id_portfolio_id_fk", + "tableFrom": "property_removal_requests", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "property_removal_requests_requested_by_user_id_fk": { + "name": "property_removal_requests_requested_by_user_id_fk", + "tableFrom": "property_removal_requests", + "tableTo": "user", + "columnsFrom": [ + "requested_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "property_removal_requests_reviewed_by_user_id_fk": { + "name": "property_removal_requests_reviewed_by_user_id_fk", + "tableFrom": "property_removal_requests", + "tableTo": "user", + "columnsFrom": [ + "reviewed_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.solar": { + "name": "solar", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "longitude": { + "name": "longitude", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "latitude": { + "name": "latitude", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "uprn": { + "name": "uprn", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "google_api_response": { + "name": "google_api_response", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.solar_scenario": { + "name": "solar_scenario", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "solar_id": { + "name": "solar_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "scenario_type": { + "name": "scenario_type", + "type": "scenario_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "number_panels": { + "name": "number_panels", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "array_kwhp": { + "name": "array_kwhp", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "lifetime_dc_kwh": { + "name": "lifetime_dc_kwh", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "yearly_dc_kwh": { + "name": "yearly_dc_kwh", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "lifetime_ac_kwh": { + "name": "lifetime_ac_kwh", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "yearly_ac_kwh": { + "name": "yearly_ac_kwh", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "cost": { + "name": "cost", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "expected_payback_years": { + "name": "expected_payback_years", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "panelled_roof_area": { + "name": "panelled_roof_area", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "solar_scenario_solar_id_solar_id_fk": { + "name": "solar_scenario_solar_id_solar_id_fk", + "tableFrom": "solar_scenario", + "tableTo": "solar", + "columnsFrom": [ + "solar_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.survey_requests": { + "name": "survey_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "hubspot_deal_id": { + "name": "hubspot_deal_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "survey_type": { + "name": "survey_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "requested_by": { + "name": "requested_by", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "requested_at": { + "name": "requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "fulfilled_at": { + "name": "fulfilled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_survey_requests_deal_id": { + "name": "idx_survey_requests_deal_id", + "columns": [ + { + "expression": "hubspot_deal_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_survey_requests_portfolio_id": { + "name": "idx_survey_requests_portfolio_id", + "columns": [ + { + "expression": "portfolio_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "survey_requests_portfolio_id_portfolio_id_fk": { + "name": "survey_requests_portfolio_id_portfolio_id_fk", + "tableFrom": "survey_requests", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "survey_requests_requested_by_user_id_fk": { + "name": "survey_requests_requested_by_user_id_fk", + "tableFrom": "survey_requests", + "tableTo": "user", + "columnsFrom": [ + "requested_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.portfolio_tag": { + "name": "portfolio_tag", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "colour": { + "name": "colour", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "uq_portfolio_tag_portfolio_lower_name": { + "name": "uq_portfolio_tag_portfolio_lower_name", + "columns": [ + { + "expression": "portfolio_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"name\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "portfolio_tag_portfolio_id_portfolio_id_fk": { + "name": "portfolio_tag_portfolio_id_portfolio_id_fk", + "tableFrom": "portfolio_tag", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.property_tag": { + "name": "property_tag", + "schema": "", + "columns": { + "property_id": { + "name": "property_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "tag_id": { + "name": "tag_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ix_property_tag_tag": { + "name": "ix_property_tag_tag", + "columns": [ + { + "expression": "tag_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "property_tag_property_id_property_id_fk": { + "name": "property_tag_property_id_property_id_fk", + "tableFrom": "property_tag", + "tableTo": "property", + "columnsFrom": [ + "property_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "property_tag_tag_id_portfolio_tag_id_fk": { + "name": "property_tag_tag_id_portfolio_tag_id_fk", + "tableFrom": "property_tag", + "tableTo": "portfolio_tag", + "columnsFrom": [ + "tag_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "property_tag_property_id_tag_id_pk": { + "name": "property_tag_property_id_tag_id_pk", + "columns": [ + "property_id", + "tag_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sub_task": { + "name": "sub_task", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "task_id": { + "name": "task_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "job_started": { + "name": "job_started", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "job_completed": { + "name": "job_completed", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'In Progress'" + }, + "service": { + "name": "service", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "inputs": { + "name": "inputs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "outputs": { + "name": "outputs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cloud_logs_url": { + "name": "cloud_logs_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "sub_task_task_id_tasks_id_fk": { + "name": "sub_task_task_id_tasks_id_fk", + "tableFrom": "sub_task", + "tableTo": "tasks", + "columnsFrom": [ + "task_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tasks": { + "name": "tasks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "task_source": { + "name": "task_source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "job_started": { + "name": "job_started", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "job_completed": { + "name": "job_completed", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'In Progress'" + }, + "service": { + "name": "service", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "source", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "source_id": { + "name": "source_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "inputs": { + "name": "inputs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team": { + "name": "team", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "team_org_id_organisation_id_fk": { + "name": "team_org_id_organisation_id_fk", + "tableFrom": "team", + "tableTo": "organisation", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_members": { + "name": "team_members", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "team_members_user_id_user_id_fk": { + "name": "team_members_user_id_user_id_fk", + "tableFrom": "team_members", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "team_members_team_id_team_id_fk": { + "name": "team_members_team_id_team_id_fk", + "tableFrom": "team_members", + "tableTo": "team", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_portfolio_permissions": { + "name": "team_portfolio_permissions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "team_portfolio_permissions_team_id_team_id_fk": { + "name": "team_portfolio_permissions_team_id_team_id_fk", + "tableFrom": "team_portfolio_permissions", + "tableTo": "team", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "team_portfolio_permissions_portfolio_id_portfolio_id_fk": { + "name": "team_portfolio_permissions_portfolio_id_portfolio_id_fk", + "tableFrom": "team_portfolio_permissions", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.uploaded_files": { + "name": "uploaded_files", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "s3_file_bucket": { + "name": "s3_file_bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "s3_file_key": { + "name": "s3_file_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "s3_upload_timestamp": { + "name": "s3_upload_timestamp", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "landlord_property_id": { + "name": "landlord_property_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uprn": { + "name": "uprn", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "hubspot_deal_id": { + "name": "hubspot_deal_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "hubspot_listing_id": { + "name": "hubspot_listing_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "file_type": { + "name": "file_type", + "type": "file_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "file_source": { + "name": "file_source", + "type": "file_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "measure_name": { + "name": "measure_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploaded_by": { + "name": "uploaded_by", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "work_order_id": { + "name": "work_order_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "project_workstream_evidence_requirement_id": { + "name": "project_workstream_evidence_requirement_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "uploaded_files_uploaded_by_user_id_fk": { + "name": "uploaded_files_uploaded_by_user_id_fk", + "tableFrom": "uploaded_files", + "tableTo": "user", + "columnsFrom": [ + "uploaded_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "uploaded_files_work_order_id_work_order_id_fk": { + "name": "uploaded_files_work_order_id_work_order_id_fk", + "tableFrom": "uploaded_files", + "tableTo": "work_order", + "columnsFrom": [ + "work_order_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "uploaded_files_project_workstream_evidence_requirement_id_project_workstream_evidence_requirement_id_fk": { + "name": "uploaded_files_project_workstream_evidence_requirement_id_project_workstream_evidence_requirement_id_fk", + "tableFrom": "uploaded_files", + "tableTo": "project_workstream_evidence_requirement", + "columnsFrom": [ + "project_workstream_evidence_requirement_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_defined_deal_measures": { + "name": "user_defined_deal_measures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "hubspot_deal_id": { + "name": "hubspot_deal_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "measure_name": { + "name": "measure_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "user_defined_deal_measure_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "pushed_at": { + "name": "pushed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "confirmed_in_hubspot_at": { + "name": "confirmed_in_hubspot_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_user_defined_deal_measures_deal_id": { + "name": "idx_user_defined_deal_measures_deal_id", + "columns": [ + { + "expression": "hubspot_deal_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_user_defined_deal_measures_source": { + "name": "idx_user_defined_deal_measures_source", + "columns": [ + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_defined_deal_measures_created_by_user_id_user_id_fk": { + "name": "user_defined_deal_measures_created_by_user_id_user_id_fk", + "tableFrom": "user_defined_deal_measures", + "tableTo": "user", + "columnsFrom": [ + "created_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_portfolio_config": { + "name": "user_portfolio_config", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "portfolio_id": { + "name": "portfolio_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "starred_at": { + "name": "starred_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "folder_id": { + "name": "folder_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_user_portfolio_config_folder": { + "name": "idx_user_portfolio_config_folder", + "columns": [ + { + "expression": "folder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_portfolio_config_user_id_user_id_fk": { + "name": "user_portfolio_config_user_id_user_id_fk", + "tableFrom": "user_portfolio_config", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_portfolio_config_portfolio_id_portfolio_id_fk": { + "name": "user_portfolio_config_portfolio_id_portfolio_id_fk", + "tableFrom": "user_portfolio_config", + "tableTo": "portfolio", + "columnsFrom": [ + "portfolio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_portfolio_config_folder_owner_fk": { + "name": "user_portfolio_config_folder_owner_fk", + "tableFrom": "user_portfolio_config", + "tableTo": "user_portfolio_folders", + "columnsFrom": [ + "folder_id", + "user_id" + ], + "columnsTo": [ + "id", + "user_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_portfolio_config_user_portfolio_unique": { + "name": "user_portfolio_config_user_portfolio_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id", + "portfolio_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_portfolio_folders": { + "name": "user_portfolio_folders", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "position": { + "name": "position", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_user_portfolio_folders_user_position": { + "name": "idx_user_portfolio_folders_user_position", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "position", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_portfolio_folders_user_id_user_id_fk": { + "name": "user_portfolio_folders_user_id_user_id_fk", + "tableFrom": "user_portfolio_folders", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_portfolio_folders_id_user_unique": { + "name": "user_portfolio_folders_id_user_unique", + "nullsNotDistinct": false, + "columns": [ + "id", + "user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerAccountId": { + "name": "providerAccountId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "session_state": { + "name": "session_state", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_userId_user_id_fk": { + "name": "account_userId_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "account_provider_providerAccountId_pk": { + "name": "account_provider_providerAccountId_pk", + "columns": [ + "provider", + "providerAccountId" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.authRateLimits": { + "name": "authRateLimits", + "schema": "", + "columns": { + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "count": { + "name": "count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "window_start": { + "name": "window_start", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "authRateLimits_scope_key_pk": { + "name": "authRateLimits_scope_key_pk", + "columns": [ + "scope", + "key" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "sessionToken": { + "name": "sessionToken", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "expires": { + "name": "expires", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "firstName": { + "name": "firstName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "emailVerified": { + "name": "emailVerified", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "oauth_id": { + "name": "oauth_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "oauth_provider": { + "name": "oauth_provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "onboarded": { + "name": "onboarded", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "last_login": { + "name": "last_login", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_profiles": { + "name": "user_profiles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "user_type": { + "name": "user_type", + "type": "user_profiles_user_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "property_count": { + "name": "property_count", + "type": "user_profiles_property_count", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "goals": { + "name": "goals", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "referral_source": { + "name": "referral_source", + "type": "user_profiles_referral_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "nrla_membership_id": { + "name": "nrla_membership_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "accepted_privacy": { + "name": "accepted_privacy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accepted_privacy_at": { + "name": "accepted_privacy_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "marketing_opt_in": { + "name": "marketing_opt_in", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "marketing_opt_in_at": { + "name": "marketing_opt_in_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": false + }, + "first_name": { + "name": "first_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_name": { + "name": "last_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (6) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_profiles_user_id_user_id_fk": { + "name": "user_profiles_user_id_user_id_fk", + "tableFrom": "user_profiles", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verificationToken": { + "name": "verificationToken", + "schema": "", + "columns": { + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires": { + "name": "expires", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "code_hash": { + "name": "code_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "verificationToken_identifier_token_pk": { + "name": "verificationToken_identifier_token_pk", + "columns": [ + "identifier", + "token" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.whlg": { + "name": "whlg", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "postcode": { + "name": "postcode", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.aspect_type": { + "name": "aspect_type", + "schema": "public", + "values": [ + "material", + "condition", + "type", + "area", + "configuration", + "presence", + "risk", + "severity", + "location", + "finish", + "insulation", + "pointing", + "spalling", + "lintels", + "cladding", + "category", + "quantity", + "adequacy", + "rating", + "strategy", + "extent", + "distribution", + "structure", + "covering", + "fire_rating", + "external_decoration", + "work_required", + "age_band", + "construction_type", + "classification", + "system" + ] + }, + "public.element_type": { + "name": "element_type", + "schema": "public", + "values": [ + "property", + "property_construction_type", + "property_classification", + "property_age_band", + "storey_count", + "floor_level", + "floor_level_front_door", + "accessible_housing_register", + "asbestos", + "quality_standard", + "ccu", + "passenger_lift", + "stairlift", + "disabled_hoist_tracking", + "disabled_facilities", + "steps_to_front_door", + "roof", + "pitched_roof_covering", + "flat_roof_covering", + "rainwater_goods", + "loft_insulation", + "porch_canopy", + "chimney", + "fascia", + "soffit", + "fascia_soffit_bargeboards", + "gutters", + "store_roof", + "garage_roof", + "garage_and_store_roof", + "external_wall", + "external_noise_insulation", + "primary_wall", + "secondary_wall", + "downpipes", + "external_decoration", + "cladding", + "spandrel_panels", + "garage_walls", + "party_wall_fire_break", + "external_brickwork_pointing", + "internal_downpipes_external_area", + "external_windows", + "communal_windows", + "secondary_glazing", + "store_windows", + "garage_windows", + "garage_and_store_windows", + "external_door", + "front_door", + "rear_door", + "store_door", + "garage_door", + "garage_and_store_door", + "communal_entrance_door", + "main_door", + "block_entrance_door", + "lintel", + "patio_french_door", + "door_entry_handset", + "paths_and_hardstandings", + "parking_areas", + "boundary_walls", + "front_fencing", + "rear_fencing", + "side_fencing", + "rear_gate", + "front_gate", + "gates", + "retaining_walls", + "private_balcony", + "balcony_balustrade", + "outbuildings", + "garage_structure", + "paving", + "roads", + "soil_and_vent", + "solar_thermals", + "drop_kerb", + "outbuilding_overhaul", + "external_structural_defects", + "access_ramp", + "kitchen", + "kitchen_space_layout", + "tenant_installed_kitchen", + "kitchen_extractor_fan", + "bathroom", + "secondary_bathroom", + "secondary_toilet", + "bathroom_extractor_fan", + "additional_wc_or_whb", + "bathroom_remaining_life_source", + "kitchen_remaining_life_source", + "central_heating", + "heating_boiler", + "heating_distribution", + "secondary_heating", + "hot_water_system", + "cold_water_storage", + "heating_system", + "boiler_fuel", + "water_heating", + "programmable_heating", + "community_heating", + "gas_available", + "heat_recovery_units", + "heating_improvements", + "electrical_wiring", + "consumer_unit", + "smoke_detection", + "heat_detection", + "carbon_monoxide_detection", + "fire_door_rating", + "fire_risk_assessment", + "internal_wiring", + "electrics", + "communal_heating", + "communal_boiler", + "communal_electrics", + "communal_fire_alarm", + "communal_emergency_lighting", + "communal_door_entry", + "communal_cctv", + "communal_bin_store", + "communal_bin_store_doors", + "communal_bin_store_walls", + "communal_bin_store_roof", + "communal_refuse_chute", + "communal_floor_covering", + "communal_kitchen", + "communal_bathroom", + "communal_toilets", + "communal_gates", + "communal_lift", + "communal_passenger_lift", + "communal_balcony_walkway", + "communal_entrance", + "communal_internal_decorations", + "communal_internal_floor", + "communal_walkways", + "communal_external_doors", + "communal_stairs", + "communal_aerial", + "communal_aov", + "communal_internal_doors", + "communal_lateral_mains", + "communal_lighting", + "communal_lighting_conductor", + "communal_store_roof", + "communal_store_walls", + "communal_store_doors", + "communal_warden_call_system", + "communal_bms", + "communal_booster_pump", + "communal_dry_riser", + "communal_wet_riser", + "communal_cold_water_storage", + "communal_sprinkler", + "communal_plug_sockets", + "communal_circulation_space", + "ffhh_damp", + "ffhh_hold_and_cold_water", + "ffhh_drainage_lavatories", + "ffhh_neglected", + "ffhh_natural_light", + "ffhh_ventilation", + "ffhh_food_prep_and_washup", + "ffhh_unsafe_layout", + "ffhh_unstable_building", + "hhsrs_damp_and_mould", + "hhsrs_excess_cold", + "hhsrs_excess_heat", + "hhsrs_asbestos_and_mmf", + "hhsrs_biocides", + "hhsrs_carbon_monoxide", + "hhsrs_lead", + "hhsrs_radiation", + "hhsrs_uncombusted_fuel_gas", + "hhsrs_volatile_organic_compounds", + "hhsrs_crowding_and_space", + "hhsrs_entry_by_intruders", + "hhsrs_lighting", + "hhsrs_noise", + "hhsrs_domestic_hygiene_pests_refuse", + "hhsrs_food_safety", + "hhsrs_personal_hygiene_sanitation", + "hhsrs_water_supply", + "hhsrs_falls_associated_with_baths", + "hhsrs_falls_on_level_surfaces", + "hhsrs_falls_on_stairs", + "hhsrs_falls_between_levels", + "hhsrs_electrical_hazards", + "hhsrs_fire", + "hhsrs_flames_hot_surfaces", + "hhsrs_collision_and_entrapment", + "hhsrs_collision_hazards_low_headroom", + "hhsrs_explosions", + "hhsrs_ergonomics", + "hhsrs_structural_collapse", + "hhsrs_amenities" + ] + }, + "public.document_type": { + "name": "document_type", + "schema": "public", + "values": [ + "EPR", + "Condition Report", + "Evidence Report", + "Summary Information", + "Floor Plan", + "Scenario Draft EPC", + "Scenario Site Notes" + ] + }, + "public.scheme": { + "name": "scheme", + "schema": "public", + "values": [ + "eco4", + "gbis", + "whlg", + "none" + ] + }, + "public.inspection_archetype_2": { + "name": "inspection_archetype_2", + "schema": "public", + "values": [ + "detached", + "mid-terrace", + "enclosed mid-terrace", + "end-terrace", + "enclosed end-terrace", + "semi-detached" + ] + }, + "public.inspection_archetype": { + "name": "inspection_archetype", + "schema": "public", + "values": [ + "Bungalow", + "Flat", + "Maisonette", + "House", + "non-domestic" + ] + }, + "public.inspection_borescoped": { + "name": "inspection_borescoped", + "schema": "public", + "values": [ + "yes", + "no", + "refused" + ] + }, + "public.inspections_access_issues": { + "name": "inspections_access_issues", + "schema": "public", + "values": [ + "see notes", + "damp issues", + "foliage on walls", + "bushes against wall", + "trees around/anove property", + "high rise block flats/maisonettes", + "conservatory", + "lean-to", + "garage", + "extension", + "decking", + "shed against wall" + ] + }, + "public.inspections_cladding": { + "name": "inspections_cladding", + "schema": "public", + "values": [ + "none", + "cladded with “sufficient space to fill the wall”", + "cladded with “insufficient space to fill the wall”" + ] + }, + "public.inspections_insulation_material": { + "name": "inspections_insulation_material", + "schema": "public", + "values": [ + "empty 50-90", + "empty 100+", + "empty 30-40", + "empty less than 30", + "loose fibre/wool", + "eps/celo/king", + "fibre batts - with cavity", + "fibre batts - no cavity", + "loose bead", + "glued bead", + "formaldehyde", + "bubble wrap", + "poly chunks" + ] + }, + "public.inspections_rendered": { + "name": "inspections_rendered", + "schema": "public", + "values": [ + "no render", + "rendered with “insufficient” space between dpc and render", + "rendered with “sufficient” space between dpc and render" + ] + }, + "public.inspections_roof_orientation": { + "name": "inspections_roof_orientation", + "schema": "public", + "values": [ + "north", + "east", + "south", + "west", + "north-east", + "north-west", + "south-east", + "south-west", + "n/s split", + "e/w split", + "ne/sw split", + "nw/se split", + "flat roof", + "no roof", + "roof too small", + "already has solar pv" + ] + }, + "public.inspections_tile_hung": { + "name": "inspections_tile_hung", + "schema": "public", + "values": [ + "yes", + "no", + "first floor flats are tile hung" + ] + }, + "public.inspections_wall_construction": { + "name": "inspections_wall_construction", + "schema": "public", + "values": [ + "cavity", + "solid", + "system built", + "timber framed", + "steel framed", + "re-walled cavity", + "mansard pre-fab", + "mansard ewi", + "mansard re-walled" + ] + }, + "public.inspections_wall_insulation": { + "name": "inspections_wall_insulation", + "schema": "public", + "values": [ + "empty cavity", + "filled at build", + "partial", + "retro drilled", + "ewi", + "iwi", + "solid non-cavity", + "system built", + "timber framed", + "steel framed" + ] + }, + "public.built_form_type": { + "name": "built_form_type", + "schema": "public", + "values": [ + "Detached", + "Semi-Detached", + "Mid-Terrace", + "End-Terrace", + "Enclosed Mid-Terrace", + "Enclosed End-Terrace", + "Not Recorded", + "Unknown" + ] + }, + "public.construction_age_band": { + "name": "construction_age_band", + "schema": "public", + "values": [ + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "Unknown" + ] + }, + "public.glazing": { + "name": "glazing", + "schema": "public", + "values": [ + "Single glazing", + "Double glazing, 2002 or later", + "Double glazing, pre-2002", + "Triple glazing, pre-2002", + "Triple glazing, 2002 or later", + "Mixed glazing", + "Secondary glazing", + "Unknown" + ] + }, + "public.main_fuel": { + "name": "main_fuel", + "schema": "public", + "values": [ + "mains gas", + "mains gas (community)", + "electricity", + "electricity (community)", + "LPG (bulk)", + "bottled LPG", + "LPG special condition", + "oil", + "house coal", + "smokeless coal", + "dual fuel (mineral and wood)", + "biomass (community)", + "wood logs", + "Unknown" + ] + }, + "public.main_heating_system": { + "name": "main_heating_system", + "schema": "public", + "values": [ + "Gas boiler, combi", + "Gas boiler, regular", + "Gas CPSU", + "Oil boiler, regular", + "Oil boiler, combi", + "Solid fuel boiler", + "Electric storage heaters, old", + "Electric storage heaters, slimline", + "Electric storage heaters, convector", + "Electric storage heaters, fan", + "Direct-acting electric", + "Electric room heaters", + "Solid fuel room heater, closed", + "Air source heat pump", + "Community heating, boilers", + "Community heating, CHP and boilers", + "Oil room heater, 2000 or later", + "Gas room heater, condensing fire", + "Gas room heater, decorative fuel-effect", + "Gas room heater, flush live-effect", + "Gas room heater, open flue 1980 or later", + "Gas room heater, open flue pre-1980", + "Electric storage heaters, high heat retention", + "Solid fuel room heater, open fire", + "Solid fuel room heater, open fire with back boiler", + "Solid fuel room heater, closed with boiler", + "Electric boiler", + "Electric CPSU", + "Electric underfloor, in concrete slab (off-peak)", + "Electric underfloor, integrated storage and direct-acting", + "Electric underfloor, in screed above insulation", + "Unknown" + ] + }, + "public.override_source": { + "name": "override_source", + "schema": "public", + "values": [ + "classifier", + "user", + "os_places" + ] + }, + "public.property_type": { + "name": "property_type", + "schema": "public", + "values": [ + "House", + "Bungalow", + "Flat", + "Maisonette", + "Park home", + "Unknown" + ] + }, + "public.roof_type": { + "name": "roof_type", + "schema": "public", + "values": [ + "Flat, insulated", + "Flat, insulated (assumed)", + "Flat, limited insulation", + "Flat, limited insulation (assumed)", + "Flat, no insulation", + "Flat, no insulation (assumed)", + "Flat, 12 mm insulation", + "Flat, 25 mm insulation", + "Flat, 50 mm insulation", + "Flat, 75 mm insulation", + "Flat, 100 mm insulation", + "Flat, 125 mm insulation", + "Flat, 150 mm insulation", + "Flat, 175 mm insulation", + "Flat, 200 mm insulation", + "Flat, 225 mm insulation", + "Flat, 250 mm insulation", + "Flat, 270 mm insulation", + "Flat, 300 mm insulation", + "Flat, 350 mm insulation", + "Flat, 400 mm insulation", + "Flat, 400+ mm insulation", + "Pitched, insulated", + "Pitched, insulated (assumed)", + "Pitched, insulated at rafters", + "Pitched, limited insulation", + "Pitched, limited insulation (assumed)", + "Pitched, no insulation", + "Pitched, no insulation (assumed)", + "Pitched, Unknown loft insulation", + "Pitched, 0 mm loft insulation", + "Pitched, 12 mm loft insulation", + "Pitched, 25 mm loft insulation", + "Pitched, 50 mm loft insulation", + "Pitched, 75 mm loft insulation", + "Pitched, 100 mm loft insulation", + "Pitched, 125 mm loft insulation", + "Pitched, 150 mm loft insulation", + "Pitched, 175 mm loft insulation", + "Pitched, 200 mm loft insulation", + "Pitched, 225 mm loft insulation", + "Pitched, 250 mm loft insulation", + "Pitched, 270 mm loft insulation", + "Pitched, 300 mm loft insulation", + "Pitched, 350 mm loft insulation", + "Pitched, 400 mm loft insulation", + "Pitched, 400+ mm loft insulation", + "Pitched, sloping ceiling, as built", + "Pitched, sloping ceiling, 12 mm insulation", + "Pitched, sloping ceiling, 25 mm insulation", + "Pitched, sloping ceiling, 50 mm insulation", + "Pitched, sloping ceiling, 75 mm insulation", + "Pitched, sloping ceiling, 100 mm insulation", + "Pitched, sloping ceiling, 125 mm insulation", + "Pitched, sloping ceiling, 150 mm insulation", + "Pitched, sloping ceiling, 175 mm insulation", + "Pitched, sloping ceiling, 200 mm insulation", + "Pitched, sloping ceiling, 225 mm insulation", + "Pitched, sloping ceiling, 250 mm insulation", + "Pitched, sloping ceiling, 270 mm insulation", + "Pitched, sloping ceiling, 300 mm insulation", + "Pitched, sloping ceiling, 350 mm insulation", + "Pitched, sloping ceiling, 400 mm insulation", + "Pitched, sloping ceiling, 400+ mm insulation", + "Roof room(s), insulated", + "Roof room(s), insulated (assumed)", + "Roof room(s), limited insulation", + "Roof room(s), limited insulation (assumed)", + "Roof room(s), no insulation", + "Roof room(s), no insulation (assumed)", + "Roof room(s), ceiling insulated", + "Roof room(s), thatched", + "Roof room(s), thatched with additional insulation", + "Thatched", + "Thatched, with additional insulation", + "(another dwelling above)", + "(same dwelling above)", + "(other premises above)", + "(another premises above)", + "Another Premises Above", + "Unknown" + ] + }, + "public.wall_type": { + "name": "wall_type", + "schema": "public", + "values": [ + "Cavity wall, filled cavity", + "Cavity wall, as built, insulated (assumed)", + "Cavity wall, as built, no insulation (assumed)", + "Cavity wall, as built, partial insulation (assumed)", + "Cavity wall, with internal insulation", + "Cavity wall, with external insulation", + "Cavity wall, filled cavity and internal insulation", + "Cavity wall, filled cavity and external insulation", + "Solid brick, as built, no insulation (assumed)", + "Solid brick, as built, insulated (assumed)", + "Solid brick, as built, partial insulation (assumed)", + "Solid brick, with internal insulation", + "Solid brick, with external insulation", + "Timber frame, as built, no insulation (assumed)", + "Timber frame, as built, insulated (assumed)", + "Timber frame, as built, partial insulation (assumed)", + "Timber frame, with additional insulation", + "Sandstone, as built, no insulation (assumed)", + "Sandstone, as built, insulated (assumed)", + "Sandstone, as built, partial insulation (assumed)", + "Sandstone, with internal insulation", + "Sandstone, with external insulation", + "Granite or whin, as built, no insulation (assumed)", + "Granite or whin, as built, insulated (assumed)", + "Granite or whin, as built, partial insulation (assumed)", + "Granite or whin, with internal insulation", + "Granite or whin, with external insulation", + "System built, as built, no insulation (assumed)", + "System built, as built, insulated (assumed)", + "System built, as built, partial insulation (assumed)", + "System built, with internal insulation", + "System built, with external insulation", + "Park home wall, as built", + "Park home wall, with internal insulation", + "Park home wall, with external insulation", + "Cob, as built", + "Cob, with internal insulation", + "Cob, with external insulation", + "Curtain wall", + "Curtain Wall, as built, no insulation (assumed)", + "Curtain Wall, as built, insulated (assumed)", + "Curtain Wall, filled cavity", + "Curtain Wall, with internal insulation", + "Basement wall", + "Basement wall, as built", + "Unknown" + ] + }, + "public.water_heating": { + "name": "water_heating", + "schema": "public", + "values": [ + "From main system, mains gas", + "From main system, electricity", + "From main system, oil", + "From main system, LPG (bulk)", + "From main system, bottled LPG", + "From main system, house coal", + "Electric immersion, electricity", + "Gas boiler/circulator, mains gas", + "From main system, wood logs", + "From main system, biomass (community)", + "From main system, dual fuel (mineral and wood)", + "From main system, biodiesel (community)", + "Unknown" + ] + }, + "public.cost_unit": { + "name": "cost_unit", + "schema": "public", + "values": [ + "gbp_sq_meter", + "gbp_per_unit", + "gbp_per_m2", + "gbp_per_m" + ] + }, + "public.depth_unit": { + "name": "depth_unit", + "schema": "public", + "values": [ + "mm" + ] + }, + "public.type": { + "name": "type", + "schema": "public", + "values": [ + "suspended_floor_insulation", + "solid_floor_insulation", + "external_wall_insulation", + "internal_wall_insulation", + "cavity_wall_insulation", + "mechanical_ventilation", + "loft_insulation", + "exposed_floor_insulation", + "flat_roof_insulation", + "room_roof_insulation", + "cavity_wall_extraction", + "iwi_wall_demolition", + "iwi_vapour_barrier", + "iwi_redecoration", + "suspended_floor_demolition", + "suspended_floor_redecoration", + "suspended_floor_vapour_barrier", + "solid_floor_demolition", + "solid_floor_preparation", + "solid_floor_vapour_barrier", + "solid_floor_redecoration", + "ewi_wall_demolition", + "ewi_wall_preparation", + "ewi_wall_redecoration", + "low_energy_lighting_installation", + "flat_roof_preparation", + "flat_roof_vapour_barrier", + "flat_roof_waterproofing", + "windows_glazing", + "secondary_glazing", + "double_glazing", + "trickle_vent", + "door_undercut", + "solar_pv", + "solar_battery", + "scaffolding", + "high_heat_retention_storage_heaters", + "air_source_heat_pump", + "boiler_upgrade", + "roomstat_programmer_trvs", + "time_temperature_zone_control", + "sealing_fireplace" + ] + }, + "public.r_value_unit": { + "name": "r_value_unit", + "schema": "public", + "values": [ + "square_meter_kelvin_per_watt" + ] + }, + "public.size_unit": { + "name": "size_unit", + "schema": "public", + "values": [ + "kWp", + "kW", + "watt", + "storey" + ] + }, + "public.thermal_conductivity_unit": { + "name": "thermal_conductivity_unit", + "schema": "public", + "values": [ + "watt_per_meter_kelvin" + ] + }, + "public.goal": { + "name": "goal", + "schema": "public", + "values": [ + "Valuation Improvement", + "Increasing EPC", + "Reducing CO2 emissions", + "Energy Savings", + "None" + ] + }, + "public.portfolio_capability": { + "name": "portfolio_capability", + "schema": "public", + "values": [ + "approver", + "contractor" + ] + }, + "public.role": { + "name": "role", + "schema": "public", + "values": [ + "creator", + "admin", + "read", + "write" + ] + }, + "public.status": { + "name": "status", + "schema": "public", + "values": [ + "scoping", + "survey", + "assessment", + "tendering", + "project underway", + "completion; status: on track", + "completion; status: delayed", + "completion; status: at risk", + "completion; status: completed", + "needs review" + ] + }, + "public.override_component": { + "name": "override_component", + "schema": "public", + "values": [ + "wall_type", + "roof_type", + "property_type", + "built_form_type", + "main_fuel", + "glazing", + "construction_age_band", + "water_heating", + "main_heating_system" + ] + }, + "public.energy_element_type": { + "name": "energy_element_type", + "schema": "public", + "values": [ + "roof", + "wall", + "floor", + "main_heating", + "window", + "lighting", + "hot_water", + "secondary_heating", + "main_heating_controls" + ] + }, + "public.epc": { + "name": "epc", + "schema": "public", + "values": [ + "A", + "B", + "C", + "D", + "E", + "F", + "G" + ] + }, + "public.creation_status": { + "name": "creation_status", + "schema": "public", + "values": [ + "LOADING", + "READY", + "ERROR" + ] + }, + "public.rebaseline_reason": { + "name": "rebaseline_reason", + "schema": "public", + "values": [ + "none", + "pre_sap10", + "physical_state_changed", + "both" + ] + }, + "public.housing_type": { + "name": "housing_type", + "schema": "public", + "values": [ + "Private", + "Social" + ] + }, + "public.measure_type": { + "name": "measure_type", + "schema": "public", + "values": [ + "air_source_heat_pump", + "boiler_upgrade", + "high_heat_retention_storage_heaters", + "secondary_heating", + "roomstat_programmer_trvs", + "time_temperature_zone_control", + "cylinder_thermostat", + "cavity_wall_insulation", + "extension_cavity_wall_insulation", + "external_wall_insulation", + "internal_wall_insulation", + "loft_insulation", + "flat_roof_insulation", + "room_roof_insulation", + "solid_floor_insulation", + "suspended_floor_insulation", + "double_glazing", + "secondary_glazing", + "draught_proofing", + "mechanical_ventilation", + "low_energy_lighting", + "solar_pv", + "hot_water_tank_insulation", + "sealing_open_fireplace" + ] + }, + "public.plan_type": { + "name": "plan_type", + "schema": "public", + "values": [ + "solar_eco4", + "solar_hhrsh_eco4", + "empty_cavity_eco", + "partial_cavity_eco", + "extraction_eco" + ] + }, + "public.unit_quantity": { + "name": "unit_quantity", + "schema": "public", + "values": [ + "m2", + "part", + "kwp" + ] + }, + "public.scenario_type": { + "name": "scenario_type", + "schema": "public", + "values": [ + "unit", + "building" + ] + }, + "public.source": { + "name": "source", + "schema": "public", + "values": [ + "portfolio_id", + "hubspot_deal_id", + "property_id" + ] + }, + "public.file_source": { + "name": "file_source", + "schema": "public", + "values": [ + "pas hub", + "sharepoint", + "hubspot", + "ecmk", + "contractor", + "magic_plan", + "coordination_hub", + "audit_generator", + "projects" + ] + }, + "public.file_type": { + "name": "file_type", + "schema": "public", + "values": [ + "photo_pack", + "site_note", + "rd_sap_site_note", + "pas_2023_ventilation", + "pas_2023_condition", + "pas_significance", + "par_photo_pack", + "pas_2023_property", + "pas_2023_occupancy", + "ecmk_site_note", + "ecmk_rd_sap_site_note", + "ecmk_survey_xml", + "pre_photo", + "mid_photo", + "post_photo", + "loft_hatch_photo", + "dmev_photos", + "door_undercut_photos", + "trickle_vent_photos", + "pre_installation_building_inspection", + "point_of_work_risk_assessment", + "claim_of_compliance", + "mcs_compliance_certificate", + "certificate_of_conformity", + "minor_works_electrical_certificate", + "trustmark_licence_numbers", + "operative_competency", + "ventilation_assessment_checklist", + "anemometer_readings", + "commissioning_records", + "part_f_ventilation_document", + "handover_pack", + "insurance_guarantee", + "workmanship_warranty", + "g98_notification", + "installer_qualifications", + "installer_feedback", + "contractor_other", + "magic_plan_json", + "improvement_option_evaluation", + "medium_term_improvement_plan", + "retrofit_design_doc", + "ventilation_audit", + "other" + ] + }, + "public.user_defined_deal_measure_source": { + "name": "user_defined_deal_measure_source", + "schema": "public", + "values": [ + "instructed", + "pibi_ordered" + ] + }, + "public.user_profiles_property_count": { + "name": "user_profiles_property_count", + "schema": "public", + "values": [ + "1", + "2–5", + "6–20", + "21+", + "1–50", + "51–100", + "101–300", + "301–1000", + "1000+" + ] + }, + "public.user_profiles_referral_source": { + "name": "user_profiles_referral_source", + "schema": "public", + "values": [ + "search", + "social_media", + "NRLA", + "partner", + "word_of_mouth", + "other" + ] + }, + "public.user_profiles_user_type": { + "name": "user_profiles_user_type", + "schema": "public", + "values": [ + "private_landlord", + "private_tenant", + "social_landlord", + "social_tenant", + "homeowner", + "other" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/src/app/db/migrations/meta/_journal.json b/src/app/db/migrations/meta/_journal.json index c43181f7..dbed7878 100644 --- a/src/app/db/migrations/meta/_journal.json +++ b/src/app/db/migrations/meta/_journal.json @@ -1961,6 +1961,13 @@ "when": 1784819275099, "tag": "0280_flashy_dreadnoughts", "breakpoints": true + }, + { + "idx": 281, + "version": "7", + "when": 1784882283273, + "tag": "0281_work_order_status_default", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/app/db/schema/projects/projects.ts b/src/app/db/schema/projects/projects.ts index dc5293ed..c20b7042 100644 --- a/src/app/db/schema/projects/projects.ts +++ b/src/app/db/schema/projects/projects.ts @@ -181,8 +181,13 @@ export const workOrder = pgTable("work_order", { }) .notNull() .references(() => projectWorkstreamStage.id), - // Enum values TBC — modelled as text until the value sets are agreed. - status: text("status").notNull(), + // Enum values TBC — modelled as text until the value sets are agreed. Carries + // a DB-level DEFAULT '' (migration 0281) so the work-order import (#417) can + // insert a row WITHOUT writing status: v1's only lifecycle mechanism is the + // stage FK, and CONTEXT.md ("Stage") requires status stay untouched. The + // default mirrors the priority fix in 0280 — a NOT NULL column an insert may + // legitimately omit needs a default, or every omitting insert fails. + status: text("status").notNull().default(""), forecastEnd: date("forecast_end"), // A flag, not a scale: a work order either is flagged as priority or is not. // NOT NULL DEFAULT false because a third "unknown" state has no meaning for a diff --git a/src/app/projects/[projectId]/import/components/ImportUpload.tsx b/src/app/projects/[projectId]/import/components/ImportUpload.tsx index 532e0867..2964778b 100644 --- a/src/app/projects/[projectId]/import/components/ImportUpload.tsx +++ b/src/app/projects/[projectId]/import/components/ImportUpload.tsx @@ -5,7 +5,6 @@ import { useMutation } from "@tanstack/react-query"; import { ArrowDownTrayIcon, ArrowPathIcon, - CheckCircleIcon, CloudArrowUpIcon, DocumentTextIcon, ExclamationTriangleIcon, @@ -19,6 +18,7 @@ import { import type { ImportMappingTargets } from "@/lib/projects/import/mappingTargets"; import type { ImportSession } from "@/lib/projects/import/session"; import { ImportMapping, type MappingHandoff } from "./ImportMapping"; +import { ImportValidation } from "./ImportValidation"; /** The parse route's response — the whole file, not just the preview. */ export interface ParsedImportResponse { @@ -32,7 +32,7 @@ export interface ParsedImportResponse { const MAX_MB = MAX_IMPORT_FILE_BYTES / (1024 * 1024); /** Which of the wizard's steps this component is showing. */ -type ImportStep = "upload" | "mapping" | "mapped"; +type ImportStep = "upload" | "mapping" | "validation"; /** * Work-order import drop zone (issue #414) and the step-2 mapping screen it @@ -147,17 +147,19 @@ export function ImportUpload({ onBack={() => setStep("upload")} onContinue={(next) => { setHandoff(next); - setStep("mapped"); + setStep("validation"); }} /> ); } - if (parsed && step === "mapped" && handoff) { + if (parsed && step === "validation" && handoff) { return ( - setStep("mapping")} + setStep("mapping")} onStartOver={reset} /> ); @@ -317,64 +319,3 @@ export function ImportUpload({ ); } - -/** - * The end of step 2: the mapping is settled and held in the import session, - * waiting for validation and commit (#417) to consume it. - * - * It states what was captured rather than claiming anything was saved to the - * server, because nothing was — an import writes nothing until commit - * (ADR-0020). Both ways back are offered: edit the mapping, or start again with - * a different file. - */ -function MappingSaved({ - handoff, - onEdit, - onStartOver, -}: { - handoff: MappingHandoff; - onEdit: () => void; - onStartOver: () => void; -}) { - const { session, warningRows } = handoff; - const mappedFields = Object.keys(session.columnMapping.fields).length; - - return ( -
-
- -
-

- Mapping complete -

-

- {mappedFields} field{mappedFields === 1 ? "" : "s"} mapped across{" "} - {session.rowCount.toLocaleString("en-GB")} row - {session.rowCount === 1 ? "" : "s"} of {session.filename}. Nothing - has been written yet — validation and commit arrive in #417. -

- - {warningRows > 0 && ( -

- {warningRows.toLocaleString("en-GB")} row - {warningRows === 1 ? "" : "s"} still carry values that map to - nothing and will fail validation. -

- )} - -
- - -
-
-
-
- ); -} diff --git a/src/app/projects/[projectId]/import/components/ImportValidation.tsx b/src/app/projects/[projectId]/import/components/ImportValidation.tsx new file mode 100644 index 00000000..7b8429e9 --- /dev/null +++ b/src/app/projects/[projectId]/import/components/ImportValidation.tsx @@ -0,0 +1,505 @@ +"use client"; + +import { useState } from "react"; +import Link from "next/link"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { + ArrowLeftIcon, + ArrowPathIcon, + ArrowRightIcon, + ArrowDownTrayIcon, + CheckCircleIcon, + ExclamationTriangleIcon, + NoSymbolIcon, +} from "@heroicons/react/24/outline"; +import { Button } from "@/app/shadcn_components/ui/button"; +import type { ImportSession } from "@/lib/projects/import/session"; + +/** One reported issue row — mirrors `RowIssue` on the server. */ +interface RowIssue { + rowNumber: number; + identifier: string; + issue: string; + field: string; +} + +interface CreatedByWorkstream { + projectWorkstreamId: string; + workstreamName: string; + count: number; +} + +interface ValidateResponse { + summary: { total: number; ready: number; errors: number; duplicates: number }; + errors: RowIssue[]; + duplicates: RowIssue[]; + createdByWorkstream: CreatedByWorkstream[]; +} + +interface CommitResponse { + committed: { workOrdersCreated: number; propertiesLinked: number }; + createdByWorkstream: CreatedByWorkstream[]; + summary: ValidateResponse["summary"]; +} + +/** + * Work-order import, step 3 of 3 — the validation report and commit (#417). + * + * Wireframe: `10-validate-import.html` (reduced — no Updates/Omissions/Priority + * tabs; those are #425). Stat tiles for ready / errors / skipped-duplicates over + * a tabbed issues table, then a commit that writes every ready row in one + * transaction and shows the per-workstream summary. + * + * The validation call is a `useQuery` rather than an effect: it writes nothing + * server-side, so it is a read, and `useQuery` runs it on mount without the + * `useEffect` this codebase avoids. The commit is a `useMutation` — the one and + * only write in the whole import journey. + */ +export function ImportValidation({ + projectId, + session, + rows, + onBack, + onStartOver, +}: { + projectId: string; + session: ImportSession; + /** The parsed file's rows — held client-side since #414, POSTed here. */ + rows: string[][]; + onBack: () => void; + onStartOver: () => void; +}) { + const [tab, setTab] = useState<"errors" | "duplicates">("errors"); + const [committed, setCommitted] = useState(null); + + const validation = useQuery({ + queryKey: ["import-validate", projectId, session.filename, session.rowCount], + queryFn: async () => { + const res = await fetch(`/api/projects/${projectId}/import/validate`, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ session, rows }), + }); + const body = await res.json(); + if (!res.ok) throw new Error(body.error ?? "Couldn't validate the import"); + return body as ValidateResponse; + }, + refetchOnWindowFocus: false, + retry: false, + }); + + const commit = useMutation({ + mutationFn: async () => { + const res = await fetch(`/api/projects/${projectId}/import/commit`, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ session, rows }), + }); + const body = await res.json(); + if (!res.ok) throw new Error(body.error ?? "Couldn't commit the import"); + return body as CommitResponse; + }, + onSuccess: (data) => setCommitted(data), + }); + + if (committed) { + return ( + + ); + } + + if (validation.isLoading) { + return ( +
+ +

+ Validating {session.rowCount.toLocaleString("en-GB")} rows… +

+

+ Matching properties, workstreams, contractors and stages. +

+
+ ); + } + + if (validation.isError || !validation.data) { + return ( +
+
+ +
+

Couldn't validate the import

+

+ {validation.error?.message ?? "Something went wrong."} +

+
+ + +
+
+
+
+ ); + } + + const { summary, errors, duplicates, createdByWorkstream } = validation.data; + const issues = tab === "errors" ? errors : duplicates; + + return ( +
+
+
+

+ Validate import +

+

+ Review and resolve issues before committing. Error rows are excluded; + duplicates are skipped. +

+
+
+ + +
+
+ +
+ } + /> + } + /> + } + /> +
+ + {commit.isError && ( +
+ +

{commit.error.message}

+
+ )} + +
+
+
+ setTab("errors")} + > + Errors ({summary.errors}) + + setTab("duplicates")} + > + Skipped duplicates ({summary.duplicates}) + +
+ {(errors.length > 0 || duplicates.length > 0) && ( + + )} +
+ + {issues.length === 0 ? ( +
+ {tab === "errors" + ? "No errors — every row either imports or is a skipped duplicate." + : "No duplicate rows."} +
+ ) : ( +
+ + + + + + + + + + + {issues.map((issue) => ( + + + + + + + ))} + +
Row #IdentifierIssueAffected field
+ {issue.rowNumber} + + {issue.identifier || ( + + )} + {issue.issue}{issue.field}
+
+ )} +
+ + {summary.ready > 0 && ( +
+

+ What will be created +

+
+ {createdByWorkstream.map((w) => ( + + {w.workstreamName} + + {w.count.toLocaleString("en-GB")} + + + ))} +
+
+ )} +
+ ); +} + +/** + * The post-commit summary — created counts per workstream, with links into the + * work-orders table. States plainly that the write happened (unlike the mapping + * step, which only captured intent). + */ +function ImportCommitted({ + projectId, + result, + onStartOver, +}: { + projectId: string; + result: CommitResponse; + onStartOver: () => void; +}) { + const { committed, createdByWorkstream, summary } = result; + return ( +
+
+ +
+

Import committed

+

+ Created{" "} + + {committed.workOrdersCreated.toLocaleString("en-GB")} + {" "} + work order{committed.workOrdersCreated === 1 ? "" : "s"} across{" "} + {committed.propertiesLinked.toLocaleString("en-GB")} propert + {committed.propertiesLinked === 1 ? "y" : "ies"}. + {summary.duplicates > 0 && ( + <> + {" "} + {summary.duplicates.toLocaleString("en-GB")} duplicate row + {summary.duplicates === 1 ? " was" : "s were"} skipped. + + )} + {summary.errors > 0 && ( + <> + {" "} + {summary.errors.toLocaleString("en-GB")} error row + {summary.errors === 1 ? " was" : "s were"} excluded. + + )} +

+ + {createdByWorkstream.length > 0 && ( +
    + {createdByWorkstream.map((w) => ( +
  • + {w.workstreamName} + + {w.count.toLocaleString("en-GB")} + +
  • + ))} +
+ )} + +
+ + + + +
+
+
+
+ ); +} + +function StatTile({ + testId, + label, + value, + tone, + icon, +}: { + testId: string; + label: string; + value: number; + tone: "success" | "error" | "muted"; + icon: React.ReactNode; +}) { + const tones = { + success: "border-green-200 bg-green-50 text-green-700", + error: "border-red-200 bg-red-50 text-red-700", + muted: "border-gray-200 bg-gray-50 text-gray-600", + } as const; + return ( +
+
+ {label} + {icon} +
+

+ {value.toLocaleString("en-GB")} +

+
+ ); +} + +function TabButton({ + active, + onClick, + testId, + children, +}: { + active: boolean; + onClick: () => void; + testId: string; + children: React.ReactNode; +}) { + return ( + + ); +} + +function Th({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} + +/** Client-side CSV of every reported issue — the wireframe's optional export. */ +function downloadIssuesCsv( + filename: string, + errors: RowIssue[], + duplicates: RowIssue[], +): void { + const rows = [ + ["Row", "Identifier", "Type", "Issue", "Affected field"], + ...errors.map((e) => [e.rowNumber, e.identifier, "Error", e.issue, e.field]), + ...duplicates.map((d) => [ + d.rowNumber, + d.identifier, + "Skipped duplicate", + d.issue, + d.field, + ]), + ]; + const csv = rows + .map((r) => r.map((cell) => csvCell(String(cell))).join(",")) + .join("\r\n"); + const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" }); + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = `${filename.replace(/\.[^.]+$/, "")}-import-issues.csv`; + link.click(); + URL.revokeObjectURL(url); +} + +function csvCell(value: string): string { + return /[",\r\n]/.test(value) ? `"${value.replace(/"/g, '""')}"` : value; +} diff --git a/src/app/projects/[projectId]/import/page.tsx b/src/app/projects/[projectId]/import/page.tsx index b3368ef0..af44c64b 100644 --- a/src/app/projects/[projectId]/import/page.tsx +++ b/src/app/projects/[projectId]/import/page.tsx @@ -14,14 +14,16 @@ export const metadata = { }; /** - * Work-order import, steps 1 and 2 of 3 — upload and parse (issue #414), then - * column mapping and value mapping (issue #415). + * Work-order import, all three steps — upload and parse (issue #414), column + * and value mapping (issue #415), then validation and the insert-only commit + * (issue #417). * - * Wireframes: `07-import-upload.html`, then `08-map-columns-rebuilt.html` and - * `09-map-columns-workstream-unlocked.html`. Both steps live behind this one - * route because the parsed file is held client-side (ADR-0020) — a separate - * mapping URL would drop it. Validation and commit (#417) follow; this page - * still writes nothing. + * Wireframes: `07-import-upload.html`, `08-map-columns-rebuilt.html`, + * `09-map-columns-workstream-unlocked.html`, `10-validate-import.html`. All + * three steps live behind this one route because the parsed file is held + * client-side (ADR-0020) — a separate URL per step would drop it. This server + * component still writes nothing; the only write is the commit route the + * validation step POSTs to. * * Only internal and client-org managers may import, so this checks * `canManageProject` rather than settling for the layout's view-level guard — @@ -73,7 +75,7 @@ export default async function ImportPage(props: {

- Work-order import · Steps 1–2 of 3 + Work-order import · Steps 1–3 of 3

{ + // The org's portfolios. With none, nothing can match — and `inArray` over an + // empty set is invalid — so short-circuit to an all-unmatched context. + const portfolioRows = await db + .select({ portfolioId: portfolioOrganisation.portfolioId }) + .from(portfolioOrganisation) + .where(eq(portfolioOrganisation.organisationId, organisationId)); + const portfolioIds = portfolioRows.map((r) => r.portfolioId); + + const [propertyMatch, existingWorkOrderKeys, references] = await Promise.all([ + portfolioIds.length === 0 + ? Promise.resolve({ + propertyIdByLandlordId: {}, + propertyIdByUprn: {}, + ambiguousIdentifiers: [], + }) + : loadPropertyMatches(portfolioIds, landlordIds, uprns), + loadExistingWorkOrderKeys(projectId), + loadReferenceFacts(fileReferences), + ]); + + return { + ...propertyMatch, + existingWorkOrderKeys, + existingReferences: references.colliding, + autoReferenceStart: references.autoStart, + }; +} + +/** + * Match the file's identifiers to properties in the given portfolios, building + * landlord-id and UPRN lookup maps. An identifier that resolves to more than + * one property is dropped from its map and recorded as ambiguous — a work order + * must not silently pick one of two candidates. + */ +async function loadPropertyMatches( + portfolioIds: bigint[], + landlordIds: string[], + uprns: string[], +): Promise<{ + propertyIdByLandlordId: Record; + propertyIdByUprn: Record; + ambiguousIdentifiers: string[]; +}> { + // UPRNs are a bigint column; keep only the well-formed ones for the lookup. + const uprnBigints = uprns + .filter((u) => /^\d+$/.test(u)) + .map((u) => BigInt(u)); + + const filters = []; + if (landlordIds.length > 0) { + filters.push(inArray(property.landlordPropertyId, landlordIds)); + } + if (uprnBigints.length > 0) { + filters.push(inArray(property.uprn, uprnBigints)); + } + // Nothing to look up (a file with no usable identifiers at all). + if (filters.length === 0) { + return { + propertyIdByLandlordId: {}, + propertyIdByUprn: {}, + ambiguousIdentifiers: [], + }; + } + + const rows = await db + .select({ + id: property.id, + landlordPropertyId: property.landlordPropertyId, + uprn: property.uprn, + }) + .from(property) + .where( + and( + inArray(property.portfolioId, portfolioIds), + eq(property.markedForDeletion, false), + // Matched by either identifier column. + filters.length === 1 ? filters[0] : or(...filters), + ), + ); + + const wantedLandlord = new Set(landlordIds); + const wantedUprn = new Set(uprns); + const ambiguous = new Set(); + + const byLandlord: Record = {}; + const byUprn: Record = {}; + + for (const row of rows) { + const propId = row.id.toString(); + if (row.landlordPropertyId !== null && wantedLandlord.has(row.landlordPropertyId)) { + recordMatch(byLandlord, ambiguous, row.landlordPropertyId, propId); + } + if (row.uprn !== null) { + const uprnStr = row.uprn.toString(); + if (wantedUprn.has(uprnStr)) { + recordMatch(byUprn, ambiguous, uprnStr, propId); + } + } + } + + return { + propertyIdByLandlordId: byLandlord, + propertyIdByUprn: byUprn, + ambiguousIdentifiers: [...ambiguous], + }; +} + +/** + * Record one identifier→property match. The first match populates the map; a + * second, different property makes the identifier ambiguous — it is removed + * from the map and never restored, so no row resolves against it. + */ +function recordMatch( + map: Record, + ambiguous: Set, + key: string, + propertyId: string, +): void { + if (ambiguous.has(key)) return; + const existing = map[key]; + if (existing === undefined) { + map[key] = propertyId; + } else if (existing !== propertyId) { + delete map[key]; + ambiguous.add(key); + } +} + +/** + * Every (property, workstream) pair that already has a work order in this + * project, as the duplicate/idempotency key. Joined work_order → stage → + * workstream so the pair is (work_order.property_id, project_workstream.id). + */ +async function loadExistingWorkOrderKeys(projectId: bigint): Promise { + const rows = await db + .select({ + propertyId: workOrder.propertyId, + projectWorkstreamId: projectWorkstreamStage.projectWorkstreamId, + }) + .from(workOrder) + .innerJoin( + projectWorkstreamStage, + eq(workOrder.projectWorkstreamStageId, projectWorkstreamStage.id), + ) + .innerJoin( + projectWorkstream, + eq(projectWorkstreamStage.projectWorkstreamId, projectWorkstream.id), + ) + .where(eq(projectWorkstream.projectId, projectId)); + + return rows.map((r) => + workOrderKey(r.propertyId.toString(), r.projectWorkstreamId.toString()), + ); +} + +/** + * Reference facts: which of the file's provided references are already taken + * (so a colliding one is an error, not a transaction failure), and where the + * auto `WO-` sequence should start (one past the highest existing `WO-n`, + * so auto refs never collide across imports). + */ +async function loadReferenceFacts( + fileReferences: string[], +): Promise<{ colliding: string[]; autoStart: number }> { + const [colliding, autoRows] = await Promise.all([ + fileReferences.length === 0 + ? Promise.resolve<{ reference: string }[]>([]) + : db + .select({ reference: workOrder.reference }) + .from(workOrder) + .where(inArray(workOrder.reference, fileReferences)), + // Bounded by however many auto references prior imports created. + db + .select({ reference: workOrder.reference }) + .from(workOrder) + .where(sql`${workOrder.reference} ~ '^WO-[0-9]+$'`), + ]); + + let maxSeq = 0; + for (const { reference } of autoRows) { + const match = reference.match(/^WO-(\d+)$/); + if (match) maxSeq = Math.max(maxSeq, Number(match[1])); + } + + return { + colliding: colliding.map((r) => r.reference), + autoStart: maxSeq + 1, + }; +} + +export interface CommitImportResult { + workOrdersCreated: number; + propertiesLinked: number; +} + +/** + * Insert the ready set in one transaction — all rows or none (the #417 AC's + * "all-or-nothing per valid row set"). + * + * - `project_property` is upserted (`onConflictDoNothing` on the + * `(project_id, property_id)` unique) so a property already on the project + * is left alone and a re-run adds nothing. + * - one `work_order` per ready row. `status` and `priority` are **never + * written** (the #417 AC): both are omitted from the insert and take their + * DB defaults (`''` from migration 0281, `false` from 0280). The stage FK is + * the row's resolved stage — v1's only lifecycle mechanism. + * + * Returns nothing to insert as a no-op success, so committing an all-duplicate + * re-run does not open an empty transaction. + */ +export async function commitImport( + projectId: bigint, + ready: ReadyWorkOrder[], +): Promise { + if (ready.length === 0) { + return { workOrdersCreated: 0, propertiesLinked: 0 }; + } + + const distinctPropertyIds = [...new Set(ready.map((r) => r.propertyId))]; + + return db.transaction(async (tx) => { + await tx + .insert(projectProperty) + .values( + distinctPropertyIds.map((propertyId) => ({ + projectId, + propertyId: BigInt(propertyId), + })), + ) + .onConflictDoNothing(); + + await tx.insert(workOrder).values( + ready.map((r) => ({ + reference: r.reference, + projectWorkstreamContractorId: BigInt(r.projectWorkstreamContractorId), + propertyId: BigInt(r.propertyId), + projectWorkstreamStageId: BigInt(r.projectWorkstreamStageId), + forecastEnd: r.forecastEnd, + // status and priority deliberately omitted — see the doc comment. + })), + ); + + return { + workOrdersCreated: ready.length, + propertiesLinked: distinctPropertyIds.length, + }; + }); +} diff --git a/src/lib/projects/import/validation.test.ts b/src/lib/projects/import/validation.test.ts new file mode 100644 index 00000000..43798414 --- /dev/null +++ b/src/lib/projects/import/validation.test.ts @@ -0,0 +1,341 @@ +import { describe, it, expect } from "vitest"; +import type { ImportColumnMapping } from "./columnMapping"; +import { EMPTY_VALUE_MAPPING, type ImportValueMapping } from "./valueMapping"; +import type { ImportMappingTargets } from "./mappingTargets"; +import { + formatAutoReference, + validateImportRows, + workOrderKey, + type ImportMatchContext, +} from "./validation"; + +/** + * Column order matches the template: identifier, UPRN, workstream, contractor, + * stage, WO reference, forecast end. + */ +const COLUMN_MAPPING: ImportColumnMapping = { + fields: { + landlordPropertyId: 0, + uprn: 1, + workstream: 2, + contractor: 3, + stage: 4, + workOrderReference: 5, + forecastEndDate: 6, + }, + ignoredColumns: [], +}; + +/** [landlord, uprn, workstream, contractor, stage, reference, forecastEnd]. */ +function row( + landlord: string, + uprn: string, + workstream: string, + contractor: string, + stage = "", + reference = "", + forecastEnd = "", +): string[] { + return [landlord, uprn, workstream, contractor, stage, reference, forecastEnd]; +} + +const TARGETS: ImportMappingTargets = { + workstreams: [ + { + id: "10", + workstreamId: "1", + name: "Windows", + stages: [ + { id: "100", name: "Ordered", order: 1 }, + { id: "101", name: "In progress", order: 2 }, + ], + contractors: [ + { id: "1000", organisationId: "org-a", name: "Acme Windows Ltd" }, + ], + }, + { + id: "11", + workstreamId: "2", + name: "Doors", + stages: [{ id: "110", name: "Ordered", order: 1 }], + contractors: [ + { id: "1100", organisationId: "org-b", name: "Best Doors Ltd" }, + ], + }, + ], +}; + +function context(overrides: Partial = {}): ImportMatchContext { + return { + propertyIdByLandlordId: { "PROP-1": "500", "PROP-2": "501" }, + propertyIdByUprn: { "999": "502" }, + ambiguousIdentifiers: [], + existingWorkOrderKeys: [], + existingReferences: [], + autoReferenceStart: 1, + ...overrides, + }; +} + +function validate( + rows: string[][], + ctx = context(), + valueMapping: ImportValueMapping = EMPTY_VALUE_MAPPING, +) { + return validateImportRows(rows, COLUMN_MAPPING, valueMapping, TARGETS, ctx); +} + +describe("validateImportRows — happy path", () => { + it("resolves a fully-specified row to a ready work order", () => { + const result = validate([ + row("PROP-1", "", "Windows", "Acme Windows Ltd", "In progress", "WO-A", "2026-09-30"), + ]); + + expect(result.summary).toEqual({ total: 1, ready: 1, errors: 0, duplicates: 0 }); + expect(result.ready[0]).toMatchObject({ + rowNumber: 1, + identifier: "PROP-1", + propertyId: "500", + projectWorkstreamId: "10", + projectWorkstreamContractorId: "1000", + projectWorkstreamStageId: "101", + reference: "WO-A", + forecastEnd: "2026-09-30", + }); + }); + + it("never carries status or priority on a ready row", () => { + const result = validate([row("PROP-1", "", "Windows", "Acme Windows Ltd")]); + expect(result.ready[0]).not.toHaveProperty("status"); + expect(result.ready[0]).not.toHaveProperty("priority"); + }); +}); + +describe("validateImportRows — stage", () => { + it("falls back to the workstream's first stage when the cell is blank", () => { + const result = validate([row("PROP-1", "", "Windows", "Acme Windows Ltd", "")]); + // stages[0] for Windows is Ordered (order 1) = id 100. + expect(result.ready[0].projectWorkstreamStageId).toBe("100"); + }); + + it("uses the matched stage when one is provided", () => { + const result = validate([ + row("PROP-1", "", "Windows", "Acme Windows Ltd", "In progress"), + ]); + expect(result.ready[0].projectWorkstreamStageId).toBe("101"); + }); + + it("errors on a provided stage that is not on the ladder", () => { + const result = validate([ + row("PROP-1", "", "Windows", "Acme Windows Ltd", "Charged"), + ]); + expect(result.summary.ready).toBe(0); + expect(result.errors[0]).toMatchObject({ rowNumber: 1, field: "Stage" }); + }); +}); + +describe("validateImportRows — property matching", () => { + it("matches on landlord property id first", () => { + const result = validate([row("PROP-2", "", "Doors", "Best Doors Ltd")]); + expect(result.ready[0].propertyId).toBe("501"); + }); + + it("falls back to UPRN when the landlord id does not match", () => { + const result = validate([row("", "999", "Windows", "Acme Windows Ltd")]); + expect(result.ready[0].propertyId).toBe("502"); + }); + + it("errors when neither identifier matches", () => { + const result = validate([row("NOPE", "", "Windows", "Acme Windows Ltd")]); + expect(result.errors[0]).toMatchObject({ + rowNumber: 1, + field: "Property identifier", + }); + expect(result.errors[0].issue).toContain("No property"); + }); + + it("errors when the row has no identifier at all", () => { + const result = validate([row("", "", "Windows", "Acme Windows Ltd")]); + expect(result.errors[0].issue).toContain("no property identifier"); + }); + + it("reports an ambiguous identifier distinctly, and never guesses", () => { + const result = validate( + [row("AMB", "", "Windows", "Acme Windows Ltd")], + context({ ambiguousIdentifiers: ["AMB"] }), + ); + expect(result.summary.ready).toBe(0); + expect(result.errors[0].issue).toContain("more than one property"); + }); +}); + +describe("validateImportRows — workstream and contractor", () => { + it("errors on a blank workstream", () => { + const result = validate([row("PROP-1", "", "", "Acme Windows Ltd")]); + expect(result.errors[0]).toMatchObject({ field: "Workstream" }); + }); + + it("errors on an unmapped workstream value", () => { + const result = validate([row("PROP-1", "", "Loft insulation", "Acme Windows Ltd")]); + expect(result.errors[0]).toMatchObject({ field: "Workstream" }); + }); + + it("errors on a blank contractor (contractor is required from the file)", () => { + const result = validate([row("PROP-1", "", "Windows", "")]); + expect(result.errors[0]).toMatchObject({ field: "Contractor" }); + }); + + it("errors when the contractor is not assigned to that workstream", () => { + // Best Doors is a real contractor, but only on Doors — not on Windows. + const result = validate([row("PROP-1", "", "Windows", "Best Doors Ltd")]); + expect(result.summary.ready).toBe(0); + expect(result.errors[0]).toMatchObject({ field: "Contractor" }); + expect(result.errors[0].issue).toContain("Windows"); + }); + + it("resolves the same contractor scoped to its own workstream", () => { + const result = validate([row("PROP-1", "", "Doors", "Best Doors Ltd")]); + expect(result.ready[0].projectWorkstreamContractorId).toBe("1100"); + }); +}); + +describe("validateImportRows — duplicates", () => { + it("skips a (property, workstream) pair that already has a work order", () => { + const result = validate( + [row("PROP-1", "", "Windows", "Acme Windows Ltd")], + context({ existingWorkOrderKeys: [workOrderKey("500", "10")] }), + ); + expect(result.summary).toMatchObject({ ready: 0, errors: 0, duplicates: 1 }); + expect(result.duplicates[0]).toMatchObject({ rowNumber: 1, field: "Workstream" }); + }); + + it("imports a repeated pair once — first ready, rest skipped", () => { + const result = validate([ + row("PROP-1", "", "Windows", "Acme Windows Ltd"), + row("PROP-1", "", "Windows", "Acme Windows Ltd"), + ]); + expect(result.summary).toMatchObject({ ready: 1, duplicates: 1 }); + expect(result.ready[0].rowNumber).toBe(1); + expect(result.duplicates[0].rowNumber).toBe(2); + }); + + it("does not let an errored row reserve the pair for a later good row", () => { + const result = validate([ + // Same (property, workstream) but a bad contractor — an error, not ready. + row("PROP-1", "", "Windows", "Nobody Ltd"), + row("PROP-1", "", "Windows", "Acme Windows Ltd"), + ]); + expect(result.summary).toMatchObject({ ready: 1, errors: 1, duplicates: 0 }); + expect(result.ready[0].rowNumber).toBe(2); + }); + + it("is idempotent: a re-run of an already-committed file is all duplicates", () => { + const rows = [ + row("PROP-1", "", "Windows", "Acme Windows Ltd"), + row("PROP-2", "", "Doors", "Best Doors Ltd"), + ]; + const first = validate(rows); + expect(first.summary.ready).toBe(2); + + const seeded = context({ + existingWorkOrderKeys: first.ready.map((r) => + workOrderKey(r.propertyId, r.projectWorkstreamId), + ), + }); + const rerun = validate(rows, seeded); + expect(rerun.summary).toMatchObject({ ready: 0, duplicates: 2, errors: 0 }); + }); +}); + +describe("validateImportRows — references", () => { + it("auto-generates zero-padded WO references from the given start", () => { + const result = validate([ + row("PROP-1", "", "Windows", "Acme Windows Ltd"), + row("PROP-2", "", "Doors", "Best Doors Ltd"), + ]); + expect(result.ready.map((r) => r.reference)).toEqual(["WO-0001", "WO-0002"]); + }); + + it("starts the auto sequence past the highest existing WO number", () => { + const result = validate( + [row("PROP-1", "", "Windows", "Acme Windows Ltd")], + context({ autoReferenceStart: 42 }), + ); + expect(result.ready[0].reference).toBe("WO-0042"); + }); + + it("keeps a provided reference and skips it in the auto sequence", () => { + const result = validate([ + row("PROP-1", "", "Windows", "Acme Windows Ltd", "", "ASSET-7"), + row("PROP-2", "", "Doors", "Best Doors Ltd"), + ]); + expect(result.ready.map((r) => r.reference)).toEqual(["ASSET-7", "WO-0001"]); + }); + + it("errors on a provided reference already used in the database", () => { + const result = validate( + [row("PROP-1", "", "Windows", "Acme Windows Ltd", "", "WO-EXISTING")], + context({ existingReferences: ["WO-EXISTING"] }), + ); + expect(result.errors[0]).toMatchObject({ field: "Work order reference" }); + }); + + it("errors on a reference repeated within the same file", () => { + const result = validate([ + row("PROP-1", "", "Windows", "Acme Windows Ltd", "", "DUP"), + row("PROP-2", "", "Doors", "Best Doors Ltd", "", "DUP"), + ]); + expect(result.summary).toMatchObject({ ready: 1, errors: 1 }); + expect(result.errors[0].rowNumber).toBe(2); + }); +}); + +describe("validateImportRows — forecast end date", () => { + it("passes an ISO date through", () => { + const result = validate([ + row("PROP-1", "", "Windows", "Acme Windows Ltd", "", "", "2026-12-01"), + ]); + expect(result.ready[0].forecastEnd).toBe("2026-12-01"); + }); + + it("parses a UK dd/mm/yyyy date", () => { + const result = validate([ + row("PROP-1", "", "Windows", "Acme Windows Ltd", "", "", "01/12/2026"), + ]); + expect(result.ready[0].forecastEnd).toBe("2026-12-01"); + }); + + it("treats a blank or unparseable forecast end as null", () => { + const blank = validate([row("PROP-1", "", "Windows", "Acme Windows Ltd")]); + expect(blank.ready[0].forecastEnd).toBeNull(); + + const junk = validate([ + row("PROP-2", "", "Doors", "Best Doors Ltd", "", "", "sometime"), + ]); + expect(junk.ready[0].forecastEnd).toBeNull(); + }); +}); + +describe("validateImportRows — one issue per row, in order", () => { + it("reports the property problem before the workstream problem", () => { + // Both the identifier and the workstream are bad; property wins. + const result = validate([row("NOPE", "", "Loft", "Acme Windows Ltd")]); + expect(result.errors).toHaveLength(1); + expect(result.errors[0].field).toBe("Property identifier"); + }); + + it("settles a duplicate before looking at its contractor", () => { + const result = validate( + [row("PROP-1", "", "Windows", "Nobody At All")], + context({ existingWorkOrderKeys: [workOrderKey("500", "10")] }), + ); + expect(result.summary).toMatchObject({ duplicates: 1, errors: 0 }); + }); +}); + +describe("formatAutoReference", () => { + it("zero-pads to four digits and does not clip larger numbers", () => { + expect(formatAutoReference(7)).toBe("WO-0007"); + expect(formatAutoReference(12345)).toBe("WO-12345"); + }); +}); diff --git a/src/lib/projects/import/validation.ts b/src/lib/projects/import/validation.ts new file mode 100644 index 00000000..75671496 --- /dev/null +++ b/src/lib/projects/import/validation.ts @@ -0,0 +1,440 @@ +/** + * Work-order import — validation and row resolution (issue #417, step 3 of 3). + * + * This is the pure core the validation report and the commit route both run. + * It takes the parsed rows, the mapping the user settled in step 2 + * (`ImportSession`), the project's targets, and a bundle of database-derived + * facts (`ImportMatchContext`), and decides — per row — one of three outcomes: + * + * - **ready** — every value resolved; the row becomes exactly one + * `work_order`. Carries the resolved ids the commit inserts. + * - **error** — a value could not be resolved (unmatched property, blank or + * unmapped workstream/contractor, a *provided* stage that matches nothing, + * or a work-order reference already in use). Excluded from the commit. + * - **duplicate** — the row's (property, workstream) pair already has a work + * order in this project (or an earlier ready row in the same file already + * claimed it). Reported and skipped — never an error (ADR-0019 / #417 AC). + * + * Pure and DB-free on purpose, mirroring the rest of `@/lib/projects/import`: + * every fact it needs is passed in as plain data, so it unit-tests against + * in-memory fixtures with no connection. The repository + * (`@/app/repositories/projects/importCommitRepository`) gathers the facts; + * the route handlers combine the two. + * + * ## Why commit re-resolves rather than trusting the client + * + * The value mapping stores only the user's *explicit* decisions — an untouched + * value relies on auto-match. So "what the user saw as ready" is not written + * down anywhere; it is re-derived from `(value, decisions, targets)` by the + * exact same {@link resolveValue} the mapping screen used. Running that here + * guarantees the committed set matches the previewed one, and it means a + * tampered or stale client payload cannot smuggle in a row whose values do not + * actually resolve against the project's live configuration. + * + * ## One issue per row + * + * A row is reported once, against the first check it fails, in this order: + * property → workstream → duplicate → contractor → stage → reference. The order + * is deliberate: a row with no matchable property cannot have a meaningful + * workstream check, and a duplicate is skipped whatever else is wrong with it, + * so it is settled before contractor/stage are even looked at. + */ + +import { formatUkDate, parseUkDate } from "@/utils/dates"; +import { cellFor, type ImportColumnMapping } from "./columnMapping"; +import { + resolveValue, + scopedValueKey, + valueKey, + type ImportValueMapping, +} from "./valueMapping"; +import type { + ContractorTarget, + ImportMappingTargets, + StageTarget, + WorkstreamTarget, +} from "./mappingTargets"; + +/** The field labels the report shows in its "Affected field" column. */ +export const IMPORT_FIELD_LABELS = { + property: "Property identifier", + workstream: "Workstream", + contractor: "Contractor", + stage: "Stage", + reference: "Work order reference", +} as const; + +/** Default zero-pad width for an auto-generated `WO-` reference. */ +export const AUTO_REFERENCE_PAD = 4; + +/** + * A resolved row, ready to become one `work_order`. Every id is a string (the + * `bigserial`/uuid form that crossed the boundary from the mapping targets); + * the commit parses them back to `bigint` at the insert. + */ +export interface ReadyWorkOrder { + /** 1-based data-row number, as the report and the file's user count rows. */ + rowNumber: number; + /** The landlord property id or UPRN cell, for display in the report. */ + identifier: string; + /** `property.id`. */ + propertyId: string; + /** `project_workstream.id`. */ + projectWorkstreamId: string; + /** `workstream.name`, for the post-commit per-workstream summary. */ + workstreamName: string; + /** `project_workstream_contractor.id` — the assignment, never null. */ + projectWorkstreamContractorId: string; + /** `project_workstream_stage.id` — the mapped stage, or the ladder's first. */ + projectWorkstreamStageId: string; + /** The mapped reference, or the auto `WO-` this row was given. */ + reference: string; + /** `YYYY-MM-DD`, or null when the column is unmapped/blank/unparseable. */ + forecastEnd: string | null; +} + +/** One reported problem — an error or a skipped duplicate. */ +export interface RowIssue { + rowNumber: number; + /** The row's identifier cell, for display; "" when even that is blank. */ + identifier: string; + /** Human-readable description of what is wrong. */ + issue: string; + /** Which field the issue is against — the report's "Affected field". */ + field: string; +} + +/** The database-derived facts row resolution needs, all as plain data. */ +export interface ImportMatchContext { + /** + * `landlordPropertyId` → `property.id`, for properties in the project org's + * portfolios only. An identifier that matches more than one property is + * absent here and present in {@link ambiguousIdentifiers} instead. + */ + propertyIdByLandlordId: Record; + /** `uprn` (as text) → `property.id`, same scoping and ambiguity handling. */ + propertyIdByUprn: Record; + /** Identifiers that matched more than one property — reported, never guessed. */ + ambiguousIdentifiers: string[]; + /** + * `":"` pairs that already have a work order + * in this project. Seeds the duplicate check, which is what makes a re-run of + * the same file idempotent. + */ + existingWorkOrderKeys: string[]; + /** Every existing `work_order.reference` — a provided ref colliding is an error. */ + existingReferences: string[]; + /** + * The seq the first auto `WO-` reference starts from — one past the + * highest existing `WO-`, so auto refs never collide across imports. + */ + autoReferenceStart: number; +} + +export interface ImportValidationResult { + ready: ReadyWorkOrder[]; + errors: RowIssue[]; + duplicates: RowIssue[]; + summary: { + total: number; + ready: number; + errors: number; + duplicates: number; + }; +} + +/** `":"` — the duplicate/idempotency key. */ +export function workOrderKey( + propertyId: string, + projectWorkstreamId: string, +): string { + return `${propertyId}:${projectWorkstreamId}`; +} + +const name = (t: T): string => t.name; +const id = (t: T): string => t.id; + +/** + * Resolve and classify every row. See the module header for the outcome set and + * the fixed check order. + */ +export function validateImportRows( + rows: string[][], + columnMapping: ImportColumnMapping, + valueMapping: ImportValueMapping, + targets: ImportMappingTargets, + context: ImportMatchContext, +): ImportValidationResult { + const workstreamsById = new Map(targets.workstreams.map((w) => [w.id, w])); + const ambiguous = new Set(context.ambiguousIdentifiers); + + // Seeded with the project's existing (property, workstream) pairs so a re-run + // skips what it already created; grows as ready rows claim new pairs, so a + // file that names the same pair twice imports it once. + const seen = new Set(context.existingWorkOrderKeys); + // Existing + this-run references, so no two committed work orders collide on + // `reference` (which is UNIQUE) and the transaction never fails on it. + const usedReferences = new Set(context.existingReferences); + let autoSeq = context.autoReferenceStart; + + const ready: ReadyWorkOrder[] = []; + const errors: RowIssue[] = []; + const duplicates: RowIssue[] = []; + + rows.forEach((row, i) => { + const rowNumber = i + 1; + const landlord = cellFor(row, columnMapping, "landlordPropertyId"); + const uprn = cellFor(row, columnMapping, "uprn"); + const identifier = landlord !== "" ? landlord : uprn; + + // 1 — Property. Landlord property id first, then UPRN (the matching rule). + const propertyId = matchProperty(landlord, uprn, context, ambiguous); + if (propertyId === null) { + errors.push({ + rowNumber, + identifier, + issue: propertyIssue(landlord, uprn, ambiguous), + field: IMPORT_FIELD_LABELS.property, + }); + return; + } + + // 2 — Workstream. Blank or unmatched is an error. + const workstreamCell = cellFor(row, columnMapping, "workstream"); + if (workstreamCell === "") { + errors.push({ + rowNumber, + identifier, + issue: "This row has no workstream value.", + field: IMPORT_FIELD_LABELS.workstream, + }); + return; + } + const workstreamMatch = resolveValue( + valueKey(workstreamCell), + workstreamCell, + valueMapping.workstreams, + targets.workstreams, + name, + id, + ); + const workstream = + workstreamMatch.targetId !== null + ? workstreamsById.get(workstreamMatch.targetId) + : undefined; + if (!workstream) { + errors.push({ + rowNumber, + identifier, + issue: `Workstream "${workstreamCell}" isn't one of this project's workstreams.`, + field: IMPORT_FIELD_LABELS.workstream, + }); + return; + } + + // 3 — Duplicate. A pair already claimed (in the DB or earlier in this file) + // is skipped whatever else the row says, so it is settled before contractor + // and stage are checked. + const key = workOrderKey(propertyId, workstream.id); + if (seen.has(key)) { + duplicates.push({ + rowNumber, + identifier, + issue: `This property already has a ${workstream.name} work order in this project.`, + field: IMPORT_FIELD_LABELS.workstream, + }); + return; + } + + // 4 — Contractor. Blank or not-assigned-to-this-workstream is an error. + const contractorCell = cellFor(row, columnMapping, "contractor"); + if (contractorCell === "") { + errors.push({ + rowNumber, + identifier, + issue: "This row has no contractor value.", + field: IMPORT_FIELD_LABELS.contractor, + }); + return; + } + const contractorMatch = resolveValue( + scopedValueKey(workstream.id, contractorCell), + contractorCell, + valueMapping.contractors, + workstream.contractors, + name, + id, + ); + if (contractorMatch.targetId === null) { + errors.push({ + rowNumber, + identifier, + issue: `Contractor "${contractorCell}" isn't assigned to the ${workstream.name} workstream.`, + field: IMPORT_FIELD_LABELS.contractor, + }); + return; + } + + // 5 — Stage. Blank falls back to the ladder's first stage; a *provided* + // stage that matches nothing is an error. + const stageCell = cellFor(row, columnMapping, "stage"); + const stageId = resolveStage( + stageCell, + workstream, + valueMapping, + ); + if (stageId === null) { + errors.push({ + rowNumber, + identifier, + issue: + stageCell === "" + ? `The ${workstream.name} workstream has no stages configured.` + : `Stage "${stageCell}" isn't on the ${workstream.name} workstream's ladder.`, + field: IMPORT_FIELD_LABELS.stage, + }); + return; + } + + // 6 — Reference. A provided one that collides is an error; otherwise the row + // is given the next free auto `WO-`. + const referenceCell = cellFor(row, columnMapping, "workOrderReference"); + let reference: string; + if (referenceCell !== "") { + if (usedReferences.has(referenceCell)) { + errors.push({ + rowNumber, + identifier, + issue: `Work order reference "${referenceCell}" is already in use.`, + field: IMPORT_FIELD_LABELS.reference, + }); + return; + } + reference = referenceCell; + } else { + reference = nextAutoReference(autoSeq, usedReferences); + autoSeq = referenceSeq(reference) + 1; + } + + // 7 — Forecast end. Optional: blank or unparseable becomes null. + const forecastEnd = parseForecastEnd( + cellFor(row, columnMapping, "forecastEndDate"), + ); + + seen.add(key); + usedReferences.add(reference); + ready.push({ + rowNumber, + identifier, + propertyId, + projectWorkstreamId: workstream.id, + workstreamName: workstream.name, + projectWorkstreamContractorId: contractorMatch.targetId, + projectWorkstreamStageId: stageId, + reference, + forecastEnd, + }); + }); + + return { + ready, + errors, + duplicates, + summary: { + total: rows.length, + ready: ready.length, + errors: errors.length, + duplicates: duplicates.length, + }, + }; +} + +/** Landlord property id first, then UPRN. null when neither resolves. */ +function matchProperty( + landlord: string, + uprn: string, + context: ImportMatchContext, + ambiguous: Set, +): string | null { + if (landlord !== "" && !ambiguous.has(landlord)) { + const byLandlord = context.propertyIdByLandlordId[landlord]; + if (byLandlord !== undefined) return byLandlord; + } + if (uprn !== "" && !ambiguous.has(uprn)) { + const byUprn = context.propertyIdByUprn[uprn]; + if (byUprn !== undefined) return byUprn; + } + return null; +} + +/** Why a property did not match — ambiguity gets its own message. */ +function propertyIssue( + landlord: string, + uprn: string, + ambiguous: Set, +): string { + if (landlord === "" && uprn === "") { + return "This row has no property identifier."; + } + if (ambiguous.has(landlord) || ambiguous.has(uprn)) { + return "This identifier matches more than one property in the project's portfolios."; + } + return "No property in this project's portfolios matches this identifier."; +} + +/** + * The stage id for a row: the ladder's first stage when the cell is blank + * (ADR-0019), the matched stage when it is not, or null when a provided stage + * matches nothing or the ladder is somehow empty. + */ +function resolveStage( + stageCell: string, + workstream: WorkstreamTarget, + valueMapping: ImportValueMapping, +): string | null { + if (stageCell === "") { + // stages[0] is the ladder start (mappingTargets sorts by `order` asc). The + // readiness gate guarantees ≥1 stage; the ?? null is defence, not a path. + return workstream.stages[0]?.id ?? null; + } + const match = resolveValue( + scopedValueKey(workstream.id, stageCell), + stageCell, + valueMapping.stages, + workstream.stages, + name, + id, + ); + return match.targetId; +} + +/** `YYYY-MM-DD` for an ISO or dd/mm/yyyy cell; null for blank or unparseable. */ +function parseForecastEnd(cell: string): string | null { + if (cell === "") return null; + // Already a real ISO date? formatUkDate returns "" for anything that isn't. + if (formatUkDate(cell) !== "") return cell; + // Otherwise accept what a person would type into the UK date field. + return parseUkDate(cell); +} + +/** The numeric seq of a `WO-` reference, or -1 when it is not one. */ +function referenceSeq(reference: string): number { + const match = reference.match(/^WO-(\d+)$/); + return match ? Number(match[1]) : -1; +} + +/** The next `WO-` at or after `seq` that is not already taken. */ +function nextAutoReference(seq: number, used: Set): string { + let candidate = seq; + let reference = formatAutoReference(candidate); + while (used.has(reference)) { + candidate += 1; + reference = formatAutoReference(candidate); + } + return reference; +} + +/** `WO-` + zero-padded seq. Padding is a floor, so large seqs are not clipped. */ +export function formatAutoReference(seq: number): string { + return `WO-${String(seq).padStart(AUTO_REFERENCE_PAD, "0")}`; +} diff --git a/src/lib/projects/import/valueMapping.ts b/src/lib/projects/import/valueMapping.ts index 38b77ecf..e363aa49 100644 --- a/src/lib/projects/import/valueMapping.ts +++ b/src/lib/projects/import/valueMapping.ts @@ -368,7 +368,7 @@ function tally( * assigned to) is discarded rather than kept as a dangling id — the value falls * back to needing review, which is the truth of the situation. */ -function resolveValue( +export function resolveValue( key: string, value: string, decisions: Record,