From 83f14a9524c24f5d8c0ed43e81e1978e6f8facc8 Mon Sep 17 00:00:00 2001 From: important-new Date: Sat, 18 Jul 2026 19:50:18 +0800 Subject: [PATCH 001/111] fix(auth): scope standalone login lookup to tenant, fail closed on ambiguity --- server/api/auth.ts | 3 +- server/services/auth.service.ts | 26 ++++- tests/unit/auth/auth.service.spec.ts | 4 +- tests/unit/auth/login-tenant-scope.spec.ts | 97 +++++++++++++++++++ .../tenancy/team-remove-softdelete.spec.ts | 2 +- 5 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 tests/unit/auth/login-tenant-scope.spec.ts diff --git a/server/api/auth.ts b/server/api/auth.ts index 0884f0339..676b40c42 100644 --- a/server/api/auth.ts +++ b/server/api/auth.ts @@ -272,7 +272,8 @@ const coreAuthRoutes = createApiRouter() await checkRateLimit(c, 'login'); const body = c.req.valid('json'); - const user = await c.var.services.auth.validateCredentials(body.email, body.password); + const tenantId = c.env.SINGLE_TENANT_ID || '00000000-0000-0000-0000-000000000000'; + const user = await c.var.services.auth.validateCredentials(body.email, body.password, tenantId); const keyring = await c.var.keyringPromise!; const now = Math.floor(Date.now() / 1000); diff --git a/server/services/auth.service.ts b/server/services/auth.service.ts index ca27330cd..dd23c801c 100644 --- a/server/services/auth.service.ts +++ b/server/services/auth.service.ts @@ -47,16 +47,38 @@ export class AuthService { return hashPassword(password); } + /** + * Standalone login row selection. Scoped to the resolved single tenant so a same-email + * global agent (tenant_id NULL) or another tenant's row can never be authenticated, and + * fails closed if the composite unique index is ever violated. See spec login-email-ambiguity. + */ + async findLoginUser(email: string, tenantId: string) { + const db = this.getDrizzle(); + const rows = await db.select().from(users) + .where(and( + eq(users.email, email), + eq(users.tenantId, tenantId), // excludes NULL-tenant agents + other tenants + isNull(users.deletedAt), + )) + .all(); + if (rows.length === 0) return null; + if (rows.length > 1) { + logger.error('[login] ambiguous email within tenant — refusing auth', { tenantId }); + return null; + } + return rows[0]; + } + /** * Validates a user's credentials. Lazily upgrades legacy SHA-256 hashes to PBKDF2. * Runs PBKDF2 even when the email is unknown to hide user-existence via timing. */ - async validateCredentials(email: string, password: string) { + async validateCredentials(email: string, password: string, tenantId: string) { const db = this.getDrizzle(); // Soft-deleted (removed member / self-deleted account) rows are // excluded — a matching row that isn't NULL-deleted-at must never // authenticate, even if the caller somehow still knows the password. - const user = await db.select().from(users).where(and(eq(users.email, email), isNull(users.deletedAt))).get(); + const user = await this.findLoginUser(email, tenantId); if (!user) { // Perform a throwaway verification against a fixed hash so the response time diff --git a/tests/unit/auth/auth.service.spec.ts b/tests/unit/auth/auth.service.spec.ts index 40cbeba4f..aba004060 100644 --- a/tests/unit/auth/auth.service.spec.ts +++ b/tests/unit/auth/auth.service.spec.ts @@ -82,7 +82,7 @@ describe('AuthService', () => { createdAt: new Date(), }); - const result = await authService.validateCredentials(email, password); + const result = await authService.validateCredentials(email, password, 't1'); expect(result.id).toBe('u1'); }); @@ -99,7 +99,7 @@ describe('AuthService', () => { createdAt: new Date(), }); - await expect(authService.validateCredentials(email, 'wrong')) + await expect(authService.validateCredentials(email, 'wrong', 't1')) .rejects.toThrow('Invalid email or password'); }); diff --git a/tests/unit/auth/login-tenant-scope.spec.ts b/tests/unit/auth/login-tenant-scope.spec.ts new file mode 100644 index 000000000..827f30929 --- /dev/null +++ b/tests/unit/auth/login-tenant-scope.spec.ts @@ -0,0 +1,97 @@ +/** + * Standalone login row selection must be tenant-scoped: a same-email global + * agent (`users.tenant_id IS NULL`, `role='agent'`) must never be the row + * authenticated by `/login`. See spec login-email-ambiguity. + */ +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; +import { AuthService } from '../../../server/services/auth.service'; +import { MockKV } from '../mocks'; +import { createTestDb, setupSchema } from '../db'; +import { users, tenants } from '../../../server/lib/db/schema'; +import { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3'; +import * as schema from '../../../server/lib/db/schema'; + +// Mock the drizzle-orm/d1 module to return our in-memory SQLite DB — mirrors +// the harness in auth.service.spec.ts. AuthService.getDrizzle() calls +// drizzle(this.db), so the mock returns the test db regardless of the ctor's +// first arg. +vi.mock('drizzle-orm/d1', () => ({ + drizzle: vi.fn(), +})); + +import { drizzle as mockDrizzle } from 'drizzle-orm/d1'; + +const TENANT = 't1'; + +describe('AuthService.findLoginUser — tenant-scoped, fail-closed row selection', () => { + let authService: AuthService; + let mockKV: MockKV; + let testDb: BetterSQLite3Database; + let sqlite: any; + + beforeEach(async () => { + const setup = createTestDb(); + testDb = setup.db; + sqlite = setup.sqlite; + await setupSchema(sqlite); + + (mockDrizzle as any).mockReturnValue(testDb); + mockKV = new MockKV(); + + await testDb.insert(tenants).values({ + id: TENANT, + name: 'Test Tenant', + slug: 'test', + createdAt: new Date(), + }); + + // Same email on two rows: a tenant-scoped inspector, and a global + // (NULL-tenant) agent account. tenantId is nullable in the schema + // (Agent Accounts A1), so this seeds cleanly without a NOT NULL + // constraint violation. + await testDb.insert(users).values({ + id: 'u_insp', + tenantId: TENANT, + email: 'dup@x.com', + passwordHash: 'H', + role: 'inspector', + createdAt: new Date(), + }); + await testDb.insert(users).values({ + id: 'u_agent', + tenantId: null, + email: 'dup@x.com', + passwordHash: 'H', + role: 'agent', + createdAt: new Date(), + } as any); + + authService = new AuthService({} as any, mockKV as any); + }); + + afterEach(() => { + sqlite.close(); + vi.clearAllMocks(); + }); + + it('selects the tenant inspector, never the global agent', async () => { + const user = await authService.findLoginUser('dup@x.com', TENANT); + expect(user?.id).toBe('u_insp'); + }); + + it('returns null when only a global (NULL-tenant) agent matches', async () => { + // Seed a lone global agent under a DIFFERENT email so the tenant-scoped + // query for it has nothing to match. + await testDb.insert(users).values({ + id: 'u_agent_only', + tenantId: null, + email: 'only-agent@x.com', + passwordHash: 'H', + role: 'agent', + createdAt: new Date(), + } as any); + + const user = await authService.findLoginUser('only-agent@x.com', TENANT); + expect(user).toBeNull(); + }); +}); diff --git a/tests/unit/tenancy/team-remove-softdelete.spec.ts b/tests/unit/tenancy/team-remove-softdelete.spec.ts index 8f9e15ae4..149620041 100644 --- a/tests/unit/tenancy/team-remove-softdelete.spec.ts +++ b/tests/unit/tenancy/team-remove-softdelete.spec.ts @@ -141,7 +141,7 @@ describe('TeamService.removeMember — soft-delete (Task 8a)', () => { // validateCredentials should reject with the same generic error it // uses for an unknown email, never reach a password comparison that // could succeed. - await expect(auth.validateCredentials(MEMBER_EMAIL, 'whatever')) + await expect(auth.validateCredentials(MEMBER_EMAIL, 'whatever', TENANT)) .rejects.toThrow('Invalid email or password'); }); From 4947a505a5a28f64b4a7918c1c99218e3b924850 Mon Sep 17 00:00:00 2001 From: important-new Date: Sat, 18 Jul 2026 19:59:00 +0800 Subject: [PATCH 002/111] chore(auth): refresh tenant-scope baseline for line shift; full-suite gate green --- scripts/tenant-scoping-baseline.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/tenant-scoping-baseline.json b/scripts/tenant-scoping-baseline.json index d3e082731..deefc9483 100644 --- a/scripts/tenant-scoping-baseline.json +++ b/scripts/tenant-scoping-baseline.json @@ -34,9 +34,9 @@ "server/services/agreement/signer-state.ts:53", "server/services/agreement/signer-state.ts:68", "server/services/ai.service.ts:91", - "server/services/auth.service.ts:128", - "server/services/auth.service.ts:184", - "server/services/auth.service.ts:213", + "server/services/auth.service.ts:150", + "server/services/auth.service.ts:206", + "server/services/auth.service.ts:235", "server/services/automation/core.ts:126", "server/services/automation/core.ts:153", "server/services/automation/delivery.ts:135", From 2ef61369f3a4ddbd0096d8d5d0ca04b80dca698b Mon Sep 17 00:00:00 2001 From: important-new Date: Mon, 3 Aug 2026 22:18:44 +0800 Subject: [PATCH 003/111] feat(reports): generate reports from sold services, publish and notify per report MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An order sold three services and delivered one document, because nothing created a second report: the `reports` entity could model a radon report but no code path produced one. Selling the services now produces the deliverables, and each one publishes and announces itself on its own schedule. Generation runs at the point the work is scheduled to begin, never at booking — a report that materialises weeks early clutters the order and freezes a template the tenant may still be editing. A cron sweep picks up orders whose `scheduled_start_ms` has arrived; `inspections.reports_generated_at` is its latch, so finished orders are not re-scanned for ever. Reconciliation itself is idempotent per LINE rather than latched, because a client who adds a sewer scope at the door should get a sewer report. Title and template are snapshotted when the report is created, so a catalogue default configured tomorrow rewrites nothing. The placeholder primary every order is born with is ADOPTED by the first line rather than left orphaned beside it — but only while it is still empty; a primary somebody has written into keeps its title and its template. Two notification faults fall out of publishing per report, and each is invisible on its own: - The `report.published` dedup key named only the inspection, so the radon report's first publish read as a retry of the standard report's and was dropped for ever, not for a window. The key is per report now. - `resolvePublishTrigger` asked "has anything on this inspection been published" and so called the radon report's FIRST publish an amendment, sending the client "your report was updated" about a document they had never seen. It asks per report now. Fixing those makes two documents finished in one sitting cost the client two emails, so publishes coalesce inside a one-hour window: the second report is still published, versioned and signed, only the second announcement is suppressed. An amendment is never coalesced. `reports.published_at` / `notified_at` record which document shipped and which publish did the telling — `inspections.report_status` stays the order-wide roll-up every existing reader consumes and cannot answer either question. Migration 0030: four ADD COLUMNs, no table rebuild. Verified with db:check. Not in this change: the reports LIST surface and its delete-with-confirmation modal. There is no per-report UI in the tree at all yet, so a confirmation modal would have nothing to confirm; publishing a named report is reachable through the API (`POST /inspections/:id/publish` now takes `reportId`) and that is the seam the UI will use. Two behaviour-preserving extractions to keep the publish service inside the file-size gate: auto-sign-on-publish and the per-report publish bookkeeping both move to lib/inspection. The remaining +7/+2/+4 lines on three already- grandfathered files are baselined. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01FGxHcE92k4t3d79y6DQfi3 --- migrations/0030_glamorous_blindfold.sql | 4 + migrations/meta/0030_snapshot.json | 10295 ++++++++++++++++ migrations/meta/_journal.json | 7 + scripts/file-size-baseline.json | 6 +- server/api/inspections/publish.ts | 6 +- server/lib/compliance/erasure-manifest.ts | 3 + server/lib/db/schema/inspection/core.ts | 10 + server/lib/db/schema/inspection/reports.ts | 21 + server/lib/inspection/auto-sign.ts | 52 + server/lib/inspection/report-generation.ts | 250 + server/lib/inspection/report-notifications.ts | 123 + server/lib/inspection/reports.ts | 21 +- server/lib/validations/inspection/crud.ts | 5 + server/scheduled.ts | 14 + server/services/automation/shared.ts | 8 + server/services/automation/trigger.ts | 7 +- server/services/inspection.service.ts | 4 +- .../inspection/inspection-publish.service.ts | 83 +- server/services/inspection/shared.ts | 26 +- .../publish-notification-coalescing.spec.ts | 189 + .../inspections/report-generation.spec.ts | 233 + .../reports/pca-foundation-schema.spec.ts | 6 +- 22 files changed, 11323 insertions(+), 50 deletions(-) create mode 100644 migrations/0030_glamorous_blindfold.sql create mode 100644 migrations/meta/0030_snapshot.json create mode 100644 server/lib/inspection/auto-sign.ts create mode 100644 server/lib/inspection/report-generation.ts create mode 100644 server/lib/inspection/report-notifications.ts create mode 100644 tests/unit/inspections/publish-notification-coalescing.spec.ts create mode 100644 tests/unit/inspections/report-generation.spec.ts diff --git a/migrations/0030_glamorous_blindfold.sql b/migrations/0030_glamorous_blindfold.sql new file mode 100644 index 000000000..892f73e1d --- /dev/null +++ b/migrations/0030_glamorous_blindfold.sql @@ -0,0 +1,4 @@ +ALTER TABLE `inspections` ADD `reports_generated_at` integer;--> statement-breakpoint +ALTER TABLE `reports` ADD `published_at` integer;--> statement-breakpoint +ALTER TABLE `reports` ADD `notified_at` integer;--> statement-breakpoint +ALTER TABLE `reports` ADD `sort_order` integer DEFAULT 0 NOT NULL; \ No newline at end of file diff --git a/migrations/meta/0030_snapshot.json b/migrations/meta/0030_snapshot.json new file mode 100644 index 000000000..85921336e --- /dev/null +++ b/migrations/meta/0030_snapshot.json @@ -0,0 +1,10295 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "cbb5fe33-b453-49b9-9b8c-70d695db1074", + "prevId": "e4241f07-66b5-42cf-be26-2bf9742f9cc7", + "tables": { + "agreement_requests": { + "name": "agreement_requests", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "agreement_id": { + "name": "agreement_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_email": { + "name": "client_email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_name": { + "name": "client_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "signature_base64": { + "name": "signature_base64", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "signed_at": { + "name": "signed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "viewed_at": { + "name": "viewed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sent_at": { + "name": "sent_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inspector_signature_base64": { + "name": "inspector_signature_base64", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inspector_signed_at": { + "name": "inspector_signed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inspector_user_id": { + "name": "inspector_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "verification_token": { + "name": "verification_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "content_snapshot": { + "name": "content_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completion_policy": { + "name": "completion_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'all'" + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "purged_at": { + "name": "purged_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "agreement_requests_token_unique": { + "name": "agreement_requests_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_agreement_requests_verify_token": { + "name": "idx_agreement_requests_verify_token", + "columns": [ + "verification_token" + ], + "isUnique": true + }, + "idx_agreement_requests_tenant": { + "name": "idx_agreement_requests_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_agreement_requests_inspection": { + "name": "idx_agreement_requests_inspection", + "columns": [ + "inspection_id" + ], + "isUnique": false + }, + "idx_agreement_requests_token_hash": { + "name": "idx_agreement_requests_token_hash", + "columns": [ + "token_hash" + ], + "isUnique": true + } + }, + "foreignKeys": { + "agreement_requests_tenant_id_tenants_id_fk": { + "name": "agreement_requests_tenant_id_tenants_id_fk", + "tableFrom": "agreement_requests", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agreement_requests_inspection_id_inspections_id_fk": { + "name": "agreement_requests_inspection_id_inspections_id_fk", + "tableFrom": "agreement_requests", + "tableTo": "inspections", + "columnsFrom": [ + "inspection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agreement_requests_agreement_id_agreements_id_fk": { + "name": "agreement_requests_agreement_id_agreements_id_fk", + "tableFrom": "agreement_requests", + "tableTo": "agreements", + "columnsFrom": [ + "agreement_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agreement_requests_inspector_user_id_users_id_fk": { + "name": "agreement_requests_inspector_user_id_users_id_fk", + "tableFrom": "agreement_requests", + "tableTo": "users", + "columnsFrom": [ + "inspector_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "agreement_signers": { + "name": "agreement_signers", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'client'" + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_enc": { + "name": "token_enc", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "signature_base64": { + "name": "signature_base64", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "signed_at": { + "name": "signed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "viewed_at": { + "name": "viewed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "on_behalf_of": { + "name": "on_behalf_of", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "on_behalf_disclaimer": { + "name": "on_behalf_disclaimer", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_reminded_at": { + "name": "last_reminded_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_agreement_signers_tenant_request": { + "name": "idx_agreement_signers_tenant_request", + "columns": [ + "tenant_id", + "request_id" + ], + "isUnique": false + }, + "idx_agreement_signers_request_email": { + "name": "idx_agreement_signers_request_email", + "columns": [ + "request_id", + "email" + ], + "isUnique": true + }, + "idx_agreement_signers_token_hash": { + "name": "idx_agreement_signers_token_hash", + "columns": [ + "token_hash" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "agreements": { + "name": "agreements", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_agreements_tenant": { + "name": "idx_agreements_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "agreements_tenant_id_tenants_id_fk": { + "name": "agreements_tenant_id_tenants_id_fk", + "tableFrom": "agreements", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "automation_logs": { + "name": "automation_logs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "automation_id": { + "name": "automation_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "recipient": { + "name": "recipient", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "recipient_role_key": { + "name": "recipient_role_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'email'" + }, + "send_at": { + "name": "send_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "delivered_at": { + "name": "delivered_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "event_id": { + "name": "event_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "recipient_contact_id": { + "name": "recipient_contact_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notice_id": { + "name": "notice_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_automation_logs_pending": { + "name": "idx_automation_logs_pending", + "columns": [ + "tenant_id", + "status", + "send_at" + ], + "isUnique": false + }, + "idx_automation_logs_insp": { + "name": "idx_automation_logs_insp", + "columns": [ + "inspection_id" + ], + "isUnique": false + }, + "uq_automation_logs_event": { + "name": "uq_automation_logs_event", + "columns": [ + "automation_id", + "inspection_id", + "event_id", + "channel", + "recipient" + ], + "isUnique": true, + "where": "event_id IS NOT NULL" + } + }, + "foreignKeys": { + "automation_logs_tenant_id_tenants_id_fk": { + "name": "automation_logs_tenant_id_tenants_id_fk", + "tableFrom": "automation_logs", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "automations": { + "name": "automations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "recipient_kind": { + "name": "recipient_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "recipient_role_profile_id": { + "name": "recipient_role_profile_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "delay_minutes": { + "name": "delay_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "subject_template": { + "name": "subject_template", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "body_template": { + "name": "body_template", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_template_id": { + "name": "email_template_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "conditions": { + "name": "conditions", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "channels": { + "name": "channels", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[\"email\"]'" + }, + "sms_body": { + "name": "sms_body", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sms_template_id": { + "name": "sms_template_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "is_default": { + "name": "is_default", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "in_app_template_id": { + "name": "in_app_template_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_automations_tenant": { + "name": "idx_automations_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "automations_tenant_id_tenants_id_fk": { + "name": "automations_tenant_id_tenants_id_fk", + "tableFrom": "automations", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "availability": { + "name": "availability", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspector_id": { + "name": "inspector_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "day_of_week": { + "name": "day_of_week", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "start_time": { + "name": "start_time", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "end_time": { + "name": "end_time", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_availability_inspector": { + "name": "idx_availability_inspector", + "columns": [ + "inspector_id" + ], + "isUnique": false + }, + "idx_availability_window_unique": { + "name": "idx_availability_window_unique", + "columns": [ + "inspector_id", + "day_of_week", + "start_time" + ], + "isUnique": true + } + }, + "foreignKeys": { + "availability_tenant_id_tenants_id_fk": { + "name": "availability_tenant_id_tenants_id_fk", + "tableFrom": "availability", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "availability_inspector_id_users_id_fk": { + "name": "availability_inspector_id_users_id_fk", + "tableFrom": "availability", + "tableTo": "users", + "columnsFrom": [ + "inspector_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "availability_overrides": { + "name": "availability_overrides", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspector_id": { + "name": "inspector_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "date": { + "name": "date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_available": { + "name": "is_available", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "start_time": { + "name": "start_time", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "end_time": { + "name": "end_time", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "transparency": { + "name": "transparency", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_avail_overrides_insp": { + "name": "idx_avail_overrides_insp", + "columns": [ + "inspector_id" + ], + "isUnique": false + }, + "idx_avail_overrides_block_unique": { + "name": "idx_avail_overrides_block_unique", + "columns": [ + "inspector_id", + "date" + ], + "isUnique": true, + "where": "is_available = 0 AND source IS NULL" + }, + "uq_avail_overrides_external": { + "name": "uq_avail_overrides_external", + "columns": [ + "inspector_id", + "source", + "external_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "availability_overrides_tenant_id_tenants_id_fk": { + "name": "availability_overrides_tenant_id_tenants_id_fk", + "tableFrom": "availability_overrides", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "availability_overrides_inspector_id_users_id_fk": { + "name": "availability_overrides_inspector_id_users_id_fk", + "tableFrom": "availability_overrides", + "tableTo": "users", + "columnsFrom": [ + "inspector_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "calendar_blocks": { + "name": "calendar_blocks", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "date": { + "name": "date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "start_time": { + "name": "start_time", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "end_time": { + "name": "end_time", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_all_day": { + "name": "is_all_day", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_calendar_blocks_tenant_user_date": { + "name": "idx_calendar_blocks_tenant_user_date", + "columns": [ + "tenant_id", + "user_id", + "date" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "calendar_connection_read_calendars": { + "name": "calendar_connection_read_calendars", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "connection_id": { + "name": "connection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "external_calendar_id": { + "name": "external_calendar_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_role": { + "name": "access_role", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "uq_conn_read_cal": { + "name": "uq_conn_read_cal", + "columns": [ + "connection_id", + "external_calendar_id" + ], + "isUnique": true + }, + "idx_conn_read_cal_tenant": { + "name": "idx_conn_read_cal_tenant", + "columns": [ + "tenant_id", + "connection_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "calendar_connections": { + "name": "calendar_connections", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "auth_type": { + "name": "auth_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "credentials_enc": { + "name": "credentials_enc", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "credentials_dek_enc": { + "name": "credentials_dek_enc", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "capabilities": { + "name": "capabilities", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "calendar_id": { + "name": "calendar_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "connected_at": { + "name": "connected_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_sync_at": { + "name": "last_sync_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "uq_calendar_connections_user_provider": { + "name": "uq_calendar_connections_user_provider", + "columns": [ + "user_id", + "provider" + ], + "isUnique": true + }, + "idx_calendar_connections_tenant_user": { + "name": "idx_calendar_connections_tenant_user", + "columns": [ + "tenant_id", + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "comment_usage": { + "name": "comment_usage", + "columns": { + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "comment_id": { + "name": "comment_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "use_count": { + "name": "use_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "last_used_at": { + "name": "last_used_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_comment_usage_user_last_used": { + "name": "idx_comment_usage_user_last_used", + "columns": [ + "tenant_id", + "user_id", + "last_used_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "comment_usage_comment_id_comments_id_fk": { + "name": "comment_usage_comment_id_comments_id_fk", + "tableFrom": "comment_usage", + "tableTo": "comments", + "columnsFrom": [ + "comment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "comment_usage_tenant_id_user_id_comment_id_pk": { + "columns": [ + "tenant_id", + "user_id", + "comment_id" + ], + "name": "comment_usage_tenant_id_user_id_comment_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "comments": { + "name": "comments", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rating_bucket": { + "name": "rating_bucket", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "section": { + "name": "section", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "library_id": { + "name": "library_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "section_ids": { + "name": "section_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "item_labels": { + "name": "item_labels", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "trigger_code": { + "name": "trigger_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "search_keywords": { + "name": "search_keywords", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "item_label": { + "name": "item_label", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "severity": { + "name": "severity", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "repair_summary": { + "name": "repair_summary", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "estimate_min_cents": { + "name": "estimate_min_cents", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "estimate_max_cents": { + "name": "estimate_max_cents", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "recommended_contractor_type_id": { + "name": "recommended_contractor_type_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_comments_tenant": { + "name": "idx_comments_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_comments_rating_bucket": { + "name": "idx_comments_rating_bucket", + "columns": [ + "tenant_id", + "rating_bucket" + ], + "isUnique": false + }, + "idx_comments_library_id": { + "name": "idx_comments_library_id", + "columns": [ + "library_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "comments_tenant_id_tenants_id_fk": { + "name": "comments_tenant_id_tenants_id_fk", + "tableFrom": "comments", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "commercial_subtypes": { + "name": "commercial_subtypes", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "based_on": { + "name": "based_on", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_disabled": { + "name": "is_disabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_commercial_subtypes_tenant_name": { + "name": "idx_commercial_subtypes_tenant_name", + "columns": [ + "tenant_id", + "name" + ], + "isUnique": true + } + }, + "foreignKeys": { + "commercial_subtypes_tenant_id_tenants_id_fk": { + "name": "commercial_subtypes_tenant_id_tenants_id_fk", + "tableFrom": "commercial_subtypes", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "concierge_confirm_tokens": { + "name": "concierge_confirm_tokens", + "columns": { + "token": { + "name": "token", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_email": { + "name": "client_email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_concierge_tokens_expiry": { + "name": "idx_concierge_tokens_expiry", + "columns": [ + "expires_at" + ], + "isUnique": false + }, + "idx_concierge_confirm_token_hash": { + "name": "idx_concierge_confirm_token_hash", + "columns": [ + "token_hash" + ], + "isUnique": true + } + }, + "foreignKeys": { + "concierge_confirm_tokens_inspection_id_inspections_id_fk": { + "name": "concierge_confirm_tokens_inspection_id_inspections_id_fk", + "tableFrom": "concierge_confirm_tokens", + "tableTo": "inspections", + "columnsFrom": [ + "inspection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "contact_role_profiles": { + "name": "contact_role_profiles", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_template_id": { + "name": "email_template_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sms_template_id": { + "name": "sms_template_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_system": { + "name": "is_system", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "capability_overrides": { + "name": "capability_overrides", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_crp_tenant": { + "name": "idx_crp_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "uq_crp_tenant_key": { + "name": "uq_crp_tenant_key", + "columns": [ + "tenant_id", + "key" + ], + "isUnique": true, + "where": "is_active = 1" + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "contacts": { + "name": "contacts", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'client'" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "agency": { + "name": "agency", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "archived_at": { + "name": "archived_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "agent_user_id": { + "name": "agent_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "agent_linked_at": { + "name": "agent_linked_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "agent_revoked_at": { + "name": "agent_revoked_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_contacts_type": { + "name": "idx_contacts_type", + "columns": [ + "tenant_id", + "type" + ], + "isUnique": false + }, + "idx_contacts_tenant": { + "name": "idx_contacts_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "uq_contacts_tenant_email": { + "name": "uq_contacts_tenant_email", + "columns": [ + "tenant_id", + "email" + ], + "isUnique": true, + "where": "email IS NOT NULL AND archived_at IS NULL" + }, + "uq_contacts_tenant_agent_user": { + "name": "uq_contacts_tenant_agent_user", + "columns": [ + "tenant_id", + "agent_user_id" + ], + "isUnique": true, + "where": "agent_user_id IS NOT NULL AND archived_at IS NULL" + }, + "idx_contacts_agent_user": { + "name": "idx_contacts_agent_user", + "columns": [ + "agent_user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "contacts_tenant_id_tenants_id_fk": { + "name": "contacts_tenant_id_tenants_id_fk", + "tableFrom": "contacts", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "contractor_types": { + "name": "contractor_types", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_contractor_types_tenant": { + "name": "idx_contractor_types_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "cost_items": { + "name": "cost_items", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "building_id": { + "name": "building_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "instance_index": { + "name": "instance_index", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit_id": { + "name": "unit_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "finding_key": { + "name": "finding_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "system": { + "name": "system", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "component": { + "name": "component", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cost_method": { + "name": "cost_method", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "uom": { + "name": "uom", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit_cost_cents": { + "name": "unit_cost_cents", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "lump_sum_cents": { + "name": "lump_sum_cents", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "eul": { + "name": "eul", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "eff_age": { + "name": "eff_age", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rul": { + "name": "rul", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "suggested_remedy": { + "name": "suggested_remedy", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "section_ref": { + "name": "section_ref", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "photo_ref": { + "name": "photo_ref", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_cost_items_tenant_inspection": { + "name": "idx_cost_items_tenant_inspection", + "columns": [ + "tenant_id", + "inspection_id" + ], + "isUnique": false + }, + "idx_cost_items_finding_key": { + "name": "idx_cost_items_finding_key", + "columns": [ + "finding_key" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "defect_categories": { + "name": "defect_categories", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'#6b7280'" + }, + "is_summary_driver": { + "name": "is_summary_driver", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "is_seed": { + "name": "is_seed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_defect_categories_tenant": { + "name": "idx_defect_categories_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "discount_codes": { + "name": "discount_codes", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "max_uses": { + "name": "max_uses", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "uses_count": { + "name": "uses_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_discount_codes_tenant": { + "name": "idx_discount_codes_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "uq_discount_codes_code_tenant": { + "name": "uq_discount_codes_code_tenant", + "columns": [ + "upper(code)", + "tenant_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "discount_codes_tenant_id_tenants_id_fk": { + "name": "discount_codes_tenant_id_tenants_id_fk", + "tableFrom": "discount_codes", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "document_review_items": { + "name": "document_review_items", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "document_key": { + "name": "document_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_requested": { + "name": "is_requested", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_received": { + "name": "is_received", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_reviewed": { + "name": "is_reviewed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_na": { + "name": "is_na", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": { + "idx_doc_review_inspection": { + "name": "idx_doc_review_inspection", + "columns": [ + "tenant_id", + "inspection_id" + ], + "isUnique": false + }, + "uq_doc_review_item": { + "name": "uq_doc_review_item", + "columns": [ + "inspection_id", + "document_key" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "email_suppressions": { + "name": "email_suppressions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_provider": { + "name": "source_provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_event_id": { + "name": "provider_event_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_email_suppressions_email": { + "name": "idx_email_suppressions_email", + "columns": [ + "tenant_id", + "email" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "erasure_log": { + "name": "erasure_log", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject_email": { + "name": "subject_email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "requested_by": { + "name": "requested_by", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "identity_basis": { + "name": "identity_basis", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "decisions_json": { + "name": "decisions_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "retained_count": { + "name": "retained_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "anonymized_count": { + "name": "anonymized_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "deleted_count": { + "name": "deleted_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "response_note": { + "name": "response_note", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_erasure_log_tenant": { + "name": "idx_erasure_log_tenant", + "columns": [ + "tenant_id", + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "esign_audit_logs": { + "name": "esign_audit_logs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "event": { + "name": "event", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payload_json": { + "name": "payload_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "prev_hash": { + "name": "prev_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hash": { + "name": "hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "signature": { + "name": "signature", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "key_fingerprint": { + "name": "key_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_esign_audit_logs_request": { + "name": "idx_esign_audit_logs_request", + "columns": [ + "tenant_id", + "request_id", + "created_at" + ], + "isUnique": false + }, + "idx_esign_audit_logs_event_dedup": { + "name": "idx_esign_audit_logs_event_dedup", + "columns": [ + "tenant_id", + "request_id", + "event" + ], + "isUnique": true, + "where": "event NOT LIKE 'signer.%'" + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "event_types": { + "name": "event_types", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "default_duration_min": { + "name": "default_duration_min", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 30 + }, + "default_price_cents": { + "name": "default_price_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'#6366f1'" + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "uq_event_types_tenant_slug": { + "name": "uq_event_types_tenant_slug", + "columns": [ + "tenant_id", + "slug" + ], + "isUnique": true + } + }, + "foreignKeys": { + "event_types_tenant_id_tenants_id_fk": { + "name": "event_types_tenant_id_tenants_id_fk", + "tableFrom": "event_types", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_access_tokens": { + "name": "inspection_access_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "recipient_email": { + "name": "recipient_email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'client'" + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_enc": { + "name": "token_enc", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_iat_token": { + "name": "idx_iat_token", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_iat_inspection": { + "name": "idx_iat_inspection", + "columns": [ + "tenant_id", + "inspection_id" + ], + "isUnique": false + }, + "idx_iat_recipient": { + "name": "idx_iat_recipient", + "columns": [ + "inspection_id", + "recipient_email" + ], + "isUnique": true + }, + "idx_iat_token_hash": { + "name": "idx_iat_token_hash", + "columns": [ + "token_hash" + ], + "isUnique": true + } + }, + "foreignKeys": { + "inspection_access_tokens_tenant_id_tenants_id_fk": { + "name": "inspection_access_tokens_tenant_id_tenants_id_fk", + "tableFrom": "inspection_access_tokens", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_events": { + "name": "inspection_events", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "event_type_id": { + "name": "event_type_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspector_id": { + "name": "inspector_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scheduled_at": { + "name": "scheduled_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "duration_min": { + "name": "duration_min", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "price_cents": { + "name": "price_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'scheduled'" + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "results_received_at": { + "name": "results_received_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "gcal_event_id": { + "name": "gcal_event_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_inspection_events_scheduled": { + "name": "idx_inspection_events_scheduled", + "columns": [ + "tenant_id", + "scheduled_at" + ], + "isUnique": false + }, + "idx_inspection_events_inspection": { + "name": "idx_inspection_events_inspection", + "columns": [ + "inspection_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "inspection_events_tenant_id_tenants_id_fk": { + "name": "inspection_events_tenant_id_tenants_id_fk", + "tableFrom": "inspection_events", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_events_inspection_id_inspections_id_fk": { + "name": "inspection_events_inspection_id_inspections_id_fk", + "tableFrom": "inspection_events", + "tableTo": "inspections", + "columnsFrom": [ + "inspection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "inspection_events_event_type_id_event_types_id_fk": { + "name": "inspection_events_event_type_id_event_types_id_fk", + "tableFrom": "inspection_events", + "tableTo": "event_types", + "columnsFrom": [ + "event_type_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_events_inspector_id_users_id_fk": { + "name": "inspection_events_inspector_id_users_id_fk", + "tableFrom": "inspection_events", + "tableTo": "users", + "columnsFrom": [ + "inspector_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_inspectors": { + "name": "inspection_inspectors", + "columns": { + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'lead'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_insp_inspectors_tenant_user": { + "name": "idx_insp_inspectors_tenant_user", + "columns": [ + "tenant_id", + "user_id" + ], + "isUnique": false + }, + "idx_insp_inspectors_user": { + "name": "idx_insp_inspectors_user", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "inspection_inspectors_inspection_id_user_id_pk": { + "columns": [ + "inspection_id", + "user_id" + ], + "name": "inspection_inspectors_inspection_id_user_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_item_tag_links": { + "name": "inspection_item_tag_links", + "columns": { + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "item_id": { + "name": "item_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tag_id": { + "name": "tag_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_tag_links_tenant": { + "name": "idx_tag_links_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_tag_links_tag": { + "name": "idx_tag_links_tag", + "columns": [ + "tag_id" + ], + "isUnique": false + }, + "idx_tag_links_inspection_item": { + "name": "idx_tag_links_inspection_item", + "columns": [ + "inspection_id", + "item_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "inspection_item_tag_links_inspection_id_item_id_tag_id_pk": { + "columns": [ + "inspection_id", + "item_id", + "tag_id" + ], + "name": "inspection_item_tag_links_inspection_id_item_id_tag_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_media_pool": { + "name": "inspection_media_pool", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "r2_key": { + "name": "r2_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "exif_data": { + "name": "exif_data", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "annotations": { + "name": "annotations", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "caption": { + "name": "caption", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "media_type": { + "name": "media_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'photo'" + }, + "stream_uid": { + "name": "stream_uid", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "poster_pct": { + "name": "poster_pct", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "duration_sec": { + "name": "duration_sec", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'stream'" + }, + "poster_key": { + "name": "poster_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_media_pool_tenant": { + "name": "idx_media_pool_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_media_pool_inspection": { + "name": "idx_media_pool_inspection", + "columns": [ + "inspection_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_messages": { + "name": "inspection_messages", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "from_role": { + "name": "from_role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "from_name": { + "name": "from_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "attachments": { + "name": "attachments", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "read_at": { + "name": "read_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "from_user_id": { + "name": "from_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_msg_inspection": { + "name": "idx_msg_inspection", + "columns": [ + "inspection_id", + "created_at" + ], + "isUnique": false + }, + "idx_msg_contact": { + "name": "idx_msg_contact", + "columns": [ + "tenant_id", + "contact_id", + "created_at" + ], + "isUnique": false + }, + "idx_msg_unread": { + "name": "idx_msg_unread", + "columns": [ + "tenant_id", + "contact_id", + "from_role" + ], + "isUnique": false, + "where": "\"inspection_messages\".\"read_at\" IS NULL" + } + }, + "foreignKeys": { + "inspection_messages_tenant_id_tenants_id_fk": { + "name": "inspection_messages_tenant_id_tenants_id_fk", + "tableFrom": "inspection_messages", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_messages_inspection_id_inspections_id_fk": { + "name": "inspection_messages_inspection_id_inspections_id_fk", + "tableFrom": "inspection_messages", + "tableTo": "inspections", + "columnsFrom": [ + "inspection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_people": { + "name": "inspection_people", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role_profile_id": { + "name": "role_profile_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_ip_inspection": { + "name": "idx_ip_inspection", + "columns": [ + "inspection_id" + ], + "isUnique": false + }, + "idx_ip_tenant": { + "name": "idx_ip_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "uq_ip_insp_contact_role": { + "name": "uq_ip_insp_contact_role", + "columns": [ + "inspection_id", + "contact_id", + "role_profile_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_requests": { + "name": "inspection_requests", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_name": { + "name": "client_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_email": { + "name": "client_email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "client_phone": { + "name": "client_phone", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "property_address": { + "name": "property_address", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "property_city": { + "name": "property_city", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "property_state": { + "name": "property_state", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "property_zip": { + "name": "property_zip", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scheduled_at": { + "name": "scheduled_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "total_amount_cents": { + "name": "total_amount_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "payment_status": { + "name": "payment_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'unpaid'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_inspection_requests_tenant": { + "name": "idx_inspection_requests_tenant", + "columns": [ + "tenant_id", + "status", + "scheduled_at" + ], + "isUnique": false + }, + "idx_inspection_requests_email": { + "name": "idx_inspection_requests_email", + "columns": [ + "tenant_id", + "client_email" + ], + "isUnique": false + } + }, + "foreignKeys": { + "inspection_requests_tenant_id_tenants_id_fk": { + "name": "inspection_requests_tenant_id_tenants_id_fk", + "tableFrom": "inspection_requests", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_results": { + "name": "inspection_results", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "data": { + "name": "data", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ydoc_state": { + "name": "ydoc_state", + "type": "blob", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "rating_system_id": { + "name": "rating_system_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rating_system_snapshot": { + "name": "rating_system_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "report_id": { + "name": "report_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_results_tenant": { + "name": "idx_results_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_results_inspection": { + "name": "idx_results_inspection", + "columns": [ + "inspection_id" + ], + "isUnique": false + }, + "uq_results_report": { + "name": "uq_results_report", + "columns": [ + "report_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "inspection_results_tenant_id_tenants_id_fk": { + "name": "inspection_results_tenant_id_tenants_id_fk", + "tableFrom": "inspection_results", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_results_inspection_id_inspections_id_fk": { + "name": "inspection_results_inspection_id_inspections_id_fk", + "tableFrom": "inspection_results", + "tableTo": "inspections", + "columnsFrom": [ + "inspection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_services": { + "name": "inspection_services", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "service_id": { + "name": "service_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "price_override_cents": { + "name": "price_override_cents", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name_snapshot": { + "name": "name_snapshot", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "price_snapshot_cents": { + "name": "price_snapshot_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + } + }, + "indexes": { + "idx_insp_services_tenant": { + "name": "idx_insp_services_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_insp_services_insp": { + "name": "idx_insp_services_insp", + "columns": [ + "inspection_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "inspection_services_tenant_id_tenants_id_fk": { + "name": "inspection_services_tenant_id_tenants_id_fk", + "tableFrom": "inspection_services", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_services_inspection_id_inspections_id_fk": { + "name": "inspection_services_inspection_id_inspections_id_fk", + "tableFrom": "inspection_services", + "tableTo": "inspections", + "columnsFrom": [ + "inspection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "inspection_services_service_id_services_id_fk": { + "name": "inspection_services_service_id_services_id_fk", + "tableFrom": "inspection_services", + "tableTo": "services", + "columnsFrom": [ + "service_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_types": { + "name": "inspection_types", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "based_on": { + "name": "based_on", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_inspection_types_tenant_name": { + "name": "idx_inspection_types_tenant_name", + "columns": [ + "tenant_id", + "name" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_units": { + "name": "inspection_units", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "parent_unit_id": { + "name": "parent_unit_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'unit'" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + }, + "attrs": { + "name": "attrs", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_inspection_units_tenant_inspection": { + "name": "idx_inspection_units_tenant_inspection", + "columns": [ + "tenant_id", + "inspection_id" + ], + "isUnique": false + }, + "idx_inspection_units_parent": { + "name": "idx_inspection_units_parent", + "columns": [ + "parent_unit_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspections": { + "name": "inspections", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspector_id": { + "name": "inspector_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "property_address": { + "name": "property_address", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "address_place_id": { + "name": "address_place_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address_street": { + "name": "address_street", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address_city": { + "name": "address_city", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address_state": { + "name": "address_state", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address_zip": { + "name": "address_zip", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address_county": { + "name": "address_county", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address_lat": { + "name": "address_lat", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address_lng": { + "name": "address_lng", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address_geocoded_at": { + "name": "address_geocoded_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "template_id": { + "name": "template_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "date": { + "name": "date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'requested'" + }, + "report_status": { + "name": "report_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'in_progress'" + }, + "payment_status": { + "name": "payment_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'unpaid'" + }, + "price_cents": { + "name": "price_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cancel_reason": { + "name": "cancel_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cancel_notes": { + "name": "cancel_notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_payment_required": { + "name": "is_payment_required", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_agreement_required": { + "name": "is_agreement_required", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_auto_sign_on_publish": { + "name": "is_auto_sign_on_publish", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "discount_code_id": { + "name": "discount_code_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "discount_amount_cents": { + "name": "discount_amount_cents", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "closing_date": { + "name": "closing_date", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "referral_source": { + "name": "referral_source", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reference_number": { + "name": "reference_number", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "internal_notes": { + "name": "internal_notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "year_built": { + "name": "year_built", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sqft": { + "name": "sqft", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "foundation_type": { + "name": "foundation_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "bedrooms": { + "name": "bedrooms", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "bathrooms": { + "name": "bathrooms", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "lot_size": { + "name": "lot_size", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "property_facts": { + "name": "property_facts", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cover_photo_id": { + "name": "cover_photo_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cover_crop": { + "name": "cover_crop", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cover_image_key": { + "name": "cover_image_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit": { + "name": "unit", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "property_type": { + "name": "property_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "commercial_subtype": { + "name": "commercial_subtype", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "report_tier": { + "name": "report_tier", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "county": { + "name": "county", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_automations_disabled": { + "name": "is_automations_disabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "template_snapshot": { + "name": "template_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "template_snapshot_version": { + "name": "template_snapshot_version", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 1 + }, + "profile_override": { + "name": "profile_override", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "require_defect_fields_override": { + "name": "require_defect_fields_override", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "concierge_status": { + "name": "concierge_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_team_mode": { + "name": "is_team_mode", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "lead_inspector_id": { + "name": "lead_inspector_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "helper_inspector_ids": { + "name": "helper_inspector_ids", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "data_version": { + "name": "data_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "source_inspection_id": { + "name": "source_inspection_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "root_inspection_id": { + "name": "root_inspection_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reinspection_round": { + "name": "reinspection_round", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit_inspection_mode": { + "name": "unit_inspection_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'tagged'" + }, + "location_options": { + "name": "location_options", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sampling_declaration": { + "name": "sampling_declaration", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "pca_narrative": { + "name": "pca_narrative", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "deviations": { + "name": "deviations", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "report_photo_mode": { + "name": "report_photo_mode", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scheduled_start_ms": { + "name": "scheduled_start_ms", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scheduled_end_ms": { + "name": "scheduled_end_ms", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "duration_min": { + "name": "duration_min", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "badge_layout_override": { + "name": "badge_layout_override", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "report_photo_columns": { + "name": "report_photo_columns", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "referred_by_contact_id": { + "name": "referred_by_contact_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unlocked_at": { + "name": "unlocked_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unlocked_by": { + "name": "unlocked_by", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unlock_reason": { + "name": "unlock_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reports_generated_at": { + "name": "reports_generated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_inspections_tenant": { + "name": "idx_inspections_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_inspections_request": { + "name": "idx_inspections_request", + "columns": [ + "request_id" + ], + "isUnique": false + }, + "idx_inspections_inspector": { + "name": "idx_inspections_inspector", + "columns": [ + "inspector_id" + ], + "isUnique": false + }, + "idx_inspections_tenant_status": { + "name": "idx_inspections_tenant_status", + "columns": [ + "tenant_id", + "status" + ], + "isUnique": false + }, + "idx_inspections_tenant_date": { + "name": "idx_inspections_tenant_date", + "columns": [ + "tenant_id", + "date" + ], + "isUnique": false + }, + "idx_inspections_inspector_date": { + "name": "idx_inspections_inspector_date", + "columns": [ + "inspector_id", + "date" + ], + "isUnique": false + }, + "idx_inspections_root": { + "name": "idx_inspections_root", + "columns": [ + "root_inspection_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "inspections_tenant_id_tenants_id_fk": { + "name": "inspections_tenant_id_tenants_id_fk", + "tableFrom": "inspections", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspections_inspector_id_users_id_fk": { + "name": "inspections_inspector_id_users_id_fk", + "tableFrom": "inspections", + "tableTo": "users", + "columnsFrom": [ + "inspector_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspections_template_id_templates_id_fk": { + "name": "inspections_template_id_templates_id_fk", + "tableFrom": "inspections", + "tableTo": "templates", + "columnsFrom": [ + "template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspections_discount_code_id_discount_codes_id_fk": { + "name": "inspections_discount_code_id_discount_codes_id_fk", + "tableFrom": "inspections", + "tableTo": "discount_codes", + "columnsFrom": [ + "discount_code_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspections_request_id_inspection_requests_id_fk": { + "name": "inspections_request_id_inspection_requests_id_fk", + "tableFrom": "inspections", + "tableTo": "inspection_requests", + "columnsFrom": [ + "request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspector_credentials": { + "name": "inspector_credentials", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "member_number": { + "name": "member_number", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "image_r2_key": { + "name": "image_r2_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_inspector_credentials_tenant": { + "name": "idx_inspector_credentials_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_inspector_credentials_user": { + "name": "idx_inspector_credentials_user", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "invoices": { + "name": "invoices", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "client_name": { + "name": "client_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "client_email": { + "name": "client_email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "amount_cents": { + "name": "amount_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "line_items": { + "name": "line_items", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "due_date": { + "name": "due_date", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sent_at": { + "name": "sent_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "paid_at": { + "name": "paid_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "payment_method": { + "name": "payment_method", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "partial_paid_at": { + "name": "partial_paid_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "voided_at": { + "name": "voided_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "qbo_sync_status": { + "name": "qbo_sync_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'USD'" + } + }, + "indexes": { + "idx_invoices_tenant": { + "name": "idx_invoices_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_invoices_inspection": { + "name": "idx_invoices_inspection", + "columns": [ + "inspection_id" + ], + "isUnique": false + }, + "idx_invoices_contact": { + "name": "idx_invoices_contact", + "columns": [ + "tenant_id", + "contact_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "invoices_tenant_id_tenants_id_fk": { + "name": "invoices_tenant_id_tenants_id_fk", + "tableFrom": "invoices", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "invoices_inspection_id_inspections_id_fk": { + "name": "invoices_inspection_id_inspections_id_fk", + "tableFrom": "invoices", + "tableTo": "inspections", + "columnsFrom": [ + "inspection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "invoices_contact_id_contacts_id_fk": { + "name": "invoices_contact_id_contacts_id_fk", + "tableFrom": "invoices", + "tableTo": "contacts", + "columnsFrom": [ + "contact_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "marketplace_libraries": { + "name": "marketplace_libraries", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "semver": { + "name": "semver", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "schema": { + "name": "schema", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'system'" + }, + "changelog": { + "name": "changelog", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "download_count": { + "name": "download_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "is_featured": { + "name": "is_featured", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_marketplace_libraries_kind_featured": { + "name": "idx_marketplace_libraries_kind_featured", + "columns": [ + "kind", + "is_featured" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "marketplace_templates": { + "name": "marketplace_templates", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "semver": { + "name": "semver", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "schema": { + "name": "schema", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'system'" + }, + "changelog": { + "name": "changelog", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "download_count": { + "name": "download_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "is_featured": { + "name": "is_featured", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "message_templates": { + "name": "message_templates", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "variables": { + "name": "variables", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_seeded": { + "name": "is_seeded", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_message_templates_tenant_channel": { + "name": "idx_message_templates_tenant_channel", + "columns": [ + "tenant_id", + "channel" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "messaging_compliance": { + "name": "messaging_compliance", + "columns": { + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'own'" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "subaccount_sid": { + "name": "subaccount_sid", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "customer_profile_sid": { + "name": "customer_profile_sid", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "customer_profile_status": { + "name": "customer_profile_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "brand_sid": { + "name": "brand_sid", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "brand_status": { + "name": "brand_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "campaign_sid": { + "name": "campaign_sid", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "campaign_status": { + "name": "campaign_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tfv_sid": { + "name": "tfv_sid", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tfv_status": { + "name": "tfv_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "messaging_resource_sid": { + "name": "messaging_resource_sid", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provider_meta": { + "name": "provider_meta", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provisioned_number": { + "name": "provisioned_number", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provisioned_number_sid": { + "name": "provisioned_number_sid", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "has_sender_attached": { + "name": "has_sender_attached", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "compliance_status": { + "name": "compliance_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'not_started'" + }, + "rejection_reason": { + "name": "rejection_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_sync_at": { + "name": "last_sync_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "notification_preferences": { + "name": "notification_preferences", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject_kind": { + "name": "subject_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject_id": { + "name": "subject_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "class_id": { + "name": "class_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_notification_prefs_unique": { + "name": "idx_notification_prefs_unique", + "columns": [ + "tenant_id", + "subject_kind", + "subject_id", + "class_id", + "channel" + ], + "isUnique": true + }, + "idx_notification_prefs_subject": { + "name": "idx_notification_prefs_subject", + "columns": [ + "tenant_id", + "subject_kind", + "subject_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "orphaned_media": { + "name": "orphaned_media", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "r2_key": { + "name": "r2_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "first_seen_at": { + "name": "first_seen_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_orphaned_media_key": { + "name": "idx_orphaned_media_key", + "columns": [ + "tenant_id", + "r2_key" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "processed_webhook_events": { + "name": "processed_webhook_events", + "columns": { + "event_id": { + "name": "event_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "received_at": { + "name": "received_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "psq_responses": { + "name": "psq_responses", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "responses": { + "name": "responses", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'sent'" + }, + "share_token": { + "name": "share_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sent_at": { + "name": "sent_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "received_at": { + "name": "received_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "uq_psq_inspection": { + "name": "uq_psq_inspection", + "columns": [ + "tenant_id", + "inspection_id" + ], + "isUnique": true + }, + "idx_psq_share_token": { + "name": "idx_psq_share_token", + "columns": [ + "share_token" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "qbo_connections": { + "name": "qbo_connections", + "columns": { + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "realm_id": { + "name": "realm_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "company_name": { + "name": "company_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_enc": { + "name": "access_token_enc", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token_enc": { + "name": "refresh_token_enc", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token_expires_at": { + "name": "token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_sync_at": { + "name": "last_sync_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_sync_enabled": { + "name": "is_sync_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "default_item_id": { + "name": "default_item_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "qbo_entity_map": { + "name": "qbo_entity_map", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "oi_type": { + "name": "oi_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "oi_id": { + "name": "oi_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qbo_type": { + "name": "qbo_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qbo_id": { + "name": "qbo_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qbo_sync_token": { + "name": "qbo_sync_token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "synced_at": { + "name": "synced_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_qbo_entity_map_qbo": { + "name": "idx_qbo_entity_map_qbo", + "columns": [ + "tenant_id", + "qbo_type", + "qbo_id" + ], + "isUnique": true + }, + "idx_qbo_entity_map_oi": { + "name": "idx_qbo_entity_map_oi", + "columns": [ + "tenant_id", + "oi_type", + "oi_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "qbo_sync_errors": { + "name": "qbo_sync_errors", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "oi_type": { + "name": "oi_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "oi_id": { + "name": "oi_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "error_msg": { + "name": "error_msg", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "retries": { + "name": "retries", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "is_resolved": { + "name": "is_resolved", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "rating_systems": { + "name": "rating_systems", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "levels": { + "name": "levels", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_default": { + "name": "is_default", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_seed": { + "name": "is_seed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_rating_systems_tenant_slug": { + "name": "idx_rating_systems_tenant_slug", + "columns": [ + "tenant_id", + "slug" + ], + "isUnique": true + }, + "idx_rating_systems_tenant": { + "name": "idx_rating_systems_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "rating_systems_tenant_id_tenants_id_fk": { + "name": "rating_systems_tenant_id_tenants_id_fk", + "tableFrom": "rating_systems", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "repair_request_items": { + "name": "repair_request_items", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "repair_request_id": { + "name": "repair_request_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "finding_key": { + "name": "finding_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "section_title": { + "name": "section_title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "item_label": { + "name": "item_label", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "comment_snapshot": { + "name": "comment_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "requested_credit_cents": { + "name": "requested_credit_cents", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "defect_title_snapshot": { + "name": "defect_title_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "location_snapshot": { + "name": "location_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "category_snapshot": { + "name": "category_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "trade_snapshot": { + "name": "trade_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_repair_request_items_rr": { + "name": "idx_repair_request_items_rr", + "columns": [ + "repair_request_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "repair_requests": { + "name": "repair_requests", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by_kind": { + "name": "created_by_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by_ref": { + "name": "created_by_ref", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "custom_intro": { + "name": "custom_intro", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "share_token": { + "name": "share_token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_repair_requests_inspection": { + "name": "idx_repair_requests_inspection", + "columns": [ + "tenant_id", + "inspection_id" + ], + "isUnique": false + }, + "idx_repair_requests_share_token": { + "name": "idx_repair_requests_share_token", + "columns": [ + "share_token" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "report_exports": { + "name": "report_exports", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "format": { + "name": "format", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "r2_key": { + "name": "r2_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "size_bytes": { + "name": "size_bytes", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_report_exports_inspection": { + "name": "idx_report_exports_inspection", + "columns": [ + "tenant_id", + "inspection_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "report_pdfs": { + "name": "report_pdfs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "r2_key": { + "name": "r2_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "rendered_at": { + "name": "rendered_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_version": { + "name": "source_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "version_number": { + "name": "version_number", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "size_bytes": { + "name": "size_bytes", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'ready'" + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "uq_report_pdfs_inspection_type": { + "name": "uq_report_pdfs_inspection_type", + "columns": [ + "inspection_id", + "type", + "version_number" + ], + "isUnique": true + }, + "idx_report_pdfs_tenant": { + "name": "idx_report_pdfs_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_report_pdfs_status": { + "name": "idx_report_pdfs_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_report_pdfs_content_hash": { + "name": "idx_report_pdfs_content_hash", + "columns": [ + "inspection_id", + "type", + "content_hash" + ], + "isUnique": false + } + }, + "foreignKeys": { + "report_pdfs_tenant_id_tenants_id_fk": { + "name": "report_pdfs_tenant_id_tenants_id_fk", + "tableFrom": "report_pdfs", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "report_signoff": { + "name": "report_signoff", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "person_id": { + "name": "person_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "license": { + "name": "license", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "qualifications_ref": { + "name": "qualifications_ref", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "signed_at": { + "name": "signed_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "signature_ref": { + "name": "signature_ref", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_dual_role": { + "name": "is_dual_role", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + } + }, + "indexes": { + "idx_report_signoff_inspection": { + "name": "idx_report_signoff_inspection", + "columns": [ + "tenant_id", + "inspection_id" + ], + "isUnique": false + }, + "uq_report_signoff_role": { + "name": "uq_report_signoff_role", + "columns": [ + "inspection_id", + "role" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "report_versions": { + "name": "report_versions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "version_number": { + "name": "version_number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "snapshot_json": { + "name": "snapshot_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "prev_hash": { + "name": "prev_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "signature": { + "name": "signature", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "key_fingerprint": { + "name": "key_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_amendment": { + "name": "is_amendment", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "verification_token": { + "name": "verification_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "published_at": { + "name": "published_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "published_by": { + "name": "published_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + }, + "report_id": { + "name": "report_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_report_versions_report": { + "name": "idx_report_versions_report", + "columns": [ + "report_id", + "version_number" + ], + "isUnique": false + }, + "uq_report_versions_report_version": { + "name": "uq_report_versions_report_version", + "columns": [ + "report_id", + "version_number" + ], + "isUnique": true + }, + "idx_report_versions_verify_token": { + "name": "idx_report_versions_verify_token", + "columns": [ + "verification_token" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "reports": { + "name": "reports", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_service_id": { + "name": "inspection_service_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "template_id": { + "name": "template_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'in_progress'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "published_at": { + "name": "published_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notified_at": { + "name": "notified_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": { + "idx_reports_inspection": { + "name": "idx_reports_inspection", + "columns": [ + "inspection_id" + ], + "isUnique": false + }, + "idx_reports_tenant": { + "name": "idx_reports_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "uq_reports_primary": { + "name": "uq_reports_primary", + "columns": [ + "inspection_id" + ], + "isUnique": true, + "where": "kind = 'primary'" + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "service_inspectors": { + "name": "service_inspectors", + "columns": { + "service_id": { + "name": "service_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_service_inspectors_tenant": { + "name": "idx_service_inspectors_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "service_inspectors_service_id_user_id_pk": { + "columns": [ + "service_id", + "user_id" + ], + "name": "service_inspectors_service_id_user_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "services": { + "name": "services", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "price_cents": { + "name": "price_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "duration_minutes": { + "name": "duration_minutes", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "template_id": { + "name": "template_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "agreement_id": { + "name": "agreement_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "default_event_type_slugs": { + "name": "default_event_type_slugs", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_services_tenant": { + "name": "idx_services_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "services_tenant_id_tenants_id_fk": { + "name": "services_tenant_id_tenants_id_fk", + "tableFrom": "services", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "services_template_id_templates_id_fk": { + "name": "services_template_id_templates_id_fk", + "tableFrom": "services", + "tableTo": "templates", + "columnsFrom": [ + "template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "services_agreement_id_agreements_id_fk": { + "name": "services_agreement_id_agreements_id_fk", + "tableFrom": "services", + "tableTo": "agreements", + "columnsFrom": [ + "agreement_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "signing_keys": { + "name": "signing_keys", + "columns": { + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "private_key_enc": { + "name": "private_key_enc", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "private_key_iv": { + "name": "private_key_iv", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "fingerprint": { + "name": "fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "algorithm": { + "name": "algorithm", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'Ed25519'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "rotated_at": { + "name": "rotated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "signing_keys_tenant_id_tenants_id_fk": { + "name": "signing_keys_tenant_id_tenants_id_fk", + "tableFrom": "signing_keys", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sms_consent_log": { + "name": "sms_consent_log", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "recipient_type": { + "name": "recipient_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "disclosure_version": { + "name": "disclosure_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "captured_via": { + "name": "captured_via", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip": { + "name": "ip", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject_kind": { + "name": "subject_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'contact'" + }, + "subject_id": { + "name": "subject_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + } + }, + "indexes": { + "idx_sms_consent_contact": { + "name": "idx_sms_consent_contact", + "columns": [ + "tenant_id", + "contact_id", + "created_at" + ], + "isUnique": false + }, + "idx_sms_consent_subject": { + "name": "idx_sms_consent_subject", + "columns": [ + "tenant_id", + "subject_kind", + "subject_id", + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sms_delivery_status": { + "name": "sms_delivery_status", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_message_id": { + "name": "provider_message_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_sms_delivery_status_msg": { + "name": "idx_sms_delivery_status_msg", + "columns": [ + "tenant_id", + "provider_message_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sms_disclosure_versions": { + "name": "sms_disclosure_versions", + "columns": { + "version": { + "name": "version", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "published_at": { + "name": "published_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tags": { + "name": "tags", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_seed": { + "name": "is_seed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_tags_tenant_name": { + "name": "idx_tags_tenant_name", + "columns": [ + "tenant_id", + "name" + ], + "isUnique": true + }, + "idx_tags_tenant": { + "name": "idx_tags_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "templates": { + "name": "templates", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "schema": { + "name": "schema", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "rating_system_id": { + "name": "rating_system_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "property_type": { + "name": "property_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "commercial_subtype": { + "name": "commercial_subtype", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_featured": { + "name": "is_featured", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "default_profile_id": { + "name": "default_profile_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_templates_tenant": { + "name": "idx_templates_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_templates_rating_system": { + "name": "idx_templates_rating_system", + "columns": [ + "rating_system_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "templates_tenant_id_tenants_id_fk": { + "name": "templates_tenant_id_tenants_id_fk", + "tableFrom": "templates", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tenant_custom_holidays": { + "name": "tenant_custom_holidays", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "date": { + "name": "date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "uq_tenant_custom_holidays_tenant_date": { + "name": "uq_tenant_custom_holidays_tenant_date", + "columns": [ + "tenant_id", + "date" + ], + "isUnique": true + }, + "idx_tenant_custom_holidays_tenant_date": { + "name": "idx_tenant_custom_holidays_tenant_date", + "columns": [ + "tenant_id", + "date" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tenant_library_imports": { + "name": "tenant_library_imports", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "library_id": { + "name": "library_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "imported_semver": { + "name": "imported_semver", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "imported_at": { + "name": "imported_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "row_count": { + "name": "row_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": { + "uq_tenant_library_import": { + "name": "uq_tenant_library_import", + "columns": [ + "tenant_id", + "library_id" + ], + "isUnique": true + }, + "idx_tenant_library_imports_tenant": { + "name": "idx_tenant_library_imports_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tenant_marketplace_import_history": { + "name": "tenant_marketplace_import_history", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "library_id": { + "name": "library_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "template_id": { + "name": "template_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_version": { + "name": "source_version", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "target_version": { + "name": "target_version", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rows_affected": { + "name": "rows_affected", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_marketplace_history_tenant": { + "name": "idx_marketplace_history_tenant", + "columns": [ + "tenant_id", + "created_at" + ], + "isUnique": false + }, + "idx_marketplace_history_template": { + "name": "idx_marketplace_history_template", + "columns": [ + "template_id" + ], + "isUnique": false + }, + "idx_marketplace_history_library": { + "name": "idx_marketplace_history_library", + "columns": [ + "library_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tenant_marketplace_imports": { + "name": "tenant_marketplace_imports", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "marketplace_template_id": { + "name": "marketplace_template_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "imported_semver": { + "name": "imported_semver", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "local_template_id": { + "name": "local_template_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "imported_at": { + "name": "imported_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_mkt_imports_tmpl": { + "name": "idx_mkt_imports_tmpl", + "columns": [ + "marketplace_template_id" + ], + "isUnique": false + }, + "idx_mkt_imports_tenant": { + "name": "idx_mkt_imports_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "tenant_marketplace_imports_marketplace_template_id_marketplace_templates_id_fk": { + "name": "tenant_marketplace_imports_marketplace_template_id_marketplace_templates_id_fk", + "tableFrom": "tenant_marketplace_imports", + "tableTo": "marketplace_templates", + "columnsFrom": [ + "marketplace_template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tenant_marketplace_imports_local_template_id_templates_id_fk": { + "name": "tenant_marketplace_imports_local_template_id_templates_id_fk", + "tableFrom": "tenant_marketplace_imports", + "tableTo": "templates", + "columnsFrom": [ + "local_template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "usage_counters": { + "name": "usage_counters", + "columns": { + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "metric": { + "name": "metric", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "period_key": { + "name": "period_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_usage_counters_tenant": { + "name": "idx_usage_counters_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "usage_counters_tenant_id_metric_period_key_pk": { + "columns": [ + "tenant_id", + "metric", + "period_key" + ], + "name": "usage_counters_tenant_id_metric_period_key_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "email_templates": { + "name": "email_templates", + "columns": { + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "blocks": { + "name": "blocks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "email_templates_tenant_id_tenants_id_fk": { + "name": "email_templates_tenant_id_tenants_id_fk", + "tableFrom": "email_templates", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "email_templates_tenant_id_trigger_pk": { + "columns": [ + "tenant_id", + "trigger" + ], + "name": "email_templates_tenant_id_trigger_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tenant_configs": { + "name": "tenant_configs", + "columns": { + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "company_name": { + "name": "company_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "primary_color": { + "name": "primary_color", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "logo_url": { + "name": "logo_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "support_email": { + "name": "support_email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "company_address": { + "name": "company_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_pdf_footer_shown": { + "name": "is_pdf_footer_shown", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "is_pdf_page_numbers_shown": { + "name": "is_pdf_page_numbers_shown", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "is_pdf_license_shown": { + "name": "is_pdf_license_shown", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "sender_email": { + "name": "sender_email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reply_to": { + "name": "reply_to", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email_mode": { + "name": "email_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'platform'" + }, + "video_mode": { + "name": "video_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'r2'" + }, + "sms_mode": { + "name": "sms_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'platform'" + }, + "sender_display_name": { + "name": "sender_display_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "point_of_contact": { + "name": "point_of_contact", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'company'" + }, + "billing_url": { + "name": "billing_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "review_url": { + "name": "review_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "company_phone": { + "name": "company_phone", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "integration_config": { + "name": "integration_config", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "secrets_enc": { + "name": "secrets_enc", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "dek_enc": { + "name": "dek_enc", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ics_token": { + "name": "ics_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "widget_allowed_origins": { + "name": "widget_allowed_origins", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_profile_id": { + "name": "default_profile_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'signature'" + }, + "attention_thresholds": { + "name": "attention_thresholds", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'{\"agreement_unsigned_h\":72,\"invoice_overdue_h\":72,\"report_unpublished_h\":72}'" + }, + "inspection_prefs": { + "name": "inspection_prefs", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_estimates_shown": { + "name": "is_estimates_shown", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_repair_list_enabled": { + "name": "is_repair_list_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_customer_repair_export_enabled": { + "name": "is_customer_repair_export_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_unpaid_blocked": { + "name": "is_unpaid_blocked", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_unsigned_agreement_blocked": { + "name": "is_unsigned_agreement_blocked", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "custom_referral_sources": { + "name": "custom_referral_sources", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "dashboard_column_prefs": { + "name": "dashboard_column_prefs", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_concierge_review_required": { + "name": "is_concierge_review_required", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_inspector_choice_allowed": { + "name": "is_inspector_choice_allowed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_pdf_pipeline_enabled": { + "name": "is_pdf_pipeline_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_team_mode_default": { + "name": "is_team_mode_default", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_apprentice_review_required": { + "name": "is_apprentice_review_required", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_guest_invites_enabled": { + "name": "is_guest_invites_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "require_defect_fields": { + "name": "require_defect_fields", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'none'" + }, + "agreement_retention_years": { + "name": "agreement_retention_years", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 6 + }, + "reinspection_statuses": { + "name": "reinspection_statuses", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_collab_editing_enabled": { + "name": "is_collab_editing_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sms_byo_provider": { + "name": "sms_byo_provider", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email_byo_provider": { + "name": "email_byo_provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'resend'" + }, + "is_managed_eligible": { + "name": "is_managed_eligible", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "managed_provider": { + "name": "managed_provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'twilio'" + }, + "is_reserve_schedule_enabled": { + "name": "is_reserve_schedule_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "reserve_term_years": { + "name": "reserve_term_years", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 12 + }, + "inflation_rate_bps": { + "name": "inflation_rate_bps", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_timezone": { + "name": "default_timezone", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'UTC'" + }, + "booking_slot_mode": { + "name": "booking_slot_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'fixed'" + }, + "booking_slot_interval_min": { + "name": "booking_slot_interval_min", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 30 + }, + "holiday_region": { + "name": "holiday_region", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "holiday_public_policy": { + "name": "holiday_public_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'open'" + }, + "holiday_internal_policy": { + "name": "holiday_internal_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'advisory'" + }, + "default_locale": { + "name": "default_locale", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'en-US'" + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'USD'" + }, + "is_archive_revoking_access": { + "name": "is_archive_revoking_access", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "legal_mode": { + "name": "legal_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'hosted'" + }, + "custom_privacy_url": { + "name": "custom_privacy_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "custom_terms_url": { + "name": "custom_terms_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "privacy_body": { + "name": "privacy_body", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "terms_body": { + "name": "terms_body", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "tenant_configs_tenant_id_tenants_id_fk": { + "name": "tenant_configs_tenant_id_tenants_id_fk", + "tableFrom": "tenant_configs", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tenants": { + "name": "tenants", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tier": { + "name": "tier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'free'" + }, + "stripe_connect_account_id": { + "name": "stripe_connect_account_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "max_users": { + "name": "max_users", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 5 + }, + "deployment_mode": { + "name": "deployment_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'shared'" + }, + "applied_cmd_seq": { + "name": "applied_cmd_seq", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "applied_cred_seq": { + "name": "applied_cred_seq", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "tenants_slug_unique": { + "name": "tenants_slug_unique", + "columns": [ + "slug" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tenant_invites": { + "name": "tenant_invites", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'inspector'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "mentor_id": { + "name": "mentor_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "assigned_section_ids": { + "name": "assigned_section_ids", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "permission_overrides": { + "name": "permission_overrides", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_invites_tenant": { + "name": "idx_invites_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "uq_tenant_invites_pending_email": { + "name": "uq_tenant_invites_pending_email", + "columns": [ + "tenant_id", + "email" + ], + "isUnique": true, + "where": "status = 'pending'" + } + }, + "foreignKeys": { + "tenant_invites_tenant_id_tenants_id_fk": { + "name": "tenant_invites_tenant_id_tenants_id_fk", + "tableFrom": "tenant_invites", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "password_hash": { + "name": "password_hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "photo_url": { + "name": "photo_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_signature_base64": { + "name": "default_signature_base64", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_signature_enabled": { + "name": "is_signature_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'manager'" + }, + "onboarding_state": { + "name": "onboarding_state", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "totp_secret": { + "name": "totp_secret", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_totp_enabled": { + "name": "is_totp_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "totp_recovery_codes": { + "name": "totp_recovery_codes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "totp_verified_at": { + "name": "totp_verified_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_active_at": { + "name": "last_active_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "mentor_id": { + "name": "mentor_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "assigned_section_ids": { + "name": "assigned_section_ids", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "terms_accepted": { + "name": "terms_accepted", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "permission_overrides": { + "name": "permission_overrides", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "locale": { + "name": "locale", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_users_deleted_at": { + "name": "idx_users_deleted_at", + "columns": [ + "deleted_at" + ], + "isUnique": false + }, + "uq_users_tenant_email": { + "name": "uq_users_tenant_email", + "columns": [ + "tenant_id", + "email" + ], + "isUnique": true, + "where": "deleted_at IS NULL" + }, + "idx_users_tenant": { + "name": "idx_users_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_users_slug_per_tenant": { + "name": "idx_users_slug_per_tenant", + "columns": [ + "tenant_id", + "slug" + ], + "isUnique": true + }, + "idx_users_email": { + "name": "idx_users_email", + "columns": [ + "email" + ], + "isUnique": false + } + }, + "foreignKeys": { + "users_tenant_id_tenants_id_fk": { + "name": "users_tenant_id_tenants_id_fk", + "tableFrom": "users", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "audit_logs": { + "name": "audit_logs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inspector_slug": { + "name": "inspector_slug", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_audit_tenant_created": { + "name": "idx_audit_tenant_created", + "columns": [ + "tenant_id", + "created_at" + ], + "isUnique": false + }, + "idx_audit_entity": { + "name": "idx_audit_entity", + "columns": [ + "entity_type", + "entity_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "audit_logs_tenant_id_tenants_id_fk": { + "name": "audit_logs_tenant_id_tenants_id_fk", + "tableFrom": "audit_logs", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "integration_test_results": { + "name": "integration_test_results", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "target": { + "name": "target", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_ok": { + "name": "is_ok", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "detail": { + "name": "detail", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tested_by_user_id": { + "name": "tested_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tested_at": { + "name": "tested_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_integration_test_tenant_target": { + "name": "idx_integration_test_tenant_target", + "columns": [ + "tenant_id", + "target", + "tested_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "notifications": { + "name": "notifications", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "read_at": { + "name": "read_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "archived_at": { + "name": "archived_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_notifications_tenant_user_created": { + "name": "idx_notifications_tenant_user_created", + "columns": [ + "tenant_id", + "user_id", + "created_at" + ], + "isUnique": false + }, + "idx_notifications_tenant_user_unread": { + "name": "idx_notifications_tenant_user_unread", + "columns": [ + "tenant_id", + "user_id", + "read_at" + ], + "isUnique": false + }, + "idx_notifications_tenant_contact_created": { + "name": "idx_notifications_tenant_contact_created", + "columns": [ + "tenant_id", + "contact_id", + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "notifications_tenant_id_tenants_id_fk": { + "name": "notifications_tenant_id_tenants_id_fk", + "tableFrom": "notifications", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notifications_user_id_users_id_fk": { + "name": "notifications_user_id_users_id_fk", + "tableFrom": "notifications", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "parked_cmd_events": { + "name": "parked_cmd_events", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "envelope": { + "name": "envelope", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "received_at": { + "name": "received_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_parked_cmd_events_received_at": { + "name": "idx_parked_cmd_events_received_at", + "columns": [ + "received_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "processed_cmd_events": { + "name": "processed_cmd_events", + "columns": { + "event_id": { + "name": "event_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "cmd_type": { + "name": "cmd_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "processed_at": { + "name": "processed_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "slug_reservations": { + "name": "slug_reservations", + "columns": { + "slug": { + "name": "slug", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sync_outbox": { + "name": "sync_outbox", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_tried_at": { + "name": "last_tried_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_sync_outbox_status_created": { + "name": "idx_sync_outbox_status_created", + "columns": [ + "status", + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tenant_destruction_records": { + "name": "tenant_destruction_records", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tenant_slug": { + "name": "tenant_slug", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rows_deleted": { + "name": "rows_deleted", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "r2_objects": { + "name": "r2_objects", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "r2_bytes": { + "name": "r2_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "kv_keys": { + "name": "kv_keys", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "destroyed_at": { + "name": "destroyed_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_destruction_tenant": { + "name": "idx_destruction_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_destruction_destroyed_at": { + "name": "idx_destruction_destroyed_at", + "columns": [ + "destroyed_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tenant_legal_versions": { + "name": "tenant_legal_versions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "doc": { + "name": "doc", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "body_snapshot": { + "name": "body_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_material": { + "name": "is_material", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "published_at": { + "name": "published_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "published_by_user_id": { + "name": "published_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_tenant_legal_versions_doc_version": { + "name": "idx_tenant_legal_versions_doc_version", + "columns": [ + "tenant_id", + "doc", + "version" + ], + "isUnique": true + }, + "idx_tenant_legal_versions_latest": { + "name": "idx_tenant_legal_versions_latest", + "columns": [ + "tenant_id", + "doc", + "published_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "client_uploads": { + "name": "client_uploads", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "uploaded_by_kind": { + "name": "uploaded_by_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "uploaded_by_ref": { + "name": "uploaded_by_ref", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "uploaded_by_name": { + "name": "uploaded_by_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "r2_key": { + "name": "r2_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "filename": { + "name": "filename", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "size_bytes": { + "name": "size_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_client_uploads_inspection": { + "name": "idx_client_uploads_inspection", + "columns": [ + "tenant_id", + "inspection_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": { + "uq_discount_codes_code_tenant": { + "columns": { + "upper(code)": { + "isExpression": true + } + } + } + } + } +} \ No newline at end of file diff --git a/migrations/meta/_journal.json b/migrations/meta/_journal.json index 2cc5f0e98..87d66e227 100644 --- a/migrations/meta/_journal.json +++ b/migrations/meta/_journal.json @@ -211,6 +211,13 @@ "when": 1785721030453, "tag": "0029_rich_leader", "breakpoints": true + }, + { + "idx": 30, + "version": "6", + "when": 1785765436571, + "tag": "0030_glamorous_blindfold", + "breakpoints": true } ] } \ No newline at end of file diff --git a/scripts/file-size-baseline.json b/scripts/file-size-baseline.json index a8dd1a4c0..af617f24b 100644 --- a/scripts/file-size-baseline.json +++ b/scripts/file-size-baseline.json @@ -10,7 +10,7 @@ "server/api/sms.ts": 843, "app/components/portal/sections/ReportView.tsx": 813, "app/routes/settings-communication.tsx": 777, - "server/services/inspection.service.ts": 767, + "server/services/inspection.service.ts": 769, "server/api/admin/admin-settings.ts": 742, "server/api/inspections/report-delivery.ts": 736, "app/routes/settings-communication-templates.tsx": 731, @@ -18,8 +18,8 @@ "app/routes/template-edit.tsx": 719, "server/index.ts": 693, "app/components/media-studio/PhotoAnnotator.tsx": 692, + "server/services/inspection/inspection-publish.service.ts": 668, "app/hooks/usePhotoOps.ts": 661, - "server/services/inspection/inspection-publish.service.ts": 661, "server/lib/messaging/providers/telnyx-compliance.ts": 657, "app/components/editor/ItemEditor.tsx": 637, "app/components/inspection-edit/CompliancePanel.tsx": 623, @@ -38,7 +38,7 @@ "server/api/inspections/media-studio.ts": 530, "server/api/portal.ts": 525, "server/services/portal-access.service.ts": 525, - "server/api/inspections/publish.ts": 516, + "server/api/inspections/publish.ts": 520, "app/components/settings/ManagedComplianceWizard.tsx": 514, "server/api/bookings/agreement.ts": 510, "app/routes/settings-profile.tsx": 509, diff --git a/server/api/inspections/publish.ts b/server/api/inspections/publish.ts index c82a6ffc0..6503859e9 100644 --- a/server/api/inspections/publish.ts +++ b/server/api/inspections/publish.ts @@ -387,6 +387,7 @@ const publishRoutes = createApiRouter() requirePayment: body.requirePayment, sendAgreementCopy: body.sendAgreementCopy, ...(body.recipients ? { recipients: body.recipients } : {}), + ...(body.reportId ? { reportId: body.reportId } : {}), }; const result = await service.publishInspection(id, tenantId, publishOptions); @@ -401,8 +402,11 @@ const publishRoutes = createApiRouter() let publishedVersion: number | null = null; if (userId) { try { + // The version chain is per REPORT. Passing the id through is + // what keeps two deliverables on one order from interleaving + // into a single chain, which fails verification for both. const out = await c.var.services.reportVersion.snapshotOnPublish( - tenantId, id, userId, body.summary, + tenantId, id, userId, body.summary, body.reportId, ); publishedVersion = out.versionNumber; logger.info('report-version snapshot saved', { diff --git a/server/lib/compliance/erasure-manifest.ts b/server/lib/compliance/erasure-manifest.ts index 4eb344146..ef740ba5c 100644 --- a/server/lib/compliance/erasure-manifest.ts +++ b/server/lib/compliance/erasure-manifest.ts @@ -216,6 +216,9 @@ export const ERASURE_OUT_OF_SCOPE: ErasureOutOfScopeEntry[] = [ { table: 'reports', column: 'template_id', reason: 'template reference, not personal data' }, { table: 'reports', column: 'status', reason: 'workflow enum, not personal data' }, { table: 'reports', column: 'created_at', reason: 'record timestamp, not personal data' }, + { table: 'reports', column: 'published_at', reason: 'record timestamp, not personal data' }, + { table: 'reports', column: 'notified_at', reason: 'record timestamp, not personal data' }, + { table: 'reports', column: 'sort_order', reason: 'presentation ordering integer, not personal data' }, // The tenant's own published Privacy / Terms. `body_snapshot` is the // company's prose, not a data subject's data, and the row's whole purpose is // to be immutable — erasing it would destroy the record of what a document diff --git a/server/lib/db/schema/inspection/core.ts b/server/lib/db/schema/inspection/core.ts index a673fcec5..ae8cbff8d 100644 --- a/server/lib/db/schema/inspection/core.ts +++ b/server/lib/db/schema/inspection/core.ts @@ -226,6 +226,16 @@ export const inspections = sqliteTable('inspections', { unlockedAt: integer('unlocked_at', { mode: 'timestamp_ms' }), unlockedBy: text('unlocked_by'), unlockReason: text('unlock_reason'), + // When the sold service lines were turned into `reports` rows. + // + // Generation runs ONCE, at the point the work is scheduled to begin, and + // this column is what makes that true. Not at booking: a report that + // materialises weeks early clutters the order and freezes a template the + // tenant may still be editing. Not again afterwards either — re-running + // would re-title and re-template documents somebody has already filled in. + // NULL = the lines have not been turned into deliverables yet. + // Appended at table end for D1 rebuild safety. + reportsGeneratedAt: integer('reports_generated_at', { mode: 'timestamp_ms' }), }, (t) => [ index('idx_inspections_tenant').on(t.tenantId), index('idx_inspections_request').on(t.requestId), diff --git a/server/lib/db/schema/inspection/reports.ts b/server/lib/db/schema/inspection/reports.ts index 00b04c023..dffec713f 100644 --- a/server/lib/db/schema/inspection/reports.ts +++ b/server/lib/db/schema/inspection/reports.ts @@ -40,6 +40,27 @@ export const reports = sqliteTable('reports', { title: text('title').notNull(), status: text('status', { enum: ['in_progress', 'published'] }).notNull().default('in_progress'), createdAt: integer('created_at', { mode: 'timestamp_ms' }).notNull(), + // When THIS deliverable went out. `inspections.report_status` is the + // order-wide roll-up and cannot answer "when did the radon report ship", + // which is the question a client waiting on one of several documents is + // actually asking. NULL = not published. + // Appended at table end for D1 rebuild safety. + publishedAt: integer('published_at', { mode: 'timestamp_ms' }), + // When a publish of THIS report actually raised a notification. + // + // Not the same instant as `published_at`, and that difference is the whole + // point: a standard report and a sewer scope finished in one sitting are one + // delivery to the client, so the second publish coalesces into the first and + // leaves this NULL. A report published two days later announces itself, + // which is the reason the client waited. See + // `lib/inspection/report-notifications.ts` for the window. + notifiedAt: integer('notified_at', { mode: 'timestamp_ms' }), + // Presentation order within one order. Reports are generated from the sold + // service lines, and the sequence a tenant put their catalogue in is the + // sequence they think of the work in — an ordering by `created_at` alone + // ties when several rows are written in the same millisecond, which is + // exactly what generation does. + sortOrder: integer('sort_order').notNull().default(0), }, (t) => [ index('idx_reports_inspection').on(t.inspectionId), index('idx_reports_tenant').on(t.tenantId), diff --git a/server/lib/inspection/auto-sign.ts b/server/lib/inspection/auto-sign.ts new file mode 100644 index 000000000..754c83e9c --- /dev/null +++ b/server/lib/inspection/auto-sign.ts @@ -0,0 +1,52 @@ +/** + * Auto-sign on publish. + * + * When the inspection carries the flag AND the assigned inspector has a saved + * signature, the signature is injected into `inspection_results.data` so the + * published report renders signed without anyone doing a manual step. + * + * Behaviour-preserving extraction from the publish service — the body is a move, + * not a rewrite. + */ +import { and, eq } from 'drizzle-orm'; +import type { DrizzleD1Database } from 'drizzle-orm/d1'; +import { inspectionResults, inspections, users } from '../db/schema'; + +export async function applyAutoSignatureOnPublish( + db: DrizzleD1Database, + tenantId: string, + inspectionId: string, +): Promise { + const insp = await db.select().from(inspections) + .where(and(eq(inspections.id, inspectionId), eq(inspections.tenantId, tenantId))) + .get(); + if (!insp?.autoSignOnPublish || !insp.inspectorId) return; + + const inspector = await db.select().from(users) + .where(eq(users.id, insp.inspectorId)).get(); + if (!inspector?.defaultSignatureBase64) return; + + const resultsRow = await db.select().from(inspectionResults) + .where(eq(inspectionResults.inspectionId, inspectionId)).get(); + const data: Record = (resultsRow?.data as Record) ?? {}; + data._inspector_signature = { + signatureBase64: inspector.defaultSignatureBase64, + signedAt: Date.now(), + userId: inspector.id, + auto: true, + }; + + if (resultsRow) { + await db.update(inspectionResults) + .set({ data: data as object, lastSyncedAt: new Date() }) + .where(and(eq(inspectionResults.id, resultsRow.id), eq(inspectionResults.tenantId, tenantId))); + } else { + await db.insert(inspectionResults).values({ + id: crypto.randomUUID(), + tenantId, + inspectionId, + data: data as object, + lastSyncedAt: new Date(), + }); + } +} diff --git a/server/lib/inspection/report-generation.ts b/server/lib/inspection/report-generation.ts new file mode 100644 index 000000000..93e5a30bd --- /dev/null +++ b/server/lib/inspection/report-generation.ts @@ -0,0 +1,250 @@ +/** + * Reports are GENERATED, not assembled. + * + * Selling the services produces the deliverables: three lines on the order — + * standard, sewer, radon — are three reports, each with its own document, its + * own signature chain and its own notification. Adding one by hand exists for + * the two cases that genuinely need it (a late finding, a specialised separated + * report) and is the exception path, not the primary interaction. + * + * WHEN it runs is as load-bearing as what it does. Generation happens at the + * point the work is scheduled to begin, never at booking: a report that + * materialises weeks early clutters the order and, worse, freezes a template the + * tenant may still be editing. It runs ONCE — `inspections.reports_generated_at` + * is the latch — because a second run would re-title and re-template documents + * somebody has already filled in. + */ +import { and, asc, eq, isNull, lte, ne } from 'drizzle-orm'; +import type { DrizzleD1Database } from 'drizzle-orm/d1'; +import { inspections, inspectionResults, inspectionServices, reports, services } from '../db/schema'; +import { INSPECTION_STATUS } from '../status/inspection-status'; +import { REPORT_STATUS } from '../status/report-status'; + +/** + * The number of reports on one order past which things get unpleasant. + * + * A soft ceiling, deliberately: the competitor's own guidance is "no strict + * limit, keep below 15", and ours carry Yjs documents so the same pressure + * applies — every extra report is another collaborative document to open, sync + * and render. Nothing is refused. An inspector who genuinely needs sixteen + * deliverables knows something the software does not. + */ +export const REPORT_COUNT_SOFT_CEILING = 15; + +export interface ReportGenerationResult { + /** Reports inserted by this run. Adopting the placeholder primary is not a creation. */ + created: number; + /** Advisory only — nothing here blocks the caller. */ + warnings: string[]; +} + +/** The soft-ceiling advisory, or nothing. Pure, so the wording is testable. */ +export function reportCountWarnings(reportCount: number): string[] { + if (reportCount <= REPORT_COUNT_SOFT_CEILING) return []; + return [ + `This order has ${reportCount} reports. Keeping an order under ${REPORT_COUNT_SOFT_CEILING} ` + + 'reports is recommended — each one carries its own editable document, and past that the ' + + 'order gets slow to open and hard to read. Nothing is blocked.', + ]; +} + +/** Has anyone written into the primary report's document yet? */ +async function primaryIsUntouched( + db: DrizzleD1Database, + tenantId: string, + inspectionId: string, + primaryReportId: string, +): Promise { + // Both shapes count: a row already bound to this report, and the row the + // inspection was born with, which carries no `report_id` until the + // collaborative document writes one. + const rows = await db.select({ + data: inspectionResults.data, + ydocState: inspectionResults.ydocState, + reportId: inspectionResults.reportId, + }).from(inspectionResults) + .where(and( + eq(inspectionResults.tenantId, tenantId), + eq(inspectionResults.inspectionId, inspectionId), + )) + .all(); + + for (const row of rows) { + if (row.reportId != null && row.reportId !== primaryReportId) continue; + if (row.ydocState != null) return false; + const raw = row.data; + const parsed = typeof raw === 'string' ? JSON.parse(raw) as unknown : raw; + if (parsed && typeof parsed === 'object' && Object.keys(parsed).length > 0) return false; + } + return true; +} + +/** + * Turn this order's sold service lines into reports. + * + * Idempotent PER LINE: a line that already has a report is skipped, and the + * title and template are snapshotted at the moment the report is created. That + * pair is what makes "a default template configured today must not replace a + * document already filled in" true rather than merely intended — running this + * again after the catalogue changes rewrites nothing. + * + * It does not refuse to run twice, deliberately. A client who adds a sewer + * scope at the door should get a sewer report, and the `reports_generated_at` + * latch belongs to the SWEEP (which must not re-scan finished orders for ever), + * not to the reconciliation. + * + * The FIRST line in catalogue order becomes the primary. Every order is born + * with a placeholder primary (the collab route resolves an inspection to its + * primary and fails closed without one), so that placeholder is ADOPTED by the + * first line rather than left orphaned beside a fourth report nobody asked for + * — but only while it is still empty. A primary somebody has already written + * into keeps its title and its template, and the line gets its own report. + */ +export async function generateReportsForServices( + db: DrizzleD1Database, + tenantId: string, + inspectionId: string, + now: Date = new Date(), +): Promise { + const inspection = await db.select({ + id: inspections.id, + reportsGeneratedAt: inspections.reportsGeneratedAt, + }).from(inspections) + .where(and(eq(inspections.id, inspectionId), eq(inspections.tenantId, tenantId))) + .get(); + if (!inspection) return { created: 0, warnings: [] }; + + // Catalogue order, which is the order the tenant thinks of the work in — + // and the same order `listServices` returns, so the order screen and the + // report list cannot disagree. Soft-deleted lines are excluded: a service + // the client declined at the door is history, not a deliverable. + const lines = await db.select({ + lineId: inspectionServices.id, + title: inspectionServices.nameSnapshot, + templateId: services.templateId, + }).from(inspectionServices) + .innerJoin(services, and( + eq(services.id, inspectionServices.serviceId), + eq(services.tenantId, tenantId), + )) + .where(and( + eq(inspectionServices.tenantId, tenantId), + eq(inspectionServices.inspectionId, inspectionId), + eq(inspectionServices.active, true), + )) + .orderBy(asc(services.sortOrder), asc(services.name)) + .all(); + + const existing = await db.select().from(reports) + .where(and(eq(reports.tenantId, tenantId), eq(reports.inspectionId, inspectionId))) + .all(); + const boundLines = new Set(existing.map((r) => r.inspectionServiceId).filter((v): v is string => v != null)); + + // The placeholder the order was born with, if it is still adoptable. Set to + // null the moment it is claimed, so no second line can claim it too. + let adoptable = existing.find((r) => r.kind === 'primary') ?? null; + let hasPrimary = adoptable != null; + let created = 0; + let position = 0; + + for (const line of lines) { + if (boundLines.has(line.lineId)) { position += 1; continue; } + + if (adoptable && adoptable.inspectionServiceId == null + && adoptable.status === REPORT_STATUS.IN_PROGRESS + && await primaryIsUntouched(db, tenantId, inspectionId, adoptable.id)) { + await db.update(reports).set({ + inspectionServiceId: line.lineId, + title: line.title, + sortOrder: position, + // A template chosen when the order was booked is an explicit + // decision and outranks the service default; only an unset one + // is filled in from the catalogue. + ...(adoptable.templateId ? {} : { templateId: line.templateId ?? null }), + }).where(and(eq(reports.id, adoptable.id), eq(reports.tenantId, tenantId))); + adoptable = null; + position += 1; + continue; + } + + await db.insert(reports).values({ + id: crypto.randomUUID(), + tenantId, + inspectionId, + kind: hasPrimary ? 'ancillary' : 'primary', + inspectionServiceId: line.lineId, + templateId: line.templateId ?? null, + title: line.title, + status: REPORT_STATUS.IN_PROGRESS, + createdAt: now, + sortOrder: position, + }); + hasPrimary = true; + adoptable = null; + created += 1; + position += 1; + } + + // First run only. The column answers "when did this order's deliverables + // come into existence", which is the sweep's latch — not "when did anyone + // last reconcile", which would move every time and latch nothing. + if (!inspection.reportsGeneratedAt) { + await db.update(inspections).set({ reportsGeneratedAt: now }) + .where(and(eq(inspections.id, inspectionId), eq(inspections.tenantId, tenantId))); + } + + return { created, warnings: reportCountWarnings(existing.length + created) }; +} + +/** + * The hook the schedule calls: the work on this order is starting, so its + * deliverables should exist. + * + * A separate name from the mechanism on purpose — the WHEN is the decision this + * feature had to make, and a caller reading `onScheduledStart` is told which + * moment it is wiring itself to. + */ +export async function onScheduledStart( + db: DrizzleD1Database, + tenantId: string, + inspectionId: string, + now: Date = new Date(), +): Promise { + return generateReportsForServices(db, tenantId, inspectionId, now); +} + +/** + * The cron half: every order whose scheduled start has arrived and whose lines + * have not been turned into deliverables yet. + * + * `scheduled_start_ms` is the precise booked instant, so this is the literal + * "point the work is scheduled to begin" — and an order that has no such + * instant recorded has no such point, so it is not swept. It keeps the primary + * report it was born with until something else names a moment for it. + * + * Batched, and safe to run again: the per-inspection latch means a row swept + * twice is a no-op the second time. + */ +export async function sweepScheduledReportGeneration( + db: DrizzleD1Database, + nowMs: number, + limit = 100, +): Promise { + const due = await db.select({ id: inspections.id, tenantId: inspections.tenantId }) + .from(inspections) + .where(and( + isNull(inspections.reportsGeneratedAt), + lte(inspections.scheduledStartMs, new Date(nowMs)), + ne(inspections.status, INSPECTION_STATUS.CANCELLED), + )) + .limit(limit) + .all(); + + const now = new Date(nowMs); + let swept = 0; + for (const row of due) { + await generateReportsForServices(db, row.tenantId, row.id, now); + swept += 1; + } + return swept; +} diff --git a/server/lib/inspection/report-notifications.ts b/server/lib/inspection/report-notifications.ts new file mode 100644 index 000000000..72e5e6605 --- /dev/null +++ b/server/lib/inspection/report-notifications.ts @@ -0,0 +1,123 @@ +/** + * Per-report publish bookkeeping, and the rule that stops it spamming. + * + * One order, several reports — but not several "your report is ready" emails. + * + * A standard inspection and a sewer scope finished in the same sitting are ONE + * delivery as far as the client is concerned, and charging them two + * notifications for it is the most visible way this feature could make the + * product worse than the single-report version it replaces. A radon report + * published two days later is the opposite case: announcing itself is the whole + * reason the client waited. + * + * The line between those is time, so that is what this module measures. + */ +import { and, desc, eq, isNotNull, ne } from 'drizzle-orm'; +import type { DrizzleD1Database } from 'drizzle-orm/d1'; +import { reports } from '../db/schema'; +import { Errors } from '../errors'; +import { resolvePrimaryReportId } from './reports'; +import { REPORT_STATUS } from '../status/report-status'; + +/** + * How long after a report notification a SIBLING report's publish is treated as + * part of the same delivery. + * + * One hour. Short enough that a genuinely later deliverable still announces + * itself, long enough to cover an inspector wrapping up a job and publishing + * the documents one after another — which in practice is minutes apart, not + * hours. It is a window on the NOTIFICATION, not on the publish: a report + * published inside the window is still published, still versioned and still + * signed; only the second announcement is suppressed. + */ +export const REPORT_NOTIFY_COALESCE_WINDOW_MS = 60 * 60 * 1000; + +/** + * Which deliverable a publish is about. + * + * A caller-supplied id is re-resolved inside this tenant AND this inspection: a + * report id is the only part of a publish payload that names a row, so it is the + * only part that can publish somebody else's document. Omitted, it means the + * order's primary report — what every caller predating multi-report delivery + * intends. Null comes back only when the order has no primary at all, which the + * publish path treats as "nothing per-report to record" rather than an error. + */ +export async function resolvePublishTargetReport( + db: DrizzleD1Database, + tenantId: string, + inspectionId: string, + reportId?: string, +): Promise { + if (!reportId) return resolvePrimaryReportId(db, tenantId, inspectionId); + const owned = await db.select({ id: reports.id }).from(reports) + .where(and( + eq(reports.id, reportId), + eq(reports.tenantId, tenantId), + eq(reports.inspectionId, inspectionId), + )) + .get(); + if (!owned) throw Errors.NotFound('Report not found'); + return owned.id; +} + +/** + * Record that THIS deliverable shipped. + * + * `inspections.report_status` stays the order-wide roll-up every existing reader + * consumes; it cannot say which document shipped or when, which is exactly the + * question a client waiting on one of several is asking. + */ +export async function markReportPublished( + db: DrizzleD1Database, tenantId: string, reportId: string, now: Date, +): Promise { + await db.update(reports) + .set({ status: REPORT_STATUS.PUBLISHED, publishedAt: now }) + .where(and(eq(reports.id, reportId), eq(reports.tenantId, tenantId))); +} + +/** Record that this deliverable's publish is the one that told people. */ +export async function markReportNotified( + db: DrizzleD1Database, tenantId: string, reportId: string, now: Date, +): Promise { + await db.update(reports).set({ notifiedAt: now }) + .where(and(eq(reports.id, reportId), eq(reports.tenantId, tenantId))); +} + +/** Pure decision, so the window can be tested without a database. */ +export function shouldCoalesceNotification( + lastSiblingNotifiedAtMs: number | null, + nowMs: number, + windowMs: number = REPORT_NOTIFY_COALESCE_WINDOW_MS, +): boolean { + if (lastSiblingNotifiedAtMs == null) return false; + const elapsed = nowMs - lastSiblingNotifiedAtMs; + // A negative elapsed (clock skew, a stamp from the future) is not evidence + // of a recent delivery — coalescing on it would silently drop a real one. + return elapsed >= 0 && elapsed <= windowMs; +} + +/** + * When another report on this order last actually notified anyone. + * + * Excludes the report being published: republishing one document is an + * amendment to it, not a second announcement of a sibling, and letting it + * suppress itself would mean an amended report never told anyone it changed. + */ +export async function lastSiblingNotifiedAt( + db: DrizzleD1Database, + tenantId: string, + inspectionId: string, + exceptReportId: string, +): Promise { + const row = await db.select({ notifiedAt: reports.notifiedAt }).from(reports) + .where(and( + eq(reports.tenantId, tenantId), + eq(reports.inspectionId, inspectionId), + ne(reports.id, exceptReportId), + isNotNull(reports.notifiedAt), + )) + .orderBy(desc(reports.notifiedAt)) + .limit(1) + .get(); + return row?.notifiedAt ? row.notifiedAt.getTime() : null; +} diff --git a/server/lib/inspection/reports.ts b/server/lib/inspection/reports.ts index 3c9489ab5..2633a3527 100644 --- a/server/lib/inspection/reports.ts +++ b/server/lib/inspection/reports.ts @@ -6,12 +6,31 @@ * WHICH. This module is where callers that still address things by inspection * resolve that. */ -import { and, eq } from 'drizzle-orm'; +import { and, asc, eq } from 'drizzle-orm'; import type { DrizzleD1Database } from 'drizzle-orm/d1'; import { reports } from '../db/schema'; import { logger } from '../logger'; import { REPORT_STATUS } from '../status/report-status'; +/** + * Every deliverable on one order, in the order a person would read them. + * + * `sort_order` first, `created_at` second: generation writes several rows in the + * same millisecond, so a timestamp alone leaves the list to whatever the + * database happened to return — and "which report is the standard one" is not a + * question the UI should answer differently on two page loads. + */ +export async function listReports( + db: DrizzleD1Database, + tenantId: string, + inspectionId: string, +): Promise> { + return db.select().from(reports) + .where(and(eq(reports.tenantId, tenantId), eq(reports.inspectionId, inspectionId))) + .orderBy(asc(reports.sortOrder), asc(reports.createdAt)) + .all(); +} + /** * The primary report of an inspection, or null when it has none. * diff --git a/server/lib/validations/inspection/crud.ts b/server/lib/validations/inspection/crud.ts index b90450506..99ead2470 100644 --- a/server/lib/validations/inspection/crud.ts +++ b/server/lib/validations/inspection/crud.ts @@ -255,6 +255,11 @@ export const PublishInspectionSchema = z.object({ // created by ReportVersionService.snapshotOnPublish during this publish. // Optional; max 500 chars. NULL on first publish. summary: z.string().max(500).optional().describe('TODO describe summary field for the OpenInspection MCP integration'), + // Which deliverable is being published. One order can carry several reports — + // a standard inspection and a radon test — and they publish independently, + // each with its own version chain. Omitted = the order's primary report, + // which is what every caller predating multi-report delivery means. + reportId: z.string().min(1).optional().describe('Report to publish; defaults to the inspection primary report.'), }).openapi('PublishInspection'); /** diff --git a/server/scheduled.ts b/server/scheduled.ts index 9ffc5089c..d99b35b2d 100644 --- a/server/scheduled.ts +++ b/server/scheduled.ts @@ -254,6 +254,20 @@ export async function scheduled( logger.error('[cron] _pending cleanup failed', {}, e instanceof Error ? e : undefined); } + // 5a-bis. Turn sold service lines into reports for orders whose scheduled + // start has arrived. Deliberately not at booking: a report that + // materialises weeks early clutters the order and freezes a template the + // tenant may still be editing. Latched per inspection, so a re-run is a + // no-op rather than a re-titling of documents someone has filled in. + try { + const { sweepScheduledReportGeneration } = await import('./lib/inspection/report-generation'); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const generated = await sweepScheduledReportGeneration(drizzle(env.DB) as any, Date.now()); + if (generated > 0) logger.info('[cron] generated reports from sold services', { inspections: generated }); + } catch (e) { + logger.error('[cron] report generation sweep failed', {}, e instanceof Error ? e : undefined); + } + // 5b. Background GC of orphaned inspection R2 blobs (Q8). Idempotent; grace-windowed. try { if (env.PHOTOS) { diff --git a/server/services/automation/shared.ts b/server/services/automation/shared.ts index 4ce538d07..eb96c1349 100644 --- a/server/services/automation/shared.ts +++ b/server/services/automation/shared.ts @@ -32,6 +32,14 @@ export interface TriggerContext { triggerEvent: string; companyName: string; reportBaseUrl: string; + /** + * Which DELIVERABLE a report event is about. One order can publish a + * standard report on Tuesday and a radon report on Thursday, and the + * `report.published` dedup key has to tell those apart — keyed on the + * inspection alone, the second publish is silently swallowed as a retry of + * the first. Absent for every non-report trigger. + */ + reportId?: string; } // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/server/services/automation/trigger.ts b/server/services/automation/trigger.ts index b05597c79..089e5aab6 100644 --- a/server/services/automation/trigger.ts +++ b/server/services/automation/trigger.ts @@ -90,8 +90,13 @@ export function AutomationTrigger }>; sendAgreementCopy?: boolean; + reportId?: string; }) { return this.publish.publishInspection(inspectionId, tenantId, _options); } diff --git a/server/services/inspection/inspection-publish.service.ts b/server/services/inspection/inspection-publish.service.ts index 16ecaa2ce..b899288d4 100644 --- a/server/services/inspection/inspection-publish.service.ts +++ b/server/services/inspection/inspection-publish.service.ts @@ -1,6 +1,15 @@ import { eq, and, desc, inArray } from 'drizzle-orm'; import { inspections, inspectionResults, templates, users, tenantConfigs, tenants, inspectionServices, agreements, agreementRequests, agreementSigners, invoices, contacts } from '../../lib/db/schema'; import { Errors } from '../../lib/errors'; +import { logger } from '../../lib/logger'; +import { applyAutoSignatureOnPublish } from '../../lib/inspection/auto-sign'; +import { + lastSiblingNotifiedAt, + markReportNotified, + markReportPublished, + resolvePublishTargetReport, + shouldCoalesceNotification, +} from '../../lib/inspection/report-notifications'; import { safeISODate } from '../../lib/date'; import { resolveLocale } from '../../lib/locale'; import { InvoiceService } from '../invoice.service'; @@ -510,7 +519,11 @@ export class InspectionPublishService extends InspectionSubService { } /** - * Publishes an inspection report (transitions to delivered status). + * Publishes one of an inspection's reports (transitions to delivered status). + * + * `reportId` names WHICH deliverable — a standard report today, the radon + * report on Thursday. Omitted, it means the order's primary report, which is + * what every caller predating multi-report delivery intends. */ async publishInspection(inspectionId: string, tenantId: string, _options: { theme: string; @@ -522,6 +535,8 @@ export class InspectionPublishService extends InspectionSubService { // (legacy publish modal, AI agent flows) keep working without it. recipients?: Array<{ contactId: string | null; channels: Array<'email' | 'text'> }>; sendAgreementCopy?: boolean; + /** Which report to publish. Defaults to the inspection's primary. */ + reportId?: string; }) { const db = this.getDrizzle(); @@ -532,9 +547,17 @@ export class InspectionPublishService extends InspectionSubService { // The order lifecycle does not gate delivery — a report can ship while // the order is still scheduled. Content completeness: publishReadiness. + // Which deliverable — the standard report today, radon on Thursday. + // Validation and tenant re-resolution live in resolvePublishTargetReport. + const now = new Date(); + const targetReportId = await resolvePublishTargetReport( + db, tenantId, inspectionId, _options.reportId); + await db.update(inspections) .set({ reportStatus: REPORT_STATUS.PUBLISHED }) .where(and(eq(inspections.id, inspectionId), eq(inspections.tenantId, tenantId))); + if (targetReportId) await markReportPublished(db, tenantId, targetReportId, now); + // Await so AutomationService.trigger actually inserts automation_logs // before the response goes out — the prior fire-and-forget pattern // dangled the promise so CF terminated the isolate before the insert @@ -542,45 +565,29 @@ export class InspectionPublishService extends InspectionSubService { // below — all four paths now block on trigger). First publish fires // report.published; a re-publish (a prior version row exists) fires // report.amended so the client gets a distinct amendment notice. - const reportTrigger = await resolvePublishTrigger(this.db, tenantId, inspectionId); - await fireAutomation(this.db, tenantId, inspectionId, reportTrigger); - - // Spec 5H D2 — auto-sign on publish: if the inspection has the flag - // enabled AND the assigned inspector has a saved signature, inject - // _inspector_signature into inspection_results.data so the published - // report renders with the signature without requiring a manual step. - const inspForSign = await db.select().from(inspections) - .where(and(eq(inspections.id, inspectionId), eq(inspections.tenantId, tenantId))) - .get(); - if (inspForSign?.autoSignOnPublish && inspForSign.inspectorId) { - const inspector = await db.select().from(users) - .where(eq(users.id, inspForSign.inspectorId)).get(); - if (inspector?.defaultSignatureBase64) { - const resultsRow = await db.select().from(inspectionResults) - .where(eq(inspectionResults.inspectionId, inspectionId)).get(); - const data: Record = (resultsRow?.data as Record) ?? {}; - data._inspector_signature = { - signatureBase64: inspector.defaultSignatureBase64, - signedAt: Date.now(), - userId: inspector.id, - auto: true, - }; - if (resultsRow) { - await db.update(inspectionResults) - .set({ data: data as object, lastSyncedAt: new Date() }) - .where(and(eq(inspectionResults.id, resultsRow.id), eq(inspectionResults.tenantId, tenantId))); - } else { - await db.insert(inspectionResults).values({ - id: crypto.randomUUID(), - tenantId, - inspectionId, - data: data as object, - lastSyncedAt: new Date(), - }); - } - } + const reportTrigger = await resolvePublishTrigger( + this.db, tenantId, inspectionId, targetReportId ?? undefined); + + // COALESCE WITHIN A WINDOW (see lib/inspection/report-notifications). + // An AMENDMENT is never coalesced — "your report changed" is its own + // thing to say, and suppressing it leaves the client reading a document + // they think they have already seen. + const coalesced = reportTrigger === 'report.published' && shouldCoalesceNotification( + targetReportId ? await lastSiblingNotifiedAt(db, tenantId, inspectionId, targetReportId) : null, + now.getTime()); + + if (coalesced) { + logger.info('report notification coalesced into a sibling delivery', + { inspectionId, reportId: targetReportId }); + } else { + await fireAutomation(this.db, tenantId, inspectionId, reportTrigger, + targetReportId ?? undefined); + if (targetReportId) await markReportNotified(db, tenantId, targetReportId, now); } + // Spec 5H D2 — auto-sign on publish (lib/inspection/auto-sign). + await applyAutoSignatureOnPublish(db, tenantId, inspectionId); + const tenantRow = await db.select({ slug: tenants.slug }) .from(tenants).where(eq(tenants.id, tenantId)).get(); const tenantSlug = tenantRow?.slug ?? ''; diff --git a/server/services/inspection/shared.ts b/server/services/inspection/shared.ts index 980299660..59fb5d84d 100644 --- a/server/services/inspection/shared.ts +++ b/server/services/inspection/shared.ts @@ -98,9 +98,18 @@ export function sanitizeDefectStates(data: Record): void { * automation_logs row, and report.published / inspection.confirmed / * inspection.cancelled / inspection.created automations never fired. */ -export function fireAutomation(db: D1Database, tenantId: string, inspectionId: string, event: string): Promise { +export function fireAutomation( + db: D1Database, tenantId: string, inspectionId: string, event: string, reportId?: string, +): Promise { return new AutomationService(db) - .trigger({ tenantId, inspectionId, triggerEvent: event, companyName: '', reportBaseUrl: '' }) + .trigger({ + tenantId, inspectionId, triggerEvent: event, companyName: '', reportBaseUrl: '', + // Which DELIVERABLE this is about. `report.published` dedups on a + // synthetic per-event key, and an inspection-only key collapses the + // radon report's first publish into the standard report's — for + // ever, not for a window. + ...(reportId ? { reportId } : {}), + }) .catch(err => logger.error('automation trigger failed', { event }, err instanceof Error ? err : undefined)); } @@ -116,11 +125,22 @@ export async function resolvePublishTrigger( db: D1Database, tenantId: string, inspectionId: string, + reportId?: string, ): Promise<'report.published' | 'report.amended'> { + // "A prior version exists" is a question about THIS deliverable. Asked of + // the whole inspection, the radon report's first publish reads as an + // amendment because the standard report was published on Tuesday — and the + // client gets a "your report was updated" notice about a document they have + // never seen. const prior = await drizzle(db) .select({ id: reportVersions.id }) .from(reportVersions) - .where(and(eq(reportVersions.tenantId, tenantId), eq(reportVersions.inspectionId, inspectionId))) + .where(and( + eq(reportVersions.tenantId, tenantId), + reportId + ? eq(reportVersions.reportId, reportId) + : eq(reportVersions.inspectionId, inspectionId), + )) .limit(1) .get(); return prior ? 'report.amended' : 'report.published'; diff --git a/tests/unit/inspections/publish-notification-coalescing.spec.ts b/tests/unit/inspections/publish-notification-coalescing.spec.ts new file mode 100644 index 000000000..4a3813cdd --- /dev/null +++ b/tests/unit/inspections/publish-notification-coalescing.spec.ts @@ -0,0 +1,189 @@ +/** + * One order, several reports — but not several "your report is ready" emails. + * + * Two things had to change together here, and each is invisible on its own. The + * `report.published` dedup key used to name only the INSPECTION, so the radon + * report's first publish looked like a retry of the standard report's and was + * silently dropped — for ever, not for a window. Making the key per-report fixes + * that and immediately creates the opposite problem: two documents finished in + * one sitting now cost the client two emails. The coalescing window is what + * separates "same delivery" from "genuinely later". + */ +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; +import { eq } from 'drizzle-orm'; +import type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3'; +import * as schema from '../../../server/lib/db/schema'; +import { createTestDb, setupSchema } from '../db'; +import { + REPORT_NOTIFY_COALESCE_WINDOW_MS, + shouldCoalesceNotification, +} from '../../../server/lib/inspection/report-notifications'; + +vi.mock('drizzle-orm/d1', () => ({ drizzle: vi.fn() })); +import { drizzle as mockDrizzle } from 'drizzle-orm/d1'; +import { InspectionService } from '../../../server/services/inspection.service'; +import { AutomationService } from '../../../server/services/automation.service'; +import { PeopleService } from '../../../server/services/people.service'; +import { ScopedDB } from '../../../server/lib/db/scoped'; +import { seedRoleProfiles } from '../../../server/services/seed/seed-role-profiles'; + +const TENANT = '00000000-0000-0000-0000-0000000000c0'; +const INSPECTION = 'insp-coalesce'; +const PRIMARY = 'rpt-primary'; +const SEWER = 'rpt-sewer'; +const RADON = 'rpt-radon'; + +const MINUTE = 60_000; +const DAY = 24 * 60 * MINUTE; +const T0 = Date.parse('2026-08-03T15:00:00.000Z'); + +let db: BetterSQLite3Database; +let inspections: InspectionService; + +const publishOptions = (reportId: string) => ({ + theme: 'modern', notifyClient: true, notifyAgent: true, + requireSignature: false, requirePayment: false, reportId, +}); + +async function seedReport(id: string, kind: 'primary' | 'ancillary', title: string, sortOrder: number) { + await db.insert(schema.reports).values({ + id, tenantId: TENANT, inspectionId: INSPECTION, kind, + inspectionServiceId: null, templateId: null, title, + status: 'in_progress', createdAt: new Date(T0), sortOrder, + } as never); +} + +async function notificationCount(): Promise { + const rows = await db.select().from(schema.automationLogs) + .where(eq(schema.automationLogs.inspectionId, INSPECTION)).all(); + return rows.length; +} + +async function notifiedAt(reportId: string): Promise { + const row = await db.select({ notifiedAt: schema.reports.notifiedAt }) + .from(schema.reports).where(eq(schema.reports.id, reportId)).get(); + return row?.notifiedAt ?? null; +} + +beforeEach(async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date(T0)); + + const fx = createTestDb(); + db = fx.db; + await setupSchema(fx.sqlite); + (mockDrizzle as unknown as ReturnType).mockReturnValue(db); + + await db.insert(schema.tenants).values({ + id: TENANT, name: 'Coalesce Co', slug: 'coalesce-co', status: 'active', + deploymentMode: 'shared', tier: 'free', createdAt: new Date(T0), + } as never); + await seedRoleProfiles(db, TENANT, new Date(T0)); + await db.insert(schema.inspections).values({ + id: INSPECTION, tenantId: TENANT, propertyAddress: '9 Coalesce Court', + date: '2026-08-03', status: 'completed', reportStatus: 'in_progress', + paymentStatus: 'unpaid', price: 0, agreementRequired: false, paymentRequired: false, + createdAt: new Date(T0), + } as never); + await db.insert(schema.contacts).values({ + id: 'c-client', tenantId: TENANT, type: 'client', name: 'Jane Client', + email: 'jane@example.com', createdAt: new Date(T0), + } as never); + await new PeopleService({ DB: {} as D1Database }) + .addPerson(TENANT, INSPECTION, 'c-client', `crp_${TENANT}_client`); + + await new AutomationService({} as D1Database).create(TENANT, { + name: 'Report Ready', trigger: 'report.published', recipientKind: 'all', + recipientRoleProfileId: null, delayMinutes: 0, channels: ['email'], + } as never); + + await seedReport(PRIMARY, 'primary', 'Standard Home Inspection', 0); + await seedReport(SEWER, 'ancillary', 'Sewer Scope', 1); + await seedReport(RADON, 'ancillary', 'Radon Testing', 2); + + inspections = new InspectionService( + {} as D1Database, undefined, new ScopedDB(db as never, TENANT)); +}); + +afterEach(() => { + vi.useRealTimers(); +}); + +describe('publish — one notification per delivery, not per document', () => { + it('sends one notification for two reports published in the same minute', async () => { + await inspections.publishInspection(INSPECTION, TENANT, publishOptions(PRIMARY)); + const afterFirst = await notificationCount(); + expect(afterFirst, 'the first publish must actually notify someone').toBeGreaterThan(0); + + vi.setSystemTime(new Date(T0 + MINUTE)); + await inspections.publishInspection(INSPECTION, TENANT, publishOptions(SEWER)); + + // A standard inspection and a sewer scope finished the same afternoon + // must not cost the client two emails. + expect(await notificationCount()).toBe(afterFirst); + expect(await notifiedAt(PRIMARY)).not.toBeNull(); + expect(await notifiedAt(SEWER)).toBeNull(); + // Coalescing suppresses the ANNOUNCEMENT, never the delivery. + const sewer = await db.select().from(schema.reports) + .where(eq(schema.reports.id, SEWER)).get(); + expect(sewer!.status).toBe('published'); + expect(sewer!.publishedAt).not.toBeNull(); + }); + + it('sends a separate notification for a report published two days later', async () => { + await inspections.publishInspection(INSPECTION, TENANT, publishOptions(PRIMARY)); + const afterFirst = await notificationCount(); + + vi.setSystemTime(new Date(T0 + 2 * DAY)); + await inspections.publishInspection(INSPECTION, TENANT, publishOptions(RADON)); + + // Radon is genuinely later, and announcing itself is the whole reason + // the client waited. + expect(await notificationCount()).toBeGreaterThan(afterFirst); + expect(await notifiedAt(RADON)).not.toBeNull(); + }); + + it('publishing the primary with no reportId still stamps the primary', async () => { + await inspections.publishInspection(INSPECTION, TENANT, { + theme: 'modern', notifyClient: true, notifyAgent: true, + requireSignature: false, requirePayment: false, + }); + + expect(await notifiedAt(PRIMARY)).not.toBeNull(); + expect(await notifiedAt(SEWER)).toBeNull(); + }); + + it('refuses a report id belonging to another order', async () => { + await db.insert(schema.inspections).values({ + id: 'insp-other', tenantId: TENANT, propertyAddress: '1 Elsewhere', + date: '2026-08-03', status: 'completed', reportStatus: 'in_progress', + paymentStatus: 'unpaid', price: 0, agreementRequired: false, paymentRequired: false, + createdAt: new Date(T0), + } as never); + await db.insert(schema.reports).values({ + id: 'rpt-elsewhere', tenantId: TENANT, inspectionId: 'insp-other', kind: 'primary', + inspectionServiceId: null, templateId: null, title: 'Someone Else', + status: 'in_progress', createdAt: new Date(T0), sortOrder: 0, + } as never); + + await expect(inspections.publishInspection( + INSPECTION, TENANT, publishOptions('rpt-elsewhere'))).rejects.toThrow(); + }); +}); + +describe('the coalescing window itself', () => { + it('treats a first-ever delivery as not coalesced', () => { + expect(shouldCoalesceNotification(null, T0)).toBe(false); + }); + + it('coalesces inside the window and announces outside it', () => { + expect(shouldCoalesceNotification(T0, T0 + REPORT_NOTIFY_COALESCE_WINDOW_MS)).toBe(true); + expect(shouldCoalesceNotification(T0, T0 + REPORT_NOTIFY_COALESCE_WINDOW_MS + 1)).toBe(false); + }); + + it('does not coalesce on a stamp from the future', () => { + // Clock skew is not evidence of a recent delivery, and treating it as + // such drops a real announcement silently. + expect(shouldCoalesceNotification(T0 + MINUTE, T0)).toBe(false); + }); +}); diff --git a/tests/unit/inspections/report-generation.spec.ts b/tests/unit/inspections/report-generation.spec.ts new file mode 100644 index 000000000..67df78902 --- /dev/null +++ b/tests/unit/inspections/report-generation.spec.ts @@ -0,0 +1,233 @@ +/** + * Selling the services produces the reports. + * + * Before this, an order was born with ONE placeholder report and nothing ever + * created a second, so the `reports` entity could model a radon report but no + * code path could produce one. These specs pin the three things that decide + * whether generation is safe to run: it happens once, at the point the work is + * scheduled to begin, and it never re-titles or re-templates a document + * somebody has already written into. + */ +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { eq } from 'drizzle-orm'; +import type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3'; +import type { DrizzleD1Database } from 'drizzle-orm/d1'; +import * as schema from '../../../server/lib/db/schema'; +import { createTestDb, setupSchema } from '../db'; +import { + REPORT_COUNT_SOFT_CEILING, + generateReportsForServices, + onScheduledStart, + reportCountWarnings, + sweepScheduledReportGeneration, +} from '../../../server/lib/inspection/report-generation'; +import { createPrimaryReport, listReports } from '../../../server/lib/inspection/reports'; + +vi.mock('drizzle-orm/d1', () => ({ drizzle: vi.fn() })); + +const TENANT = '00000000-0000-0000-0000-0000000000r1'; +const INSPECTION = 'insp-generation'; + +let db: BetterSQLite3Database; +/** The unit-test driver stands in for the D1 one; every call site is identical. */ +const asD1 = () => db as unknown as DrizzleD1Database; + +beforeEach(async () => { + const fx = createTestDb(); + db = fx.db; + await setupSchema(fx.sqlite); + await db.insert(schema.tenants).values({ + id: TENANT, name: 'Generation Co', slug: 'generation-co', status: 'active', + deploymentMode: 'shared', tier: 'free', createdAt: new Date(), + } as never); +}); + +async function seedInspection(over: Partial = {}) { + await db.insert(schema.inspections).values({ + id: INSPECTION, tenantId: TENANT, propertyAddress: '1 Deliverable Way', + date: '2026-08-03', status: 'scheduled', reportStatus: 'in_progress', + paymentStatus: 'unpaid', price: 0, agreementRequired: false, paymentRequired: false, + createdAt: new Date(), ...over, + } as never); +} + +async function seedTemplate(id: string) { + await db.insert(schema.templates).values({ + id, tenantId: TENANT, name: id, version: 1, schema: { schemaVersion: 2, sections: [] }, + createdAt: new Date(), + } as never); +} + +/** A catalogue entry plus the line that sells it on this order. */ +async function sellService( + key: string, + name: string, + opts: { templateId?: string | null; sortOrder?: number; inspectionId?: string } = {}, +) { + if (opts.templateId) await seedTemplate(opts.templateId); + await db.insert(schema.services).values({ + id: `svc-${key}`, tenantId: TENANT, name, price: 30000, + templateId: opts.templateId ?? null, active: true, + sortOrder: opts.sortOrder ?? 0, createdAt: new Date(), + } as never); + await db.insert(schema.inspectionServices).values({ + id: `line-${key}`, tenantId: TENANT, inspectionId: opts.inspectionId ?? INSPECTION, + serviceId: `svc-${key}`, nameSnapshot: name, priceSnapshot: 30000, active: true, + } as never); +} + +describe('report generation from sold services', () => { + it('creates one report per sold service, in catalogue order, with the service default template', async () => { + await seedInspection(); + await createPrimaryReport(db, TENANT, INSPECTION, null); + await sellService('standard', 'Standard Home Inspection', { templateId: 'tpl-standard', sortOrder: 1 }); + await sellService('sewer', 'Sewer Scope', { templateId: 'tpl-sewer', sortOrder: 2 }); + await sellService('radon', 'Radon Testing', { templateId: 'tpl-radon', sortOrder: 3 }); + + await onScheduledStart(asD1(), TENANT, INSPECTION); + + const rows = await listReports(asD1(), TENANT, INSPECTION); + expect(rows.map((r) => r.title)) + .toEqual(['Standard Home Inspection', 'Sewer Scope', 'Radon Testing']); + expect(rows.map((r) => r.templateId)).toEqual(['tpl-standard', 'tpl-sewer', 'tpl-radon']); + // The first line takes over the placeholder the order was born with, + // rather than leaving an untitled fourth report beside the three real + // ones — and there is still exactly one primary. + expect(rows.filter((r) => r.kind === 'primary')).toHaveLength(1); + expect(rows[0]!.kind).toBe('primary'); + expect(rows.map((r) => r.inspectionServiceId)) + .toEqual(['line-standard', 'line-sewer', 'line-radon']); + }); + + it('does not retro-template an existing order', async () => { + await seedInspection(); + await createPrimaryReport(db, TENANT, INSPECTION, null); + await sellService('standard', 'Standard Home Inspection', { templateId: 'tpl-standard' }); + await generateReportsForServices(asD1(), TENANT, INSPECTION); + const before = await listReports(asD1(), TENANT, INSPECTION); + + // A default configured today must not replace a document already filled in. + await seedTemplate('tpl-brand-new'); + await db.update(schema.services).set({ templateId: 'tpl-brand-new' }) + .where(eq(schema.services.id, 'svc-standard')); + await generateReportsForServices(asD1(), TENANT, INSPECTION); + + expect(await listReports(asD1(), TENANT, INSPECTION)).toEqual(before); + }); + + it('gives a service added after generation its own report', async () => { + await seedInspection(); + await createPrimaryReport(db, TENANT, INSPECTION, null); + await sellService('standard', 'Standard Home Inspection', { sortOrder: 1 }); + await generateReportsForServices(asD1(), TENANT, INSPECTION); + + // Scope changes at the door are routine. Reconciling again must add the + // new deliverable without disturbing the one already in progress. + await sellService('sewer', 'Sewer Scope', { sortOrder: 2 }); + const result = await generateReportsForServices(asD1(), TENANT, INSPECTION); + + expect(result.created).toBe(1); + expect((await listReports(asD1(), TENANT, INSPECTION)).map((r) => r.title)) + .toEqual(['Standard Home Inspection', 'Sewer Scope']); + }); + + it('leaves a primary somebody has written into alone, and gives the line its own report', async () => { + await seedInspection(); + await seedTemplate('tpl-booked'); + await createPrimaryReport(db, TENANT, INSPECTION, 'tpl-booked'); + const primary = (await listReports(asD1(), TENANT, INSPECTION))[0]!; + await db.insert(schema.inspectionResults).values({ + id: 'res-touched', tenantId: TENANT, inspectionId: INSPECTION, + reportId: primary.id, data: { roof: { rating: 'defect' } }, lastSyncedAt: new Date(), + } as never); + await sellService('standard', 'Standard Home Inspection', { templateId: 'tpl-standard' }); + + await generateReportsForServices(asD1(), TENANT, INSPECTION); + + const rows = await listReports(asD1(), TENANT, INSPECTION); + expect(rows).toHaveLength(2); + const kept = rows.find((r) => r.id === primary.id)!; + expect(kept.title).toBe('Inspection Report'); + expect(kept.templateId).toBe('tpl-booked'); + expect(kept.inspectionServiceId).toBeNull(); + }); + + it('warns past the practical ceiling but enforces nothing', async () => { + await seedInspection(); + await createPrimaryReport(db, TENANT, INSPECTION, null); + for (let i = 0; i < REPORT_COUNT_SOFT_CEILING + 1; i += 1) { + await sellService(`svc${i}`, `Service ${String(i).padStart(2, '0')}`, { sortOrder: i }); + } + + const result = await generateReportsForServices(asD1(), TENANT, INSPECTION); + + expect(await listReports(asD1(), TENANT, INSPECTION)) + .toHaveLength(REPORT_COUNT_SOFT_CEILING + 1); + expect(result.warnings).toContainEqual(expect.stringMatching(/15/)); + }); + + it('says nothing at the ceiling itself', () => { + expect(reportCountWarnings(REPORT_COUNT_SOFT_CEILING)).toEqual([]); + expect(reportCountWarnings(REPORT_COUNT_SOFT_CEILING + 1)).toHaveLength(1); + }); + + it('ignores a line the client declined at the door', async () => { + await seedInspection(); + await createPrimaryReport(db, TENANT, INSPECTION, null); + await sellService('standard', 'Standard Home Inspection', { sortOrder: 1 }); + await sellService('pool', 'Pool Inspection', { sortOrder: 2 }); + await db.update(schema.inspectionServices).set({ active: false }) + .where(eq(schema.inspectionServices.id, 'line-pool')); + + await generateReportsForServices(asD1(), TENANT, INSPECTION); + + expect((await listReports(asD1(), TENANT, INSPECTION)).map((r) => r.title)) + .toEqual(['Standard Home Inspection']); + }); +}); + +describe('the scheduled-start sweep', () => { + const NOW = Date.parse('2026-08-03T15:00:00.000Z'); + + it('generates for an order whose start has arrived, and not for one still weeks away', async () => { + await seedInspection({ scheduledStartMs: new Date(NOW - 60_000) } as never); + await createPrimaryReport(db, TENANT, INSPECTION, null); + await sellService('standard', 'Standard Home Inspection'); + + await db.insert(schema.inspections).values({ + id: 'insp-future', tenantId: TENANT, propertyAddress: '2 Later Lane', + date: '2026-09-01', status: 'scheduled', reportStatus: 'in_progress', + paymentStatus: 'unpaid', price: 0, agreementRequired: false, paymentRequired: false, + createdAt: new Date(), scheduledStartMs: new Date(NOW + 7 * 86_400_000), + } as never); + await createPrimaryReport(db, TENANT, 'insp-future', null); + await sellService('sewer', 'Sewer Scope', { inspectionId: 'insp-future' }); + + const swept = await sweepScheduledReportGeneration(asD1(), NOW); + + expect(swept).toBe(1); + expect((await listReports(asD1(), TENANT, INSPECTION)).map((r) => r.title)) + .toEqual(['Standard Home Inspection']); + // The future order keeps the placeholder it was born with: a report that + // materialises weeks early freezes a template the tenant may still edit. + expect((await listReports(asD1(), TENANT, 'insp-future')).map((r) => r.title)) + .toEqual(['Inspection Report']); + }); + + it('is a no-op the second time it runs', async () => { + await seedInspection({ scheduledStartMs: new Date(NOW - 60_000) } as never); + await createPrimaryReport(db, TENANT, INSPECTION, null); + await sellService('standard', 'Standard Home Inspection'); + + expect(await sweepScheduledReportGeneration(asD1(), NOW)).toBe(1); + expect(await sweepScheduledReportGeneration(asD1(), NOW)).toBe(0); + }); + + it('skips a cancelled order', async () => { + await seedInspection({ scheduledStartMs: new Date(NOW - 60_000), status: 'cancelled' } as never); + await createPrimaryReport(db, TENANT, INSPECTION, null); + await sellService('standard', 'Standard Home Inspection'); + + expect(await sweepScheduledReportGeneration(asD1(), NOW)).toBe(0); + }); +}); diff --git a/tests/unit/reports/pca-foundation-schema.spec.ts b/tests/unit/reports/pca-foundation-schema.spec.ts index 4bbab95d5..ac4fa1aaa 100644 --- a/tests/unit/reports/pca-foundation-schema.spec.ts +++ b/tests/unit/reports/pca-foundation-schema.spec.ts @@ -21,11 +21,12 @@ describe('Commercial PCA Phase F foundation columns', () => { // appended report_photo_mode; A-polish 9b then appended the scheduled instant // trio; Report Style Presets (Plan 1a) then appended the badge_layout_override // + report_photo_columns tweak pair; the two-layer role model then appended - // referred_by_contact_id; the report-gate unlock then appended its trio. + // referred_by_contact_id; the report-gate unlock then appended its trio; + // per-deliverable reports then appended the generation latch. // All appended at the tail (never mid-list) so db:generate emits // ALTER ADD COLUMN, not a rebuild. const names = getTableConfig(inspections).columns.map((c) => c.name); - const tail = names.slice(-15); + const tail = names.slice(-16); expect(tail).toEqual([ 'unit_inspection_mode', 'location_options', @@ -42,6 +43,7 @@ describe('Commercial PCA Phase F foundation columns', () => { 'unlocked_at', 'unlocked_by', 'unlock_reason', + 'reports_generated_at', ]); }); From 5cf55fd83726c4114a8c91ee90fb604a5bb793ed Mon Sep 17 00:00:00 2001 From: important-new Date: Mon, 3 Aug 2026 22:38:37 +0800 Subject: [PATCH 004/111] feat(i18n): per-contact language preference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NULL means no stated preference, not English. Only a stored value is evidence a client wanted another language, which is the signal this column exists to produce. resolveContactLocale owns the fall-through order in one place — contact, then the account a contact is bound to, then the company default, then the request's Accept-Language, then English — because a notification is rendered for a recipient whose language is not the request's, and in a queue or cron context there is no request at all. Each level falls through when it names a language the catalogue has no messages for, so a stored fr-FR gets the company's language rather than untranslated keys. The supported set is restated in server/ (the BFF boundary forbids importing the paraglide runtime) and the equality with project.inlang/settings.json is asserted by the spec rather than asked of a comment: resolving to a locale with no messages behind it degrades to English silently. The workers batch spec's hand-written contacts DDL gains the column too — drizzle's multi-row INSERT binds every schema column, so a missing one fails the whole batch (verified: it goes red without it). --- migrations/0031_gigantic_swarm.sql | 1 + migrations/meta/0031_snapshot.json | 10302 +++++++++++++++++++ migrations/meta/_journal.json | 7 + server/lib/db/schema/contact.ts | 9 + server/lib/i18n/contact-locale.ts | 108 + tests/unit/contacts/contact-locale.spec.ts | 68 + tests/workers/db-batch-atomicity.spec.ts | 9 +- 7 files changed, 10500 insertions(+), 4 deletions(-) create mode 100644 migrations/0031_gigantic_swarm.sql create mode 100644 migrations/meta/0031_snapshot.json create mode 100644 server/lib/i18n/contact-locale.ts create mode 100644 tests/unit/contacts/contact-locale.spec.ts diff --git a/migrations/0031_gigantic_swarm.sql b/migrations/0031_gigantic_swarm.sql new file mode 100644 index 000000000..9e39afb7e --- /dev/null +++ b/migrations/0031_gigantic_swarm.sql @@ -0,0 +1 @@ +ALTER TABLE `contacts` ADD `locale` text; \ No newline at end of file diff --git a/migrations/meta/0031_snapshot.json b/migrations/meta/0031_snapshot.json new file mode 100644 index 000000000..bb271ffc3 --- /dev/null +++ b/migrations/meta/0031_snapshot.json @@ -0,0 +1,10302 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "904b8784-fa10-4966-8644-63991a42856e", + "prevId": "cbb5fe33-b453-49b9-9b8c-70d695db1074", + "tables": { + "agreement_requests": { + "name": "agreement_requests", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "agreement_id": { + "name": "agreement_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_email": { + "name": "client_email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_name": { + "name": "client_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "signature_base64": { + "name": "signature_base64", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "signed_at": { + "name": "signed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "viewed_at": { + "name": "viewed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sent_at": { + "name": "sent_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inspector_signature_base64": { + "name": "inspector_signature_base64", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inspector_signed_at": { + "name": "inspector_signed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inspector_user_id": { + "name": "inspector_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "verification_token": { + "name": "verification_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "content_snapshot": { + "name": "content_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completion_policy": { + "name": "completion_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'all'" + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "purged_at": { + "name": "purged_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "agreement_requests_token_unique": { + "name": "agreement_requests_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_agreement_requests_verify_token": { + "name": "idx_agreement_requests_verify_token", + "columns": [ + "verification_token" + ], + "isUnique": true + }, + "idx_agreement_requests_tenant": { + "name": "idx_agreement_requests_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_agreement_requests_inspection": { + "name": "idx_agreement_requests_inspection", + "columns": [ + "inspection_id" + ], + "isUnique": false + }, + "idx_agreement_requests_token_hash": { + "name": "idx_agreement_requests_token_hash", + "columns": [ + "token_hash" + ], + "isUnique": true + } + }, + "foreignKeys": { + "agreement_requests_tenant_id_tenants_id_fk": { + "name": "agreement_requests_tenant_id_tenants_id_fk", + "tableFrom": "agreement_requests", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agreement_requests_inspection_id_inspections_id_fk": { + "name": "agreement_requests_inspection_id_inspections_id_fk", + "tableFrom": "agreement_requests", + "tableTo": "inspections", + "columnsFrom": [ + "inspection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agreement_requests_agreement_id_agreements_id_fk": { + "name": "agreement_requests_agreement_id_agreements_id_fk", + "tableFrom": "agreement_requests", + "tableTo": "agreements", + "columnsFrom": [ + "agreement_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agreement_requests_inspector_user_id_users_id_fk": { + "name": "agreement_requests_inspector_user_id_users_id_fk", + "tableFrom": "agreement_requests", + "tableTo": "users", + "columnsFrom": [ + "inspector_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "agreement_signers": { + "name": "agreement_signers", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'client'" + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_enc": { + "name": "token_enc", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "signature_base64": { + "name": "signature_base64", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "signed_at": { + "name": "signed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "viewed_at": { + "name": "viewed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "on_behalf_of": { + "name": "on_behalf_of", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "on_behalf_disclaimer": { + "name": "on_behalf_disclaimer", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_reminded_at": { + "name": "last_reminded_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_agreement_signers_tenant_request": { + "name": "idx_agreement_signers_tenant_request", + "columns": [ + "tenant_id", + "request_id" + ], + "isUnique": false + }, + "idx_agreement_signers_request_email": { + "name": "idx_agreement_signers_request_email", + "columns": [ + "request_id", + "email" + ], + "isUnique": true + }, + "idx_agreement_signers_token_hash": { + "name": "idx_agreement_signers_token_hash", + "columns": [ + "token_hash" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "agreements": { + "name": "agreements", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_agreements_tenant": { + "name": "idx_agreements_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "agreements_tenant_id_tenants_id_fk": { + "name": "agreements_tenant_id_tenants_id_fk", + "tableFrom": "agreements", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "automation_logs": { + "name": "automation_logs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "automation_id": { + "name": "automation_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "recipient": { + "name": "recipient", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "recipient_role_key": { + "name": "recipient_role_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'email'" + }, + "send_at": { + "name": "send_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "delivered_at": { + "name": "delivered_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "event_id": { + "name": "event_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "recipient_contact_id": { + "name": "recipient_contact_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notice_id": { + "name": "notice_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_automation_logs_pending": { + "name": "idx_automation_logs_pending", + "columns": [ + "tenant_id", + "status", + "send_at" + ], + "isUnique": false + }, + "idx_automation_logs_insp": { + "name": "idx_automation_logs_insp", + "columns": [ + "inspection_id" + ], + "isUnique": false + }, + "uq_automation_logs_event": { + "name": "uq_automation_logs_event", + "columns": [ + "automation_id", + "inspection_id", + "event_id", + "channel", + "recipient" + ], + "isUnique": true, + "where": "event_id IS NOT NULL" + } + }, + "foreignKeys": { + "automation_logs_tenant_id_tenants_id_fk": { + "name": "automation_logs_tenant_id_tenants_id_fk", + "tableFrom": "automation_logs", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "automations": { + "name": "automations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "recipient_kind": { + "name": "recipient_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "recipient_role_profile_id": { + "name": "recipient_role_profile_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "delay_minutes": { + "name": "delay_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "subject_template": { + "name": "subject_template", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "body_template": { + "name": "body_template", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_template_id": { + "name": "email_template_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "conditions": { + "name": "conditions", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "channels": { + "name": "channels", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[\"email\"]'" + }, + "sms_body": { + "name": "sms_body", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sms_template_id": { + "name": "sms_template_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "is_default": { + "name": "is_default", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "in_app_template_id": { + "name": "in_app_template_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_automations_tenant": { + "name": "idx_automations_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "automations_tenant_id_tenants_id_fk": { + "name": "automations_tenant_id_tenants_id_fk", + "tableFrom": "automations", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "availability": { + "name": "availability", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspector_id": { + "name": "inspector_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "day_of_week": { + "name": "day_of_week", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "start_time": { + "name": "start_time", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "end_time": { + "name": "end_time", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_availability_inspector": { + "name": "idx_availability_inspector", + "columns": [ + "inspector_id" + ], + "isUnique": false + }, + "idx_availability_window_unique": { + "name": "idx_availability_window_unique", + "columns": [ + "inspector_id", + "day_of_week", + "start_time" + ], + "isUnique": true + } + }, + "foreignKeys": { + "availability_tenant_id_tenants_id_fk": { + "name": "availability_tenant_id_tenants_id_fk", + "tableFrom": "availability", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "availability_inspector_id_users_id_fk": { + "name": "availability_inspector_id_users_id_fk", + "tableFrom": "availability", + "tableTo": "users", + "columnsFrom": [ + "inspector_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "availability_overrides": { + "name": "availability_overrides", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspector_id": { + "name": "inspector_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "date": { + "name": "date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_available": { + "name": "is_available", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "start_time": { + "name": "start_time", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "end_time": { + "name": "end_time", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "transparency": { + "name": "transparency", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_avail_overrides_insp": { + "name": "idx_avail_overrides_insp", + "columns": [ + "inspector_id" + ], + "isUnique": false + }, + "idx_avail_overrides_block_unique": { + "name": "idx_avail_overrides_block_unique", + "columns": [ + "inspector_id", + "date" + ], + "isUnique": true, + "where": "is_available = 0 AND source IS NULL" + }, + "uq_avail_overrides_external": { + "name": "uq_avail_overrides_external", + "columns": [ + "inspector_id", + "source", + "external_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "availability_overrides_tenant_id_tenants_id_fk": { + "name": "availability_overrides_tenant_id_tenants_id_fk", + "tableFrom": "availability_overrides", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "availability_overrides_inspector_id_users_id_fk": { + "name": "availability_overrides_inspector_id_users_id_fk", + "tableFrom": "availability_overrides", + "tableTo": "users", + "columnsFrom": [ + "inspector_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "calendar_blocks": { + "name": "calendar_blocks", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "date": { + "name": "date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "start_time": { + "name": "start_time", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "end_time": { + "name": "end_time", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_all_day": { + "name": "is_all_day", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_calendar_blocks_tenant_user_date": { + "name": "idx_calendar_blocks_tenant_user_date", + "columns": [ + "tenant_id", + "user_id", + "date" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "calendar_connection_read_calendars": { + "name": "calendar_connection_read_calendars", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "connection_id": { + "name": "connection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "external_calendar_id": { + "name": "external_calendar_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_role": { + "name": "access_role", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "uq_conn_read_cal": { + "name": "uq_conn_read_cal", + "columns": [ + "connection_id", + "external_calendar_id" + ], + "isUnique": true + }, + "idx_conn_read_cal_tenant": { + "name": "idx_conn_read_cal_tenant", + "columns": [ + "tenant_id", + "connection_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "calendar_connections": { + "name": "calendar_connections", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "auth_type": { + "name": "auth_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "credentials_enc": { + "name": "credentials_enc", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "credentials_dek_enc": { + "name": "credentials_dek_enc", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "capabilities": { + "name": "capabilities", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "calendar_id": { + "name": "calendar_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "connected_at": { + "name": "connected_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_sync_at": { + "name": "last_sync_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "uq_calendar_connections_user_provider": { + "name": "uq_calendar_connections_user_provider", + "columns": [ + "user_id", + "provider" + ], + "isUnique": true + }, + "idx_calendar_connections_tenant_user": { + "name": "idx_calendar_connections_tenant_user", + "columns": [ + "tenant_id", + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "comment_usage": { + "name": "comment_usage", + "columns": { + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "comment_id": { + "name": "comment_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "use_count": { + "name": "use_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "last_used_at": { + "name": "last_used_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_comment_usage_user_last_used": { + "name": "idx_comment_usage_user_last_used", + "columns": [ + "tenant_id", + "user_id", + "last_used_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "comment_usage_comment_id_comments_id_fk": { + "name": "comment_usage_comment_id_comments_id_fk", + "tableFrom": "comment_usage", + "tableTo": "comments", + "columnsFrom": [ + "comment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "comment_usage_tenant_id_user_id_comment_id_pk": { + "columns": [ + "tenant_id", + "user_id", + "comment_id" + ], + "name": "comment_usage_tenant_id_user_id_comment_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "comments": { + "name": "comments", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rating_bucket": { + "name": "rating_bucket", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "section": { + "name": "section", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "library_id": { + "name": "library_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "section_ids": { + "name": "section_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "item_labels": { + "name": "item_labels", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "trigger_code": { + "name": "trigger_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "search_keywords": { + "name": "search_keywords", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "item_label": { + "name": "item_label", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "severity": { + "name": "severity", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "repair_summary": { + "name": "repair_summary", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "estimate_min_cents": { + "name": "estimate_min_cents", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "estimate_max_cents": { + "name": "estimate_max_cents", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "recommended_contractor_type_id": { + "name": "recommended_contractor_type_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_comments_tenant": { + "name": "idx_comments_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_comments_rating_bucket": { + "name": "idx_comments_rating_bucket", + "columns": [ + "tenant_id", + "rating_bucket" + ], + "isUnique": false + }, + "idx_comments_library_id": { + "name": "idx_comments_library_id", + "columns": [ + "library_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "comments_tenant_id_tenants_id_fk": { + "name": "comments_tenant_id_tenants_id_fk", + "tableFrom": "comments", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "commercial_subtypes": { + "name": "commercial_subtypes", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "based_on": { + "name": "based_on", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_disabled": { + "name": "is_disabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_commercial_subtypes_tenant_name": { + "name": "idx_commercial_subtypes_tenant_name", + "columns": [ + "tenant_id", + "name" + ], + "isUnique": true + } + }, + "foreignKeys": { + "commercial_subtypes_tenant_id_tenants_id_fk": { + "name": "commercial_subtypes_tenant_id_tenants_id_fk", + "tableFrom": "commercial_subtypes", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "concierge_confirm_tokens": { + "name": "concierge_confirm_tokens", + "columns": { + "token": { + "name": "token", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_email": { + "name": "client_email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_concierge_tokens_expiry": { + "name": "idx_concierge_tokens_expiry", + "columns": [ + "expires_at" + ], + "isUnique": false + }, + "idx_concierge_confirm_token_hash": { + "name": "idx_concierge_confirm_token_hash", + "columns": [ + "token_hash" + ], + "isUnique": true + } + }, + "foreignKeys": { + "concierge_confirm_tokens_inspection_id_inspections_id_fk": { + "name": "concierge_confirm_tokens_inspection_id_inspections_id_fk", + "tableFrom": "concierge_confirm_tokens", + "tableTo": "inspections", + "columnsFrom": [ + "inspection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "contact_role_profiles": { + "name": "contact_role_profiles", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_template_id": { + "name": "email_template_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sms_template_id": { + "name": "sms_template_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_system": { + "name": "is_system", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "capability_overrides": { + "name": "capability_overrides", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_crp_tenant": { + "name": "idx_crp_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "uq_crp_tenant_key": { + "name": "uq_crp_tenant_key", + "columns": [ + "tenant_id", + "key" + ], + "isUnique": true, + "where": "is_active = 1" + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "contacts": { + "name": "contacts", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'client'" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "agency": { + "name": "agency", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "archived_at": { + "name": "archived_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "agent_user_id": { + "name": "agent_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "agent_linked_at": { + "name": "agent_linked_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "agent_revoked_at": { + "name": "agent_revoked_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "locale": { + "name": "locale", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_contacts_type": { + "name": "idx_contacts_type", + "columns": [ + "tenant_id", + "type" + ], + "isUnique": false + }, + "idx_contacts_tenant": { + "name": "idx_contacts_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "uq_contacts_tenant_email": { + "name": "uq_contacts_tenant_email", + "columns": [ + "tenant_id", + "email" + ], + "isUnique": true, + "where": "email IS NOT NULL AND archived_at IS NULL" + }, + "uq_contacts_tenant_agent_user": { + "name": "uq_contacts_tenant_agent_user", + "columns": [ + "tenant_id", + "agent_user_id" + ], + "isUnique": true, + "where": "agent_user_id IS NOT NULL AND archived_at IS NULL" + }, + "idx_contacts_agent_user": { + "name": "idx_contacts_agent_user", + "columns": [ + "agent_user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "contacts_tenant_id_tenants_id_fk": { + "name": "contacts_tenant_id_tenants_id_fk", + "tableFrom": "contacts", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "contractor_types": { + "name": "contractor_types", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_contractor_types_tenant": { + "name": "idx_contractor_types_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "cost_items": { + "name": "cost_items", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "building_id": { + "name": "building_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "instance_index": { + "name": "instance_index", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit_id": { + "name": "unit_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "finding_key": { + "name": "finding_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "system": { + "name": "system", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "component": { + "name": "component", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cost_method": { + "name": "cost_method", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "uom": { + "name": "uom", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit_cost_cents": { + "name": "unit_cost_cents", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "lump_sum_cents": { + "name": "lump_sum_cents", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "eul": { + "name": "eul", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "eff_age": { + "name": "eff_age", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rul": { + "name": "rul", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "suggested_remedy": { + "name": "suggested_remedy", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "section_ref": { + "name": "section_ref", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "photo_ref": { + "name": "photo_ref", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_cost_items_tenant_inspection": { + "name": "idx_cost_items_tenant_inspection", + "columns": [ + "tenant_id", + "inspection_id" + ], + "isUnique": false + }, + "idx_cost_items_finding_key": { + "name": "idx_cost_items_finding_key", + "columns": [ + "finding_key" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "defect_categories": { + "name": "defect_categories", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'#6b7280'" + }, + "is_summary_driver": { + "name": "is_summary_driver", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "is_seed": { + "name": "is_seed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_defect_categories_tenant": { + "name": "idx_defect_categories_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "discount_codes": { + "name": "discount_codes", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "max_uses": { + "name": "max_uses", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "uses_count": { + "name": "uses_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_discount_codes_tenant": { + "name": "idx_discount_codes_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "uq_discount_codes_code_tenant": { + "name": "uq_discount_codes_code_tenant", + "columns": [ + "upper(code)", + "tenant_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "discount_codes_tenant_id_tenants_id_fk": { + "name": "discount_codes_tenant_id_tenants_id_fk", + "tableFrom": "discount_codes", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "document_review_items": { + "name": "document_review_items", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "document_key": { + "name": "document_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_requested": { + "name": "is_requested", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_received": { + "name": "is_received", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_reviewed": { + "name": "is_reviewed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_na": { + "name": "is_na", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": { + "idx_doc_review_inspection": { + "name": "idx_doc_review_inspection", + "columns": [ + "tenant_id", + "inspection_id" + ], + "isUnique": false + }, + "uq_doc_review_item": { + "name": "uq_doc_review_item", + "columns": [ + "inspection_id", + "document_key" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "email_suppressions": { + "name": "email_suppressions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_provider": { + "name": "source_provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_event_id": { + "name": "provider_event_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_email_suppressions_email": { + "name": "idx_email_suppressions_email", + "columns": [ + "tenant_id", + "email" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "erasure_log": { + "name": "erasure_log", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject_email": { + "name": "subject_email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "requested_by": { + "name": "requested_by", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "identity_basis": { + "name": "identity_basis", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "decisions_json": { + "name": "decisions_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "retained_count": { + "name": "retained_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "anonymized_count": { + "name": "anonymized_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "deleted_count": { + "name": "deleted_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "response_note": { + "name": "response_note", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_erasure_log_tenant": { + "name": "idx_erasure_log_tenant", + "columns": [ + "tenant_id", + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "esign_audit_logs": { + "name": "esign_audit_logs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "event": { + "name": "event", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payload_json": { + "name": "payload_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "prev_hash": { + "name": "prev_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hash": { + "name": "hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "signature": { + "name": "signature", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "key_fingerprint": { + "name": "key_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_esign_audit_logs_request": { + "name": "idx_esign_audit_logs_request", + "columns": [ + "tenant_id", + "request_id", + "created_at" + ], + "isUnique": false + }, + "idx_esign_audit_logs_event_dedup": { + "name": "idx_esign_audit_logs_event_dedup", + "columns": [ + "tenant_id", + "request_id", + "event" + ], + "isUnique": true, + "where": "event NOT LIKE 'signer.%'" + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "event_types": { + "name": "event_types", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "default_duration_min": { + "name": "default_duration_min", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 30 + }, + "default_price_cents": { + "name": "default_price_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'#6366f1'" + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "uq_event_types_tenant_slug": { + "name": "uq_event_types_tenant_slug", + "columns": [ + "tenant_id", + "slug" + ], + "isUnique": true + } + }, + "foreignKeys": { + "event_types_tenant_id_tenants_id_fk": { + "name": "event_types_tenant_id_tenants_id_fk", + "tableFrom": "event_types", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_access_tokens": { + "name": "inspection_access_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "recipient_email": { + "name": "recipient_email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'client'" + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_enc": { + "name": "token_enc", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_iat_token": { + "name": "idx_iat_token", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_iat_inspection": { + "name": "idx_iat_inspection", + "columns": [ + "tenant_id", + "inspection_id" + ], + "isUnique": false + }, + "idx_iat_recipient": { + "name": "idx_iat_recipient", + "columns": [ + "inspection_id", + "recipient_email" + ], + "isUnique": true + }, + "idx_iat_token_hash": { + "name": "idx_iat_token_hash", + "columns": [ + "token_hash" + ], + "isUnique": true + } + }, + "foreignKeys": { + "inspection_access_tokens_tenant_id_tenants_id_fk": { + "name": "inspection_access_tokens_tenant_id_tenants_id_fk", + "tableFrom": "inspection_access_tokens", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_events": { + "name": "inspection_events", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "event_type_id": { + "name": "event_type_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspector_id": { + "name": "inspector_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scheduled_at": { + "name": "scheduled_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "duration_min": { + "name": "duration_min", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "price_cents": { + "name": "price_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'scheduled'" + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "results_received_at": { + "name": "results_received_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "gcal_event_id": { + "name": "gcal_event_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_inspection_events_scheduled": { + "name": "idx_inspection_events_scheduled", + "columns": [ + "tenant_id", + "scheduled_at" + ], + "isUnique": false + }, + "idx_inspection_events_inspection": { + "name": "idx_inspection_events_inspection", + "columns": [ + "inspection_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "inspection_events_tenant_id_tenants_id_fk": { + "name": "inspection_events_tenant_id_tenants_id_fk", + "tableFrom": "inspection_events", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_events_inspection_id_inspections_id_fk": { + "name": "inspection_events_inspection_id_inspections_id_fk", + "tableFrom": "inspection_events", + "tableTo": "inspections", + "columnsFrom": [ + "inspection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "inspection_events_event_type_id_event_types_id_fk": { + "name": "inspection_events_event_type_id_event_types_id_fk", + "tableFrom": "inspection_events", + "tableTo": "event_types", + "columnsFrom": [ + "event_type_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_events_inspector_id_users_id_fk": { + "name": "inspection_events_inspector_id_users_id_fk", + "tableFrom": "inspection_events", + "tableTo": "users", + "columnsFrom": [ + "inspector_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_inspectors": { + "name": "inspection_inspectors", + "columns": { + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'lead'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_insp_inspectors_tenant_user": { + "name": "idx_insp_inspectors_tenant_user", + "columns": [ + "tenant_id", + "user_id" + ], + "isUnique": false + }, + "idx_insp_inspectors_user": { + "name": "idx_insp_inspectors_user", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "inspection_inspectors_inspection_id_user_id_pk": { + "columns": [ + "inspection_id", + "user_id" + ], + "name": "inspection_inspectors_inspection_id_user_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_item_tag_links": { + "name": "inspection_item_tag_links", + "columns": { + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "item_id": { + "name": "item_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tag_id": { + "name": "tag_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_tag_links_tenant": { + "name": "idx_tag_links_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_tag_links_tag": { + "name": "idx_tag_links_tag", + "columns": [ + "tag_id" + ], + "isUnique": false + }, + "idx_tag_links_inspection_item": { + "name": "idx_tag_links_inspection_item", + "columns": [ + "inspection_id", + "item_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "inspection_item_tag_links_inspection_id_item_id_tag_id_pk": { + "columns": [ + "inspection_id", + "item_id", + "tag_id" + ], + "name": "inspection_item_tag_links_inspection_id_item_id_tag_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_media_pool": { + "name": "inspection_media_pool", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "r2_key": { + "name": "r2_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "exif_data": { + "name": "exif_data", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "annotations": { + "name": "annotations", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "caption": { + "name": "caption", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "media_type": { + "name": "media_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'photo'" + }, + "stream_uid": { + "name": "stream_uid", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "poster_pct": { + "name": "poster_pct", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "duration_sec": { + "name": "duration_sec", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'stream'" + }, + "poster_key": { + "name": "poster_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_media_pool_tenant": { + "name": "idx_media_pool_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_media_pool_inspection": { + "name": "idx_media_pool_inspection", + "columns": [ + "inspection_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_messages": { + "name": "inspection_messages", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "from_role": { + "name": "from_role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "from_name": { + "name": "from_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "attachments": { + "name": "attachments", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "read_at": { + "name": "read_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "from_user_id": { + "name": "from_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_msg_inspection": { + "name": "idx_msg_inspection", + "columns": [ + "inspection_id", + "created_at" + ], + "isUnique": false + }, + "idx_msg_contact": { + "name": "idx_msg_contact", + "columns": [ + "tenant_id", + "contact_id", + "created_at" + ], + "isUnique": false + }, + "idx_msg_unread": { + "name": "idx_msg_unread", + "columns": [ + "tenant_id", + "contact_id", + "from_role" + ], + "isUnique": false, + "where": "\"inspection_messages\".\"read_at\" IS NULL" + } + }, + "foreignKeys": { + "inspection_messages_tenant_id_tenants_id_fk": { + "name": "inspection_messages_tenant_id_tenants_id_fk", + "tableFrom": "inspection_messages", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_messages_inspection_id_inspections_id_fk": { + "name": "inspection_messages_inspection_id_inspections_id_fk", + "tableFrom": "inspection_messages", + "tableTo": "inspections", + "columnsFrom": [ + "inspection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_people": { + "name": "inspection_people", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role_profile_id": { + "name": "role_profile_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_ip_inspection": { + "name": "idx_ip_inspection", + "columns": [ + "inspection_id" + ], + "isUnique": false + }, + "idx_ip_tenant": { + "name": "idx_ip_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "uq_ip_insp_contact_role": { + "name": "uq_ip_insp_contact_role", + "columns": [ + "inspection_id", + "contact_id", + "role_profile_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_requests": { + "name": "inspection_requests", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_name": { + "name": "client_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_email": { + "name": "client_email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "client_phone": { + "name": "client_phone", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "property_address": { + "name": "property_address", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "property_city": { + "name": "property_city", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "property_state": { + "name": "property_state", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "property_zip": { + "name": "property_zip", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scheduled_at": { + "name": "scheduled_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "total_amount_cents": { + "name": "total_amount_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "payment_status": { + "name": "payment_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'unpaid'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_inspection_requests_tenant": { + "name": "idx_inspection_requests_tenant", + "columns": [ + "tenant_id", + "status", + "scheduled_at" + ], + "isUnique": false + }, + "idx_inspection_requests_email": { + "name": "idx_inspection_requests_email", + "columns": [ + "tenant_id", + "client_email" + ], + "isUnique": false + } + }, + "foreignKeys": { + "inspection_requests_tenant_id_tenants_id_fk": { + "name": "inspection_requests_tenant_id_tenants_id_fk", + "tableFrom": "inspection_requests", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_results": { + "name": "inspection_results", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "data": { + "name": "data", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ydoc_state": { + "name": "ydoc_state", + "type": "blob", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "rating_system_id": { + "name": "rating_system_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rating_system_snapshot": { + "name": "rating_system_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "report_id": { + "name": "report_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_results_tenant": { + "name": "idx_results_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_results_inspection": { + "name": "idx_results_inspection", + "columns": [ + "inspection_id" + ], + "isUnique": false + }, + "uq_results_report": { + "name": "uq_results_report", + "columns": [ + "report_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "inspection_results_tenant_id_tenants_id_fk": { + "name": "inspection_results_tenant_id_tenants_id_fk", + "tableFrom": "inspection_results", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_results_inspection_id_inspections_id_fk": { + "name": "inspection_results_inspection_id_inspections_id_fk", + "tableFrom": "inspection_results", + "tableTo": "inspections", + "columnsFrom": [ + "inspection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_services": { + "name": "inspection_services", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "service_id": { + "name": "service_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "price_override_cents": { + "name": "price_override_cents", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name_snapshot": { + "name": "name_snapshot", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "price_snapshot_cents": { + "name": "price_snapshot_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + } + }, + "indexes": { + "idx_insp_services_tenant": { + "name": "idx_insp_services_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_insp_services_insp": { + "name": "idx_insp_services_insp", + "columns": [ + "inspection_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "inspection_services_tenant_id_tenants_id_fk": { + "name": "inspection_services_tenant_id_tenants_id_fk", + "tableFrom": "inspection_services", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_services_inspection_id_inspections_id_fk": { + "name": "inspection_services_inspection_id_inspections_id_fk", + "tableFrom": "inspection_services", + "tableTo": "inspections", + "columnsFrom": [ + "inspection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "inspection_services_service_id_services_id_fk": { + "name": "inspection_services_service_id_services_id_fk", + "tableFrom": "inspection_services", + "tableTo": "services", + "columnsFrom": [ + "service_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_types": { + "name": "inspection_types", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "based_on": { + "name": "based_on", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_inspection_types_tenant_name": { + "name": "idx_inspection_types_tenant_name", + "columns": [ + "tenant_id", + "name" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspection_units": { + "name": "inspection_units", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "parent_unit_id": { + "name": "parent_unit_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'unit'" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + }, + "attrs": { + "name": "attrs", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_inspection_units_tenant_inspection": { + "name": "idx_inspection_units_tenant_inspection", + "columns": [ + "tenant_id", + "inspection_id" + ], + "isUnique": false + }, + "idx_inspection_units_parent": { + "name": "idx_inspection_units_parent", + "columns": [ + "parent_unit_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspections": { + "name": "inspections", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspector_id": { + "name": "inspector_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "property_address": { + "name": "property_address", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "address_place_id": { + "name": "address_place_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address_street": { + "name": "address_street", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address_city": { + "name": "address_city", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address_state": { + "name": "address_state", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address_zip": { + "name": "address_zip", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address_county": { + "name": "address_county", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address_lat": { + "name": "address_lat", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address_lng": { + "name": "address_lng", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address_geocoded_at": { + "name": "address_geocoded_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "template_id": { + "name": "template_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "date": { + "name": "date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'requested'" + }, + "report_status": { + "name": "report_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'in_progress'" + }, + "payment_status": { + "name": "payment_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'unpaid'" + }, + "price_cents": { + "name": "price_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cancel_reason": { + "name": "cancel_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cancel_notes": { + "name": "cancel_notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_payment_required": { + "name": "is_payment_required", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_agreement_required": { + "name": "is_agreement_required", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_auto_sign_on_publish": { + "name": "is_auto_sign_on_publish", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "discount_code_id": { + "name": "discount_code_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "discount_amount_cents": { + "name": "discount_amount_cents", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "closing_date": { + "name": "closing_date", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "referral_source": { + "name": "referral_source", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reference_number": { + "name": "reference_number", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "internal_notes": { + "name": "internal_notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "year_built": { + "name": "year_built", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sqft": { + "name": "sqft", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "foundation_type": { + "name": "foundation_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "bedrooms": { + "name": "bedrooms", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "bathrooms": { + "name": "bathrooms", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "lot_size": { + "name": "lot_size", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "property_facts": { + "name": "property_facts", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cover_photo_id": { + "name": "cover_photo_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cover_crop": { + "name": "cover_crop", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cover_image_key": { + "name": "cover_image_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit": { + "name": "unit", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "property_type": { + "name": "property_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "commercial_subtype": { + "name": "commercial_subtype", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "report_tier": { + "name": "report_tier", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "county": { + "name": "county", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_automations_disabled": { + "name": "is_automations_disabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "template_snapshot": { + "name": "template_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "template_snapshot_version": { + "name": "template_snapshot_version", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 1 + }, + "profile_override": { + "name": "profile_override", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "require_defect_fields_override": { + "name": "require_defect_fields_override", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "concierge_status": { + "name": "concierge_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_team_mode": { + "name": "is_team_mode", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "lead_inspector_id": { + "name": "lead_inspector_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "helper_inspector_ids": { + "name": "helper_inspector_ids", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "data_version": { + "name": "data_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "source_inspection_id": { + "name": "source_inspection_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "root_inspection_id": { + "name": "root_inspection_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reinspection_round": { + "name": "reinspection_round", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit_inspection_mode": { + "name": "unit_inspection_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'tagged'" + }, + "location_options": { + "name": "location_options", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sampling_declaration": { + "name": "sampling_declaration", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "pca_narrative": { + "name": "pca_narrative", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "deviations": { + "name": "deviations", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "report_photo_mode": { + "name": "report_photo_mode", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scheduled_start_ms": { + "name": "scheduled_start_ms", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scheduled_end_ms": { + "name": "scheduled_end_ms", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "duration_min": { + "name": "duration_min", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "badge_layout_override": { + "name": "badge_layout_override", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "report_photo_columns": { + "name": "report_photo_columns", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "referred_by_contact_id": { + "name": "referred_by_contact_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unlocked_at": { + "name": "unlocked_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unlocked_by": { + "name": "unlocked_by", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unlock_reason": { + "name": "unlock_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reports_generated_at": { + "name": "reports_generated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_inspections_tenant": { + "name": "idx_inspections_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_inspections_request": { + "name": "idx_inspections_request", + "columns": [ + "request_id" + ], + "isUnique": false + }, + "idx_inspections_inspector": { + "name": "idx_inspections_inspector", + "columns": [ + "inspector_id" + ], + "isUnique": false + }, + "idx_inspections_tenant_status": { + "name": "idx_inspections_tenant_status", + "columns": [ + "tenant_id", + "status" + ], + "isUnique": false + }, + "idx_inspections_tenant_date": { + "name": "idx_inspections_tenant_date", + "columns": [ + "tenant_id", + "date" + ], + "isUnique": false + }, + "idx_inspections_inspector_date": { + "name": "idx_inspections_inspector_date", + "columns": [ + "inspector_id", + "date" + ], + "isUnique": false + }, + "idx_inspections_root": { + "name": "idx_inspections_root", + "columns": [ + "root_inspection_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "inspections_tenant_id_tenants_id_fk": { + "name": "inspections_tenant_id_tenants_id_fk", + "tableFrom": "inspections", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspections_inspector_id_users_id_fk": { + "name": "inspections_inspector_id_users_id_fk", + "tableFrom": "inspections", + "tableTo": "users", + "columnsFrom": [ + "inspector_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspections_template_id_templates_id_fk": { + "name": "inspections_template_id_templates_id_fk", + "tableFrom": "inspections", + "tableTo": "templates", + "columnsFrom": [ + "template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspections_discount_code_id_discount_codes_id_fk": { + "name": "inspections_discount_code_id_discount_codes_id_fk", + "tableFrom": "inspections", + "tableTo": "discount_codes", + "columnsFrom": [ + "discount_code_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspections_request_id_inspection_requests_id_fk": { + "name": "inspections_request_id_inspection_requests_id_fk", + "tableFrom": "inspections", + "tableTo": "inspection_requests", + "columnsFrom": [ + "request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inspector_credentials": { + "name": "inspector_credentials", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "member_number": { + "name": "member_number", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "image_r2_key": { + "name": "image_r2_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_inspector_credentials_tenant": { + "name": "idx_inspector_credentials_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_inspector_credentials_user": { + "name": "idx_inspector_credentials_user", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "invoices": { + "name": "invoices", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "client_name": { + "name": "client_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "client_email": { + "name": "client_email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "amount_cents": { + "name": "amount_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "line_items": { + "name": "line_items", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "due_date": { + "name": "due_date", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sent_at": { + "name": "sent_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "paid_at": { + "name": "paid_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "payment_method": { + "name": "payment_method", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "partial_paid_at": { + "name": "partial_paid_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "voided_at": { + "name": "voided_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "qbo_sync_status": { + "name": "qbo_sync_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'USD'" + } + }, + "indexes": { + "idx_invoices_tenant": { + "name": "idx_invoices_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_invoices_inspection": { + "name": "idx_invoices_inspection", + "columns": [ + "inspection_id" + ], + "isUnique": false + }, + "idx_invoices_contact": { + "name": "idx_invoices_contact", + "columns": [ + "tenant_id", + "contact_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "invoices_tenant_id_tenants_id_fk": { + "name": "invoices_tenant_id_tenants_id_fk", + "tableFrom": "invoices", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "invoices_inspection_id_inspections_id_fk": { + "name": "invoices_inspection_id_inspections_id_fk", + "tableFrom": "invoices", + "tableTo": "inspections", + "columnsFrom": [ + "inspection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "invoices_contact_id_contacts_id_fk": { + "name": "invoices_contact_id_contacts_id_fk", + "tableFrom": "invoices", + "tableTo": "contacts", + "columnsFrom": [ + "contact_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "marketplace_libraries": { + "name": "marketplace_libraries", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "semver": { + "name": "semver", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "schema": { + "name": "schema", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'system'" + }, + "changelog": { + "name": "changelog", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "download_count": { + "name": "download_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "is_featured": { + "name": "is_featured", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_marketplace_libraries_kind_featured": { + "name": "idx_marketplace_libraries_kind_featured", + "columns": [ + "kind", + "is_featured" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "marketplace_templates": { + "name": "marketplace_templates", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "semver": { + "name": "semver", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "schema": { + "name": "schema", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'system'" + }, + "changelog": { + "name": "changelog", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "download_count": { + "name": "download_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "is_featured": { + "name": "is_featured", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "message_templates": { + "name": "message_templates", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "variables": { + "name": "variables", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_seeded": { + "name": "is_seeded", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_message_templates_tenant_channel": { + "name": "idx_message_templates_tenant_channel", + "columns": [ + "tenant_id", + "channel" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "messaging_compliance": { + "name": "messaging_compliance", + "columns": { + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'own'" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "subaccount_sid": { + "name": "subaccount_sid", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "customer_profile_sid": { + "name": "customer_profile_sid", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "customer_profile_status": { + "name": "customer_profile_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "brand_sid": { + "name": "brand_sid", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "brand_status": { + "name": "brand_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "campaign_sid": { + "name": "campaign_sid", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "campaign_status": { + "name": "campaign_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tfv_sid": { + "name": "tfv_sid", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tfv_status": { + "name": "tfv_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "messaging_resource_sid": { + "name": "messaging_resource_sid", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provider_meta": { + "name": "provider_meta", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provisioned_number": { + "name": "provisioned_number", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provisioned_number_sid": { + "name": "provisioned_number_sid", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "has_sender_attached": { + "name": "has_sender_attached", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "compliance_status": { + "name": "compliance_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'not_started'" + }, + "rejection_reason": { + "name": "rejection_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_sync_at": { + "name": "last_sync_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "notification_preferences": { + "name": "notification_preferences", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject_kind": { + "name": "subject_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject_id": { + "name": "subject_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "class_id": { + "name": "class_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_notification_prefs_unique": { + "name": "idx_notification_prefs_unique", + "columns": [ + "tenant_id", + "subject_kind", + "subject_id", + "class_id", + "channel" + ], + "isUnique": true + }, + "idx_notification_prefs_subject": { + "name": "idx_notification_prefs_subject", + "columns": [ + "tenant_id", + "subject_kind", + "subject_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "orphaned_media": { + "name": "orphaned_media", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "r2_key": { + "name": "r2_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "first_seen_at": { + "name": "first_seen_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_orphaned_media_key": { + "name": "idx_orphaned_media_key", + "columns": [ + "tenant_id", + "r2_key" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "processed_webhook_events": { + "name": "processed_webhook_events", + "columns": { + "event_id": { + "name": "event_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "received_at": { + "name": "received_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "psq_responses": { + "name": "psq_responses", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "responses": { + "name": "responses", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'sent'" + }, + "share_token": { + "name": "share_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sent_at": { + "name": "sent_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "received_at": { + "name": "received_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "uq_psq_inspection": { + "name": "uq_psq_inspection", + "columns": [ + "tenant_id", + "inspection_id" + ], + "isUnique": true + }, + "idx_psq_share_token": { + "name": "idx_psq_share_token", + "columns": [ + "share_token" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "qbo_connections": { + "name": "qbo_connections", + "columns": { + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "realm_id": { + "name": "realm_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "company_name": { + "name": "company_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_enc": { + "name": "access_token_enc", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token_enc": { + "name": "refresh_token_enc", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token_expires_at": { + "name": "token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_sync_at": { + "name": "last_sync_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_sync_enabled": { + "name": "is_sync_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "default_item_id": { + "name": "default_item_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "qbo_entity_map": { + "name": "qbo_entity_map", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "oi_type": { + "name": "oi_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "oi_id": { + "name": "oi_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qbo_type": { + "name": "qbo_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qbo_id": { + "name": "qbo_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qbo_sync_token": { + "name": "qbo_sync_token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "synced_at": { + "name": "synced_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_qbo_entity_map_qbo": { + "name": "idx_qbo_entity_map_qbo", + "columns": [ + "tenant_id", + "qbo_type", + "qbo_id" + ], + "isUnique": true + }, + "idx_qbo_entity_map_oi": { + "name": "idx_qbo_entity_map_oi", + "columns": [ + "tenant_id", + "oi_type", + "oi_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "qbo_sync_errors": { + "name": "qbo_sync_errors", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "oi_type": { + "name": "oi_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "oi_id": { + "name": "oi_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "error_msg": { + "name": "error_msg", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "retries": { + "name": "retries", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "is_resolved": { + "name": "is_resolved", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "rating_systems": { + "name": "rating_systems", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "levels": { + "name": "levels", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_default": { + "name": "is_default", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_seed": { + "name": "is_seed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_rating_systems_tenant_slug": { + "name": "idx_rating_systems_tenant_slug", + "columns": [ + "tenant_id", + "slug" + ], + "isUnique": true + }, + "idx_rating_systems_tenant": { + "name": "idx_rating_systems_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "rating_systems_tenant_id_tenants_id_fk": { + "name": "rating_systems_tenant_id_tenants_id_fk", + "tableFrom": "rating_systems", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "repair_request_items": { + "name": "repair_request_items", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "repair_request_id": { + "name": "repair_request_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "finding_key": { + "name": "finding_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "section_title": { + "name": "section_title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "item_label": { + "name": "item_label", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "comment_snapshot": { + "name": "comment_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "requested_credit_cents": { + "name": "requested_credit_cents", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "defect_title_snapshot": { + "name": "defect_title_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "location_snapshot": { + "name": "location_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "category_snapshot": { + "name": "category_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "trade_snapshot": { + "name": "trade_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_repair_request_items_rr": { + "name": "idx_repair_request_items_rr", + "columns": [ + "repair_request_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "repair_requests": { + "name": "repair_requests", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by_kind": { + "name": "created_by_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by_ref": { + "name": "created_by_ref", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "custom_intro": { + "name": "custom_intro", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "share_token": { + "name": "share_token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_repair_requests_inspection": { + "name": "idx_repair_requests_inspection", + "columns": [ + "tenant_id", + "inspection_id" + ], + "isUnique": false + }, + "idx_repair_requests_share_token": { + "name": "idx_repair_requests_share_token", + "columns": [ + "share_token" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "report_exports": { + "name": "report_exports", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "format": { + "name": "format", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "r2_key": { + "name": "r2_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "size_bytes": { + "name": "size_bytes", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_report_exports_inspection": { + "name": "idx_report_exports_inspection", + "columns": [ + "tenant_id", + "inspection_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "report_pdfs": { + "name": "report_pdfs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "r2_key": { + "name": "r2_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "rendered_at": { + "name": "rendered_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_version": { + "name": "source_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "version_number": { + "name": "version_number", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "size_bytes": { + "name": "size_bytes", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'ready'" + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "uq_report_pdfs_inspection_type": { + "name": "uq_report_pdfs_inspection_type", + "columns": [ + "inspection_id", + "type", + "version_number" + ], + "isUnique": true + }, + "idx_report_pdfs_tenant": { + "name": "idx_report_pdfs_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_report_pdfs_status": { + "name": "idx_report_pdfs_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_report_pdfs_content_hash": { + "name": "idx_report_pdfs_content_hash", + "columns": [ + "inspection_id", + "type", + "content_hash" + ], + "isUnique": false + } + }, + "foreignKeys": { + "report_pdfs_tenant_id_tenants_id_fk": { + "name": "report_pdfs_tenant_id_tenants_id_fk", + "tableFrom": "report_pdfs", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "report_signoff": { + "name": "report_signoff", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "person_id": { + "name": "person_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "license": { + "name": "license", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "qualifications_ref": { + "name": "qualifications_ref", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "signed_at": { + "name": "signed_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "signature_ref": { + "name": "signature_ref", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_dual_role": { + "name": "is_dual_role", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + } + }, + "indexes": { + "idx_report_signoff_inspection": { + "name": "idx_report_signoff_inspection", + "columns": [ + "tenant_id", + "inspection_id" + ], + "isUnique": false + }, + "uq_report_signoff_role": { + "name": "uq_report_signoff_role", + "columns": [ + "inspection_id", + "role" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "report_versions": { + "name": "report_versions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "version_number": { + "name": "version_number", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "snapshot_json": { + "name": "snapshot_json", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "prev_hash": { + "name": "prev_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "signature": { + "name": "signature", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "key_fingerprint": { + "name": "key_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_amendment": { + "name": "is_amendment", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "verification_token": { + "name": "verification_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "published_at": { + "name": "published_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "published_by": { + "name": "published_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + }, + "report_id": { + "name": "report_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_report_versions_report": { + "name": "idx_report_versions_report", + "columns": [ + "report_id", + "version_number" + ], + "isUnique": false + }, + "uq_report_versions_report_version": { + "name": "uq_report_versions_report_version", + "columns": [ + "report_id", + "version_number" + ], + "isUnique": true + }, + "idx_report_versions_verify_token": { + "name": "idx_report_versions_verify_token", + "columns": [ + "verification_token" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "reports": { + "name": "reports", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_service_id": { + "name": "inspection_service_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "template_id": { + "name": "template_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'in_progress'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "published_at": { + "name": "published_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notified_at": { + "name": "notified_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": { + "idx_reports_inspection": { + "name": "idx_reports_inspection", + "columns": [ + "inspection_id" + ], + "isUnique": false + }, + "idx_reports_tenant": { + "name": "idx_reports_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "uq_reports_primary": { + "name": "uq_reports_primary", + "columns": [ + "inspection_id" + ], + "isUnique": true, + "where": "kind = 'primary'" + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "service_inspectors": { + "name": "service_inspectors", + "columns": { + "service_id": { + "name": "service_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_service_inspectors_tenant": { + "name": "idx_service_inspectors_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "service_inspectors_service_id_user_id_pk": { + "columns": [ + "service_id", + "user_id" + ], + "name": "service_inspectors_service_id_user_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "services": { + "name": "services", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "price_cents": { + "name": "price_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "duration_minutes": { + "name": "duration_minutes", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "template_id": { + "name": "template_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "agreement_id": { + "name": "agreement_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "default_event_type_slugs": { + "name": "default_event_type_slugs", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_services_tenant": { + "name": "idx_services_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "services_tenant_id_tenants_id_fk": { + "name": "services_tenant_id_tenants_id_fk", + "tableFrom": "services", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "services_template_id_templates_id_fk": { + "name": "services_template_id_templates_id_fk", + "tableFrom": "services", + "tableTo": "templates", + "columnsFrom": [ + "template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "services_agreement_id_agreements_id_fk": { + "name": "services_agreement_id_agreements_id_fk", + "tableFrom": "services", + "tableTo": "agreements", + "columnsFrom": [ + "agreement_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "signing_keys": { + "name": "signing_keys", + "columns": { + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "private_key_enc": { + "name": "private_key_enc", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "private_key_iv": { + "name": "private_key_iv", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "fingerprint": { + "name": "fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "algorithm": { + "name": "algorithm", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'Ed25519'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "rotated_at": { + "name": "rotated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "signing_keys_tenant_id_tenants_id_fk": { + "name": "signing_keys_tenant_id_tenants_id_fk", + "tableFrom": "signing_keys", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sms_consent_log": { + "name": "sms_consent_log", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "recipient_type": { + "name": "recipient_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "disclosure_version": { + "name": "disclosure_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "captured_via": { + "name": "captured_via", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip": { + "name": "ip", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject_kind": { + "name": "subject_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'contact'" + }, + "subject_id": { + "name": "subject_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + } + }, + "indexes": { + "idx_sms_consent_contact": { + "name": "idx_sms_consent_contact", + "columns": [ + "tenant_id", + "contact_id", + "created_at" + ], + "isUnique": false + }, + "idx_sms_consent_subject": { + "name": "idx_sms_consent_subject", + "columns": [ + "tenant_id", + "subject_kind", + "subject_id", + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sms_delivery_status": { + "name": "sms_delivery_status", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_message_id": { + "name": "provider_message_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_sms_delivery_status_msg": { + "name": "idx_sms_delivery_status_msg", + "columns": [ + "tenant_id", + "provider_message_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sms_disclosure_versions": { + "name": "sms_disclosure_versions", + "columns": { + "version": { + "name": "version", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "published_at": { + "name": "published_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tags": { + "name": "tags", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_seed": { + "name": "is_seed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_tags_tenant_name": { + "name": "idx_tags_tenant_name", + "columns": [ + "tenant_id", + "name" + ], + "isUnique": true + }, + "idx_tags_tenant": { + "name": "idx_tags_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "templates": { + "name": "templates", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "schema": { + "name": "schema", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "rating_system_id": { + "name": "rating_system_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "property_type": { + "name": "property_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "commercial_subtype": { + "name": "commercial_subtype", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_featured": { + "name": "is_featured", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "default_profile_id": { + "name": "default_profile_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_templates_tenant": { + "name": "idx_templates_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_templates_rating_system": { + "name": "idx_templates_rating_system", + "columns": [ + "rating_system_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "templates_tenant_id_tenants_id_fk": { + "name": "templates_tenant_id_tenants_id_fk", + "tableFrom": "templates", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tenant_custom_holidays": { + "name": "tenant_custom_holidays", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "date": { + "name": "date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "uq_tenant_custom_holidays_tenant_date": { + "name": "uq_tenant_custom_holidays_tenant_date", + "columns": [ + "tenant_id", + "date" + ], + "isUnique": true + }, + "idx_tenant_custom_holidays_tenant_date": { + "name": "idx_tenant_custom_holidays_tenant_date", + "columns": [ + "tenant_id", + "date" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tenant_library_imports": { + "name": "tenant_library_imports", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "library_id": { + "name": "library_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "imported_semver": { + "name": "imported_semver", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "imported_at": { + "name": "imported_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "row_count": { + "name": "row_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": { + "uq_tenant_library_import": { + "name": "uq_tenant_library_import", + "columns": [ + "tenant_id", + "library_id" + ], + "isUnique": true + }, + "idx_tenant_library_imports_tenant": { + "name": "idx_tenant_library_imports_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tenant_marketplace_import_history": { + "name": "tenant_marketplace_import_history", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "library_id": { + "name": "library_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "template_id": { + "name": "template_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_version": { + "name": "source_version", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "target_version": { + "name": "target_version", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rows_affected": { + "name": "rows_affected", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_marketplace_history_tenant": { + "name": "idx_marketplace_history_tenant", + "columns": [ + "tenant_id", + "created_at" + ], + "isUnique": false + }, + "idx_marketplace_history_template": { + "name": "idx_marketplace_history_template", + "columns": [ + "template_id" + ], + "isUnique": false + }, + "idx_marketplace_history_library": { + "name": "idx_marketplace_history_library", + "columns": [ + "library_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tenant_marketplace_imports": { + "name": "tenant_marketplace_imports", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "marketplace_template_id": { + "name": "marketplace_template_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "imported_semver": { + "name": "imported_semver", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "local_template_id": { + "name": "local_template_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "imported_at": { + "name": "imported_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_mkt_imports_tmpl": { + "name": "idx_mkt_imports_tmpl", + "columns": [ + "marketplace_template_id" + ], + "isUnique": false + }, + "idx_mkt_imports_tenant": { + "name": "idx_mkt_imports_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "tenant_marketplace_imports_marketplace_template_id_marketplace_templates_id_fk": { + "name": "tenant_marketplace_imports_marketplace_template_id_marketplace_templates_id_fk", + "tableFrom": "tenant_marketplace_imports", + "tableTo": "marketplace_templates", + "columnsFrom": [ + "marketplace_template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tenant_marketplace_imports_local_template_id_templates_id_fk": { + "name": "tenant_marketplace_imports_local_template_id_templates_id_fk", + "tableFrom": "tenant_marketplace_imports", + "tableTo": "templates", + "columnsFrom": [ + "local_template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "usage_counters": { + "name": "usage_counters", + "columns": { + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "metric": { + "name": "metric", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "period_key": { + "name": "period_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_usage_counters_tenant": { + "name": "idx_usage_counters_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "usage_counters_tenant_id_metric_period_key_pk": { + "columns": [ + "tenant_id", + "metric", + "period_key" + ], + "name": "usage_counters_tenant_id_metric_period_key_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "email_templates": { + "name": "email_templates", + "columns": { + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "blocks": { + "name": "blocks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "email_templates_tenant_id_tenants_id_fk": { + "name": "email_templates_tenant_id_tenants_id_fk", + "tableFrom": "email_templates", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "email_templates_tenant_id_trigger_pk": { + "columns": [ + "tenant_id", + "trigger" + ], + "name": "email_templates_tenant_id_trigger_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tenant_configs": { + "name": "tenant_configs", + "columns": { + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "company_name": { + "name": "company_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "primary_color": { + "name": "primary_color", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "logo_url": { + "name": "logo_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "support_email": { + "name": "support_email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "company_address": { + "name": "company_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_pdf_footer_shown": { + "name": "is_pdf_footer_shown", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "is_pdf_page_numbers_shown": { + "name": "is_pdf_page_numbers_shown", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "is_pdf_license_shown": { + "name": "is_pdf_license_shown", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "sender_email": { + "name": "sender_email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reply_to": { + "name": "reply_to", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email_mode": { + "name": "email_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'platform'" + }, + "video_mode": { + "name": "video_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'r2'" + }, + "sms_mode": { + "name": "sms_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'platform'" + }, + "sender_display_name": { + "name": "sender_display_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "point_of_contact": { + "name": "point_of_contact", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'company'" + }, + "billing_url": { + "name": "billing_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "review_url": { + "name": "review_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "company_phone": { + "name": "company_phone", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "integration_config": { + "name": "integration_config", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "secrets_enc": { + "name": "secrets_enc", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "dek_enc": { + "name": "dek_enc", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ics_token": { + "name": "ics_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "widget_allowed_origins": { + "name": "widget_allowed_origins", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_profile_id": { + "name": "default_profile_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'signature'" + }, + "attention_thresholds": { + "name": "attention_thresholds", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'{\"agreement_unsigned_h\":72,\"invoice_overdue_h\":72,\"report_unpublished_h\":72}'" + }, + "inspection_prefs": { + "name": "inspection_prefs", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_estimates_shown": { + "name": "is_estimates_shown", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_repair_list_enabled": { + "name": "is_repair_list_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_customer_repair_export_enabled": { + "name": "is_customer_repair_export_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_unpaid_blocked": { + "name": "is_unpaid_blocked", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_unsigned_agreement_blocked": { + "name": "is_unsigned_agreement_blocked", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "custom_referral_sources": { + "name": "custom_referral_sources", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "dashboard_column_prefs": { + "name": "dashboard_column_prefs", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_concierge_review_required": { + "name": "is_concierge_review_required", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_inspector_choice_allowed": { + "name": "is_inspector_choice_allowed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_pdf_pipeline_enabled": { + "name": "is_pdf_pipeline_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_team_mode_default": { + "name": "is_team_mode_default", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_apprentice_review_required": { + "name": "is_apprentice_review_required", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_guest_invites_enabled": { + "name": "is_guest_invites_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "require_defect_fields": { + "name": "require_defect_fields", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'none'" + }, + "agreement_retention_years": { + "name": "agreement_retention_years", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 6 + }, + "reinspection_statuses": { + "name": "reinspection_statuses", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_collab_editing_enabled": { + "name": "is_collab_editing_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sms_byo_provider": { + "name": "sms_byo_provider", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email_byo_provider": { + "name": "email_byo_provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'resend'" + }, + "is_managed_eligible": { + "name": "is_managed_eligible", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "managed_provider": { + "name": "managed_provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'twilio'" + }, + "is_reserve_schedule_enabled": { + "name": "is_reserve_schedule_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "reserve_term_years": { + "name": "reserve_term_years", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 12 + }, + "inflation_rate_bps": { + "name": "inflation_rate_bps", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_timezone": { + "name": "default_timezone", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'UTC'" + }, + "booking_slot_mode": { + "name": "booking_slot_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'fixed'" + }, + "booking_slot_interval_min": { + "name": "booking_slot_interval_min", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 30 + }, + "holiday_region": { + "name": "holiday_region", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "holiday_public_policy": { + "name": "holiday_public_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'open'" + }, + "holiday_internal_policy": { + "name": "holiday_internal_policy", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'advisory'" + }, + "default_locale": { + "name": "default_locale", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'en-US'" + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'USD'" + }, + "is_archive_revoking_access": { + "name": "is_archive_revoking_access", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "legal_mode": { + "name": "legal_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'hosted'" + }, + "custom_privacy_url": { + "name": "custom_privacy_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "custom_terms_url": { + "name": "custom_terms_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "privacy_body": { + "name": "privacy_body", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "terms_body": { + "name": "terms_body", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "tenant_configs_tenant_id_tenants_id_fk": { + "name": "tenant_configs_tenant_id_tenants_id_fk", + "tableFrom": "tenant_configs", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tenants": { + "name": "tenants", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tier": { + "name": "tier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'free'" + }, + "stripe_connect_account_id": { + "name": "stripe_connect_account_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "max_users": { + "name": "max_users", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 5 + }, + "deployment_mode": { + "name": "deployment_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'shared'" + }, + "applied_cmd_seq": { + "name": "applied_cmd_seq", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "applied_cred_seq": { + "name": "applied_cred_seq", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "tenants_slug_unique": { + "name": "tenants_slug_unique", + "columns": [ + "slug" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tenant_invites": { + "name": "tenant_invites", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'inspector'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "mentor_id": { + "name": "mentor_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "assigned_section_ids": { + "name": "assigned_section_ids", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "permission_overrides": { + "name": "permission_overrides", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_invites_tenant": { + "name": "idx_invites_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "uq_tenant_invites_pending_email": { + "name": "uq_tenant_invites_pending_email", + "columns": [ + "tenant_id", + "email" + ], + "isUnique": true, + "where": "status = 'pending'" + } + }, + "foreignKeys": { + "tenant_invites_tenant_id_tenants_id_fk": { + "name": "tenant_invites_tenant_id_tenants_id_fk", + "tableFrom": "tenant_invites", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "password_hash": { + "name": "password_hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "photo_url": { + "name": "photo_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_signature_base64": { + "name": "default_signature_base64", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_signature_enabled": { + "name": "is_signature_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'manager'" + }, + "onboarding_state": { + "name": "onboarding_state", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "totp_secret": { + "name": "totp_secret", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_totp_enabled": { + "name": "is_totp_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "totp_recovery_codes": { + "name": "totp_recovery_codes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "totp_verified_at": { + "name": "totp_verified_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_active_at": { + "name": "last_active_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "mentor_id": { + "name": "mentor_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "assigned_section_ids": { + "name": "assigned_section_ids", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "terms_accepted": { + "name": "terms_accepted", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "permission_overrides": { + "name": "permission_overrides", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "locale": { + "name": "locale", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_users_deleted_at": { + "name": "idx_users_deleted_at", + "columns": [ + "deleted_at" + ], + "isUnique": false + }, + "uq_users_tenant_email": { + "name": "uq_users_tenant_email", + "columns": [ + "tenant_id", + "email" + ], + "isUnique": true, + "where": "deleted_at IS NULL" + }, + "idx_users_tenant": { + "name": "idx_users_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_users_slug_per_tenant": { + "name": "idx_users_slug_per_tenant", + "columns": [ + "tenant_id", + "slug" + ], + "isUnique": true + }, + "idx_users_email": { + "name": "idx_users_email", + "columns": [ + "email" + ], + "isUnique": false + } + }, + "foreignKeys": { + "users_tenant_id_tenants_id_fk": { + "name": "users_tenant_id_tenants_id_fk", + "tableFrom": "users", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "audit_logs": { + "name": "audit_logs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inspector_slug": { + "name": "inspector_slug", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_audit_tenant_created": { + "name": "idx_audit_tenant_created", + "columns": [ + "tenant_id", + "created_at" + ], + "isUnique": false + }, + "idx_audit_entity": { + "name": "idx_audit_entity", + "columns": [ + "entity_type", + "entity_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "audit_logs_tenant_id_tenants_id_fk": { + "name": "audit_logs_tenant_id_tenants_id_fk", + "tableFrom": "audit_logs", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "integration_test_results": { + "name": "integration_test_results", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "target": { + "name": "target", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_ok": { + "name": "is_ok", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "detail": { + "name": "detail", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tested_by_user_id": { + "name": "tested_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tested_at": { + "name": "tested_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_integration_test_tenant_target": { + "name": "idx_integration_test_tenant_target", + "columns": [ + "tenant_id", + "target", + "tested_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "notifications": { + "name": "notifications", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "read_at": { + "name": "read_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "archived_at": { + "name": "archived_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "contact_id": { + "name": "contact_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_notifications_tenant_user_created": { + "name": "idx_notifications_tenant_user_created", + "columns": [ + "tenant_id", + "user_id", + "created_at" + ], + "isUnique": false + }, + "idx_notifications_tenant_user_unread": { + "name": "idx_notifications_tenant_user_unread", + "columns": [ + "tenant_id", + "user_id", + "read_at" + ], + "isUnique": false + }, + "idx_notifications_tenant_contact_created": { + "name": "idx_notifications_tenant_contact_created", + "columns": [ + "tenant_id", + "contact_id", + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "notifications_tenant_id_tenants_id_fk": { + "name": "notifications_tenant_id_tenants_id_fk", + "tableFrom": "notifications", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notifications_user_id_users_id_fk": { + "name": "notifications_user_id_users_id_fk", + "tableFrom": "notifications", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "parked_cmd_events": { + "name": "parked_cmd_events", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "envelope": { + "name": "envelope", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "received_at": { + "name": "received_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_parked_cmd_events_received_at": { + "name": "idx_parked_cmd_events_received_at", + "columns": [ + "received_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "processed_cmd_events": { + "name": "processed_cmd_events", + "columns": { + "event_id": { + "name": "event_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "cmd_type": { + "name": "cmd_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "processed_at": { + "name": "processed_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "slug_reservations": { + "name": "slug_reservations", + "columns": { + "slug": { + "name": "slug", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sync_outbox": { + "name": "sync_outbox", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_tried_at": { + "name": "last_tried_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_sync_outbox_status_created": { + "name": "idx_sync_outbox_status_created", + "columns": [ + "status", + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tenant_destruction_records": { + "name": "tenant_destruction_records", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tenant_slug": { + "name": "tenant_slug", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rows_deleted": { + "name": "rows_deleted", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "r2_objects": { + "name": "r2_objects", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "r2_bytes": { + "name": "r2_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "kv_keys": { + "name": "kv_keys", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "destroyed_at": { + "name": "destroyed_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_destruction_tenant": { + "name": "idx_destruction_tenant", + "columns": [ + "tenant_id" + ], + "isUnique": false + }, + "idx_destruction_destroyed_at": { + "name": "idx_destruction_destroyed_at", + "columns": [ + "destroyed_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tenant_legal_versions": { + "name": "tenant_legal_versions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "doc": { + "name": "doc", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "body_snapshot": { + "name": "body_snapshot", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_material": { + "name": "is_material", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "published_at": { + "name": "published_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "published_by_user_id": { + "name": "published_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_tenant_legal_versions_doc_version": { + "name": "idx_tenant_legal_versions_doc_version", + "columns": [ + "tenant_id", + "doc", + "version" + ], + "isUnique": true + }, + "idx_tenant_legal_versions_latest": { + "name": "idx_tenant_legal_versions_latest", + "columns": [ + "tenant_id", + "doc", + "published_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "client_uploads": { + "name": "client_uploads", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inspection_id": { + "name": "inspection_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "uploaded_by_kind": { + "name": "uploaded_by_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "uploaded_by_ref": { + "name": "uploaded_by_ref", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "uploaded_by_name": { + "name": "uploaded_by_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "r2_key": { + "name": "r2_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "filename": { + "name": "filename", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "size_bytes": { + "name": "size_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_client_uploads_inspection": { + "name": "idx_client_uploads_inspection", + "columns": [ + "tenant_id", + "inspection_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": { + "uq_discount_codes_code_tenant": { + "columns": { + "upper(code)": { + "isExpression": true + } + } + } + } + } +} \ No newline at end of file diff --git a/migrations/meta/_journal.json b/migrations/meta/_journal.json index 87d66e227..de5dc9741 100644 --- a/migrations/meta/_journal.json +++ b/migrations/meta/_journal.json @@ -218,6 +218,13 @@ "when": 1785765436571, "tag": "0030_glamorous_blindfold", "breakpoints": true + }, + { + "idx": 31, + "version": "6", + "when": 1785767291432, + "tag": "0031_gigantic_swarm", + "breakpoints": true } ] } \ No newline at end of file diff --git a/server/lib/db/schema/contact.ts b/server/lib/db/schema/contact.ts index fc3929d03..cb53bd47c 100644 --- a/server/lib/db/schema/contact.ts +++ b/server/lib/db/schema/contact.ts @@ -40,6 +40,15 @@ export const contacts = sqliteTable('contacts', { * Distinct from `archived_at`: archiving retires the CONTACT, revoking * only severs the account binding while the contact stays usable. */ agentRevokedAt: integer('agent_revoked_at', { mode: 'timestamp_ms' }), + + /** The language this contact asked to be addressed in, BCP-47. + * + * NULL is an ABSENCE of a stated preference, not English: only a stored + * value is evidence that a client wanted another language, and that + * distinction is the demand signal this column exists to produce. Read it + * through `resolveContactLocale` (server/lib/i18n/contact-locale.ts), + * which owns the fall-through order — never default it at a call site. */ + locale: text('locale'), }, (t) => [ index('idx_contacts_type').on(t.tenantId, t.type), index('idx_contacts_tenant').on(t.tenantId), diff --git a/server/lib/i18n/contact-locale.ts b/server/lib/i18n/contact-locale.ts new file mode 100644 index 000000000..5d4f31d01 --- /dev/null +++ b/server/lib/i18n/contact-locale.ts @@ -0,0 +1,108 @@ +/** + * Which language a contact should be addressed in. + * + * A notification is rendered FOR a recipient, and the recipient's language is + * not the request's language — in a cron or queue context there is no request + * at all. So the answer cannot come from the ambient locale; it has to be + * resolved from what we know about that person. This module is the one place + * that decision is made, so it can be changed once rather than at every + * sender. + * + * PRECEDENCE, highest first: + * 1. `contactLocale` — what the contact told us themselves. + * 2. `linkedUserLocale` — the locale on the account a contact is bound to + * (`contacts.agent_user_id`); an explicit choice + * too, just made in a different place. + * 3. `tenantDefault` — the company's configured locale. + * 4. `acceptLanguage` — the browser hint from the request, when there is + * one. Weakest: it describes a device, not a person. + * 5. `'en'` — the base locale. + * + * Each level FALLS THROUGH when it is absent OR names a language we have no + * messages for: a contact who stored `fr-FR` gets the tenant's language, not a + * page of untranslated keys. Never treat NULL at any level as English — the + * absence is the thing that makes a stored value evidence of demand. + */ + +import { isValidLocale } from '../locale'; + +/** + * The locales the compiled message catalogue actually covers. + * + * Restated here rather than imported: `server/` may not import + * `app/paraglide/**` (BFF boundary, enforced by `no-restricted-imports`), and + * the only sanctioned crossing is the message wrapper. The equality with + * `project.inlang/settings.json` is asserted by + * `tests/unit/contacts/contact-locale.spec.ts`, because a resolver that + * returns a locale with no messages behind it degrades to English silently. + */ +export const SUPPORTED_CONTACT_LOCALES = ['en', 'es-419'] as const; + +/** A locale the product can actually speak. */ +export type ContactLocale = (typeof SUPPORTED_CONTACT_LOCALES)[number]; + +/** The base locale, used when nothing else resolves. */ +export const DEFAULT_CONTACT_LOCALE: ContactLocale = 'en'; + +/** + * A BCP-47 tag reduced to a locale we have messages for, or `null` when we + * have none. Matching is by LANGUAGE subtag, so a regional variant lands on + * its regional catalogue: `es-MX` and `es-CL` both resolve to `es-419`, + * because the alternative is answering a Spanish speaker in English over a + * country code. + */ +function normalizeLocale(raw: string | null | undefined): ContactLocale | null { + if (!raw || !isValidLocale(raw)) return null; + const language = new Intl.Locale(raw).language; + return SUPPORTED_CONTACT_LOCALES.find( + (supported) => new Intl.Locale(supported).language === language, + ) ?? null; +} + +/** + * The best supported locale named by an `Accept-Language` header, or `null`. + * Entries are ranked by their `q` weight (absent means 1), ties broken by the + * order sent, and unsupported entries are skipped rather than stopping the + * scan — `fr-FR,es-MX;q=0.9` means Spanish here, not English. + */ +function fromAcceptLanguage(header: string | null | undefined): ContactLocale | null { + if (!header) return null; + return header + .split(',') + .map((entry, index) => { + const [tag, ...params] = entry.trim().split(';'); + const q = params + .map((p) => /^\s*q=([0-9.]+)\s*$/.exec(p)) + .find((match) => match !== null); + return { tag: tag.trim(), q: q ? Number(q[1]) : 1, index }; + }) + .filter((entry) => entry.tag !== '' && entry.tag !== '*' && !Number.isNaN(entry.q)) + .sort((a, b) => (b.q - a.q) || (a.index - b.index)) + .reduce( + (found, entry) => found ?? normalizeLocale(entry.tag), + null, + ); +} + +/** Everything known about who is being written to. Every field is optional + * because every field is genuinely often absent. */ +export interface ContactLocaleInput { + /** `contacts.locale` — the contact's own stated preference. */ + contactLocale?: string | null; + /** `users.locale` of the account this contact is bound to, if any. */ + linkedUserLocale?: string | null; + /** The tenant's configured locale. */ + tenantDefault?: string | null; + /** The request's `Accept-Language` header, when resolving inside one. */ + acceptLanguage?: string | null; +} + +/** Resolve the language to address a contact in. See the precedence at the top + * of this file; it is documented there once and nowhere else. */ +export function resolveContactLocale(input: ContactLocaleInput): ContactLocale { + return normalizeLocale(input.contactLocale) + ?? normalizeLocale(input.linkedUserLocale) + ?? normalizeLocale(input.tenantDefault) + ?? fromAcceptLanguage(input.acceptLanguage) + ?? DEFAULT_CONTACT_LOCALE; +} diff --git a/tests/unit/contacts/contact-locale.spec.ts b/tests/unit/contacts/contact-locale.spec.ts new file mode 100644 index 000000000..dfda66a95 --- /dev/null +++ b/tests/unit/contacts/contact-locale.spec.ts @@ -0,0 +1,68 @@ +import { readFileSync } from 'node:fs'; +import * as path from 'node:path'; +import { describe, it, expect } from 'vitest'; +import { resolveContactLocale, SUPPORTED_CONTACT_LOCALES } from '../../../server/lib/i18n/contact-locale'; + +const NONE = { contactLocale: null, linkedUserLocale: null, tenantDefault: null, acceptLanguage: null }; + +describe('resolveContactLocale', () => { + it('prefers what the contact told us', () => { + expect(resolveContactLocale({ ...NONE, + contactLocale: 'es-MX', linkedUserLocale: 'en-US', tenantDefault: 'en-US', + })).toBe('es-419'); + }); + + it('uses the linked agent user account when the contact has no preference', () => { + // An agent contact linked to a real user (agent_user_id) has a locale on + // that user row; a plain client contact has nothing. + expect(resolveContactLocale({ ...NONE, + linkedUserLocale: 'es-419', tenantDefault: 'en-US', + })).toBe('es-419'); + }); + + it('falls through an unsupported preference instead of stopping on it', () => { + expect(resolveContactLocale({ ...NONE, + contactLocale: 'fr-FR', tenantDefault: 'es-419', + })).toBe('es-419'); + }); + + it('ends at English', () => { + expect(resolveContactLocale(NONE)).toBe('en'); + }); + + it('uses the tenant default before the browser hint', () => { + expect(resolveContactLocale({ ...NONE, + tenantDefault: 'en-US', acceptLanguage: 'es-419', + })).toBe('en'); + }); + + it('reads the highest-weighted supported entry out of Accept-Language', () => { + expect(resolveContactLocale({ ...NONE, + acceptLanguage: 'fr-FR,es-MX;q=0.9,en;q=0.8', + })).toBe('es-419'); + // q defaults to 1 and order breaks ties, so a bare list takes the first. + expect(resolveContactLocale({ ...NONE, acceptLanguage: 'en-GB,es-MX' })).toBe('en'); + expect(resolveContactLocale({ ...NONE, acceptLanguage: '*' })).toBe('en'); + }); + + it('treats junk and empty strings as an absence, not as a choice', () => { + expect(resolveContactLocale({ ...NONE, contactLocale: '', tenantDefault: 'es-419' })).toBe('es-419'); + expect(resolveContactLocale({ ...NONE, contactLocale: 'not a locale!!', tenantDefault: 'es-419' })).toBe('es-419'); + }); + + it('matches a region-qualified tag case-insensitively', () => { + expect(resolveContactLocale({ ...NONE, contactLocale: 'ES-419' })).toBe('es-419'); + }); +}); + +describe('SUPPORTED_CONTACT_LOCALES', () => { + // server/ cannot import the paraglide runtime (BFF boundary, enforced by + // no-restricted-imports), so the supported set is restated there. Assert the + // equality instead of asking a comment to keep it true: resolving to a + // locale the catalogue has no messages for renders English anyway, silently. + it('is exactly the set of locales the message catalogue is compiled for', () => { + const settingsPath = path.resolve(__dirname, '../../../project.inlang/settings.json'); + const settings = JSON.parse(readFileSync(settingsPath, 'utf8')) as { locales: string[] }; + expect([...SUPPORTED_CONTACT_LOCALES].sort()).toEqual([...settings.locales].sort()); + }); +}); diff --git a/tests/workers/db-batch-atomicity.spec.ts b/tests/workers/db-batch-atomicity.spec.ts index c9b3fee16..75168d91d 100644 --- a/tests/workers/db-batch-atomicity.spec.ts +++ b/tests/workers/db-batch-atomicity.spec.ts @@ -95,10 +95,11 @@ describe('B-29+ importContacts phase 2 — real D1 bind limit', () => { // partial unique index is included for fidelity. await b.DB.exec( // IA-104 appended the agent-binding columns (agent_user_id / - // agent_linked_at / agent_revoked_at). They must be here even - // though this spec never reads them: drizzle's INSERT lists every - // column in the schema, so a missing one fails the whole batch. - 'CREATE TABLE IF NOT EXISTS contacts (id TEXT PRIMARY KEY, tenant_id TEXT NOT NULL, type TEXT NOT NULL DEFAULT \'client\', name TEXT NOT NULL, email TEXT, phone TEXT, agency TEXT, notes TEXT, created_by_user_id TEXT, created_at INTEGER NOT NULL, archived_at INTEGER, agent_user_id TEXT, agent_linked_at INTEGER, agent_revoked_at INTEGER);', + // agent_linked_at / agent_revoked_at), and the multilingual work + // appended `locale`. They must be here even though this spec never + // reads them: drizzle's INSERT lists every column in the schema, so + // a missing one fails the whole batch. + 'CREATE TABLE IF NOT EXISTS contacts (id TEXT PRIMARY KEY, tenant_id TEXT NOT NULL, type TEXT NOT NULL DEFAULT \'client\', name TEXT NOT NULL, email TEXT, phone TEXT, agency TEXT, notes TEXT, created_by_user_id TEXT, created_at INTEGER NOT NULL, archived_at INTEGER, agent_user_id TEXT, agent_linked_at INTEGER, agent_revoked_at INTEGER, locale TEXT);', ); await b.DB.exec( 'CREATE UNIQUE INDEX IF NOT EXISTS uq_contacts_tenant_email ON contacts (tenant_id, email) WHERE email IS NOT NULL AND archived_at IS NULL;', From 23fb5387ebcc32b9100af018eded4ed266f40f09 Mon Sep 17 00:00:00 2001 From: important-new Date: Mon, 3 Aug 2026 23:14:56 +0800 Subject: [PATCH 005/111] feat(booking): let a client state their language on the public form MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The contact locale column landed with nowhere to fill it from. This asks the person booking, on every public surface, and stores the answer on the contact the booking creates. Nothing is selected by default, and that is the point of the change. A pre-selected English would make every booking look like a stated preference, and the reason to collect this at all is to find out how many clients actually ask for another language — which only works if silence stays distinguishable from a choice. So the payload omits the key when unanswered, and the column stays NULL. - LanguageChoice wraps the shared-ui RadioGroup and takes its option labels from the same table the Workspace and Profile pickers use, so there is one vocabulary for one choice. Labels are never translated: someone who cannot read English cannot find an option labelled in English, which would defeat the control. - Both public forms carry it — the wizard and the embed. An embed is what a company puts on its own website, so leaving it out there would have collected nothing from the higher-traffic path. - It is NOT shown when an agent books for someone else. The agent would be guessing at their client's language, and a guess recorded as a stated preference corrupts the one number this field exists to produce. - The submitted tag is reduced through the resolver's own normalizer, so es-MX lands on es-419 and a language we have no messages for is stored as NULL rather than as a promise we would break at send time. The API takes a free BCP-47 tag rather than an enum for the same reason a cosmetic field must never reject a booking. - Unlike name and phone, a locale does not fill forward: a returning client who picks a different language gets the newer answer, because being written to in English after asking for Spanish is the failure. An omitted choice still never clears a stored one. RadioGroup gains a legendClassName so one component can wear the wizard's and the embed's field-label idioms; the legend element always renders, since it is the group's accessible name. booking.service.ts is over the file-size cap already; the baseline moves 966 -> 972 rather than splitting it, which this change does not justify. --- .../booking/BookingScheduleStep.tsx | 11 +++ app/components/booking/BookingWizard.tsx | 3 + .../booking/LanguageChoice.test.tsx | 51 ++++++++++ app/components/booking/LanguageChoice.tsx | 48 +++++++++ app/components/booking/useBookingFormState.ts | 8 ++ app/lib/locales.ts | 21 ++++ app/routes/public/booking-embed-widget.tsx | 16 +++ app/routes/public/booking.test.tsx | 97 ++++++++++++++++++- messages/en/booking.json | 1 + packages/shared-ui/src/Radio.tsx | 10 +- scripts/file-size-baseline.json | 2 +- server/lib/i18n/contact-locale.ts | 12 ++- server/lib/validations/booking.schema.ts | 9 ++ server/services/booking.service.ts | 6 ++ server/services/contact.service.ts | 13 ++- .../bookings/booking-contact-upsert.spec.ts | 81 +++++++++++++++- 16 files changed, 379 insertions(+), 10 deletions(-) create mode 100644 app/components/booking/LanguageChoice.test.tsx create mode 100644 app/components/booking/LanguageChoice.tsx diff --git a/app/components/booking/BookingScheduleStep.tsx b/app/components/booking/BookingScheduleStep.tsx index 53cdadf31..35f87e416 100644 --- a/app/components/booking/BookingScheduleStep.tsx +++ b/app/components/booking/BookingScheduleStep.tsx @@ -6,6 +6,7 @@ import { useEffect, useState } from "react"; import { timeWindows } from "./booking-constants"; import { HolidayAdvisoryBanner } from "./HolidayAdvisoryBanner"; +import { LanguageChoice } from "./LanguageChoice"; import { m } from "~/paraglide/messages"; export function ScheduleStep({ @@ -25,6 +26,8 @@ export function ScheduleStep({ setClientEmail, smsOptin, setSmsOptin, + locale, + setLocale, privacyUrl, termsUrl, companyName, @@ -51,6 +54,9 @@ export function ScheduleStep({ setClientEmail: (v: string) => void; smsOptin: boolean; setSmsOptin: (v: boolean) => void; + /** The client's stated language, or null when they have not said. */ + locale: string | null; + setLocale: (v: string) => void; privacyUrl: string | null; termsUrl: string | null; companyName: string; @@ -200,6 +206,11 @@ export function ScheduleStep({

)} + {/* Asked only when the fields hold the visitor's OWN details. An agent + booking for someone else would be guessing at their client's + language, and a guess recorded as a stated preference is worse than + no answer: it is the one number this field exists to measure. */} + {contactIsSelf && } {/* Track L (D6, path A) — unchecked SMS opt-in (TCPA consent). */}