diff --git a/packages/server/prisma/migrations/20250129184320_/migration.sql b/packages/server/prisma/migrations/20250129184320_/migration.sql new file mode 100644 index 00000000..cc4a71bc --- /dev/null +++ b/packages/server/prisma/migrations/20250129184320_/migration.sql @@ -0,0 +1,22 @@ +/* + Warnings: + + - You are about to drop the column `giftKey` on the `DDNDonor` table. All the data in the column will be lost. + - A unique constraint covering the columns `[idSorter,processDate,batchId,solicitationCodeId,combinedAmount,donorHash]` on the table `DailyDepartmentNotification` will be added. If there are existing duplicate values, this will fail. + - Added the required column `donorHash` to the `DailyDepartmentNotification` table without a default value. This is not possible if the table is not empty. + +*/ +-- DropIndex +DROP INDEX "DailyDepartmentNotification_idSorter_processDate_batchId_so_key"; + +-- AlterTable +ALTER TABLE "DDNDonor" DROP COLUMN "giftKey"; + +-- AlterTable +ALTER TABLE "DDNDonorLink" ADD COLUMN "giftKey" TEXT; + +-- AlterTable +ALTER TABLE "DailyDepartmentNotification" ADD COLUMN "donorHash" TEXT NOT NULL; + +-- CreateIndex +CREATE UNIQUE INDEX "DailyDepartmentNotification_idSorter_processDate_batchId_so_key" ON "DailyDepartmentNotification"("idSorter", "processDate", "batchId", "solicitationCodeId", "combinedAmount", "donorHash"); diff --git a/packages/server/prisma/schema/ddn.prisma b/packages/server/prisma/schema/ddn.prisma index ae1c539d..3cd59077 100644 --- a/packages/server/prisma/schema/ddn.prisma +++ b/packages/server/prisma/schema/ddn.prisma @@ -44,20 +44,20 @@ model DailyDepartmentNotification { batchId Int createdAt DateTime @default(now()) @db.Timestamptz(6) updatedAt DateTime @default(now()) @db.Timestamptz(6) + donorSummary String donors DDNDonorLink[] batch DailyDepartmentNotificationBatch @relation(fields: [batchId], references: [id], onDelete: Cascade) fundraisingEntry FundraisingEntry @relation(fields: [fundraisingEntryId], references: [id], onDelete: Cascade, map: "DailyDepartmentNotification_fundraisingEntry") solicitationCode SolicitationCode @relation(fields: [solicitationCodeId], references: [id]) fundraisingEntryWithMeta FundraisingEntryWithMeta? @relation(fields: [fundraisingEntryId], references: [id], map: "DailyDepartmentNotification_fundraisingEntryWithMeta") - @@unique([idSorter, processDate, batchId, solicitationCodeId, combinedAmount, comment]) + @@unique([idSorter, processDate, batchId, solicitationCodeId, combinedAmount, donorSummary]) } model DDNDonor { id Int @id @default(autoincrement()) uuid String @unique() @default(dbgenerated("gen_random_uuid()")) @db.Uuid donorId String @unique() - giftKey String? name String? deceased Boolean constituency String? @@ -73,6 +73,7 @@ model DDNDonor { model DDNDonorLink { donorId Int ddnId Int + giftKey String? amount Decimal relation String? createdAt DateTime @default(now()) @db.Timestamptz(6) diff --git a/packages/server/src/repositories/dailyDepartmentNotification/DDNRepository.ts b/packages/server/src/repositories/dailyDepartmentNotification/DDNRepository.ts index 6c398cc6..5e0f2732 100644 --- a/packages/server/src/repositories/dailyDepartmentNotification/DDNRepository.ts +++ b/packages/server/src/repositories/dailyDepartmentNotification/DDNRepository.ts @@ -95,11 +95,11 @@ interface ParsedDDNInit< donors: { amount: number; relation: string | undefined; + giftKey: string | undefined; donor: { donorId: string; constituency: string | undefined; deceased: boolean; - giftKey: string | undefined; name: string | undefined; titleBar: string | undefined; degrees: string[]; @@ -337,15 +337,16 @@ export class DailyDepartmentNotificationRepository extends buildDefaultRepositor } const donors: ParsedDDNInit["donors"] = []; + let donorSummary = comment ?? ""; if (donor1Id) { donors.push({ amount: donor1Amount ?? 0, relation: donor1Relation, + giftKey: donor1GiftKey, donor: { donorId: donor1Id, constituency: donor1Constituency, deceased: donor1Deceased ?? false, - giftKey: donor1GiftKey, name: donor1Name, titleBar: donor1TitleBar, degrees: donor1Degrees ? donor1Degrees.split(", ") : [], @@ -353,16 +354,17 @@ export class DailyDepartmentNotificationRepository extends buildDefaultRepositor pm: donor1Pm, }, }); + donorSummary += `${donor1Id}-${donor1GiftKey}`; } if (donor2Id) { donors.push({ amount: donor2Amount ?? 0, relation: donor2Relation, + giftKey: donor2GiftKey, donor: { donorId: donor2Id, constituency: donor2Constituency, deceased: donor2Deceased ?? false, - giftKey: donor2GiftKey, name: donor2Name, titleBar: donor2TitleBar, degrees: donor2Degrees ? donor2Degrees.split(", ") : [], @@ -370,6 +372,7 @@ export class DailyDepartmentNotificationRepository extends buildDefaultRepositor pm: donor2Pm, }, }); + donorSummary += `${donor2Id}-${donor2GiftKey}`; } return Ok({ @@ -381,6 +384,8 @@ export class DailyDepartmentNotificationRepository extends buildDefaultRepositor effectiveDate: effectiveDate && localDateToJs(effectiveDate), transactionDate: transactionDate && localDateToJs(transactionDate), + donorSummary, + accountName, accountNumber, combinedAmount, @@ -711,24 +716,19 @@ export class DailyDepartmentNotificationRepository extends buildDefaultRepositor logger.trace("Batch already exists", { batchId: row.batchId }); } - const idSorter_processDate_batchId_solicitationCodeId_combinedAmount_comment = - { - idSorter: row.ddn.idSorter, - processDate: row.ddn.processDate, - batchId: batch.id, - solicitationCodeId: solicitationCode.id, - combinedAmount: row.ddn.combinedAmount, - comment: row.ddn.comment ?? "", - }; + const uniques = { + idSorter: row.ddn.idSorter, + processDate: row.ddn.processDate, + batchId: batch.id, + solicitationCodeId: solicitationCode.id, + combinedAmount: row.ddn.combinedAmount, + donorSummary: row.ddn.donorSummary ?? null, + }; const uniqueStr = `${ - idSorter_processDate_batchId_solicitationCodeId_combinedAmount_comment.idSorter - }-${idSorter_processDate_batchId_solicitationCodeId_combinedAmount_comment.processDate.toString()}-${ - idSorter_processDate_batchId_solicitationCodeId_combinedAmount_comment.batchId - }-${ - idSorter_processDate_batchId_solicitationCodeId_combinedAmount_comment.solicitationCodeId - }-$${idSorter_processDate_batchId_solicitationCodeId_combinedAmount_comment.combinedAmount.toString()}-${ - idSorter_processDate_batchId_solicitationCodeId_combinedAmount_comment.comment - }`; + uniques.idSorter + }-${uniques.processDate.toString()}-${uniques.batchId}-${ + uniques.solicitationCodeId + }-$${uniques.combinedAmount.toString()}-${uniques.donorSummary}`; if (uniqueDDns.has(uniqueStr)) { return Err( new InvalidArgumentError( @@ -743,7 +743,8 @@ export class DailyDepartmentNotificationRepository extends buildDefaultRepositor // eslint-disable-next-line no-await-in-loop await prisma.dailyDepartmentNotification.upsert({ where: { - idSorter_processDate_batchId_solicitationCodeId_combinedAmount_comment, + idSorter_processDate_batchId_solicitationCodeId_combinedAmount_donorSummary: + uniques, }, create: { ...row.ddn, diff --git a/packages/server/src/repositories/dailyDepartmentNotification/ddnModelToResource.ts b/packages/server/src/repositories/dailyDepartmentNotification/ddnModelToResource.ts index d11b3a8a..d194d592 100644 --- a/packages/server/src/repositories/dailyDepartmentNotification/ddnModelToResource.ts +++ b/packages/server/src/repositories/dailyDepartmentNotification/ddnModelToResource.ts @@ -62,7 +62,7 @@ export function dailyDepartmentNotificationModelToResource( combinedDonorSalutation: ddn.combinedDonorSalutation, combinedDonorSort: ddn.combinedDonorSort ?? undefined, donor1Id: ddn.donors[0]?.donor.donorId ?? undefined, - donor1GiftKey: ddn.donors[0]?.donor.giftKey ?? undefined, + donor1GiftKey: ddn.donors[0]?.giftKey ?? undefined, donor1Name: ddn.donors[0]?.donor.name ?? undefined, donor1Deceased: ddn.donors[0]?.donor.deceased ?? undefined, donor1Constituency: ddn.donors[0]?.donor.constituency ?? undefined, @@ -70,7 +70,7 @@ export function dailyDepartmentNotificationModelToResource( donor1Pm: ddn.donors[0]?.donor.pm ?? undefined, donor1Degrees: ddn.donors[0]?.donor.degrees?.join(", ") ?? undefined, donor2Id: ddn.donors[1]?.donor.donorId ?? undefined, - donor2GiftKey: ddn.donors[1]?.donor.giftKey ?? undefined, + donor2GiftKey: ddn.donors[1]?.giftKey ?? undefined, donor2Name: ddn.donors[1]?.donor.name ?? undefined, donor2Deceased: ddn.donors[1]?.donor.deceased ?? undefined, donor2Constituency: ddn.donors[1]?.donor.constituency ?? undefined,