import { MigrationInterface, QueryRunner } from 'typeorm'; export class InitialSchema1734256800000 implements MigrationInterface { name = 'InitialSchema1734256800000'; public async up(queryRunner: QueryRunner): Promise { await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`); await queryRunner.query(` CREATE TYPE "public"."transaction_type_enum" AS ENUM('INCOME', 'EXPENSE') `); await queryRunner.query(` CREATE TYPE "public"."budget_group_type_enum" AS ENUM('ESSENTIAL', 'PERSONAL', 'SAVINGS') `); await queryRunner.query(` CREATE TYPE "public"."payment_method_enum" AS ENUM('CASH', 'CARD', 'BANK_TRANSFER') `); await queryRunner.query(` CREATE TYPE "public"."goal_status_enum" AS ENUM('ACTIVE', 'COMPLETED', 'PAUSED', 'CANCELLED') `); await queryRunner.query(` CREATE TYPE "public"."goal_priority_enum" AS ENUM('LOW', 'MEDIUM', 'HIGH') `); await queryRunner.query(` CREATE TYPE "public"."goal_auto_save_frequency_enum" AS ENUM('DAILY', 'WEEKLY', 'MONTHLY') `); await queryRunner.query(` CREATE TYPE "public"."recommendation_type_enum" AS ENUM('SAVING', 'SPENDING', 'INVESTMENT', 'TAX', 'DEBT', 'BUDGET', 'GOAL') `); await queryRunner.query(` CREATE TYPE "public"."recommendation_status_enum" AS ENUM('NEW', 'VIEWED', 'APPLIED', 'DISMISSED') `); await queryRunner.query(` CREATE TABLE "users" ( "id" uuid NOT NULL DEFAULT uuid_generate_v4(), "email" character varying(255) NOT NULL, "phone" character varying(20), "password_hash" character varying(255) NOT NULL, "first_name" character varying(100), "last_name" character varying(100), "currency" character varying(3) NOT NULL DEFAULT 'RUB', "language" character varying(10) NOT NULL DEFAULT 'ru', "timezone" character varying(50) NOT NULL DEFAULT 'Europe/Moscow', "is_email_verified" boolean NOT NULL DEFAULT false, "is_phone_verified" boolean NOT NULL DEFAULT false, "is_active" boolean NOT NULL DEFAULT true, "failed_login_attempts" integer NOT NULL DEFAULT 0, "locked_until" TIMESTAMP, "last_login_at" TIMESTAMP, "monthly_income" numeric(15,2) NOT NULL DEFAULT 0, "financial_goals" jsonb, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), "deleted_at" TIMESTAMP, CONSTRAINT "UQ_users_email" UNIQUE ("email"), CONSTRAINT "UQ_users_phone" UNIQUE ("phone"), CONSTRAINT "PK_users" PRIMARY KEY ("id") ) `); await queryRunner.query( `CREATE INDEX "IDX_users_email" ON "users" ("email")`, ); await queryRunner.query( `CREATE INDEX "IDX_users_phone" ON "users" ("phone")`, ); await queryRunner.query(` CREATE TABLE "refresh_tokens" ( "id" uuid NOT NULL DEFAULT uuid_generate_v4(), "user_id" uuid NOT NULL, "token_hash" character varying(255) NOT NULL, "user_agent" text, "ip_address" inet, "expires_at" TIMESTAMP NOT NULL, "is_revoked" boolean NOT NULL DEFAULT false, "replaced_by_token_hash" character varying(255), "created_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_refresh_tokens" PRIMARY KEY ("id"), CONSTRAINT "FK_refresh_tokens_user" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ) `); await queryRunner.query( `CREATE INDEX "IDX_refresh_tokens_user_id" ON "refresh_tokens" ("user_id")`, ); await queryRunner.query( `CREATE INDEX "IDX_refresh_tokens_expires_at" ON "refresh_tokens" ("expires_at")`, ); await queryRunner.query(` CREATE TABLE "audit_logs" ( "id" uuid NOT NULL DEFAULT uuid_generate_v4(), "user_id" uuid, "action" character varying(50) NOT NULL, "entity_type" character varying(50) NOT NULL, "entity_id" uuid, "old_values" jsonb, "new_values" jsonb, "ip_address" inet, "user_agent" text, "created_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_audit_logs" PRIMARY KEY ("id"), CONSTRAINT "FK_audit_logs_user" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE SET NULL ) `); await queryRunner.query( `CREATE INDEX "IDX_audit_logs_user_id" ON "audit_logs" ("user_id")`, ); await queryRunner.query( `CREATE INDEX "IDX_audit_logs_created_at" ON "audit_logs" ("created_at")`, ); await queryRunner.query(` CREATE TABLE "categories" ( "id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name_ru" character varying(100) NOT NULL, "name_en" character varying(100), "type" "public"."transaction_type_enum" NOT NULL, "group_type" "public"."budget_group_type_enum", "icon" character varying(50), "color" character varying(7), "is_default" boolean NOT NULL DEFAULT true, "user_id" uuid, "parent_id" uuid, CONSTRAINT "PK_categories" PRIMARY KEY ("id"), CONSTRAINT "FK_categories_user" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE, CONSTRAINT "FK_categories_parent" FOREIGN KEY ("parent_id") REFERENCES "categories"("id") ON DELETE CASCADE ) `); await queryRunner.query( `CREATE INDEX "IDX_categories_type" ON "categories" ("type")`, ); await queryRunner.query( `CREATE INDEX "IDX_categories_user_id" ON "categories" ("user_id")`, ); await queryRunner.query(` CREATE TABLE "transactions" ( "id" uuid NOT NULL DEFAULT uuid_generate_v4(), "user_id" uuid NOT NULL, "amount" numeric(15,2) NOT NULL, "currency" character varying(3) NOT NULL DEFAULT 'RUB', "type" "public"."transaction_type_enum" NOT NULL, "category_id" uuid, "description" text, "transaction_date" date NOT NULL, "payment_method" "public"."payment_method_enum", "receipt_url" character varying(500), "receipt_processed" boolean NOT NULL DEFAULT false, "created_by" uuid, "updated_by" uuid, "is_recurring" boolean NOT NULL DEFAULT false, "recurring_pattern" jsonb, "is_planned" boolean NOT NULL DEFAULT false, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), "deleted_at" TIMESTAMP, CONSTRAINT "CHK_transactions_amount" CHECK ("amount" > 0), CONSTRAINT "PK_transactions" PRIMARY KEY ("id"), CONSTRAINT "FK_transactions_user" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE, CONSTRAINT "FK_transactions_category" FOREIGN KEY ("category_id") REFERENCES "categories"("id") ON DELETE SET NULL ) `); await queryRunner.query( `CREATE INDEX "IDX_transactions_user_id" ON "transactions" ("user_id")`, ); await queryRunner.query( `CREATE INDEX "IDX_transactions_type" ON "transactions" ("type")`, ); await queryRunner.query( `CREATE INDEX "IDX_transactions_category_id" ON "transactions" ("category_id")`, ); await queryRunner.query( `CREATE INDEX "IDX_transactions_transaction_date" ON "transactions" ("transaction_date")`, ); await queryRunner.query(` CREATE TABLE "budgets" ( "id" uuid NOT NULL DEFAULT uuid_generate_v4(), "user_id" uuid NOT NULL, "month" date NOT NULL, "total_income" numeric(15,2) NOT NULL DEFAULT 0, "essentials_limit" numeric(15,2) NOT NULL DEFAULT 0, "essentials_spent" numeric(15,2) NOT NULL DEFAULT 0, "personal_limit" numeric(15,2) NOT NULL DEFAULT 0, "personal_spent" numeric(15,2) NOT NULL DEFAULT 0, "savings_limit" numeric(15,2) NOT NULL DEFAULT 0, "savings_spent" numeric(15,2) NOT NULL DEFAULT 0, "custom_allocations" jsonb, "is_active" boolean NOT NULL DEFAULT true, CONSTRAINT "UQ_budgets_user_month" UNIQUE ("user_id", "month"), CONSTRAINT "PK_budgets" PRIMARY KEY ("id"), CONSTRAINT "FK_budgets_user" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ) `); await queryRunner.query( `CREATE INDEX "IDX_budgets_user_id" ON "budgets" ("user_id")`, ); await queryRunner.query( `CREATE INDEX "IDX_budgets_month" ON "budgets" ("month")`, ); await queryRunner.query(` CREATE TABLE "goals" ( "id" uuid NOT NULL DEFAULT uuid_generate_v4(), "user_id" uuid NOT NULL, "title_ru" character varying(200) NOT NULL, "description_ru" text, "target_amount" numeric(15,2) NOT NULL, "current_amount" numeric(15,2) NOT NULL DEFAULT 0, "currency" character varying(3) NOT NULL DEFAULT 'RUB', "target_date" date, "status" "public"."goal_status_enum" NOT NULL DEFAULT 'ACTIVE', "priority" "public"."goal_priority_enum" NOT NULL DEFAULT 'MEDIUM', "icon" character varying(50), "color" character varying(7), "auto_save_enabled" boolean NOT NULL DEFAULT false, "auto_save_amount" numeric(15,2), "auto_save_frequency" "public"."goal_auto_save_frequency_enum", "created_at" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), "completed_at" TIMESTAMP, CONSTRAINT "PK_goals" PRIMARY KEY ("id"), CONSTRAINT "FK_goals_user" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ) `); await queryRunner.query( `CREATE INDEX "IDX_goals_user_id" ON "goals" ("user_id")`, ); await queryRunner.query(` CREATE TABLE "recommendations" ( "id" uuid NOT NULL DEFAULT uuid_generate_v4(), "user_id" uuid NOT NULL, "type" "public"."recommendation_type_enum" NOT NULL, "title_ru" character varying(200) NOT NULL, "description_ru" text NOT NULL, "action_text_ru" character varying(200), "priority_score" numeric(3,2) NOT NULL DEFAULT 0.5, "confidence_score" numeric(3,2) NOT NULL DEFAULT 0.5, "potential_savings" numeric(15,2), "status" "public"."recommendation_status_enum" NOT NULL DEFAULT 'NEW', "action_data" jsonb, "expires_at" TIMESTAMP, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "viewed_at" TIMESTAMP, "applied_at" TIMESTAMP, CONSTRAINT "PK_recommendations" PRIMARY KEY ("id"), CONSTRAINT "FK_recommendations_user" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ) `); await queryRunner.query( `CREATE INDEX "IDX_recommendations_user_id" ON "recommendations" ("user_id")`, ); } public async down(queryRunner: QueryRunner): Promise { await queryRunner.query(`DROP TABLE IF EXISTS "recommendations"`); await queryRunner.query(`DROP TABLE IF EXISTS "goals"`); await queryRunner.query(`DROP TABLE IF EXISTS "budgets"`); await queryRunner.query(`DROP TABLE IF EXISTS "transactions"`); await queryRunner.query(`DROP TABLE IF EXISTS "categories"`); await queryRunner.query(`DROP TABLE IF EXISTS "audit_logs"`); await queryRunner.query(`DROP TABLE IF EXISTS "refresh_tokens"`); await queryRunner.query(`DROP TABLE IF EXISTS "users"`); await queryRunner.query( `DROP TYPE IF EXISTS "public"."recommendation_status_enum"`, ); await queryRunner.query( `DROP TYPE IF EXISTS "public"."recommendation_type_enum"`, ); await queryRunner.query( `DROP TYPE IF EXISTS "public"."goal_auto_save_frequency_enum"`, ); await queryRunner.query( `DROP TYPE IF EXISTS "public"."goal_priority_enum"`, ); await queryRunner.query(`DROP TYPE IF EXISTS "public"."goal_status_enum"`); await queryRunner.query( `DROP TYPE IF EXISTS "public"."payment_method_enum"`, ); await queryRunner.query( `DROP TYPE IF EXISTS "public"."budget_group_type_enum"`, ); await queryRunner.query( `DROP TYPE IF EXISTS "public"."transaction_type_enum"`, ); } }