Add recurring expense functionality (#263)

* code complete

* Smaller updates

* delete ambitious TODOs (add to PR)

* add transactionality to recurring expense creation

* Remove unnecessary `let`s

* Add default english labels to non-en-US translations

* Accept `es.json` translations

* add condition to ensure links are only modified when applicable
This commit is contained in:
trandall
2025-04-19 12:23:23 -07:00
committed by GitHub
parent 2bced00f82
commit 94c101cf7b
17 changed files with 464 additions and 3 deletions

View File

@@ -0,0 +1,29 @@
-- CreateEnum
CREATE TYPE "RecurrenceRule" AS ENUM ('NONE', 'DAILY', 'WEEKLY', 'MONTHLY');
-- AlterTable
ALTER TABLE "Expense" ADD COLUMN "recurrenceRule" "RecurrenceRule" DEFAULT 'NONE',
ADD COLUMN "recurringExpenseLinkId" TEXT;
-- CreateTable
CREATE TABLE "RecurringExpenseLink" (
"id" TEXT NOT NULL,
"groupId" TEXT NOT NULL,
"currentFrameExpenseId" TEXT NOT NULL,
"nextExpenseCreatedAt" TIMESTAMP(3),
"nextExpenseDate" TIMESTAMP(3) NOT NULL,
CONSTRAINT "RecurringExpenseLink_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "RecurringExpenseLink_currentFrameExpenseId_key" ON "RecurringExpenseLink"("currentFrameExpenseId");
-- CreateIndex
CREATE INDEX "RecurringExpenseLink_groupId_idx" ON "RecurringExpenseLink"("groupId");
-- CreateIndex
CREATE INDEX "RecurringExpenseLink_groupId_nextExpenseCreatedAt_nextExpen_idx" ON "RecurringExpenseLink"("groupId", "nextExpenseCreatedAt", "nextExpenseDate" DESC);
-- AddForeignKey
ALTER TABLE "RecurringExpenseLink" ADD CONSTRAINT "RecurringExpenseLink_currentFrameExpenseId_fkey" FOREIGN KEY ("currentFrameExpenseId") REFERENCES "Expense"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -55,6 +55,10 @@ model Expense {
createdAt DateTime @default(now())
documents ExpenseDocument[]
notes String?
recurrenceRule RecurrenceRule? @default(NONE)
recurringExpenseLink RecurringExpenseLink?
recurringExpenseLinkId String?
}
model ExpenseDocument {
@@ -73,6 +77,29 @@ enum SplitMode {
BY_AMOUNT
}
model RecurringExpenseLink {
id String @id
groupId String
currentFrameExpense Expense @relation(fields: [currentFrameExpenseId], references: [id], onDelete: Cascade)
currentFrameExpenseId String @unique
// Note: We do not want to link to the next expense because once it is created, it should be
// treated as it's own independent entity. This means that if a user wants to delete an Expense
// and any prior related recurring expenses, they'll need to delete them one by one.
nextExpenseCreatedAt DateTime?
nextExpenseDate DateTime
@@index([groupId])
@@index([groupId, nextExpenseCreatedAt, nextExpenseDate(sort: Desc)])
}
enum RecurrenceRule {
NONE
DAILY
WEEKLY
MONTHLY
}
model ExpensePaidFor {
expense Expense @relation(fields: [expenseId], references: [id], onDelete: Cascade)
participant Participant @relation(fields: [participantId], references: [id], onDelete: Cascade)