diff --git a/src/app/db/migrations/0273_fixed_doctor_spectrum.sql b/src/app/db/migrations/0273_smiling_skullbuster.sql similarity index 94% rename from src/app/db/migrations/0273_fixed_doctor_spectrum.sql rename to src/app/db/migrations/0273_smiling_skullbuster.sql index 6d4fdab1..ee2f8db7 100644 --- a/src/app/db/migrations/0273_fixed_doctor_spectrum.sql +++ b/src/app/db/migrations/0273_smiling_skullbuster.sql @@ -6,11 +6,6 @@ CREATE TABLE "contractor" ( "email_address" varchar ); --> statement-breakpoint -CREATE TABLE "document_type" ( - "id" bigserial PRIMARY KEY NOT NULL, - "name" varchar NOT NULL -); ---> statement-breakpoint CREATE TABLE "project" ( "id" bigserial PRIMARY KEY NOT NULL, "name" varchar NOT NULL, @@ -55,7 +50,7 @@ CREATE TABLE "project_workstream_contractor" ( CREATE TABLE "project_workstream_evidence_requirement" ( "id" bigserial PRIMARY KEY NOT NULL, "project_workstream_id" bigint NOT NULL, - "document_type_id" bigint NOT NULL, + "file_type" "file_type" NOT NULL, "project_workstream_stage_id" bigint, "required" boolean DEFAULT true NOT NULL, "naming_rule" varchar @@ -104,7 +99,6 @@ ALTER TABLE "project_workstream" ADD CONSTRAINT "project_workstream_current_stag ALTER TABLE "project_workstream_contractor" ADD CONSTRAINT "project_workstream_contractor_project_workstream_id_project_workstream_id_fk" FOREIGN KEY ("project_workstream_id") REFERENCES "public"."project_workstream"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "project_workstream_contractor" ADD CONSTRAINT "project_workstream_contractor_contractor_id_contractor_id_fk" FOREIGN KEY ("contractor_id") REFERENCES "public"."contractor"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "project_workstream_evidence_requirement" ADD CONSTRAINT "project_workstream_evidence_requirement_project_workstream_id_project_workstream_id_fk" FOREIGN KEY ("project_workstream_id") REFERENCES "public"."project_workstream"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "project_workstream_evidence_requirement" ADD CONSTRAINT "project_workstream_evidence_requirement_document_type_id_document_type_id_fk" FOREIGN KEY ("document_type_id") REFERENCES "public"."document_type"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "project_workstream_evidence_requirement" ADD CONSTRAINT "project_workstream_evidence_requirement_project_workstream_stage_id_project_workstream_stage_id_fk" FOREIGN KEY ("project_workstream_stage_id") REFERENCES "public"."project_workstream_stage"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "project_workstream_stage" ADD CONSTRAINT "project_workstream_stage_project_workstream_id_project_workstream_id_fk" FOREIGN KEY ("project_workstream_id") REFERENCES "public"."project_workstream"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "work_order" ADD CONSTRAINT "work_order_project_workstream_contractor_id_project_workstream_contractor_id_fk" FOREIGN KEY ("project_workstream_contractor_id") REFERENCES "public"."project_workstream_contractor"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint diff --git a/src/app/db/migrations/meta/0273_snapshot.json b/src/app/db/migrations/meta/0273_snapshot.json index 9dd2eb06..bc1ba7d5 100644 --- a/src/app/db/migrations/meta/0273_snapshot.json +++ b/src/app/db/migrations/meta/0273_snapshot.json @@ -1,5 +1,5 @@ { - "id": "d7c0bcad-5b51-4018-8d6f-f07c8ba85414", + "id": "d48f1f71-fbb0-439d-983e-0a96db03c521", "prevId": "44eb87f5-2979-43c6-a96e-ba128b3d79a0", "version": "7", "dialect": "postgresql", @@ -4782,31 +4782,6 @@ "checkConstraints": {}, "isRLSEnabled": false }, - "public.document_type": { - "name": "document_type", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "bigserial", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "varchar", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, "public.project": { "name": "project", "schema": "", @@ -5172,9 +5147,10 @@ "primaryKey": false, "notNull": true }, - "document_type_id": { - "name": "document_type_id", - "type": "bigint", + "file_type": { + "name": "file_type", + "type": "file_type", + "typeSchema": "public", "primaryKey": false, "notNull": true }, @@ -5213,19 +5189,6 @@ "onDelete": "no action", "onUpdate": "no action" }, - "project_workstream_evidence_requirement_document_type_id_document_type_id_fk": { - "name": "project_workstream_evidence_requirement_document_type_id_document_type_id_fk", - "tableFrom": "project_workstream_evidence_requirement", - "tableTo": "document_type", - "columnsFrom": [ - "document_type_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, "project_workstream_evidence_requirement_project_workstream_stage_id_project_workstream_stage_id_fk": { "name": "project_workstream_evidence_requirement_project_workstream_stage_id_project_workstream_stage_id_fk", "tableFrom": "project_workstream_evidence_requirement", diff --git a/src/app/db/migrations/meta/_journal.json b/src/app/db/migrations/meta/_journal.json index e29694fa..da3f41f1 100644 --- a/src/app/db/migrations/meta/_journal.json +++ b/src/app/db/migrations/meta/_journal.json @@ -1909,8 +1909,8 @@ { "idx": 273, "version": "7", - "when": 1784302480213, - "tag": "0273_fixed_doctor_spectrum", + "when": 1784305436995, + "tag": "0273_smiling_skullbuster", "breakpoints": true } ] diff --git a/src/app/db/schema/projects/projects.ts b/src/app/db/schema/projects/projects.ts index ad8354ed..aa55a467 100644 --- a/src/app/db/schema/projects/projects.ts +++ b/src/app/db/schema/projects/projects.ts @@ -25,6 +25,7 @@ import { import { InferModel } from "drizzle-orm"; import { property } from "../property"; import { organisation } from "../organisation"; +import { fileType } from "../uploaded_files_enums"; // Reference data: the available project types (e.g. Retrofit, New Build). export const projectType = pgTable("project_type", { @@ -146,13 +147,10 @@ export const projectWorkstreamContractor = pgTable( } ); -// Reference data: the documents that may be requested. -export const documentType = pgTable("document_type", { - id: bigserial("id", { mode: "bigint" }).primaryKey(), - name: varchar("name").notNull(), -}); - -// Configuration describing which evidence is required. +// Configuration describing which evidence is required. The required document is +// expressed as a `file_type` enum value (reusing the existing uploaded_files +// taxonomy) rather than a separate document_type table, so a submitted file's +// `file_type` can be matched directly against what the requirement expects. export const projectWorkstreamEvidenceRequirement = pgTable( "project_workstream_evidence_requirement", { @@ -160,9 +158,7 @@ export const projectWorkstreamEvidenceRequirement = pgTable( projectWorkstreamId: bigint("project_workstream_id", { mode: "bigint" }) .notNull() .references(() => projectWorkstream.id), - documentTypeId: bigint("document_type_id", { mode: "bigint" }) - .notNull() - .references(() => documentType.id), + fileType: fileType("file_type").notNull(), // Null when the requirement applies to the workstream as a whole. projectWorkstreamStageId: bigint("project_workstream_stage_id", { mode: "bigint", @@ -211,7 +207,6 @@ export type ProjectWorkstreamContractor = InferModel< typeof projectWorkstreamContractor, "select" >; -export type DocumentType = InferModel; export type ProjectWorkstreamEvidenceRequirement = InferModel< typeof projectWorkstreamEvidenceRequirement, "select" diff --git a/src/app/db/schema/projects/schema.md b/src/app/db/schema/projects/schema.md index 8d7b6050..a8e5d89d 100644 --- a/src/app/db/schema/projects/schema.md +++ b/src/app/db/schema/projects/schema.md @@ -63,6 +63,9 @@ Project > `pgEnum` once the values are agreed. > - **No new `uploaded_by` column** on `uploaded_files`; the existing > `uploaded_by` `bigint` FK → user is reused. +> - **No `document_type` table.** Evidence requirements reference the existing +> `file_type` enum instead, so the required and submitted document kinds share +> one taxonomy. Conventions: bigint autoincrement primary keys for all new tables (and bigint FKs between them); FKs to pre-existing tables match the referenced PK's type. `timestamp` columns default to `now()`. Status-like columns were proposed as Postgres enums but are implemented as `text` for now (values TBC). @@ -207,37 +210,24 @@ Represents a contractor assigned to deliver a Project Workstream. --- -## Document Type - -Reference data describing the documents that may be requested. - -Examples: - -- Completion Photo -- RAMS -- Insurance Certificate -- Warranty -- Handover Pack - -| Column | Type | Nullable | Notes | -| ------ | ------- | -------- | ----------- | -| id | bigint | No | Primary Key | -| name | varchar | No | | - ---- - ## Project Workstream Evidence Requirement Configuration describing which evidence is required. -| Column | Type | Nullable | Notes | -| --------------------------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------- | -| id | bigint | No | Primary Key | -| project_workstream_id | bigint | No | FK → Project Workstream | -| document_type_id | bigint | No | FK → Document Type | -| project_workstream_stage_id | bigint | Yes | FK → Project Workstream Stage; null when the requirement applies to the workstream as a whole rather than a specific stage | -| required | boolean | No | Default `true` | -| naming_rule | varchar | Yes | Optional regex/template | +The required document is expressed as a `file_type` **enum** value, reusing the +existing `uploaded_files.file_type` taxonomy, so a submitted file's `file_type` +can be matched directly against what the requirement expects. _(The originally +proposed separate `document_type` reference table was dropped in favour of this +reuse — see the implementation note at the top.)_ + +| Column | Type | Nullable | Notes | +| --------------------------- | --------- | -------- | -------------------------------------------------------------------------------------------------------------------------- | +| id | bigint | No | Primary Key | +| project_workstream_id | bigint | No | FK → Project Workstream | +| file_type | file_type | No | Postgres enum (existing `uploaded_files` `file_type`); the required document kind | +| project_workstream_stage_id | bigint | Yes | FK → Project Workstream Stage; null when the requirement applies to the workstream as a whole rather than a specific stage | +| required | boolean | No | Default `true` | +| naming_rule | varchar | Yes | Optional regex/template | --- diff --git a/src/app/db/schema/relations.ts b/src/app/db/schema/relations.ts index 04f417c8..8155dd7f 100644 --- a/src/app/db/schema/relations.ts +++ b/src/app/db/schema/relations.ts @@ -33,7 +33,6 @@ import { projectWorkstream, projectWorkstreamStage, projectWorkstreamContractor, - documentType, projectWorkstreamEvidenceRequirement, workOrder, } from "./projects/projects"; @@ -313,10 +312,6 @@ export const projectWorkstreamContractorRelations = relations( }) ); -export const documentTypeRelations = relations(documentType, ({ many }) => ({ - evidenceRequirements: many(projectWorkstreamEvidenceRequirement), -})); - export const projectWorkstreamEvidenceRequirementRelations = relations( projectWorkstreamEvidenceRequirement, ({ one, many }) => ({ @@ -324,10 +319,6 @@ export const projectWorkstreamEvidenceRequirementRelations = relations( fields: [projectWorkstreamEvidenceRequirement.projectWorkstreamId], references: [projectWorkstream.id], }), - documentType: one(documentType, { - fields: [projectWorkstreamEvidenceRequirement.documentTypeId], - references: [documentType.id], - }), stage: one(projectWorkstreamStage, { fields: [projectWorkstreamEvidenceRequirement.projectWorkstreamStageId], references: [projectWorkstreamStage.id], diff --git a/src/app/db/schema/uploaded_files.ts b/src/app/db/schema/uploaded_files.ts index bb08b7e9..f68304b5 100644 --- a/src/app/db/schema/uploaded_files.ts +++ b/src/app/db/schema/uploaded_files.ts @@ -1,84 +1,14 @@ -import { bigint, bigserial, pgEnum, pgTable, text, timestamp } from "drizzle-orm/pg-core"; +import { bigint, bigserial, pgTable, text, timestamp } from "drizzle-orm/pg-core"; import { user } from "./users"; import { projectWorkstream, projectWorkstreamContractor, projectWorkstreamEvidenceRequirement, } from "./projects/projects"; +import { fileType, fileSource } from "./uploaded_files_enums"; -export const fileType = pgEnum("file_type", [ - // Assessment - "photo_pack", - "site_note", - "rd_sap_site_note", - "pas_2023_ventilation", - "pas_2023_condition", - "pas_significance", - "par_photo_pack", - "pas_2023_property", - "pas_2023_occupancy", - "ecmk_site_note", - "ecmk_rd_sap_site_note", - "ecmk_survey_xml", - // Contractor install documentation - // Photos - "pre_photo", - "mid_photo", - "post_photo", - "loft_hatch_photo", - "dmev_photos", - "door_undercut_photos", - "trickle_vent_photos", - // Pre-installation - "pre_installation_building_inspection", - "point_of_work_risk_assessment", - // Compliance & lodgement - "claim_of_compliance", - "mcs_compliance_certificate", - "certificate_of_conformity", - "minor_works_electrical_certificate", - "trustmark_licence_numbers", - "operative_competency", - // Ventilation - "ventilation_assessment_checklist", - "anemometer_readings", - "commissioning_records", - "part_f_ventilation_document", - // Handover & warranties - "handover_pack", - "insurance_guarantee", - "workmanship_warranty", - "g98_notification", - // Qualifications & other - "installer_qualifications", - "installer_feedback", - "contractor_other", - // MagicPlan - "magic_plan_json", - // Coordination - "improvement_option_evaluation", - "medium_term_improvement_plan", - // Design - "retrofit_design_doc", - // Audit - "ventilation_audit", - // Other - "other" - -]); - -export const fileSource = pgEnum("file_source", [ - "pas hub", - "sharepoint", - "hubspot", - "ecmk", - "contractor", - "magic_plan", - "coordination_hub", - "audit_generator", - // Uploads originating from the Projects module. - "projects" -]); +// Re-exported so existing importers of these enums keep working. +export { fileType, fileSource }; export const uploadedFiles = pgTable( "uploaded_files", diff --git a/src/app/db/schema/uploaded_files_enums.ts b/src/app/db/schema/uploaded_files_enums.ts new file mode 100644 index 00000000..514cc896 --- /dev/null +++ b/src/app/db/schema/uploaded_files_enums.ts @@ -0,0 +1,83 @@ +// Enums for the uploaded_files table. +// +// These live in their own module (rather than in uploaded_files.ts) so that +// other schema files — notably the Projects module — can reference `fileType` +// without creating an import cycle: uploaded_files.ts imports the Projects +// tables for its FK columns, and the Projects evidence-requirement table +// imports `fileType` from here. Keeping the enums dependency-free breaks that +// cycle. uploaded_files.ts re-exports these for backwards compatibility. +import { pgEnum } from "drizzle-orm/pg-core"; + +export const fileType = pgEnum("file_type", [ + // Assessment + "photo_pack", + "site_note", + "rd_sap_site_note", + "pas_2023_ventilation", + "pas_2023_condition", + "pas_significance", + "par_photo_pack", + "pas_2023_property", + "pas_2023_occupancy", + "ecmk_site_note", + "ecmk_rd_sap_site_note", + "ecmk_survey_xml", + // Contractor install documentation + // Photos + "pre_photo", + "mid_photo", + "post_photo", + "loft_hatch_photo", + "dmev_photos", + "door_undercut_photos", + "trickle_vent_photos", + // Pre-installation + "pre_installation_building_inspection", + "point_of_work_risk_assessment", + // Compliance & lodgement + "claim_of_compliance", + "mcs_compliance_certificate", + "certificate_of_conformity", + "minor_works_electrical_certificate", + "trustmark_licence_numbers", + "operative_competency", + // Ventilation + "ventilation_assessment_checklist", + "anemometer_readings", + "commissioning_records", + "part_f_ventilation_document", + // Handover & warranties + "handover_pack", + "insurance_guarantee", + "workmanship_warranty", + "g98_notification", + // Qualifications & other + "installer_qualifications", + "installer_feedback", + "contractor_other", + // MagicPlan + "magic_plan_json", + // Coordination + "improvement_option_evaluation", + "medium_term_improvement_plan", + // Design + "retrofit_design_doc", + // Audit + "ventilation_audit", + // Other + "other" + +]); + +export const fileSource = pgEnum("file_source", [ + "pas hub", + "sharepoint", + "hubspot", + "ecmk", + "contractor", + "magic_plan", + "coordination_hub", + "audit_generator", + // Uploads originating from the Projects module. + "projects" +]);