feat(documents): папки шаблонов, маппинги и поле document_template; fix(batch): атомарное создание задач, idempotency и массовое удаление в реестре

This commit is contained in:
2026-07-08 13:33:14 +03:00
parent fc8e85832c
commit 6826d82850
34 changed files with 3770 additions and 467 deletions

View File

@@ -0,0 +1,53 @@
-- Document Center: template folders, folder-level variable mappings, and generation targets
-- Safe to run multiple times (CREATE TABLE IF NOT EXISTS, CREATE INDEX IF NOT EXISTS).
CREATE TABLE IF NOT EXISTS "document_template_folders" (
"id" serial PRIMARY KEY NOT NULL,
"organization_id" integer NOT NULL,
"parent_id" integer,
"name" varchar(100) NOT NULL,
"position" integer DEFAULT 0 NOT NULL,
"created_at" timestamp DEFAULT now(),
"updated_at" timestamp DEFAULT now()
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "document_folder_mappings" (
"id" serial PRIMARY KEY NOT NULL,
"folder_id" integer NOT NULL,
"code" varchar(100) NOT NULL,
"label" varchar(200),
"description" text,
"source" varchar(30) NOT NULL,
"mapping" jsonb DEFAULT '{}'::jsonb,
"default_value" text,
"sort_order" integer DEFAULT 0,
"created_at" timestamp DEFAULT now()
);
--> statement-breakpoint
-- Add folder and target columns to document_templates
ALTER TABLE "document_templates" ADD COLUMN IF NOT EXISTS "folder_id" integer;
ALTER TABLE "document_templates" ADD COLUMN IF NOT EXISTS "target_form_id" integer;
ALTER TABLE "document_templates" ADD COLUMN IF NOT EXISTS "target_field_code" varchar(100);
ALTER TABLE "document_templates" ADD COLUMN IF NOT EXISTS "target_task_source" varchar(20) DEFAULT 'current';
ALTER TABLE "document_templates" ADD COLUMN IF NOT EXISTS "target_source_field_code" varchar(100);
-- Foreign keys
ALTER TABLE "document_template_folders" DROP CONSTRAINT IF EXISTS "document_template_folders_organization_id_organizations_id_fk";
ALTER TABLE "document_template_folders" ADD CONSTRAINT "document_template_folders_organization_id_organizations_id_fk" FOREIGN KEY ("organization_id") REFERENCES "organizations"("id") ON DELETE cascade;
-- parent_id не объявляем как FK, чтобы избежать циклической ссылки в Drizzle ORM (как у других деревьев: form_folders, directory_folders, roles)
ALTER TABLE "document_folder_mappings" DROP CONSTRAINT IF EXISTS "document_folder_mappings_folder_id_document_template_folders_id_fk";
ALTER TABLE "document_folder_mappings" ADD CONSTRAINT "document_folder_mappings_folder_id_document_template_folders_id_fk" FOREIGN KEY ("folder_id") REFERENCES "document_template_folders"("id") ON DELETE cascade;
ALTER TABLE "document_templates" DROP CONSTRAINT IF EXISTS "document_templates_folder_id_document_template_folders_id_fk";
ALTER TABLE "document_templates" ADD CONSTRAINT "document_templates_folder_id_document_template_folders_id_fk" FOREIGN KEY ("folder_id") REFERENCES "document_template_folders"("id") ON DELETE set null;
ALTER TABLE "document_templates" DROP CONSTRAINT IF EXISTS "document_templates_target_form_id_forms_id_fk";
ALTER TABLE "document_templates" ADD CONSTRAINT "document_templates_target_form_id_forms_id_fk" FOREIGN KEY ("target_form_id") REFERENCES "forms"("id") ON DELETE set null;
-- Indexes
CREATE INDEX IF NOT EXISTS "document_template_folders_org_parent_idx" ON "document_template_folders" ("organization_id", "parent_id");
CREATE INDEX IF NOT EXISTS "document_template_folders_position_idx" ON "document_template_folders" ("position");
CREATE UNIQUE INDEX IF NOT EXISTS "document_folder_mappings_code_unique" ON "document_folder_mappings" ("folder_id", "code");
CREATE INDEX IF NOT EXISTS "document_templates_org_folder_idx" ON "document_templates" ("organization_id", "folder_id");

View File

@@ -0,0 +1,23 @@
-- Idempotency keys for batch task creation and other heavy write endpoints
CREATE TABLE IF NOT EXISTS idempotency_keys (
id SERIAL PRIMARY KEY,
key VARCHAR(255) NOT NULL,
organization_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
endpoint VARCHAR(255) NOT NULL,
payload_hash VARCHAR(64) NOT NULL,
response_json JSONB NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
expires_at TIMESTAMP NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idempotency_keys_org_key_idx ON idempotency_keys(organization_id, key);
CREATE INDEX IF NOT EXISTS idempotency_keys_expires_at_idx ON idempotency_keys(expires_at);
-- Enable tenant isolation for idempotency keys
ALTER TABLE idempotency_keys ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS tenant_iso ON idempotency_keys;
CREATE POLICY tenant_iso ON idempotency_keys USING (
current_setting('app.is_superadmin', true) = 'true'
OR organization_id = NULLIF(current_setting('app.current_org_id', true), '')::int
);

View File

@@ -148,6 +148,13 @@
"when": 1783000000000,
"tag": "0039_add_asset_form_link",
"breakpoints": true
},
{
"idx": 21,
"version": "7",
"when": 1783000001000,
"tag": "0040_document_template_folders_and_targets",
"breakpoints": true
}
]
}