Phases 1-5 + rooms/floors, accounts, custom types, users, notifications
Data model - Properties, rooms (+optional floors), assets (typed custom fields + Zod runtime validator + move history), documents (polymorphic scope) - Projects -> work packages -> tasks -> subtasks - Decision events (scoped to project/property/asset/work_package) - Checklist templates + instances, maintenance schedules (time + usage) with auto-materialized checklists on event recording - Wiki (global + per-project) with revisions + tsvector FTS - Property accounts (utility/meter numbers by kind) - Notifications table + per-user channel prefs Infra - RBAC guards (requireCompany / requireAdmin) - Storage abstraction: LocalDiskStorage (HMAC signed URLs) + S3Storage behind the same interface, switchable via STORAGE_BACKEND - CSV export for assets / maintenance / decisions - QR labels: /api/qr SVG endpoint + printable /assets/[id]/label - Notifications: in-app + SMTP (own server via nodemailer) + Matrix (Client-Server API, per-company room) with opt-in per user - Company switcher + auto-select first company on login UI - Topbar: bell with unread count, theme toggle, name, Sign Out (flat) - Sidebar: main nav + dedicated Admin section (Asset types, Users, Company) - Nested-route tabs on property / project / asset detail pages - Admin UIs for users (invite, role, reset pw, deactivate) and company settings (default currency, Matrix room id) - Custom asset type creation + field-def editor with immutable key/type guard and auto-deprecate when removing a field still referenced Graph - graphify-out/ committed: GRAPH_REPORT.md, graph.html, graph.json
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
CREATE TABLE "asset_field_defs" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"asset_type_id" uuid NOT NULL,
|
||||
"key" varchar(64) NOT NULL,
|
||||
"label" varchar(128) NOT NULL,
|
||||
"type" "field_type" NOT NULL,
|
||||
"required" boolean DEFAULT false NOT NULL,
|
||||
"order" integer DEFAULT 0 NOT NULL,
|
||||
"enum_values" text[],
|
||||
"unit" varchar(32),
|
||||
"placeholder" varchar(255),
|
||||
"help_text" text,
|
||||
"deprecated_at" timestamp with time zone,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "asset_location_history" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"asset_id" uuid NOT NULL,
|
||||
"from_kind" "container_kind",
|
||||
"from_project_id" uuid,
|
||||
"from_property_id" uuid,
|
||||
"to_kind" "container_kind" NOT NULL,
|
||||
"to_project_id" uuid,
|
||||
"to_property_id" uuid,
|
||||
"moved_by" uuid,
|
||||
"moved_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"reason" text
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "asset_logs" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"asset_id" uuid NOT NULL,
|
||||
"author_id" uuid,
|
||||
"body" text NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "asset_types" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"company_id" uuid,
|
||||
"parent_id" uuid,
|
||||
"name" varchar(128) NOT NULL,
|
||||
"slug" varchar(128) NOT NULL,
|
||||
"icon" varchar(64),
|
||||
"description" text,
|
||||
"schema_version" integer DEFAULT 1 NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "assets" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"company_id" uuid NOT NULL,
|
||||
"asset_type_id" uuid NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"tag" varchar(64),
|
||||
"serial_number" varchar(128),
|
||||
"manufacturer" varchar(128),
|
||||
"model" varchar(128),
|
||||
"purchased_at" timestamp with time zone,
|
||||
"current_container_kind" "container_kind" NOT NULL,
|
||||
"current_project_id" uuid,
|
||||
"current_property_id" uuid,
|
||||
"custom_fields" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
"search_tsv" text,
|
||||
"created_by" uuid,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"deleted_at" timestamp with time zone
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "documents" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"company_id" uuid NOT NULL,
|
||||
"scope_type" "doc_scope" NOT NULL,
|
||||
"scope_id" uuid NOT NULL,
|
||||
"filename" varchar(512) NOT NULL,
|
||||
"mime_type" varchar(128) NOT NULL,
|
||||
"size_bytes" bigint NOT NULL,
|
||||
"sha256" varchar(64) NOT NULL,
|
||||
"storage_key" varchar(512) NOT NULL,
|
||||
"uploaded_by" uuid,
|
||||
"uploaded_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "properties" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"company_id" uuid NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"kind" varchar(64),
|
||||
"address_line1" varchar(255),
|
||||
"address_line2" varchar(255),
|
||||
"city" varchar(128),
|
||||
"region" varchar(128),
|
||||
"postal_code" varchar(32),
|
||||
"country_code" varchar(2),
|
||||
"lat" numeric(9, 6),
|
||||
"lng" numeric(9, 6),
|
||||
"notes" text,
|
||||
"created_by" uuid,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"deleted_at" timestamp with time zone
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "asset_field_defs" ADD CONSTRAINT "asset_field_defs_asset_type_id_asset_types_id_fk" FOREIGN KEY ("asset_type_id") REFERENCES "public"."asset_types"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "asset_location_history" ADD CONSTRAINT "asset_location_history_asset_id_assets_id_fk" FOREIGN KEY ("asset_id") REFERENCES "public"."assets"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "asset_location_history" ADD CONSTRAINT "asset_location_history_from_property_id_properties_id_fk" FOREIGN KEY ("from_property_id") REFERENCES "public"."properties"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "asset_location_history" ADD CONSTRAINT "asset_location_history_to_property_id_properties_id_fk" FOREIGN KEY ("to_property_id") REFERENCES "public"."properties"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "asset_location_history" ADD CONSTRAINT "asset_location_history_moved_by_users_id_fk" FOREIGN KEY ("moved_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "asset_logs" ADD CONSTRAINT "asset_logs_asset_id_assets_id_fk" FOREIGN KEY ("asset_id") REFERENCES "public"."assets"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "asset_logs" ADD CONSTRAINT "asset_logs_author_id_users_id_fk" FOREIGN KEY ("author_id") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "asset_types" ADD CONSTRAINT "asset_types_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "assets" ADD CONSTRAINT "assets_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "assets" ADD CONSTRAINT "assets_asset_type_id_asset_types_id_fk" FOREIGN KEY ("asset_type_id") REFERENCES "public"."asset_types"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "assets" ADD CONSTRAINT "assets_current_property_id_properties_id_fk" FOREIGN KEY ("current_property_id") REFERENCES "public"."properties"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "assets" ADD CONSTRAINT "assets_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "documents" ADD CONSTRAINT "documents_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "documents" ADD CONSTRAINT "documents_uploaded_by_users_id_fk" FOREIGN KEY ("uploaded_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "properties" ADD CONSTRAINT "properties_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "properties" ADD CONSTRAINT "properties_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "asset_field_defs_type_key_uq" ON "asset_field_defs" USING btree ("asset_type_id","key");--> statement-breakpoint
|
||||
CREATE INDEX "asset_field_defs_by_type" ON "asset_field_defs" USING btree ("asset_type_id","order");--> statement-breakpoint
|
||||
CREATE INDEX "alh_by_asset" ON "asset_location_history" USING btree ("asset_id","moved_at");--> statement-breakpoint
|
||||
CREATE INDEX "asset_logs_by_asset" ON "asset_logs" USING btree ("asset_id","created_at");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "asset_types_company_slug_uq" ON "asset_types" USING btree ("company_id","slug");--> statement-breakpoint
|
||||
CREATE INDEX "asset_types_by_parent" ON "asset_types" USING btree ("parent_id");--> statement-breakpoint
|
||||
CREATE INDEX "assets_by_company" ON "assets" USING btree ("company_id");--> statement-breakpoint
|
||||
CREATE INDEX "assets_by_type" ON "assets" USING btree ("asset_type_id");--> statement-breakpoint
|
||||
CREATE INDEX "assets_by_project" ON "assets" USING btree ("current_project_id");--> statement-breakpoint
|
||||
CREATE INDEX "assets_by_property" ON "assets" USING btree ("current_property_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "assets_company_tag_uq" ON "assets" USING btree ("company_id","tag");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "assets_company_serial_uq" ON "assets" USING btree ("company_id","serial_number");--> statement-breakpoint
|
||||
CREATE INDEX "docs_by_scope" ON "documents" USING btree ("scope_type","scope_id");--> statement-breakpoint
|
||||
CREATE INDEX "docs_by_company" ON "documents" USING btree ("company_id");--> statement-breakpoint
|
||||
CREATE INDEX "docs_by_hash" ON "documents" USING btree ("sha256");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "docs_storage_key_uq" ON "documents" USING btree ("storage_key");--> statement-breakpoint
|
||||
CREATE INDEX "properties_by_company" ON "properties" USING btree ("company_id");
|
||||
@@ -0,0 +1,112 @@
|
||||
-- Phase 1 follow-up: integrity constraints, FTS, GIN, partial FK,
|
||||
-- and updated_at + asset_types.schema_version triggers.
|
||||
-- Drizzle-kit can't express any of these from the TS schema, so they live here.
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 1. assets.current_container_kind XOR location FK
|
||||
-- ---------------------------------------------------------------------------
|
||||
ALTER TABLE "assets" ADD CONSTRAINT "assets_location_xor" CHECK (
|
||||
(current_container_kind = 'project' AND current_project_id IS NOT NULL AND current_property_id IS NULL)
|
||||
OR
|
||||
(current_container_kind = 'property' AND current_property_id IS NOT NULL AND current_project_id IS NULL)
|
||||
);
|
||||
--> statement-breakpoint
|
||||
|
||||
ALTER TABLE "asset_location_history" ADD CONSTRAINT "alh_to_xor" CHECK (
|
||||
(to_kind = 'project' AND to_project_id IS NOT NULL AND to_property_id IS NULL)
|
||||
OR
|
||||
(to_kind = 'property' AND to_property_id IS NOT NULL AND to_project_id IS NULL)
|
||||
);
|
||||
--> statement-breakpoint
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 2. Self-referential FK on asset_types.parent_id
|
||||
-- ---------------------------------------------------------------------------
|
||||
ALTER TABLE "asset_types" ADD CONSTRAINT "asset_types_parent_id_fk"
|
||||
FOREIGN KEY ("parent_id") REFERENCES "asset_types"("id") ON DELETE SET NULL;
|
||||
--> statement-breakpoint
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 3. JSONB GIN index on assets.custom_fields
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE INDEX "assets_custom_fields_gin" ON "assets" USING GIN ("custom_fields" jsonb_path_ops);
|
||||
--> statement-breakpoint
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 4. Full-text search on assets (tsvector + trigger + GIN)
|
||||
-- ---------------------------------------------------------------------------
|
||||
ALTER TABLE "assets" ALTER COLUMN "search_tsv" TYPE tsvector USING NULL::tsvector;
|
||||
--> statement-breakpoint
|
||||
|
||||
CREATE OR REPLACE FUNCTION assets_tsv_trigger() RETURNS trigger AS $$
|
||||
BEGIN
|
||||
NEW.search_tsv :=
|
||||
setweight(to_tsvector('simple', coalesce(NEW.name, '')), 'A') ||
|
||||
setweight(to_tsvector('simple', coalesce(NEW.tag, '')), 'B') ||
|
||||
setweight(to_tsvector('simple', coalesce(NEW.serial_number, '')), 'B') ||
|
||||
setweight(to_tsvector('simple', coalesce(NEW.manufacturer, '')), 'C') ||
|
||||
setweight(to_tsvector('simple', coalesce(NEW.model, '')), 'C');
|
||||
RETURN NEW;
|
||||
END $$ LANGUAGE plpgsql;
|
||||
--> statement-breakpoint
|
||||
|
||||
CREATE TRIGGER assets_tsv_upd BEFORE INSERT OR UPDATE
|
||||
ON "assets" FOR EACH ROW EXECUTE FUNCTION assets_tsv_trigger();
|
||||
--> statement-breakpoint
|
||||
|
||||
CREATE INDEX "assets_search_tsv_gin" ON "assets" USING GIN ("search_tsv");
|
||||
--> statement-breakpoint
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 5. Generic updated_at trigger, attached to every Phase-0/1 table that has it
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE OR REPLACE FUNCTION set_updated_at() RETURNS trigger AS $$
|
||||
BEGIN
|
||||
NEW.updated_at := now();
|
||||
RETURN NEW;
|
||||
END $$ LANGUAGE plpgsql;
|
||||
--> statement-breakpoint
|
||||
|
||||
CREATE TRIGGER companies_set_updated_at BEFORE UPDATE ON "companies"
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
--> statement-breakpoint
|
||||
CREATE TRIGGER users_set_updated_at BEFORE UPDATE ON "users"
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
--> statement-breakpoint
|
||||
CREATE TRIGGER properties_set_updated_at BEFORE UPDATE ON "properties"
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
--> statement-breakpoint
|
||||
CREATE TRIGGER asset_types_set_updated_at BEFORE UPDATE ON "asset_types"
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
--> statement-breakpoint
|
||||
CREATE TRIGGER asset_field_defs_set_updated_at BEFORE UPDATE ON "asset_field_defs"
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
--> statement-breakpoint
|
||||
CREATE TRIGGER assets_set_updated_at BEFORE UPDATE ON "assets"
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
--> statement-breakpoint
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 6. Bump asset_types.schema_version when its field defs change
|
||||
-- (cache key for the runtime Zod validator)
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE OR REPLACE FUNCTION bump_asset_type_schema_version() RETURNS trigger AS $$
|
||||
DECLARE
|
||||
target_id uuid;
|
||||
BEGIN
|
||||
IF TG_OP = 'DELETE' THEN
|
||||
target_id := OLD.asset_type_id;
|
||||
ELSE
|
||||
target_id := NEW.asset_type_id;
|
||||
END IF;
|
||||
UPDATE asset_types
|
||||
SET schema_version = schema_version + 1,
|
||||
updated_at = now()
|
||||
WHERE id = target_id;
|
||||
RETURN NULL;
|
||||
END $$ LANGUAGE plpgsql;
|
||||
--> statement-breakpoint
|
||||
|
||||
CREATE TRIGGER asset_field_defs_bump_version
|
||||
AFTER INSERT OR UPDATE OR DELETE ON "asset_field_defs"
|
||||
FOR EACH ROW EXECUTE FUNCTION bump_asset_type_schema_version();
|
||||
@@ -0,0 +1,109 @@
|
||||
CREATE TABLE "checklist_instances" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"company_id" uuid NOT NULL,
|
||||
"template_id" uuid,
|
||||
"scope_type" "checklist_scope" NOT NULL,
|
||||
"scope_id" uuid,
|
||||
"title" varchar(255),
|
||||
"created_by" uuid,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"completed_at" timestamp with time zone
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "checklist_items" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"instance_id" uuid NOT NULL,
|
||||
"text" varchar(500) NOT NULL,
|
||||
"done" boolean DEFAULT false NOT NULL,
|
||||
"done_at" timestamp with time zone,
|
||||
"done_by" uuid,
|
||||
"required" boolean DEFAULT false NOT NULL,
|
||||
"order" integer DEFAULT 0 NOT NULL,
|
||||
"note" text
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "checklist_template_items" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"template_id" uuid NOT NULL,
|
||||
"text" varchar(500) NOT NULL,
|
||||
"order" integer DEFAULT 0 NOT NULL,
|
||||
"required" boolean DEFAULT false NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "checklist_templates" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"company_id" uuid NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"description" text,
|
||||
"created_by" uuid,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "maintenance_events" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"asset_id" uuid NOT NULL,
|
||||
"schedule_id" uuid,
|
||||
"performed_at" timestamp with time zone NOT NULL,
|
||||
"performed_by" uuid,
|
||||
"notes" text,
|
||||
"usage_reading" numeric(18, 4),
|
||||
"checklist_instance_id" uuid,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "maintenance_schedules" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"asset_id" uuid NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"kind" "schedule_kind" NOT NULL,
|
||||
"interval_value" integer NOT NULL,
|
||||
"interval_unit" interval_unit NOT NULL,
|
||||
"last_serviced_at" timestamp with time zone,
|
||||
"next_due_at" timestamp with time zone,
|
||||
"next_due_usage" numeric(18, 4),
|
||||
"checklist_template_id" uuid,
|
||||
"active" boolean DEFAULT true NOT NULL,
|
||||
"notes" text,
|
||||
"created_by" uuid,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "usage_readings" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"asset_id" uuid NOT NULL,
|
||||
"reading" numeric(18, 4) NOT NULL,
|
||||
"unit" interval_unit NOT NULL,
|
||||
"recorded_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"recorded_by" uuid,
|
||||
"notes" text
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "checklist_instances" ADD CONSTRAINT "checklist_instances_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "checklist_instances" ADD CONSTRAINT "checklist_instances_template_id_checklist_templates_id_fk" FOREIGN KEY ("template_id") REFERENCES "public"."checklist_templates"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "checklist_instances" ADD CONSTRAINT "checklist_instances_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "checklist_items" ADD CONSTRAINT "checklist_items_instance_id_checklist_instances_id_fk" FOREIGN KEY ("instance_id") REFERENCES "public"."checklist_instances"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "checklist_items" ADD CONSTRAINT "checklist_items_done_by_users_id_fk" FOREIGN KEY ("done_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "checklist_template_items" ADD CONSTRAINT "checklist_template_items_template_id_checklist_templates_id_fk" FOREIGN KEY ("template_id") REFERENCES "public"."checklist_templates"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "checklist_templates" ADD CONSTRAINT "checklist_templates_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "checklist_templates" ADD CONSTRAINT "checklist_templates_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "maintenance_events" ADD CONSTRAINT "maintenance_events_asset_id_assets_id_fk" FOREIGN KEY ("asset_id") REFERENCES "public"."assets"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "maintenance_events" ADD CONSTRAINT "maintenance_events_schedule_id_maintenance_schedules_id_fk" FOREIGN KEY ("schedule_id") REFERENCES "public"."maintenance_schedules"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "maintenance_events" ADD CONSTRAINT "maintenance_events_performed_by_users_id_fk" FOREIGN KEY ("performed_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "maintenance_events" ADD CONSTRAINT "maintenance_events_checklist_instance_id_checklist_instances_id_fk" FOREIGN KEY ("checklist_instance_id") REFERENCES "public"."checklist_instances"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "maintenance_schedules" ADD CONSTRAINT "maintenance_schedules_asset_id_assets_id_fk" FOREIGN KEY ("asset_id") REFERENCES "public"."assets"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "maintenance_schedules" ADD CONSTRAINT "maintenance_schedules_checklist_template_id_checklist_templates_id_fk" FOREIGN KEY ("checklist_template_id") REFERENCES "public"."checklist_templates"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "maintenance_schedules" ADD CONSTRAINT "maintenance_schedules_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "usage_readings" ADD CONSTRAINT "usage_readings_asset_id_assets_id_fk" FOREIGN KEY ("asset_id") REFERENCES "public"."assets"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "usage_readings" ADD CONSTRAINT "usage_readings_recorded_by_users_id_fk" FOREIGN KEY ("recorded_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE INDEX "ci_by_scope" ON "checklist_instances" USING btree ("scope_type","scope_id");--> statement-breakpoint
|
||||
CREATE INDEX "ci_by_company" ON "checklist_instances" USING btree ("company_id");--> statement-breakpoint
|
||||
CREATE INDEX "cit_by_instance" ON "checklist_items" USING btree ("instance_id","order");--> statement-breakpoint
|
||||
CREATE INDEX "cti_by_template" ON "checklist_template_items" USING btree ("template_id","order");--> statement-breakpoint
|
||||
CREATE INDEX "ct_by_company" ON "checklist_templates" USING btree ("company_id");--> statement-breakpoint
|
||||
CREATE INDEX "me_by_asset_time" ON "maintenance_events" USING btree ("asset_id","performed_at");--> statement-breakpoint
|
||||
CREATE INDEX "me_by_schedule" ON "maintenance_events" USING btree ("schedule_id");--> statement-breakpoint
|
||||
CREATE INDEX "ms_by_asset" ON "maintenance_schedules" USING btree ("asset_id");--> statement-breakpoint
|
||||
CREATE INDEX "ms_by_next_due" ON "maintenance_schedules" USING btree ("next_due_at");--> statement-breakpoint
|
||||
CREATE INDEX "ur_by_asset_time" ON "usage_readings" USING btree ("asset_id","recorded_at");
|
||||
@@ -0,0 +1,14 @@
|
||||
-- Phase 2 follow-up: partial indexes + updated_at triggers for the new tables.
|
||||
|
||||
-- Partial index for the dashboard "overdue / upcoming time-based maintenance" query.
|
||||
CREATE INDEX "ms_next_due_active"
|
||||
ON "maintenance_schedules" ("next_due_at")
|
||||
WHERE "kind" = 'time' AND "active" = true;
|
||||
--> statement-breakpoint
|
||||
|
||||
-- Reuse the set_updated_at() function from migration 0002.
|
||||
CREATE TRIGGER checklist_templates_set_updated_at BEFORE UPDATE ON "checklist_templates"
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
--> statement-breakpoint
|
||||
CREATE TRIGGER maintenance_schedules_set_updated_at BEFORE UPDATE ON "maintenance_schedules"
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
@@ -0,0 +1,94 @@
|
||||
CREATE TABLE "decision_events" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"company_id" uuid NOT NULL,
|
||||
"scope_type" "decision_scope" NOT NULL,
|
||||
"scope_id" uuid NOT NULL,
|
||||
"title" varchar(255) NOT NULL,
|
||||
"body_md" text NOT NULL,
|
||||
"alternatives_considered" text,
|
||||
"cost_impact" numeric(18, 4),
|
||||
"currency" varchar(3),
|
||||
"approved_by" uuid,
|
||||
"decided_at" timestamp with time zone NOT NULL,
|
||||
"decided_by" uuid,
|
||||
"tags" text[],
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"deleted_at" timestamp with time zone
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "projects" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"company_id" uuid NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"code" varchar(64),
|
||||
"description" text,
|
||||
"status" varchar(32) DEFAULT 'active' NOT NULL,
|
||||
"start_date" timestamp with time zone,
|
||||
"end_date" timestamp with time zone,
|
||||
"created_by" uuid,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"deleted_at" timestamp with time zone
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "subtasks" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"task_id" uuid NOT NULL,
|
||||
"name" varchar(500) NOT NULL,
|
||||
"done" boolean DEFAULT false NOT NULL,
|
||||
"order" integer DEFAULT 0 NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "tasks" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"work_package_id" uuid NOT NULL,
|
||||
"title" varchar(255) NOT NULL,
|
||||
"description" text,
|
||||
"status" "task_status" DEFAULT 'todo' NOT NULL,
|
||||
"assignee_id" uuid,
|
||||
"due_at" timestamp with time zone,
|
||||
"order" integer DEFAULT 0 NOT NULL,
|
||||
"completed_at" timestamp with time zone,
|
||||
"created_by" uuid,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"deleted_at" timestamp with time zone
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "work_packages" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"project_id" uuid NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"description" text,
|
||||
"order" integer DEFAULT 0 NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"deleted_at" timestamp with time zone
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "decision_events" ADD CONSTRAINT "decision_events_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "decision_events" ADD CONSTRAINT "decision_events_approved_by_users_id_fk" FOREIGN KEY ("approved_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "decision_events" ADD CONSTRAINT "decision_events_decided_by_users_id_fk" FOREIGN KEY ("decided_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "projects" ADD CONSTRAINT "projects_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "projects" ADD CONSTRAINT "projects_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "subtasks" ADD CONSTRAINT "subtasks_task_id_tasks_id_fk" FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "tasks" ADD CONSTRAINT "tasks_work_package_id_work_packages_id_fk" FOREIGN KEY ("work_package_id") REFERENCES "public"."work_packages"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "tasks" ADD CONSTRAINT "tasks_assignee_id_users_id_fk" FOREIGN KEY ("assignee_id") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "tasks" ADD CONSTRAINT "tasks_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "work_packages" ADD CONSTRAINT "work_packages_project_id_projects_id_fk" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE INDEX "de_by_scope" ON "decision_events" USING btree ("scope_type","scope_id","decided_at");--> statement-breakpoint
|
||||
CREATE INDEX "de_by_company" ON "decision_events" USING btree ("company_id");--> statement-breakpoint
|
||||
CREATE INDEX "de_tags_gin" ON "decision_events" USING gin ("tags");--> statement-breakpoint
|
||||
CREATE INDEX "projects_by_company" ON "projects" USING btree ("company_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "projects_company_code_uq" ON "projects" USING btree ("company_id","code");--> statement-breakpoint
|
||||
CREATE INDEX "subtasks_by_task" ON "subtasks" USING btree ("task_id","order");--> statement-breakpoint
|
||||
CREATE INDEX "tasks_by_wp" ON "tasks" USING btree ("work_package_id");--> statement-breakpoint
|
||||
CREATE INDEX "tasks_by_assignee" ON "tasks" USING btree ("assignee_id");--> statement-breakpoint
|
||||
CREATE INDEX "tasks_status_due" ON "tasks" USING btree ("status","due_at");--> statement-breakpoint
|
||||
CREATE INDEX "work_packages_by_project" ON "work_packages" USING btree ("project_id");--> statement-breakpoint
|
||||
ALTER TABLE "asset_location_history" ADD CONSTRAINT "asset_location_history_from_project_id_projects_id_fk" FOREIGN KEY ("from_project_id") REFERENCES "public"."projects"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "asset_location_history" ADD CONSTRAINT "asset_location_history_to_project_id_projects_id_fk" FOREIGN KEY ("to_project_id") REFERENCES "public"."projects"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "assets" ADD CONSTRAINT "assets_current_project_id_projects_id_fk" FOREIGN KEY ("current_project_id") REFERENCES "public"."projects"("id") ON DELETE restrict ON UPDATE no action;
|
||||
@@ -0,0 +1,21 @@
|
||||
-- Phase 3 follow-up: partial index for open + due tasks, updated_at triggers.
|
||||
|
||||
CREATE INDEX "tasks_open_due"
|
||||
ON "tasks" ("due_at")
|
||||
WHERE "status" IN ('todo','doing','blocked') AND "deleted_at" IS NULL;
|
||||
--> statement-breakpoint
|
||||
|
||||
CREATE TRIGGER projects_set_updated_at BEFORE UPDATE ON "projects"
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
--> statement-breakpoint
|
||||
CREATE TRIGGER work_packages_set_updated_at BEFORE UPDATE ON "work_packages"
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
--> statement-breakpoint
|
||||
CREATE TRIGGER tasks_set_updated_at BEFORE UPDATE ON "tasks"
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
--> statement-breakpoint
|
||||
CREATE TRIGGER subtasks_set_updated_at BEFORE UPDATE ON "subtasks"
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
--> statement-breakpoint
|
||||
CREATE TRIGGER decision_events_set_updated_at BEFORE UPDATE ON "decision_events"
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
@@ -0,0 +1,33 @@
|
||||
CREATE TABLE "wiki_pages" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"company_id" uuid NOT NULL,
|
||||
"scope_type" "wiki_scope" NOT NULL,
|
||||
"scope_id" uuid,
|
||||
"slug" varchar(128) NOT NULL,
|
||||
"title" varchar(255) NOT NULL,
|
||||
"current_revision_id" uuid,
|
||||
"created_by" uuid,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"deleted_at" timestamp with time zone
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "wiki_revisions" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"page_id" uuid NOT NULL,
|
||||
"revision" integer NOT NULL,
|
||||
"title" varchar(255) NOT NULL,
|
||||
"body_md" text NOT NULL,
|
||||
"body_tsv" text,
|
||||
"edited_by" uuid,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"comment" varchar(500)
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "wiki_pages" ADD CONSTRAINT "wiki_pages_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "wiki_pages" ADD CONSTRAINT "wiki_pages_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "wiki_revisions" ADD CONSTRAINT "wiki_revisions_page_id_wiki_pages_id_fk" FOREIGN KEY ("page_id") REFERENCES "public"."wiki_pages"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "wiki_revisions" ADD CONSTRAINT "wiki_revisions_edited_by_users_id_fk" FOREIGN KEY ("edited_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE INDEX "wiki_by_scope" ON "wiki_pages" USING btree ("scope_type","scope_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "wiki_rev_page_rev_uq" ON "wiki_revisions" USING btree ("page_id","revision");--> statement-breakpoint
|
||||
CREATE INDEX "wiki_rev_by_page" ON "wiki_revisions" USING btree ("page_id","revision");
|
||||
@@ -0,0 +1,34 @@
|
||||
-- Phase 4 follow-up: tsvector + FTS trigger + GIN index for wiki_revisions,
|
||||
-- NULLS NOT DISTINCT unique index on wiki_pages so global pages (scope_id IS NULL)
|
||||
-- can't share a slug, and updated_at trigger.
|
||||
|
||||
-- Convert body_tsv to a real tsvector column.
|
||||
ALTER TABLE "wiki_revisions" ALTER COLUMN "body_tsv" TYPE tsvector USING NULL::tsvector;
|
||||
--> statement-breakpoint
|
||||
|
||||
CREATE OR REPLACE FUNCTION wiki_tsv_trigger() RETURNS trigger AS $$
|
||||
BEGIN
|
||||
NEW.body_tsv := to_tsvector(
|
||||
'simple',
|
||||
coalesce(NEW.title, '') || ' ' || coalesce(NEW.body_md, '')
|
||||
);
|
||||
RETURN NEW;
|
||||
END $$ LANGUAGE plpgsql;
|
||||
--> statement-breakpoint
|
||||
|
||||
CREATE TRIGGER wiki_tsv_upd BEFORE INSERT OR UPDATE ON "wiki_revisions"
|
||||
FOR EACH ROW EXECUTE FUNCTION wiki_tsv_trigger();
|
||||
--> statement-breakpoint
|
||||
|
||||
CREATE INDEX "wiki_body_tsv_gin" ON "wiki_revisions" USING GIN ("body_tsv");
|
||||
--> statement-breakpoint
|
||||
|
||||
-- Slug uniqueness per (company, scope, slug). NULLS NOT DISTINCT (pg 15+)
|
||||
-- means two global pages (scope_id IS NULL) with the same slug collide.
|
||||
CREATE UNIQUE INDEX "wiki_scope_slug_uq"
|
||||
ON "wiki_pages" ("company_id", "scope_type", "scope_id", "slug")
|
||||
NULLS NOT DISTINCT;
|
||||
--> statement-breakpoint
|
||||
|
||||
CREATE TRIGGER wiki_pages_set_updated_at BEFORE UPDATE ON "wiki_pages"
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
@@ -0,0 +1,33 @@
|
||||
CREATE TABLE "property_floors" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"property_id" uuid NOT NULL,
|
||||
"label" varchar(32) NOT NULL,
|
||||
"name" varchar(255),
|
||||
"order" integer DEFAULT 0 NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "property_rooms" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"property_id" uuid NOT NULL,
|
||||
"floor_id" uuid,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"notes" text,
|
||||
"order" integer DEFAULT 0 NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"deleted_at" timestamp with time zone
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "assets" ADD COLUMN "current_room_id" uuid;--> statement-breakpoint
|
||||
ALTER TABLE "property_floors" ADD CONSTRAINT "property_floors_property_id_properties_id_fk" FOREIGN KEY ("property_id") REFERENCES "public"."properties"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "property_rooms" ADD CONSTRAINT "property_rooms_property_id_properties_id_fk" FOREIGN KEY ("property_id") REFERENCES "public"."properties"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "property_rooms" ADD CONSTRAINT "property_rooms_floor_id_property_floors_id_fk" FOREIGN KEY ("floor_id") REFERENCES "public"."property_floors"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE INDEX "floors_by_property" ON "property_floors" USING btree ("property_id","order");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "floors_property_label_uq" ON "property_floors" USING btree ("property_id","label");--> statement-breakpoint
|
||||
CREATE INDEX "rooms_by_property" ON "property_rooms" USING btree ("property_id","order");--> statement-breakpoint
|
||||
CREATE INDEX "rooms_by_floor" ON "property_rooms" USING btree ("floor_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "rooms_floor_name_uq" ON "property_rooms" USING btree ("property_id","floor_id","name");--> statement-breakpoint
|
||||
ALTER TABLE "assets" ADD CONSTRAINT "assets_current_room_id_property_rooms_id_fk" FOREIGN KEY ("current_room_id") REFERENCES "public"."property_rooms"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE INDEX "assets_by_room" ON "assets" USING btree ("current_room_id");
|
||||
@@ -0,0 +1,15 @@
|
||||
-- Rooms follow-up: constraints + updated_at triggers.
|
||||
|
||||
-- A room assignment only makes sense when the asset is at a property.
|
||||
ALTER TABLE "assets" ADD CONSTRAINT "assets_room_requires_property" CHECK (
|
||||
"current_room_id" IS NULL
|
||||
OR "current_container_kind" = 'property'
|
||||
);
|
||||
--> statement-breakpoint
|
||||
|
||||
CREATE TRIGGER property_floors_set_updated_at BEFORE UPDATE ON "property_floors"
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
--> statement-breakpoint
|
||||
|
||||
CREATE TRIGGER property_rooms_set_updated_at BEFORE UPDATE ON "property_rooms"
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
@@ -0,0 +1,17 @@
|
||||
CREATE TYPE "public"."account_kind" AS ENUM('water', 'electricity', 'gas', 'internet', 'phone', 'cable', 'waste', 'other');--> statement-breakpoint
|
||||
CREATE TABLE "property_accounts" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"property_id" uuid NOT NULL,
|
||||
"kind" "account_kind" NOT NULL,
|
||||
"provider" varchar(128),
|
||||
"label" varchar(128),
|
||||
"account_number" varchar(128),
|
||||
"meter_number" varchar(128),
|
||||
"notes" text,
|
||||
"order" integer DEFAULT 0 NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "property_accounts" ADD CONSTRAINT "property_accounts_property_id_properties_id_fk" FOREIGN KEY ("property_id") REFERENCES "public"."properties"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE INDEX "accounts_by_property" ON "property_accounts" USING btree ("property_id","kind","order");
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Updated_at trigger for property_accounts.
|
||||
CREATE TRIGGER property_accounts_set_updated_at BEFORE UPDATE ON "property_accounts"
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
@@ -0,0 +1,20 @@
|
||||
CREATE TYPE "public"."notification_kind" AS ENUM('task_assigned', 'asset_log_added', 'asset_moved', 'decision_created', 'maintenance_event_recorded', 'generic');--> statement-breakpoint
|
||||
CREATE TABLE "notifications" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"company_id" uuid NOT NULL,
|
||||
"kind" "notification_kind" NOT NULL,
|
||||
"title" varchar(255) NOT NULL,
|
||||
"body" text NOT NULL,
|
||||
"link" varchar(1024),
|
||||
"read_at" timestamp with time zone,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "users" ADD COLUMN "email_notifications" boolean DEFAULT true NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE "users" ADD COLUMN "matrix_notifications" boolean DEFAULT false NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE "users" ADD COLUMN "matrix_user_id" varchar(255);--> statement-breakpoint
|
||||
ALTER TABLE "notifications" ADD CONSTRAINT "notifications_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "notifications" ADD CONSTRAINT "notifications_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE INDEX "notifications_by_user_unread" ON "notifications" USING btree ("user_id","read_at","created_at");--> statement-breakpoint
|
||||
CREATE INDEX "notifications_by_user_company" ON "notifications" USING btree ("user_id","company_id","created_at");
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,97 @@
|
||||
"when": 1776760498088,
|
||||
"tag": "0000_init",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "7",
|
||||
"when": 1776912796532,
|
||||
"tag": "0001_phase1_assets_properties_documents",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"version": "7",
|
||||
"when": 1776912900000,
|
||||
"tag": "0002_phase1_constraints_and_search",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 3,
|
||||
"version": "7",
|
||||
"when": 1776913896873,
|
||||
"tag": "0003_phase2_checklists_and_maintenance",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 4,
|
||||
"version": "7",
|
||||
"when": 1776913950000,
|
||||
"tag": "0004_phase2_partial_indexes_and_triggers",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 5,
|
||||
"version": "7",
|
||||
"when": 1776915278123,
|
||||
"tag": "0005_phase3_projects_and_decisions",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 6,
|
||||
"version": "7",
|
||||
"when": 1776915350000,
|
||||
"tag": "0006_phase3_partial_index_and_triggers",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 7,
|
||||
"version": "7",
|
||||
"when": 1776916197473,
|
||||
"tag": "0007_phase4_wiki",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"version": "7",
|
||||
"when": 1776916020000,
|
||||
"tag": "0008_phase4_wiki_fts_and_uniq",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 9,
|
||||
"version": "7",
|
||||
"when": 1776918611593,
|
||||
"tag": "0009_rooms_and_floors",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 10,
|
||||
"version": "7",
|
||||
"when": 1776918700000,
|
||||
"tag": "0010_rooms_check_and_triggers",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 11,
|
||||
"version": "7",
|
||||
"when": 1776919853043,
|
||||
"tag": "0011_property_accounts",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 12,
|
||||
"version": "7",
|
||||
"when": 1776919900000,
|
||||
"tag": "0012_accounts_updated_at_trigger",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 13,
|
||||
"version": "7",
|
||||
"when": 1776930973516,
|
||||
"tag": "0013_notifications",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user