diff --git a/apps/api/lib/dayjs.ts b/apps/api/lib/dayjs.ts new file mode 100644 index 00000000..e193b26c --- /dev/null +++ b/apps/api/lib/dayjs.ts @@ -0,0 +1,6 @@ +import dayjsExtended from 'dayjs' +import quarterOfYear from 'dayjs/plugin/quarterOfYear' + +dayjsExtended.extend(quarterOfYear) + +export { dayjsExtended } diff --git a/apps/api/package.json b/apps/api/package.json index 43b22f70..7207bbfe 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -16,6 +16,7 @@ "@hono/clerk-auth": "^2.0.0", "@hono/zod-validator": "^0.2.2", "@prisma/client": "^5.15.0", + "dayjs": "^1.11.11", "hono": "^4.4.3", "zod": "^3.23.8" }, diff --git a/apps/api/v1/services/budget.service.ts b/apps/api/v1/services/budget.service.ts index c4e36d37..f4505095 100644 --- a/apps/api/v1/services/budget.service.ts +++ b/apps/api/v1/services/budget.service.ts @@ -1,5 +1,11 @@ +import { dayjsExtended } from '@/lib/dayjs' import prisma from '@/lib/prisma' -import { type Budget, BudgetUserPermission, type User } from '@prisma/client' +import { + type Budget, + BudgetPeriodType, + BudgetUserPermission, + type User, +} from '@prisma/client' import type { CreateBudget, UpdateBudget } from '../validation' import { inviteUserToBudget } from './budget-invitation.service' @@ -89,6 +95,46 @@ export async function findBudget({ budgetId }: { budgetId: string }) { }) } +/** + * Given an anchor date and period type, + * calculate the start and end dates of the budget period. + * Example: + * - anchorDate: 2022-01-15 + * - periodType: MONTHLY + * - startDate: 2022-01-01 + * - endDate: 2022-01-31 + */ +function calculateBudgetPeriodStartEndDates({ + anchorDate, + type, +}: { + anchorDate: Date + type: BudgetPeriodType +}) { + switch (type) { + case BudgetPeriodType.MONTHLY: + return { + startDate: dayjsExtended(anchorDate).startOf('month').toDate(), + endDate: dayjsExtended(anchorDate).endOf('month').toDate(), + } + case BudgetPeriodType.QUARTERLY: + return { + startDate: dayjsExtended(anchorDate).startOf('quarter').toDate(), + endDate: dayjsExtended(anchorDate).endOf('quarter').toDate(), + } + case BudgetPeriodType.YEARLY: + return { + startDate: dayjsExtended(anchorDate).startOf('year').toDate(), + endDate: dayjsExtended(anchorDate).endOf('year').toDate(), + } + default: + return { + startDate: null, + endDate: null, + } + } +} + export async function createBudget({ user, data, @@ -105,6 +151,11 @@ export async function createBudget({ inviteeEmails = [], } = data + const periodConfig = calculateBudgetPeriodStartEndDates({ + anchorDate: new Date(), + type: period.type, + }) + const budget = await prisma.budget.create({ data: { name, @@ -115,8 +166,8 @@ export async function createBudget({ create: { type: period.type, amount: period.amount, - startDate: period.startDate, - endDate: period.endDate, + startDate: period.endDate ?? periodConfig.startDate, + endDate: period.endDate ?? periodConfig.endDate, }, }, budgetUsers: { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 809cc647..4dfb65fc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,6 +26,9 @@ importers: '@prisma/client': specifier: ^5.15.0 version: 5.15.0(prisma@5.15.0) + dayjs: + specifier: ^1.11.11 + version: 1.11.11 hono: specifier: ^4.4.3 version: 4.4.3 @@ -443,6 +446,9 @@ packages: csstype@3.1.1: resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} + dayjs@1.11.11: + resolution: {integrity: sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==} + debug@4.1.1: resolution: {integrity: sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==} deprecated: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797) @@ -1753,6 +1759,8 @@ snapshots: csstype@3.1.1: {} + dayjs@1.11.11: {} + debug@4.1.1: dependencies: ms: 2.1.3