diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index f60a6ce..4a3d9a4 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -62,15 +62,6 @@ jobs: - name: Find and Replace UI_ENV run: find -name "ui.config.ts" -type f -exec sed -i ''s/#{UI_ENV}#/test/g'' {} \; - - name: Find and Replace TEST_DB_ORG - Using Secret - run: find -name "playwright.config.ts" -type f -exec sed -i ''s/#{TEST_DB_ORG}#/${{ secrets.TEST_DB_ORG }}/g'' {} \; - - - name: Find and Replace TEST_DB_SERVICE_TOKEN - Using Secret - run: find -name "playwright.config.ts" -type f -exec sed -i ''s/#{TEST_DB_SERVICE_TOKEN}#/${{ secrets.TEST_DB_SERVICE_TOKEN }}/g'' {} \; - - - name: Find and Replace TEST_DB_TOKEN_ID - Using Secret - run: find -name "playwright.config.ts" -type f -exec sed -i ''s/#{TEST_DB_TOKEN_ID}#/${{ secrets.TEST_DB_TOKEN_ID }}/g'' {} \; - - name: Find and Replace TEST_LOGIN_USERNAME - Using Secret run: find -name "auth.setup.ts" -type f -exec sed -i ''s/#{TEST_LOGIN_USERNAME}#/${{ secrets.TEST_LOGIN_USERNAME }}/g'' {} \; @@ -91,12 +82,6 @@ jobs: - name: Install dependencies run: npm ci --legacy-peer-deps - - name: Install Planetscale CLI - run: | - mkdir pscaledir - wget -O pscalecli.tar.gz https://github.com/planetscale/cli/releases/download/v0.145.0/pscale_0.145.0_linux_amd64.tar.gz - tar -xvf pscalecli.tar.gz -C ./pscaledir - - name: Set DEBUG=pw:webserver run: echo "DEBUG=pw:webserver" >> $GITHUB_ENV diff --git a/app/lib/prisma.ts b/app/lib/prisma.ts index c2a4e03..280d0c6 100644 --- a/app/lib/prisma.ts +++ b/app/lib/prisma.ts @@ -1,6 +1,6 @@ import { PrismaClient } from "@prisma/client"; -const prisma = global.prisma || new PrismaClient(); +const prisma = global.prisma || new PrismaClient({ log: ["error", "warn"] }); if (process.env.NODE_ENV === "development") global.prisma = prisma; export default prisma; diff --git a/app/modules/recurring/recurring.service.ts b/app/modules/recurring/recurring.service.ts index e3c1e6b..78b836f 100644 --- a/app/modules/recurring/recurring.service.ts +++ b/app/modules/recurring/recurring.service.ts @@ -228,17 +228,15 @@ export async function markTransactionAsDone( export async function markAsNotified(userIds: string[], startDate: Date, endDate: Date) { try { - //Used raw query due to issue: https://github.com/prisma/prisma/issues/5043 - const result = await prisma.$executeRaw( - Prisma.sql`UPDATE RecurringTransaction SET isNotified = 1 WHERE userId IN (${Prisma.join( - userIds - )}) AND executionDate > ${formatDate_DD_MMMM_YYYY_hh_mm( - startDate - )} AND executionDate <= ${formatDate_DD_MMMM_YYYY_hh_mm( - endDate - )} AND isNotified = 0` - ); - return result > 0; + const result = await prisma.recurringTransaction.updateMany({ + where: { + userId: { in: userIds }, + executionDate: { gt: startDate, lte: endDate }, + isNotified: false, + }, + data: { isNotified: true }, + }); + return result.count > 0; } catch (error) { logError(error); return false; diff --git a/app/modules/subscriptions/gpb.subscriptions.service.ts b/app/modules/subscriptions/gpb.subscriptions.service.ts index 5bc0342..9730d73 100644 --- a/app/modules/subscriptions/gpb.subscriptions.service.ts +++ b/app/modules/subscriptions/gpb.subscriptions.service.ts @@ -92,7 +92,10 @@ export async function createGPBSubscription( } // reset isLatest flag for all purchaseTokens of current user - await prisma.$executeRaw`UPDATE GPBSubscription SET isLatest = 0 WHERE userId = ${userId} AND isLatest = 1;`; + await prisma.gPBSubscription.updateMany({ + where: { userId, isLatest: true }, + data: { isLatest: false }, + }); let expiry = 0; if ( diff --git a/app/modules/transaction/transaction.schema.ts b/app/modules/transaction/transaction.schema.ts index 9802277..c4b641a 100644 --- a/app/modules/transaction/transaction.schema.ts +++ b/app/modules/transaction/transaction.schema.ts @@ -88,6 +88,7 @@ export type MonthlyTargetInput = z.infer; export type MonthlyCategoryWiseTargetInput = z.infer< typeof MonthlyCategoryWiseTargetInputSchema >; + export type TransactionType = "income" | "expense" | "investment"; export function parseTransactionInput(transaction: unknown) { diff --git a/app/modules/transaction/transaction.service.ts b/app/modules/transaction/transaction.service.ts index d0eba22..5f37607 100644 --- a/app/modules/transaction/transaction.service.ts +++ b/app/modules/transaction/transaction.service.ts @@ -647,7 +647,7 @@ export async function editMonthlyTarget( valuesArr.push( Prisma.sql`(${Prisma.join([ userId, - formatDate_YYYY_MM_DD(date), + Prisma.sql`CAST(${formatDate_YYYY_MM_DD(date)} AS DATE)`, "expense", category, 0, @@ -657,9 +657,9 @@ export async function editMonthlyTarget( } const categoryBudgetUpdate = prisma.$executeRaw( - Prisma.sql`INSERT INTO CategoryAmount (userId, date, type, category, amount, budget) VALUES ${Prisma.join( + Prisma.sql`INSERT INTO "CategoryAmount" ("userId", "date", "type", "category", "amount", "budget") VALUES ${Prisma.join( valuesArr - )} ON DUPLICATE KEY UPDATE budget = VALUES(budget)` + )} ON CONFLICT ("userId", "date", "type", "category") DO UPDATE SET budget = EXCLUDED.budget` ); tasks.push(categoryBudgetUpdate); diff --git a/playwright.config.ts b/playwright.config.ts index 89e48ec..6c0997e 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -14,8 +14,8 @@ const config: PlaywrightTestConfig = { // globalSetup: require.resolve("./global-setup"), testDir: "./tests", /* Maximum time one test can run for. */ - timeout: 30 * 1000, - globalTimeout: 120 * 1000, + timeout: 45 * 1000, + globalTimeout: 0, expect: { /** * Maximum time expect() should wait for the condition to be met. @@ -113,11 +113,6 @@ const config: PlaywrightTestConfig = { /* Run your local dev server before starting the tests */ webServer: [ - { - command: `./pscaledir/pscale connect expense-manager dev --org #{TEST_DB_ORG}# --service-token #{TEST_DB_SERVICE_TOKEN}# --service-token-id #{TEST_DB_TOKEN_ID}#`, - port: 3306, - reuseExistingServer: true, - }, { command: "npm run testserver", port: 3000, diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 2bc8588..fb3914a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -4,9 +4,9 @@ generator client { } datasource db { - provider = "mysql" - url = env("DATABASE_URL") - relationMode = "prisma" + provider = "postgresql" + url = env("DATABASE_URL") + relationMode = "prisma" } model User { @@ -49,20 +49,14 @@ model MonthlyTarget { @@id([userId, date]) } -enum TransactionType { - expense - income - investment -} - model CategoryAmount { userId String date DateTime category String - type TransactionType - amount Decimal @db.Decimal(22, 4) - budget Decimal? @db.Decimal(16, 4) - user User @relation(fields: [userId], references: [id]) + type String @db.VarChar(10) + amount Decimal @db.Decimal(22, 4) + budget Decimal? @db.Decimal(16, 4) + user User @relation(fields: [userId], references: [id]) @@id([userId, date, type, category]) @@index([userId, date, type, amount]) @@ -71,120 +65,96 @@ model CategoryAmount { model PaymentModeAmount { userId String date DateTime - transactionType TransactionType + transactionType String @db.VarChar(10) paymentMode String amount Decimal @db.Decimal(22, 4) budget Decimal? @db.Decimal(16, 4) user User @relation(fields: [userId], references: [id]) @@id([userId, date, transactionType, paymentMode]) - @@index([userId, date, transactionType, paymentMode, amount]) + @@index([userId, date, transactionType, paymentMode, amount], map: "PaymentModeAmount_userId_d6310b-342e-4566-9d91-4c4bdd285dbc_idx") } model Transaction { - id String @id @default(nanoid(18)) - createdAt DateTime @default(now()) + id String @id @default(nanoid(18)) + createdAt DateTime @default(now()) createdAtLocal DateTime - description String? @db.VarChar(250) - amount Decimal @db.Decimal(22, 4) - type TransactionType + description String? @db.VarChar(250) + amount Decimal @db.Decimal(22, 4) + type String @db.VarChar(10) category String category2 String? category3 String? paymentMode String - user User @relation(fields: [userId], references: [id]) + user User @relation(fields: [userId], references: [id]) userId String @@index([userId, createdAtLocal, type, category]) } -enum PaymentGateway { - GPB // Google Play Billing - STR // Stripe -} - model UserPreference { - userId String @unique - currency String? @db.VarChar(3) - timezone String @db.VarChar(64) - locale String @db.VarChar(15) - paymentGateway PaymentGateway? - isActiveSubscription Boolean @default(false) - isMFAOn Boolean @default(false) - collectAnalytics Boolean? - lastModified Float @default(0) @db.Double() - user User? @relation(fields: [userId], references: [id]) + userId String @unique(map: "UserPreference_userId_idx") + currency String? @db.VarChar(3) + timezone String @db.VarChar(64) + locale String @db.VarChar(15) + paymentGateway String? @db.VarChar(3) + isActiveSubscription Boolean @default(false) + isMFAOn Boolean @default(false) + collectAnalytics Boolean? + lastModified Float @default(0) + user User? @relation(fields: [userId], references: [id]) @@index([userId, lastModified]) } model RecurringTransaction { - id String @id @default(nanoid(12)) - occurrence String @db.VarChar(5) - interval Int @db.UnsignedSmallInt - amount Decimal @db.Decimal(22, 4) - type TransactionType + id String @id @default(nanoid(12)) + occurrence String @db.VarChar(5) + interval Int @db.SmallInt + amount Decimal @db.Decimal(22, 4) + type String @db.VarChar(10) category String category2 String? category3 String? paymentMode String - description String? @db.VarChar(250) + description String? @db.VarChar(250) executionDate DateTime - isNotified Boolean @default(false) - user User? @relation(fields: [userId], references: [id]) + isNotified Boolean @default(false) userId String + user User? @relation(fields: [userId], references: [id]) @@index([userId, executionDate]) @@index([executionDate, isNotified]) } model CustomCategory { - userId String - type TransactionType - value String @db.VarChar(250) - user User? @relation(fields: [userId], references: [id]) + userId String + type String @db.VarChar(10) + value String @db.VarChar(250) + user User? @relation(fields: [userId], references: [id]) @@id([userId, type, value]) } -enum GPBSubscriptionState { - SUBSCRIPTION_STATE_ACTIVE - SUBSCRIPTION_STATE_CANCELED - SUBSCRIPTION_STATE_IN_GRACE_PERIOD - SUBSCRIPTION_STATE_ON_HOLD - SUBSCRIPTION_STATE_PAUSED - SUBSCRIPTION_STATE_EXPIRED -} model GPBSubscription { - purchaseToken String @id @db.VarChar(250) + purchaseToken String @id @db.VarChar(250) userId String - isLatest Boolean @default(false) - state GPBSubscriptionState - expiry Float? @db.Double() + isLatest Boolean @default(false) + state String @db.VarChar(40) + expiry Float? cancelReason String? @@index([userId, isLatest]) } -enum STRSubscriptionState { - trialing - active - past_due - incomplete - incomplete_expired - canceled - unpaid - paused -} - model STRSubscription { - userId String @id - customerId String - subscriptionId String - state STRSubscriptionState - expiry Float - cancelAtExpiry Boolean @default(false) + userId String @id + customerId String + subscriptionId String + state String @db.VarChar(20) + expiry Float + cancelAtExpiry Boolean @default(false) @@index([subscriptionId]) -} \ No newline at end of file +}