diff --git a/services/121-service/src/migration/1719232602304-program-registration-attribute.ts b/services/121-service/src/migration/1719232602304-program-registration-attribute.ts deleted file mode 100644 index 28cc574e34..0000000000 --- a/services/121-service/src/migration/1719232602304-program-registration-attribute.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class ProgramRegistrationAttribute1719232602304 - implements MigrationInterface -{ - name = 'ProgramRegistrationAttribute1719232602304'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "121-service"."program_registration_attribute" ("id" SERIAL NOT NULL, "created" TIMESTAMP NOT NULL DEFAULT now(), "updated" TIMESTAMP NOT NULL DEFAULT now(), "name" character varying NOT NULL, "label" json NOT NULL, "type" character varying NOT NULL, "isRequired" boolean NOT NULL, "placeholder" json, "options" json, "scoring" json NOT NULL DEFAULT '{}', "programId" integer NOT NULL, "export" json NOT NULL DEFAULT '["all-people-affected","included"]', "pattern" character varying, "duplicateCheck" boolean NOT NULL DEFAULT false, "showInPeopleAffectedTable" boolean NOT NULL DEFAULT false, "editableInPortal" boolean NOT NULL DEFAULT false, CONSTRAINT "programAttributeUnique" UNIQUE ("name", "programId"), CONSTRAINT "CHK_88f5ede846c87b3059ed09f967" CHECK ("name" NOT IN ('id', 'status', 'referenceId', 'preferredLanguage', 'inclusionScore', 'paymentAmountMultiplier', 'financialServiceProvider', 'registrationProgramId', 'maxPayments', 'lastTransactionCreated', 'lastTransactionPaymentNumber', 'lastTransactionStatus', 'lastTransactionAmount', 'lastTransactionErrorMessage', 'lastTransactionCustomData', 'paymentCount', 'paymentCountRemaining', 'registeredDate', 'validationDate', 'inclusionDate', 'deleteDate', 'completedDate', 'lastMessageStatus', 'lastMessageType', 'declinedDate')), CONSTRAINT "PK_b85642d2f95cc2fcc6145e14463" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `CREATE INDEX "IDX_1387f030d9f04f7d80c78a60d5" ON "121-service"."program_registration_attribute" ("created") `, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."registration_data" ADD "programRegistrationAttributeId" integer`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."program_registration_attribute" ADD CONSTRAINT "FK_8788ebf12909c03049a0d8c377d" FOREIGN KEY ("programId") REFERENCES "121-service"."program"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."registration_data" ADD CONSTRAINT "FK_cf696382c2af3359aedf7b52c59" FOREIGN KEY ("programRegistrationAttributeId") REFERENCES "121-service"."program_registration_attribute"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM "121-service"."typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ['VIEW', 'registration_view', '121-service'], - ); - await queryRunner.query( - `ALTER TABLE "121-service"."registration_data" DROP CONSTRAINT "FK_cf696382c2af3359aedf7b52c59"`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."program_registration_attribute" DROP CONSTRAINT "FK_8788ebf12909c03049a0d8c377d"`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."registration_data" DROP COLUMN "programRegistrationAttributeId"`, - ); - await queryRunner.query( - `DROP INDEX "121-service"."IDX_1387f030d9f04f7d80c78a60d5"`, - ); - await queryRunner.query( - `DROP TABLE "121-service"."program_registration_attribute"`, - ); - } -} diff --git a/services/121-service/src/migration/1721399866177-migrate-data-to-program-registration-attribute-table.ts b/services/121-service/src/migration/1721399866177-migrate-data-to-program-registration-attribute-table.ts deleted file mode 100644 index 1e1569b50f..0000000000 --- a/services/121-service/src/migration/1721399866177-migrate-data-to-program-registration-attribute-table.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class MigrateDataToProgramRegistrationAttributeTable1721399866177 - implements MigrationInterface -{ - name = 'MigrateDataToProgramRegistrationAttributeTable1721399866177'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.startTransaction(); - try { - await queryRunner.query(` - INSERT INTO "121-service".program_registration_attribute ( - created, - updated, - name, - label, - type, - "isRequired", - placeholder, - options, - scoring, - "programId", - export, - pattern, - "duplicateCheck", - "showInPeopleAffectedTable", - "editableInPortal" - ) - SELECT - created, - updated, - name, - label, - type, - false AS "isRequired", -- Set a default value as it's not present in the old table - NULL::json AS placeholder, -- Set to NULL as it's not present in the old table - NULL::json AS options, -- Set to NULL as it's not present in the old table - '{}'::json AS scoring, -- Set default empty JSON as specified in the new table - "programId", - '["all-people-affected","included"]'::json AS export, -- Default value as specified in the new table - NULL::character varying AS pattern, -- Set to NULL as it's not present in the old table - "duplicateCheck", - "showInPeopleAffectedTable", - false AS "editableInPortal" -- Set a default value as it's not present in the old table - FROM - "121-service".program_custom_attribute`); - - await queryRunner.query(` - INSERT INTO "121-service".program_registration_attribute ( - created, - updated, - name, - label, - type, - "isRequired", - placeholder, - options, - scoring, - "programId", - export, - pattern, - "duplicateCheck", - "showInPeopleAffectedTable", - "editableInPortal" - ) - SELECT - fspq.created, - fspq.updated, - fspq.name, - fspq.label, - fspq."answerType", - false AS "isRequired", -- Set a default value as it's not present in the old table - fspq.placeholder, - fspq.options, - '{}'::json AS scoring, -- Set default empty JSON as specified in the new table - pfsp."programId", - fspq.export, - fspq.pattern, - fspq."duplicateCheck", - fspq."showInPeopleAffectedTable", - false AS "editableInPortal" -- Set a default value as it's not present in the old table - FROM - "121-service".financial_service_provider_question fspq - JOIN - "121-service".program_financial_service_providers_financial_service_provider pfsp - ON - fspq."fspId" = pfsp."financialServiceProviderId" - ON - CONFLICT (name, "programId") DO NOTHING;`); - await queryRunner.commitTransaction(); - } catch (err) { - await queryRunner.rollbackTransaction(); - throw err; - } - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.startTransaction(); - try { - await queryRunner.query( - `TRUNCATE "121-service".program_registration_attribute RESTART IDENTITY CASCADE`, - ); - await queryRunner.commitTransaction(); - } catch (err) { - await queryRunner.rollbackTransaction(); - throw err; - } - } -} diff --git a/services/121-service/src/migration/1729241929169-fincancial-service-provider-configuration-refactor.ts b/services/121-service/src/migration/1729241929169-fincancial-service-provider-configuration-refactor.ts deleted file mode 100644 index 8f52cee1d9..0000000000 --- a/services/121-service/src/migration/1729241929169-fincancial-service-provider-configuration-refactor.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class FincancialServiceProviderConfigurationRefactor1729241929169 - implements MigrationInterface -{ - name = 'FincancialServiceProviderConfigurationRefactor1729241929169'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM "121-service"."typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ['VIEW', 'registration_view', '121-service'], - ); - await queryRunner.query(`DROP VIEW "121-service"."registration_view"`); - await queryRunner.query( - `ALTER TABLE "121-service"."transaction" DROP CONSTRAINT "FK_ba98ea5ca43ebe54f60c5aaabec"`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."registration" DROP CONSTRAINT "FK_9e5a5ef99940e591cad5b25a345"`, - ); - await queryRunner.query( - `DROP INDEX "121-service"."IDX_ba98ea5ca43ebe54f60c5aaabe"`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."registration" RENAME COLUMN "fspId" TO "programFinancialServiceProviderConfigurationId"`, - ); - await queryRunner.query( - `CREATE TABLE "121-service"."program_financial_service_provider_configuration_property" ("id" SERIAL NOT NULL, "created" TIMESTAMP NOT NULL DEFAULT now(), "updated" TIMESTAMP NOT NULL DEFAULT now(), "name" character varying NOT NULL, "value" character varying NOT NULL, "programFinancialServiceProviderConfigurationId" integer NOT NULL, CONSTRAINT "programFinancialServiceProviderConfigurationPropertyUnique" UNIQUE ("programFinancialServiceProviderConfigurationId", "name"), CONSTRAINT "PK_01dfd2b0e5d93a8cf4090254cfc" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `CREATE INDEX "IDX_2ea95dd85e592bad75d0278873" ON "121-service"."program_financial_service_provider_configuration_property" ("created") `, - ); - await queryRunner.query( - `CREATE TABLE "121-service"."program_financial_service_provider_configuration" ("id" SERIAL NOT NULL, "created" TIMESTAMP NOT NULL DEFAULT now(), "updated" TIMESTAMP NOT NULL DEFAULT now(), "programId" integer NOT NULL, "financialServiceProviderName" character varying NOT NULL, "name" character varying NOT NULL, "label" json NOT NULL, CONSTRAINT "programFinancialServiceProviderConfigurationUnique" UNIQUE ("programId", "name"), CONSTRAINT "PK_bc2d4d99fa94cb01d4566acdffc" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `CREATE INDEX "IDX_04aac36fce58b33d30d71b700f" ON "121-service"."program_financial_service_provider_configuration" ("created") `, - ); - await queryRunner.query( - `CREATE TABLE "121-service"."registration_attribute_data" ("id" SERIAL NOT NULL, "created" TIMESTAMP NOT NULL DEFAULT now(), "updated" TIMESTAMP NOT NULL DEFAULT now(), "registrationId" integer NOT NULL, "programRegistrationAttributeId" integer, "value" character varying NOT NULL, CONSTRAINT "registrationProgramAttributeUnique" UNIQUE ("registrationId", "programRegistrationAttributeId"), CONSTRAINT "PK_bef7662581d64d69db3f6405411" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `CREATE INDEX "IDX_29cd6ac9bf4002df266d0ba23e" ON "121-service"."registration_attribute_data" ("created") `, - ); - await queryRunner.query( - `CREATE INDEX "IDX_8914b71c0e30c44291ab68a9b8" ON "121-service"."registration_attribute_data" ("registrationId") `, - ); - await queryRunner.query( - `CREATE INDEX "IDX_3037018a626cd41bd16c588170" ON "121-service"."registration_attribute_data" ("value") `, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."transaction" DROP COLUMN "financialServiceProviderId"`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."transaction" ADD "programFinancialServiceProviderConfigurationId" integer NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."transaction" ADD "programFinancialServiceProviderConfigurationConfigurationId" integer`, - ); - await queryRunner.query( - `CREATE INDEX "IDX_d8a56a1864ef40e1551833430b" ON "121-service"."transaction" ("programFinancialServiceProviderConfigurationId") `, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."program_financial_service_provider_configuration_property" ADD CONSTRAINT "FK_5e40569627925419cd94db0da36" FOREIGN KEY ("programFinancialServiceProviderConfigurationId") REFERENCES "121-service"."program_financial_service_provider_configuration"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."program_financial_service_provider_configuration" ADD CONSTRAINT "FK_f7400125e09c4d8fec5747ec588" FOREIGN KEY ("programId") REFERENCES "121-service"."program"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."transaction" ADD CONSTRAINT "FK_c4308a455ed658828fab537bb38" FOREIGN KEY ("programFinancialServiceProviderConfigurationConfigurationId") REFERENCES "121-service"."program_financial_service_provider_configuration"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."registration_attribute_data" ADD CONSTRAINT "FK_8914b71c0e30c44291ab68a9b8a" FOREIGN KEY ("registrationId") REFERENCES "121-service"."registration"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."registration_attribute_data" ADD CONSTRAINT "FK_3bd62b57d06901bcd85e28fd060" FOREIGN KEY ("programRegistrationAttributeId") REFERENCES "121-service"."program_registration_attribute"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."registration" ADD CONSTRAINT "FK_148b6bb5c37ca2d444b01c00c2f" FOREIGN KEY ("programFinancialServiceProviderConfigurationId") REFERENCES "121-service"."program_financial_service_provider_configuration"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `CREATE VIEW "121-service"."registration_view" AS SELECT "registration"."id" AS "id", "registration"."created" AS "registrationCreated", "registration"."programId" AS "programId", "registration"."registrationStatus" AS "status", "registration"."referenceId" AS "referenceId", "registration"."phoneNumber" AS "phoneNumber", "registration"."preferredLanguage" AS "preferredLanguage", "registration"."inclusionScore" AS "inclusionScore", "registration"."paymentAmountMultiplier" AS "paymentAmountMultiplier", "registration"."maxPayments" AS "maxPayments", "registration"."paymentCount" AS "paymentCount", "registration"."scope" AS "scope", "fspconfig"."label" AS "fspDisplayName", CAST(CONCAT('PA #',registration."registrationProgramId") as VARCHAR) AS "personAffectedSequence", registration."registrationProgramId" AS "registrationProgramId", TO_CHAR("registration"."created",'yyyy-mm-dd') AS "registrationCreatedDate", fspconfig."name" AS "programFinancialServiceProviderConfigurationName", fspconfig."id" AS "programFinancialServiceProviderConfigurationId", fspconfig."financialServiceProviderName" AS "financialServiceProviderName", "registration"."maxPayments" - "registration"."paymentCount" AS "paymentCountRemaining", COALESCE("message"."type" || ': ' || "message"."status",'no messages yet') AS "lastMessageStatus" FROM "121-service"."registration" "registration" LEFT JOIN "121-service"."program_financial_service_provider_configuration" "fspconfig" ON "fspconfig"."id"="registration"."programFinancialServiceProviderConfigurationId" LEFT JOIN "121-service"."latest_message" "latestMessage" ON "latestMessage"."registrationId"="registration"."id" LEFT JOIN "121-service"."twilio_message" "message" ON "message"."id"="latestMessage"."messageId" ORDER BY "registration"."registrationProgramId" ASC`, - ); - await queryRunner.query( - `INSERT INTO "121-service"."typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - '121-service', - 'VIEW', - 'registration_view', - 'SELECT "registration"."id" AS "id", "registration"."created" AS "registrationCreated", "registration"."programId" AS "programId", "registration"."registrationStatus" AS "status", "registration"."referenceId" AS "referenceId", "registration"."phoneNumber" AS "phoneNumber", "registration"."preferredLanguage" AS "preferredLanguage", "registration"."inclusionScore" AS "inclusionScore", "registration"."paymentAmountMultiplier" AS "paymentAmountMultiplier", "registration"."maxPayments" AS "maxPayments", "registration"."paymentCount" AS "paymentCount", "registration"."scope" AS "scope", "fspconfig"."label" AS "fspDisplayName", CAST(CONCAT(\'PA #\',registration."registrationProgramId") as VARCHAR) AS "personAffectedSequence", registration."registrationProgramId" AS "registrationProgramId", TO_CHAR("registration"."created",\'yyyy-mm-dd\') AS "registrationCreatedDate", fspconfig."name" AS "programFinancialServiceProviderConfigurationName", fspconfig."id" AS "programFinancialServiceProviderConfigurationId", fspconfig."financialServiceProviderName" AS "financialServiceProviderName", "registration"."maxPayments" - "registration"."paymentCount" AS "paymentCountRemaining", COALESCE("message"."type" || \': \' || "message"."status",\'no messages yet\') AS "lastMessageStatus" FROM "121-service"."registration" "registration" LEFT JOIN "121-service"."program_financial_service_provider_configuration" "fspconfig" ON "fspconfig"."id"="registration"."programFinancialServiceProviderConfigurationId" LEFT JOIN "121-service"."latest_message" "latestMessage" ON "latestMessage"."registrationId"="registration"."id" LEFT JOIN "121-service"."twilio_message" "message" ON "message"."id"="latestMessage"."messageId" ORDER BY "registration"."registrationProgramId" ASC', - ], - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM "121-service"."typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ['VIEW', 'registration_view', '121-service'], - ); - await queryRunner.query(`DROP VIEW "121-service"."registration_view"`); - await queryRunner.query( - `ALTER TABLE "121-service"."registration" DROP CONSTRAINT "FK_148b6bb5c37ca2d444b01c00c2f"`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."registration_attribute_data" DROP CONSTRAINT "FK_3bd62b57d06901bcd85e28fd060"`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."registration_attribute_data" DROP CONSTRAINT "FK_8914b71c0e30c44291ab68a9b8a"`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."transaction" DROP CONSTRAINT "FK_c4308a455ed658828fab537bb38"`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."program_financial_service_provider_configuration" DROP CONSTRAINT "FK_f7400125e09c4d8fec5747ec588"`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."program_financial_service_provider_configuration_property" DROP CONSTRAINT "FK_5e40569627925419cd94db0da36"`, - ); - await queryRunner.query( - `DROP INDEX "121-service"."IDX_d8a56a1864ef40e1551833430b"`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."transaction" DROP COLUMN "programFinancialServiceProviderConfigurationConfigurationId"`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."transaction" DROP COLUMN "programFinancialServiceProviderConfigurationId"`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."transaction" ADD "financialServiceProviderId" integer NOT NULL`, - ); - await queryRunner.query( - `DROP INDEX "121-service"."IDX_3037018a626cd41bd16c588170"`, - ); - await queryRunner.query( - `DROP INDEX "121-service"."IDX_8914b71c0e30c44291ab68a9b8"`, - ); - await queryRunner.query( - `DROP INDEX "121-service"."IDX_29cd6ac9bf4002df266d0ba23e"`, - ); - await queryRunner.query( - `DROP TABLE "121-service"."registration_attribute_data"`, - ); - await queryRunner.query( - `DROP INDEX "121-service"."IDX_04aac36fce58b33d30d71b700f"`, - ); - await queryRunner.query( - `DROP TABLE "121-service"."program_financial_service_provider_configuration"`, - ); - await queryRunner.query( - `DROP INDEX "121-service"."IDX_2ea95dd85e592bad75d0278873"`, - ); - await queryRunner.query( - `DROP TABLE "121-service"."program_financial_service_provider_configuration_property"`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."registration" RENAME COLUMN "programFinancialServiceProviderConfigurationId" TO "fspId"`, - ); - await queryRunner.query( - `CREATE INDEX "IDX_ba98ea5ca43ebe54f60c5aaabe" ON "121-service"."transaction" ("financialServiceProviderId") `, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."registration" ADD CONSTRAINT "FK_9e5a5ef99940e591cad5b25a345" FOREIGN KEY ("fspId") REFERENCES "121-service"."financial_service_provider"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "121-service"."transaction" ADD CONSTRAINT "FK_ba98ea5ca43ebe54f60c5aaabec" FOREIGN KEY ("financialServiceProviderId") REFERENCES "121-service"."financial_service_provider"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `CREATE VIEW "121-service"."registration_view" AS SELECT "registration"."id" AS "id", "registration"."created" AS "registrationCreated", "registration"."programId" AS "programId", "registration"."registrationStatus" AS "status", "registration"."referenceId" AS "referenceId", "registration"."phoneNumber" AS "phoneNumber", "registration"."preferredLanguage" AS "preferredLanguage", "registration"."inclusionScore" AS "inclusionScore", "registration"."paymentAmountMultiplier" AS "paymentAmountMultiplier", "registration"."maxPayments" AS "maxPayments", "registration"."paymentCount" AS "paymentCount", "registration"."scope" AS "scope", "fsp"."fsp" AS "financialServiceProvider", "fsp"."displayName" AS "fspDisplayName", CAST(CONCAT('PA #',registration."registrationProgramId") as VARCHAR) AS "personAffectedSequence", registration."registrationProgramId" AS "registrationProgramId", TO_CHAR("registration"."created",'yyyy-mm-dd') AS "registrationCreatedDate", "registration"."maxPayments" - "registration"."paymentCount" AS "paymentCountRemaining", COALESCE("message"."type" || ': ' || "message"."status",'no messages yet') AS "lastMessageStatus" FROM "121-service"."registration" "registration" LEFT JOIN "121-service"."financial_service_provider" "fsp" ON "fsp"."id"="registration"."fspId" LEFT JOIN "121-service"."latest_message" "latestMessage" ON "latestMessage"."registrationId"="registration"."id" LEFT JOIN "121-service"."twilio_message" "message" ON "message"."id"="latestMessage"."messageId" ORDER BY "registration"."registrationProgramId" ASC`, - ); - await queryRunner.query( - `INSERT INTO "121-service"."typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - '121-service', - 'VIEW', - 'registration_view', - 'SELECT "registration"."id" AS "id", "registration"."created" AS "registrationCreated", "registration"."programId" AS "programId", "registration"."registrationStatus" AS "status", "registration"."referenceId" AS "referenceId", "registration"."phoneNumber" AS "phoneNumber", "registration"."preferredLanguage" AS "preferredLanguage", "registration"."inclusionScore" AS "inclusionScore", "registration"."paymentAmountMultiplier" AS "paymentAmountMultiplier", "registration"."maxPayments" AS "maxPayments", "registration"."paymentCount" AS "paymentCount", "registration"."scope" AS "scope", "fsp"."fsp" AS "financialServiceProvider", "fsp"."displayName" AS "fspDisplayName", CAST(CONCAT(\'PA #\',registration."registrationProgramId") as VARCHAR) AS "personAffectedSequence", registration."registrationProgramId" AS "registrationProgramId", TO_CHAR("registration"."created",\'yyyy-mm-dd\') AS "registrationCreatedDate", "registration"."maxPayments" - "registration"."paymentCount" AS "paymentCountRemaining", COALESCE("message"."type" || \': \' || "message"."status",\'no messages yet\') AS "lastMessageStatus" FROM "121-service"."registration" "registration" LEFT JOIN "121-service"."financial_service_provider" "fsp" ON "fsp"."id"="registration"."fspId" LEFT JOIN "121-service"."latest_message" "latestMessage" ON "latestMessage"."registrationId"="registration"."id" LEFT JOIN "121-service"."twilio_message" "message" ON "message"."id"="latestMessage"."messageId" ORDER BY "registration"."registrationProgramId" ASC', - ], - ); - } -} diff --git a/services/121-service/src/migration/1729241929170-rename-fspDisplayName.ts b/services/121-service/src/migration/1729241929170-rename-fspDisplayName.ts deleted file mode 100644 index 8044eb6480..0000000000 --- a/services/121-service/src/migration/1729241929170-rename-fspDisplayName.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameFspDisplayName1729241929170 implements MigrationInterface { - name = 'RenameFspDisplayName1729241929170'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP VIEW "121-service"."registration_view"`); - await queryRunner.query( - `CREATE VIEW "121-service"."registration_view" AS SELECT "registration"."id" AS "id", "registration"."created" AS "registrationCreated", "registration"."programId" AS "programId", "registration"."registrationStatus" AS "status", "registration"."referenceId" AS "referenceId", "registration"."phoneNumber" AS "phoneNumber", "registration"."preferredLanguage" AS "preferredLanguage", "registration"."inclusionScore" AS "inclusionScore", "registration"."paymentAmountMultiplier" AS "paymentAmountMultiplier", "registration"."maxPayments" AS "maxPayments", "registration"."paymentCount" AS "paymentCount", "registration"."scope" AS "scope", "fspconfig"."label" AS "programFinancialServiceProviderConfigurationLabel", CAST(CONCAT('PA #',registration."registrationProgramId") as VARCHAR) AS "personAffectedSequence", registration."registrationProgramId" AS "registrationProgramId", TO_CHAR("registration"."created",'yyyy-mm-dd') AS "registrationCreatedDate", fspconfig."name" AS "programFinancialServiceProviderConfigurationName", fspconfig."id" AS "programFinancialServiceProviderConfigurationId", fspconfig."financialServiceProviderName" AS "financialServiceProviderName", "registration"."maxPayments" - "registration"."paymentCount" AS "paymentCountRemaining", COALESCE("message"."type" || ': ' || "message"."status",'no messages yet') AS "lastMessageStatus" FROM "121-service"."registration" "registration" LEFT JOIN "121-service"."program_financial_service_provider_configuration" "fspconfig" ON "fspconfig"."id"="registration"."programFinancialServiceProviderConfigurationId" LEFT JOIN "121-service"."latest_message" "latestMessage" ON "latestMessage"."registrationId"="registration"."id" LEFT JOIN "121-service"."twilio_message" "message" ON "message"."id"="latestMessage"."messageId" ORDER BY "registration"."registrationProgramId" ASC`, - ); - await queryRunner.query( - `INSERT INTO "121-service"."typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - '121-service', - 'VIEW', - 'registration_view', - 'SELECT "registration"."id" AS "id", "registration"."created" AS "registrationCreated", "registration"."programId" AS "programId", "registration"."registrationStatus" AS "status", "registration"."referenceId" AS "referenceId", "registration"."phoneNumber" AS "phoneNumber", "registration"."preferredLanguage" AS "preferredLanguage", "registration"."inclusionScore" AS "inclusionScore", "registration"."paymentAmountMultiplier" AS "paymentAmountMultiplier", "registration"."maxPayments" AS "maxPayments", "registration"."paymentCount" AS "paymentCount", "registration"."scope" AS "scope", "fspconfig"."label" AS "programFinancialServiceProviderConfigurationLabel", CAST(CONCAT(\'PA #\',registration."registrationProgramId") as VARCHAR) AS "personAffectedSequence", registration."registrationProgramId" AS "registrationProgramId", TO_CHAR("registration"."created",\'yyyy-mm-dd\') AS "registrationCreatedDate", fspconfig."name" AS "programFinancialServiceProviderConfigurationName", fspconfig."id" AS "programFinancialServiceProviderConfigurationId", fspconfig."financialServiceProviderName" AS "financialServiceProviderName", "registration"."maxPayments" - "registration"."paymentCount" AS "paymentCountRemaining", COALESCE("message"."type" || \': \' || "message"."status",\'no messages yet\') AS "lastMessageStatus" FROM "121-service"."registration" "registration" LEFT JOIN "121-service"."program_financial_service_provider_configuration" "fspconfig" ON "fspconfig"."id"="registration"."programFinancialServiceProviderConfigurationId" LEFT JOIN "121-service"."latest_message" "latestMessage" ON "latestMessage"."registrationId"="registration"."id" LEFT JOIN "121-service"."twilio_message" "message" ON "message"."id"="latestMessage"."messageId" ORDER BY "registration"."registrationProgramId" ASC', - ], - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM "121-service"."typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ['VIEW', 'registration_view', '121-service'], - ); - await queryRunner.query(`DROP VIEW "121-service"."registration_view"`); - } -} diff --git a/services/121-service/src/migration/1729605362361-program-registration-attribute-refactor.ts b/services/121-service/src/migration/1729605362361-program-registration-attribute-refactor.ts new file mode 100644 index 0000000000..687c235a3d --- /dev/null +++ b/services/121-service/src/migration/1729605362361-program-registration-attribute-refactor.ts @@ -0,0 +1,581 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ProgramRegistrationAttributeRefactor1729605362361 + implements MigrationInterface +{ + name = 'ProgramRegistrationAttributeRefactor1729605362361'; + + public async up(queryRunner: QueryRunner): Promise { + console.time('Migration'); + await this.createNewTablesAndViews(queryRunner); + + await this.migrateFspConig(queryRunner); + await this.migrateQuestionsToProgramRegistrationAttributes(queryRunner); + await this.addConstraints(queryRunner); + + await this.checkRegistrationFspConfigMigrations(queryRunner); + await this.checkTransactionFspConfigMigrations(queryRunner); + + await this.dropOldTablesAndViews(queryRunner); + console.timeEnd('Migration'); + // throw new Error('You shall not pass! Use this to prevent the migration from passing'); + } + private async createNewTablesAndViews( + queryRunner: QueryRunner, + ): Promise { + await queryRunner.query( + `ALTER TABLE "121-service"."registration" DROP CONSTRAINT "FK_9e5a5ef99940e591cad5b25a345"`, + ); + await queryRunner.query( + `ALTER TABLE "121-service"."transaction" DROP CONSTRAINT "FK_ba98ea5ca43ebe54f60c5aaabec"`, + ); + await queryRunner.query( + `DROP INDEX "121-service"."IDX_ba98ea5ca43ebe54f60c5aaabe"`, + ); + await queryRunner.query( + `ALTER TABLE "121-service"."registration" ADD COLUMN "programFinancialServiceProviderConfigurationId" INTEGER`, + ); + await queryRunner.query( + `ALTER TABLE "121-service"."transaction" ADD COLUMN "programFinancialServiceProviderConfigurationId" INTEGER`, + ); + + await queryRunner.query( + `CREATE TABLE "121-service"."program_financial_service_provider_configuration_property" ("id" SERIAL NOT NULL, "created" TIMESTAMP NOT NULL DEFAULT now(), "updated" TIMESTAMP NOT NULL DEFAULT now(), "name" character varying NOT NULL, "value" character varying NOT NULL, "programFinancialServiceProviderConfigurationId" integer NOT NULL, CONSTRAINT "programFinancialServiceProviderConfigurationPropertyUnique" UNIQUE ("programFinancialServiceProviderConfigurationId", "name"), CONSTRAINT "PK_01dfd2b0e5d93a8cf4090254cfc" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_2ea95dd85e592bad75d0278873" ON "121-service"."program_financial_service_provider_configuration_property" ("created") `, + ); + await queryRunner.query( + `CREATE TABLE "121-service"."program_financial_service_provider_configuration" ("id" SERIAL NOT NULL, "created" TIMESTAMP NOT NULL DEFAULT now(), "updated" TIMESTAMP NOT NULL DEFAULT now(), "programId" integer NOT NULL, "financialServiceProviderName" character varying NOT NULL, "name" character varying NOT NULL, "label" json NOT NULL, CONSTRAINT "programFinancialServiceProviderConfigurationUnique" UNIQUE ("programId", "name"), CONSTRAINT "PK_bc2d4d99fa94cb01d4566acdffc" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_04aac36fce58b33d30d71b700f" ON "121-service"."program_financial_service_provider_configuration" ("created") `, + ); + await queryRunner.query( + `CREATE TABLE "121-service"."program_registration_attribute" ("id" SERIAL NOT NULL, "created" TIMESTAMP NOT NULL DEFAULT now(), "updated" TIMESTAMP NOT NULL DEFAULT now(), "name" character varying NOT NULL, "label" json NOT NULL, "type" character varying NOT NULL, "isRequired" boolean NOT NULL, "placeholder" json, "options" json, "scoring" json NOT NULL DEFAULT '{}', "programId" integer NOT NULL, "export" json NOT NULL DEFAULT '["all-people-affected","included"]', "pattern" character varying, "duplicateCheck" boolean NOT NULL DEFAULT false, "showInPeopleAffectedTable" boolean NOT NULL DEFAULT false, "editableInPortal" boolean NOT NULL DEFAULT false, CONSTRAINT "programAttributeUnique" UNIQUE ("name", "programId"), CONSTRAINT "CHK_88f5ede846c87b3059ed09f967" CHECK ("name" NOT IN ('id', 'status', 'referenceId', 'preferredLanguage', 'inclusionScore', 'paymentAmountMultiplier', 'financialServiceProvider', 'registrationProgramId', 'maxPayments', 'lastTransactionCreated', 'lastTransactionPaymentNumber', 'lastTransactionStatus', 'lastTransactionAmount', 'lastTransactionErrorMessage', 'lastTransactionCustomData', 'paymentCount', 'paymentCountRemaining', 'registeredDate', 'validationDate', 'inclusionDate', 'deleteDate', 'completedDate', 'lastMessageStatus', 'lastMessageType', 'declinedDate')), CONSTRAINT "PK_b85642d2f95cc2fcc6145e14463" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_1387f030d9f04f7d80c78a60d5" ON "121-service"."program_registration_attribute" ("created") `, + ); + await queryRunner.query( + `CREATE TABLE "121-service"."registration_attribute_data" ("id" SERIAL NOT NULL, "created" TIMESTAMP NOT NULL DEFAULT now(), "updated" TIMESTAMP NOT NULL DEFAULT now(), "registrationId" integer NOT NULL, "programRegistrationAttributeId" integer NOT NULL, "value" character varying NOT NULL, CONSTRAINT "registrationProgramAttributeUnique" UNIQUE ("registrationId", "programRegistrationAttributeId"), CONSTRAINT "PK_bef7662581d64d69db3f6405411" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_29cd6ac9bf4002df266d0ba23e" ON "121-service"."registration_attribute_data" ("created") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_8914b71c0e30c44291ab68a9b8" ON "121-service"."registration_attribute_data" ("registrationId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_3037018a626cd41bd16c588170" ON "121-service"."registration_attribute_data" ("value") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_d8a56a1864ef40e1551833430b" ON "121-service"."transaction" ("programFinancialServiceProviderConfigurationId") `, + ); + await queryRunner.query( + `ALTER TABLE "121-service"."program_financial_service_provider_configuration_property" ADD CONSTRAINT "FK_5e40569627925419cd94db0da36" FOREIGN KEY ("programFinancialServiceProviderConfigurationId") REFERENCES "121-service"."program_financial_service_provider_configuration"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "121-service"."program_financial_service_provider_configuration" ADD CONSTRAINT "FK_f7400125e09c4d8fec5747ec588" FOREIGN KEY ("programId") REFERENCES "121-service"."program"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "121-service"."program_registration_attribute" ADD CONSTRAINT "FK_8788ebf12909c03049a0d8c377d" FOREIGN KEY ("programId") REFERENCES "121-service"."program"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "121-service"."registration_attribute_data" ADD CONSTRAINT "FK_8914b71c0e30c44291ab68a9b8a" FOREIGN KEY ("registrationId") REFERENCES "121-service"."registration"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "121-service"."registration_attribute_data" ADD CONSTRAINT "FK_3bd62b57d06901bcd85e28fd060" FOREIGN KEY ("programRegistrationAttributeId") REFERENCES "121-service"."program_registration_attribute"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "121-service"."registration" ADD CONSTRAINT "FK_148b6bb5c37ca2d444b01c00c2f" FOREIGN KEY ("programFinancialServiceProviderConfigurationId") REFERENCES "121-service"."program_financial_service_provider_configuration"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "121-service"."transaction" ADD CONSTRAINT "FK_d8a56a1864ef40e1551833430bb" FOREIGN KEY ("programFinancialServiceProviderConfigurationId") REFERENCES "121-service"."program_financial_service_provider_configuration"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `DELETE FROM "121-service"."typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, + ['VIEW', 'registration_view', '121-service'], + ); + await queryRunner.query(`DROP VIEW "121-service"."registration_view"`); + await queryRunner.query( + `CREATE VIEW "121-service"."registration_view" AS SELECT "registration"."id" AS "id", "registration"."created" AS "registrationCreated", "registration"."programId" AS "programId", "registration"."registrationStatus" AS "status", "registration"."referenceId" AS "referenceId", "registration"."phoneNumber" AS "phoneNumber", "registration"."preferredLanguage" AS "preferredLanguage", "registration"."inclusionScore" AS "inclusionScore", "registration"."paymentAmountMultiplier" AS "paymentAmountMultiplier", "registration"."maxPayments" AS "maxPayments", "registration"."paymentCount" AS "paymentCount", "registration"."scope" AS "scope", "fspconfig"."label" AS "programFinancialServiceProviderConfigurationLabel", CAST(CONCAT('PA #',registration."registrationProgramId") as VARCHAR) AS "personAffectedSequence", registration."registrationProgramId" AS "registrationProgramId", TO_CHAR("registration"."created",'yyyy-mm-dd') AS "registrationCreatedDate", fspconfig."name" AS "programFinancialServiceProviderConfigurationName", fspconfig."id" AS "programFinancialServiceProviderConfigurationId", fspconfig."financialServiceProviderName" AS "financialServiceProviderName", "registration"."maxPayments" - "registration"."paymentCount" AS "paymentCountRemaining", COALESCE("message"."type" || ': ' || "message"."status",'no messages yet') AS "lastMessageStatus" FROM "121-service"."registration" "registration" LEFT JOIN "121-service"."program_financial_service_provider_configuration" "fspconfig" ON "fspconfig"."id"="registration"."programFinancialServiceProviderConfigurationId" LEFT JOIN "121-service"."latest_message" "latestMessage" ON "latestMessage"."registrationId"="registration"."id" LEFT JOIN "121-service"."twilio_message" "message" ON "message"."id"="latestMessage"."messageId" ORDER BY "registration"."registrationProgramId" ASC`, + ); + + await queryRunner.query( + `INSERT INTO "121-service"."typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, + [ + '121-service', + 'VIEW', + 'registration_view', + 'SELECT "registration"."id" AS "id", "registration"."created" AS "registrationCreated", "registration"."programId" AS "programId", "registration"."registrationStatus" AS "status", "registration"."referenceId" AS "referenceId", "registration"."phoneNumber" AS "phoneNumber", "registration"."preferredLanguage" AS "preferredLanguage", "registration"."inclusionScore" AS "inclusionScore", "registration"."paymentAmountMultiplier" AS "paymentAmountMultiplier", "registration"."maxPayments" AS "maxPayments", "registration"."paymentCount" AS "paymentCount", "registration"."scope" AS "scope", "fspconfig"."label" AS "programFinancialServiceProviderConfigurationLabel", CAST(CONCAT(\'PA #\',registration."registrationProgramId") as VARCHAR) AS "personAffectedSequence", registration."registrationProgramId" AS "registrationProgramId", TO_CHAR("registration"."created",\'yyyy-mm-dd\') AS "registrationCreatedDate", fspconfig."name" AS "programFinancialServiceProviderConfigurationName", fspconfig."id" AS "programFinancialServiceProviderConfigurationId", fspconfig."financialServiceProviderName" AS "financialServiceProviderName", "registration"."maxPayments" - "registration"."paymentCount" AS "paymentCountRemaining", COALESCE("message"."type" || \': \' || "message"."status",\'no messages yet\') AS "lastMessageStatus" FROM "121-service"."registration" "registration" LEFT JOIN "121-service"."program_financial_service_provider_configuration" "fspconfig" ON "fspconfig"."id"="registration"."programFinancialServiceProviderConfigurationId" LEFT JOIN "121-service"."latest_message" "latestMessage" ON "latestMessage"."registrationId"="registration"."id" LEFT JOIN "121-service"."twilio_message" "message" ON "message"."id"="latestMessage"."messageId" ORDER BY "registration"."registrationProgramId" ASC', + ], + ); + } + + private async migrateFspConig(queryRunner: QueryRunner) { + const programFspAssignements = await queryRunner.query(` + SELECT + pfspfsp."programId", + pfspfsp."financialServiceProviderId", + fsp.fsp AS "financialServiceProviderName", + fsp.id AS "financialServiceProviderId", + fsp."displayName" AS "financialServiceProviderNameDisplayName" + FROM + "121-service".program_financial_service_providers_financial_service_provider AS pfspfsp + LEFT JOIN + "121-service".financial_service_provider AS fsp + ON + pfspfsp."financialServiceProviderId" = fsp.id`); + for (const assignemt of programFspAssignements) { + const oldFspConfigDisplayName = await this.getOldFspConfigDisplayName( + queryRunner, + assignemt.programId, + assignemt.financialServiceProviderId, + ); + // write some code to migrate displays names from the old fsp config setup + const newFspConfig = { + created: new Date(), + updated: new Date(), + programId: assignemt.programId, + financialServiceProviderName: assignemt.financialServiceProviderName, + name: assignemt.financialServiceProviderName, + label: + oldFspConfigDisplayName ?? + JSON.stringify({ en: assignemt.financialServiceProviderName }), + }; + // raw query insert + const insterFspConfigResult = await queryRunner.query(` + INSERT INTO "121-service".program_financial_service_provider_configuration ( + created, + updated, + "programId", + "financialServiceProviderName", + name, + label + ) + VALUES ( + now(), + now(), + ${newFspConfig.programId}, + '${newFspConfig.financialServiceProviderName}', + '${newFspConfig.name}', + '${newFspConfig.label}' + ) + RETURNING id + `); + + const insertedId = insterFspConfigResult[0].id; + + // Update the transaction table so transactions are related to program_financial_service_provider_configuration instead of fspId + await queryRunner.query(` + UPDATE "121-service".transaction + SET "programFinancialServiceProviderConfigurationId" = ${insertedId} + WHERE "programId" = ${assignemt.programId} + AND "financialServiceProviderId" = ${assignemt.financialServiceProviderId}`); + // do the same per registration + await queryRunner.query(` + UPDATE "121-service".registration + SET "programFinancialServiceProviderConfigurationId" = ${insertedId} + WHERE "programId" = ${assignemt.programId} + AND "fspId" = ${assignemt.financialServiceProviderId}`); + // migrate old fsp config properties to the new fsp config properties + await this.migrateOldFspConfigToFspConfigProperties( + queryRunner, + insertedId, + assignemt.programId, + assignemt.financialServiceProviderId, + ); + } + + // migrate transactions to relate to the new fsp config instead of fsp + } + + private async migrateOldFspConfigToFspConfigProperties( + queryRunner: QueryRunner, + newFspConfigId: number, + programId: number, + fspId: number, + ): Promise { + const oldFspConfig = await queryRunner.query(` + SELECT + * + FROM + "121-service".program_fsp_configuration + WHERE + "fspId" = ${fspId} + AND + "programId" = ${programId} + AND name != 'displayName'`); + + for (const config of oldFspConfig) { + // insert the old fsp config in the new table + await queryRunner.query(` + INSERT INTO "121-service".program_financial_service_provider_configuration_property ( + name, + value, + "programFinancialServiceProviderConfigurationId" + ) + VALUES ( + '${config.name}', + '${config.value}', + ${newFspConfigId} + )`); + } + } + + private async getOldFspConfigDisplayName( + queryRunner: QueryRunner, + programId: number, + fspId: number, + ): Promise { + const oldFspConfig = await queryRunner.query(` + SELECT + * + FROM + "121-service".program_fsp_configuration + WHERE + "fspId" = ${fspId} + AND + "programId" = ${programId} + AND + "name" = 'displayName'`); + return oldFspConfig[0]?.value; + + // get the display name from the old fsp config + } + + private async migrateQuestionsToProgramRegistrationAttributes( + queryRunner: QueryRunner, + ): Promise { + await queryRunner.query(` + INSERT INTO "121-service".program_registration_attribute ( + created, + updated, + name, + label, + type, + "isRequired", + placeholder, + options, + scoring, + "programId", + export, + pattern, + "duplicateCheck", + "showInPeopleAffectedTable", + "editableInPortal" + ) + SELECT + created, + updated, + name, + label, + type, + false AS "isRequired", -- Set a default value as it's not present in the old table + NULL::json AS placeholder, -- Set to NULL as it's not present in the old table + NULL::json AS options, -- Set to NULL as it's not present in the old table + '{}'::json AS scoring, -- Set default empty JSON as specified in the new table + "programId", + '["all-people-affected","included"]'::json AS export, -- Default value as specified in the new table + NULL::character varying AS pattern, -- Set to NULL as it's not present in the old table + "duplicateCheck", + "showInPeopleAffectedTable", + true AS "editableInPortal" -- Set as true value as it's not present in the old table + FROM + "121-service".program_custom_attribute`); + + await queryRunner.query(` + INSERT INTO "121-service".program_registration_attribute ( + created, + updated, + name, + label, + type, + "isRequired", + placeholder, + options, + scoring, + "programId", + export, + pattern, + "duplicateCheck", + "showInPeopleAffectedTable", + "editableInPortal" + ) + SELECT + created, + updated, + name, + label, + "questionType" AS type, + false AS "isRequired", + placeholder, + options, + scoring, + "programId", + export, + pattern, + "duplicateCheck", + "showInPeopleAffectedTable", + "editableInPortal" + FROM + "121-service".program_question + `); + + await queryRunner.query(` + INSERT INTO "121-service".program_registration_attribute ( + created, + updated, + name, + label, + type, + "isRequired", + placeholder, + options, + scoring, + "programId", + export, + pattern, + "duplicateCheck", + "showInPeopleAffectedTable", + "editableInPortal" + ) + SELECT + fspq.created, + fspq.updated, + fspq.name, + fspq.label, + fspq."answerType", + false AS "isRequired", -- Set a default value as it's not present in the old table + fspq.placeholder, + fspq.options, + '{}'::json AS scoring, -- Set default empty JSON as specified in the new table + pfsp."programId", + fspq.export, + fspq.pattern, + fspq."duplicateCheck", + fspq."showInPeopleAffectedTable", + false AS "editableInPortal" -- Set a default value as it's not present in the old table + FROM + "121-service".financial_service_provider_question fspq + JOIN + "121-service".program_financial_service_providers_financial_service_provider pfsp + ON + fspq."fspId" = pfsp."financialServiceProviderId" + ON + CONFLICT (name, "programId") DO NOTHING;`); + + const [ + programRegistrationAttributes, + programQuestions, + programCustomAttributes, + fspQuestions, + ] = await Promise.all([ + queryRunner.query( + `SELECT id, name, "programId" FROM "121-service".program_registration_attribute`, + ), + queryRunner.query( + `SELECT id, name, "programId" FROM "121-service".program_question`, + ), + queryRunner.query( + `SELECT id, name, "programId" FROM "121-service".program_custom_attribute`, + ), + queryRunner.query( + `SELECT id, name FROM "121-service".financial_service_provider_question`, + ), + ]); + for (const programRegistrationAttribute of programRegistrationAttributes) { + const matchingProgramQuestion = programQuestions.find( + (pq) => + pq.name === programRegistrationAttribute.name && + pq.programId === programRegistrationAttribute.programId, + ); + if (matchingProgramQuestion) { + const queryPQData = ` + INSERT INTO "121-service".registration_attribute_data ( + created, + updated, + "registrationId", + "programRegistrationAttributeId", + value + ) + SELECT + rd.created, + rd.updated, + rd."registrationId", + ${programRegistrationAttribute.id}, + rd.value + FROM + "121-service".registration_data rd + WHERE + rd."programQuestionId" = ${matchingProgramQuestion.id}`; + await queryRunner.query(queryPQData); + } + } + + for (const programRegistrationAttribute of programRegistrationAttributes) { + const matchingProgramCustomAttribute = programCustomAttributes.find( + (pca) => + pca.name === programRegistrationAttribute.name && + pca.programId === programRegistrationAttribute.programId, + ); + if (matchingProgramCustomAttribute) { + const queryPQData = ` + INSERT INTO "121-service".registration_attribute_data ( + created, + updated, + "registrationId", + "programRegistrationAttributeId", + value + ) + SELECT + rd.created, + rd.updated, + rd."registrationId", + ${programRegistrationAttribute.id}, + rd.value + FROM + "121-service".registration_data rd + WHERE + rd."programCustomAttributeId" = ${matchingProgramCustomAttribute.id}`; + await queryRunner.query(queryPQData); + } + } + + for (const programRegistrationAttribute of programRegistrationAttributes) { + const matchingFspQs = fspQuestions.filter( + (fspQ) => fspQ.name === programRegistrationAttribute.name, + ); + + if (matchingFspQs.length > 0) { + const fspQuestionIds = matchingFspQs.map((fspQ) => fspQ.id).join(', '); + // Remove registration data related to unmatched fsp questions + // So for example some registrations still have data of jumbo while their fsp is now visa + const deleteRegistrationDataRelatedToUnMatchedFsp = ` + DELETE FROM "121-service".registration_data rd + USING "121-service".registration r, "121-service"."financial_service_provider_question" fq + WHERE rd."registrationId" = r.id + AND rd."fspQuestionId" = fq.id + AND fq."fspId" != r."fspId"; + `; + await queryRunner.query(deleteRegistrationDataRelatedToUnMatchedFsp); + + const queryFQData = ` + INSERT INTO "121-service".registration_attribute_data ( + created, + updated, + "registrationId", + "programRegistrationAttributeId", + value + ) + SELECT + rd.created, + rd.updated, + rd."registrationId", + ${programRegistrationAttribute.id}, + rd.value + FROM + "121-service".registration_data rd + LEFT JOIN + "121-service".registration r ON r.id = rd."registrationId" + WHERE + rd."fspQuestionId" IN (${fspQuestionIds}) AND r."programId" = ${programRegistrationAttribute.programId}`; + + await queryRunner.query(queryFQData); + } + } + + return Promise.resolve(); + } + + private async checkRegistrationFspConfigMigrations( + queryRunner: QueryRunner, + ): Promise { + // check if all the fsp config properties have been migrated + const mismatchedRegistrations = await queryRunner.query(` + SELECT r.* + FROM "121-service".registration r + LEFT JOIN "121-service".program_financial_service_provider_configuration fspConfig + ON r."programFinancialServiceProviderConfigurationId" = fspConfig.id + WHERE r."programId" != fspConfig."programId" + `); + + if (mismatchedRegistrations.length > 0) { + throw new Error( + `Data integrity issue: ${mismatchedRegistrations.length} registrations are linked to an FSP configuration with a different programId.`, + ); + } + } + + private async checkTransactionFspConfigMigrations( + queryRunner: QueryRunner, + ): Promise { + // check if all the fsp config properties have been migrated + const mismatchedTransactions = await queryRunner.query(` + SELECT t.* + FROM "121-service".transaction t + LEFT JOIN "121-service".program_financial_service_provider_configuration fspConfig + ON t."programFinancialServiceProviderConfigurationId" = fspConfig.id + WHERE t."programId" != fspConfig."programId" + `); + + if (mismatchedTransactions.length > 0) { + throw new Error( + `Data integrity issue: ${mismatchedTransactions.length} transactions are linked to an FSP configuration with a different programId.`, + ); + } + } + + private async addConstraints(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE SEQUENCE IF NOT EXISTS "121-service"."program_financial_service_pro_id_seq" OWNED BY "121-service"."program_financial_service_provider_configuration_property"."id"`, + ); + await queryRunner.query( + `ALTER TABLE "121-service"."program_financial_service_provider_configuration_property" ALTER COLUMN "id" SET DEFAULT nextval('"121-service"."program_financial_service_pro_id_seq"')`, + ); + await queryRunner.query( + `ALTER TABLE "121-service"."program_financial_service_provider_configuration_property" ALTER COLUMN "id" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "121-service"."transaction" DROP CONSTRAINT "FK_d8a56a1864ef40e1551833430bb"`, + ); + await queryRunner.query( + `ALTER TABLE "121-service"."transaction" ALTER COLUMN "programFinancialServiceProviderConfigurationId" SET NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "121-service"."transaction" ADD CONSTRAINT "FK_d8a56a1864ef40e1551833430bb" FOREIGN KEY ("programFinancialServiceProviderConfigurationId") REFERENCES "121-service"."program_financial_service_provider_configuration"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + } + + private async dropOldTablesAndViews(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `DROP TABLE "121-service"."registration_data" cascade`, + ); + await queryRunner.query( + `ALTER TABLE "121-service"."transaction" DROP COLUMN "financialServiceProviderId"`, + ); + await queryRunner.query( + `ALTER TABLE "121-service"."registration" DROP COLUMN "fspId"`, + ); + await queryRunner.query( + `DROP TABLE "121-service"."program_fsp_configuration" cascade`, + ); + await queryRunner.query( + `DROP TABLE "121-service"."program_custom_attribute" cascade`, + ); + await queryRunner.query( + `DROP TABLE "121-service"."program_question" cascade`, + ); + await queryRunner.query( + `DROP TABLE "121-service"."financial_service_provider_question" cascade`, + ); + await queryRunner.query( + `DROP TABLE "121-service"."program_financial_service_providers_financial_service_provider" cascade`, + ); + } + + public async down(_queryRunner: QueryRunner): Promise { + console.log('Not implemented'); + } +} diff --git a/services/121-service/src/migration/1729848646578-missing-program-fsp-confg-properties-seq.ts b/services/121-service/src/migration/1729848646578-missing-program-fsp-confg-properties-seq.ts new file mode 100644 index 0000000000..e8ee8ba98b --- /dev/null +++ b/services/121-service/src/migration/1729848646578-missing-program-fsp-confg-properties-seq.ts @@ -0,0 +1,27 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class MissingProgramFspConfgPropertiesSeq1729848646578 + implements MigrationInterface +{ + name = 'MissingProgramFspConfgPropertiesSeq1729848646578'; + + public async up(queryRunner: QueryRunner): Promise { + // These queries are also present in src/migration/1729605362361-program-registration-attribute-refactor.ts for some reason however typeorm still generates + // this migration file. Could not find a way around this. So leaving it as is. + await queryRunner.query( + `CREATE SEQUENCE IF NOT EXISTS "121-service"."program_financial_service_pro_id_seq" OWNED BY "121-service"."program_financial_service_provider_configuration_property"."id"`, + ); + await queryRunner.query( + `ALTER TABLE "121-service"."program_financial_service_provider_configuration_property" ALTER COLUMN "id" SET DEFAULT nextval('"121-service"."program_financial_service_pro_id_seq"')`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "121-service"."program_financial_service_provider_configuration_property" ALTER COLUMN "id" DROP DEFAULT`, + ); + await queryRunner.query( + `DROP SEQUENCE "121-service"."program_financial_service_pro_id_seq"`, + ); + } +} diff --git a/services/121-service/src/migration/test-migration.sh b/services/121-service/src/migration/test-migration.sh new file mode 100755 index 0000000000..c8567e5a00 --- /dev/null +++ b/services/121-service/src/migration/test-migration.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +# Check if both branch names are provided +if [ -z "$1" ] || [ -z "$2" ]; then + echo "Error: Missing branch name(s)." + echo "Usage: ./services/121-service/src/migration/test-migration.sh [ (optional)] [ (optional)]" + echo "Example: ./services/121-service/src/migration/test-migration.sh main feat.new-awesome-entity" + echo "Optional: ./services/121-service/src/migration/test-migration.sh old-branch-name new-branch-name custom-db-name custom-service-name" + exit 1 +fi +# Set default values for Docker container names if not provided +DOCKER_DB_NAME=${3:-'121db'} +DOCKER_SERVICE_NAME=${4:-'121-service'} + +# Source the .env file to get the environment variables +source ./services/.env + +# Check for local changes and stash them if present +STASH_NAME="automated-pre-migration-stash" +git stash push -u -m "$STASH_NAME" + + +# Checkout to the old branch +git checkout "$1" + +# Stop the specified Docker containers +docker stop "$DOCKER_SERVICE_NAME" +docker stop "$DOCKER_DB_NAME" + +# Remove the specified Docker container +docker rm "$DOCKER_DB_NAME" + +# Start the specified Docker container to apply the migration and load some data, then stop it again +npm run start:services + +# Wait until the service is up or timeout after 1 minute +timeout=60 +elapsed=0 +interval=2 + +until [ $(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${PORT_121_SERVICE}/api/health/health") -eq 200 ]; do + if [ $elapsed -ge $timeout ]; then + echo "Error: Service did not start within $timeout seconds." + fi + echo "Waiting for the service to be up..." + sleep $interval + elapsed=$((elapsed + interval)) +done + + +curl -X 'POST' \ + "http://localhost:${PORT_121_SERVICE}/api/scripts/reset?mockPv=true&mockOcw=true&isApiTests=false&script=nlrc-multiple-mock-data" \ + -H 'accept: */*' \ + -H 'Content-Type: application/json' \ + -d "{ + \"secret\": \"${RESET_SECRET}\" +}" + +docker stop "$DOCKER_SERVICE_NAME" + +# Checkout to the new branch +git checkout "$2" + +# Apply stashed changes by name +STASH_REF=$(git stash list | grep "$STASH_NAME" | head -n 1 | awk -F: '{print $1}') +if [ -n "$STASH_REF" ]; then + git stash pop "$STASH_REF" +else + echo "Error: Could not find stash with name '$STASH_NAME'" + exit 1 +fi + +# Start the specified Docker container +docker start "$DOCKER_SERVICE_NAME" diff --git a/services/121-service/src/program-financial-service-provider-configurations/dtos/program-financial-service-provider-configuration-response.dto.ts b/services/121-service/src/program-financial-service-provider-configurations/dtos/program-financial-service-provider-configuration-response.dto.ts index 07c840f282..a9d7ba7eb7 100644 --- a/services/121-service/src/program-financial-service-provider-configurations/dtos/program-financial-service-provider-configuration-response.dto.ts +++ b/services/121-service/src/program-financial-service-provider-configurations/dtos/program-financial-service-provider-configuration-response.dto.ts @@ -17,6 +17,7 @@ export class ProgramFinancialServiceProviderConfigurationResponseDto { @ApiProperty({ type: 'object' }) public label: LocalizedString; + /// Can sometimes be undefined if the financial service provider has been removed from the codebase @ApiProperty({ type: 'object' }) - public financialServiceProvider: FinancialServiceProviderDto; + public financialServiceProvider?: FinancialServiceProviderDto; } diff --git a/services/121-service/src/program-financial-service-provider-configurations/mappers/program-financial-service-provider-configuration.mapper.ts b/services/121-service/src/program-financial-service-provider-configurations/mappers/program-financial-service-provider-configuration.mapper.ts index 4c329f7661..aa5375df8e 100644 --- a/services/121-service/src/program-financial-service-provider-configurations/mappers/program-financial-service-provider-configuration.mapper.ts +++ b/services/121-service/src/program-financial-service-provider-configurations/mappers/program-financial-service-provider-configuration.mapper.ts @@ -23,11 +23,6 @@ export class ProgramFinancialServiceProviderConfigurationMapper { const financialServiceProvider = FINANCIAL_SERVICE_PROVIDERS.find( (fsp) => fsp.name === entity.financialServiceProviderName, ); - if (!financialServiceProvider) { - throw new Error( - 'Financial service provider assigned to program not found in the list of available financial service providers', - ); - } dto.financialServiceProvider = financialServiceProvider; return dto; }