From cf7775da39db707eab6feb45fad0125e68143868 Mon Sep 17 00:00:00 2001 From: Ciel Bellerose Date: Wed, 4 Feb 2026 20:14:55 -0500 Subject: [PATCH 1/2] #3951 duplicate project budget fix --- src/backend/src/services/finance.services.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/backend/src/services/finance.services.ts b/src/backend/src/services/finance.services.ts index 6bd7dcf915..52f73f7d96 100644 --- a/src/backend/src/services/finance.services.ts +++ b/src/backend/src/services/finance.services.ts @@ -817,10 +817,16 @@ export default class FinanceServices { } }); - const allTotalBudget = teams.reduce((teamAcc, team) => { - const teamBudget = team.projects.reduce((projAcc, project) => projAcc + project.budget, 0); - return teamAcc + teamBudget; - }, 0); + // project budgets no longer double counted if in multiple teams + const projectsById = new Map(); + for (const team of teams) { + for (const project of team.projects) { + if (!projectsById.has(project.projectId)) { + projectsById.set(project.projectId, { budget: project.budget }); + } + } + } + const allTotalBudget = [...projectsById.values()].reduce((acc, p) => acc + p.budget, 0); const cashTotalBudget = cashReimbursementRequests.reduce((reqAcc, rr) => { From 08547c8ab1ab1761e8acb14cc8736a4dd5e4336e Mon Sep 17 00:00:00 2001 From: Ciel Bellerose Date: Wed, 4 Feb 2026 20:35:15 -0500 Subject: [PATCH 2/2] #3951 get team type budget double counting fix --- src/backend/src/services/finance.services.ts | 92 +++++++++++++++----- 1 file changed, 68 insertions(+), 24 deletions(-) diff --git a/src/backend/src/services/finance.services.ts b/src/backend/src/services/finance.services.ts index 52f73f7d96..5f83d17658 100644 --- a/src/backend/src/services/finance.services.ts +++ b/src/backend/src/services/finance.services.ts @@ -611,33 +611,77 @@ export default class FinanceServices { if (!division) throw new NotFoundException('Team Type', teamTypeId); - const results: ReimbursementRequestData = { - totalBudget: 0, - pendingFinance: 0, - pendingLeadership: 0, - submittedToSabo: 0, - reimbursed: 0, - available: 0 - }; - + const projectsById = new Map(); for (const team of division.teams) { - const data: ReimbursementRequestData = await this.getReimbursementRequestTeamData( - organization, - team.teamId, - startDate, - endDate, - carNumber - ); - - results.totalBudget += data.totalBudget; - results.pendingFinance += data.pendingFinance; - results.pendingLeadership += data.pendingLeadership; - results.submittedToSabo += data.submittedToSabo; - results.reimbursed += data.reimbursed; - results.available += data.available; + for (const project of team.projects) { + if (!projectsById.has(project.projectId)) { + projectsById.set(project.projectId, { budget: project.budget }); + } + } } + const totalBudget = [...projectsById.values()].reduce((acc, p) => acc + p.budget, 0); + const divisionProjectIds = [...projectsById.keys()]; + + const reimbursementRequests = await prisma.reimbursement_Request.findMany({ + where: { + reimbursementProducts: { + some: { + reimbursementProductReason: { + wbsElement: { + project: { + projectId: { in: divisionProjectIds } + } + } + } + } + }, + reimbursementStatuses: { + none: { + type: Reimbursement_Status_Type.DENIED + } + }, + ...getReimbursementRequestWhereInput(startDate, endDate, carNumber) + }, + select: { + reimbursementStatuses: true, + totalCost: true, + reimbursementProducts: { + where: { + dateDeleted: null, + reimbursementProductReason: { + wbsElement: { + project: { + projectId: { in: divisionProjectIds } + } + } + } + }, + select: { + cost: true + } + } + } + }); + + const { pendingFinance, pendingLeadership, submittedToSabo, reimbursed } = computeRRTotals(reimbursementRequests); - return results; + const totalBalance = + reimbursementRequests.reduce((acc, curr) => { + const teamProductsCost = curr.reimbursementProducts.reduce((prodAcc, prod) => prodAcc + prod.cost, 0); + return acc + teamProductsCost; + }, 0) / 100; + + const available = totalBudget - totalBalance; + + const data: ReimbursementRequestData = { + totalBudget, + pendingFinance, + pendingLeadership, + submittedToSabo, + reimbursed, + available + }; + return data; } static async getSpendingBarTeamData(