Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"concat-stream": "^2.0.0",
"cookie-parser": "^1.4.5",
"cors": "^2.8.5",
"dayjs": "^1.11.19",
"decimal.js": "^10.4.3",
"dotenv": "^16.0.1",
"express": "^5.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-- AlterTable: Change date-only fields from timestamp to date type
-- PostgreSQL truncates the time portion automatically when casting timestamp to date

ALTER TABLE "Activation_CR" ALTER COLUMN "startDate" SET DATA TYPE date;
ALTER TABLE "Work_Package" ALTER COLUMN "startDate" SET DATA TYPE date;
ALTER TABLE "Task" ALTER COLUMN "deadline" SET DATA TYPE date;
ALTER TABLE "Task" ALTER COLUMN "startDate" SET DATA TYPE date;
ALTER TABLE "Reimbursement_Request" ALTER COLUMN "dateOfExpense" SET DATA TYPE date;
ALTER TABLE "Reimbursement_Request" ALTER COLUMN "dateDelivered" SET DATA TYPE date;
ALTER TABLE "Sponsor" ALTER COLUMN "joinDate" SET DATA TYPE date;
ALTER TABLE "Sponsor_Task" ALTER COLUMN "dueDate" SET DATA TYPE date;
ALTER TABLE "Sponsor_Task" ALTER COLUMN "notifyDate" SET DATA TYPE date;
ALTER TABLE "Availability" ALTER COLUMN "dateSet" SET DATA TYPE date;
ALTER TABLE "Milestone" ALTER COLUMN "dateOfEvent" SET DATA TYPE date;
ALTER TABLE "Graph" ALTER COLUMN "startDate" SET DATA TYPE date;
ALTER TABLE "Graph" ALTER COLUMN "endDate" SET DATA TYPE date;
30 changes: 15 additions & 15 deletions src/backend/src/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ model Activation_CR {
lead User @relation(name: "markAsLead", fields: [leadId], references: [userId])
managerId String
manager User @relation(name: "markAsManager", fields: [managerId], references: [userId])
startDate DateTime
startDate DateTime @db.Date
confirmDetails Boolean

@@index([changeRequestId])
Expand Down Expand Up @@ -551,7 +551,7 @@ model Work_Package {
projectId String
project Project @relation(fields: [projectId], references: [projectId])
orderInProject Int
startDate DateTime
startDate DateTime @db.Date
duration Int
blockedBy WBS_Element[] @relation(name: "blockedBy")
stage Work_Package_Stage?
Expand Down Expand Up @@ -649,8 +649,8 @@ model Task {
taskId String @id @default(uuid())
title String
notes String
deadline DateTime?
startDate DateTime?
deadline DateTime? @db.Date
startDate DateTime? @db.Date
assignees User[] @relation(name: "assignedTo")
priority Task_Priority
status Task_Status
Expand Down Expand Up @@ -700,7 +700,7 @@ model Reimbursement_Request {
saboId Int? @unique
dateCreated DateTime @default(now())
dateDeleted DateTime?
dateOfExpense DateTime?
dateOfExpense DateTime? @db.Date
reimbursementStatuses Reimbursement_Status[]
recipientId String
recipient User @relation(name: "reimbursementRequestRecipient", fields: [recipientId], references: [userId])
Expand All @@ -711,7 +711,7 @@ model Reimbursement_Request {
totalCost Int
receiptPictures Receipt[]
reimbursementProducts Reimbursement_Product[]
dateDelivered DateTime?
dateDelivered DateTime? @db.Date
accountCodeId String
accountCode Account_Code @relation(fields: [accountCodeId], references: [accountCodeId])
organizationId String
Expand Down Expand Up @@ -779,7 +779,7 @@ model Vendor {
organizationId String
organization Organization @relation(fields: [organizationId], references: [organizationId])
username String?
password String? // password is encrypted
password String? // password is encrypted
discountCode String?
twoFactorContacts User[] @relation(name: "twoFactorContactVendors")
notes String?
Expand All @@ -803,7 +803,7 @@ model Sponsor {
tier Sponsor_Tier @relation(fields: [sponsorTierId], references: [sponsorTierId])
sponsorTierId String
sponsorValue Int
joinDate DateTime
joinDate DateTime @db.Date
discountCode String?
activeYears Int[]
taxExempt Boolean
Expand All @@ -818,8 +818,8 @@ model Sponsor {

model Sponsor_Task {
sponsorTaskId String @id @default(uuid())
dueDate DateTime
notifyDate DateTime?
dueDate DateTime @db.Date
notifyDate DateTime? @db.Date
assignee User? @relation(fields: [assigneeUserId], references: [userId], name: "assignedSponsorTasks")
assigneeUserId String?
notes String
Expand Down Expand Up @@ -1088,7 +1088,7 @@ model Event {
shops Shop[]
machinery Machinery[]
workPackages Work_Package[]
documents Document[]
documents Document[]
status Event_Status
initialDateScheduled DateTime?
questionDocumentLink String?
Expand Down Expand Up @@ -1173,7 +1173,7 @@ model Availability {

// Availibilies are integers between 0 and 11 from 10am - 10pm for a given day at hour intervals see meetingTime field in Design_Review
availability Int[]
dateSet DateTime
dateSet DateTime @db.Date

@@index([scheduleSettingsId])
}
Expand Down Expand Up @@ -1383,7 +1383,7 @@ model FrequentlyAskedQuestion {
model Milestone {
milestoneId String @id @default(uuid())
name String
dateOfEvent DateTime
dateOfEvent DateTime @db.Date
description String
userCreated User @relation(fields: [userCreatedId], references: [userId], name: "milestoneCreator")
userCreatedId String
Expand All @@ -1399,8 +1399,8 @@ model Milestone {

model Graph {
id String @id @default(uuid())
startDate DateTime?
endDate DateTime?
startDate DateTime? @db.Date
endDate DateTime? @db.Date
title String
graphType Graph_Type
displayGraphType Graph_Display_Type
Expand Down
34 changes: 17 additions & 17 deletions src/backend/src/prisma/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import ProjectsService from '../services/projects.services.js';
import { Decimal } from 'decimal.js';
import BillOfMaterialsService from '../services/boms.services.js';
import UsersService from '../services/users.services.js';
import { transformDate } from '../utils/datetime.utils.js';
import { toDateString } from 'shared';
import { writeFileSync, readFileSync } from 'fs';
import WbsElementTemplatesService from '../services/wbs-element-templates.services.js';
import RecruitmentServices from '../services/recruitment.services.js';
Expand Down Expand Up @@ -1137,7 +1137,7 @@ const performSeed: () => Promise<void> = async () => {
'Bodywork Concept of Design',
changeRequestProject1Id,
WorkPackageStage.Design,
weeksAgo(12).toISOString().split('T')[0],
toDateString(weeksAgo(12)),
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

toDateString(weeksAgo(...)) formats using local timezone rules, but weeksAgo() retains the current time-of-day. That means the resulting YYYY-MM-DD string can vary depending on the machine timezone and the time the seed is run (e.g., around midnight), making seed data non-deterministic. For seed data, consider using a UTC-based conversion (like the previous toISOString().split('T')[0]) or add a dedicated UTC date-string helper for date-only fields.

Suggested change
toDateString(weeksAgo(12)),
weeksAgo(12).toISOString().split('T')[0],

Copilot uses AI. Check for mistakes.
6,
[],
[],
Expand Down Expand Up @@ -1183,7 +1183,7 @@ const performSeed: () => Promise<void> = async () => {
'Adhesive Shear Strength Test',
changeRequestProject1Id,
WorkPackageStage.Research,
weeksAgo(10).toISOString().split('T')[0],
toDateString(weeksAgo(10)),
5,
[],
[],
Expand All @@ -1201,7 +1201,7 @@ const performSeed: () => Promise<void> = async () => {
'Manufacture Wiring Harness',
changeRequestProject5Id,
WorkPackageStage.Manufacturing,
weeksAgo(9).toISOString().split('T')[0],
toDateString(weeksAgo(9)),
4,
[],
[],
Expand Down Expand Up @@ -1234,7 +1234,7 @@ const performSeed: () => Promise<void> = async () => {
'Install Wiring Harness',
changeRequestProject5Id,
WorkPackageStage.Install,
weeksAgo(5).toISOString().split('T')[0],
toDateString(weeksAgo(5)),
6,
[],
[],
Expand Down Expand Up @@ -1267,7 +1267,7 @@ const performSeed: () => Promise<void> = async () => {
'Design Plush',
changeRequestProject6Id,
WorkPackageStage.Design,
weeksAgo(16).toISOString().split('T')[0],
toDateString(weeksAgo(16)),
7,
[],
[],
Expand Down Expand Up @@ -1300,7 +1300,7 @@ const performSeed: () => Promise<void> = async () => {
'Put Plush Together',
changeRequestProject6Id,
WorkPackageStage.Manufacturing,
weeksAgo(9).toISOString().split('T')[0],
toDateString(weeksAgo(9)),
5,
[],
[],
Expand Down Expand Up @@ -1333,7 +1333,7 @@ const performSeed: () => Promise<void> = async () => {
'Plush Testing',
changeRequestProject6Id,
WorkPackageStage.Testing,
weeksAgo(4).toISOString().split('T')[0],
toDateString(weeksAgo(4)),
4,
[],
[],
Expand Down Expand Up @@ -1367,7 +1367,7 @@ const performSeed: () => Promise<void> = async () => {
'Design Laser Canon',
changeRequestProject7Id,
WorkPackageStage.Design,
weeksAgo(8).toISOString().split('T')[0],
toDateString(weeksAgo(8)),
5,
[],
[],
Expand Down Expand Up @@ -1400,7 +1400,7 @@ const performSeed: () => Promise<void> = async () => {
'Laser Canon Research',
changeRequestProject7Id,
WorkPackageStage.Research,
weeksAgo(3).toISOString().split('T')[0],
toDateString(weeksAgo(3)),
6,
[],
[],
Expand All @@ -1418,7 +1418,7 @@ const performSeed: () => Promise<void> = async () => {
'Laser Canon Testing',
changeRequestProject7Id,
WorkPackageStage.Testing,
weeksFromNow(3).toISOString().split('T')[0],
toDateString(weeksFromNow(3)),
4,
[project3WP1.wbsNum, project3WP2.wbsNum],
[],
Expand All @@ -1437,7 +1437,7 @@ const performSeed: () => Promise<void> = async () => {
'Stadium Research',
changeRequestProject8Id,
WorkPackageStage.Research,
weeksAgo(14).toISOString().split('T')[0],
toDateString(weeksAgo(14)),
7,
[],
[],
Expand Down Expand Up @@ -1470,7 +1470,7 @@ const performSeed: () => Promise<void> = async () => {
'Stadium Install',
changeRequestProject8Id,
WorkPackageStage.Install,
weeksAgo(7).toISOString().split('T')[0],
toDateString(weeksAgo(7)),
6,
[],
[],
Expand All @@ -1488,7 +1488,7 @@ const performSeed: () => Promise<void> = async () => {
'Stadium Testing',
changeRequestProject8Id,
WorkPackageStage.Testing,
weeksAgo(1).toISOString().split('T')[0],
toDateString(weeksAgo(1)),
5,
[],
[],
Expand Down Expand Up @@ -2411,7 +2411,7 @@ const performSeed: () => Promise<void> = async () => {
leadId: batman.userId,
managerId: cyborg.userId,
duration: 5,
startDate: transformDate(new Date()),
startDate: toDateString(new Date()),
stage: WorkPackageStage.Design,
blockedBy: [],
descriptionBullets: [],
Expand All @@ -2425,7 +2425,7 @@ const performSeed: () => Promise<void> = async () => {
'Slim and Light Car',
newWorkPackageChangeRequest.crId,
WorkPackageStage.Design,
weeksAgo(2).toISOString().split('T')[0],
toDateString(weeksAgo(2)),
5,
[],
[],
Expand Down Expand Up @@ -2453,7 +2453,7 @@ const performSeed: () => Promise<void> = async () => {
leadId: batman.userId,
managerId: cyborg.userId,
duration: 5,
startDate: transformDate(new Date()),
startDate: toDateString(new Date()),
stage: WorkPackageStage.Design,
blockedBy: [],
descriptionBullets: [],
Expand Down
14 changes: 7 additions & 7 deletions src/backend/src/services/notifications.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
EventWithAttendees
} from '../utils/notifications.utils.js';
import { sendMessage } from '../integrations/slack.js';
import { daysBetween, meetingStartTimePipeNumbers, startOfDay, wbsPipe } from 'shared';
import { daysBetween, startOfDay, wbsPipe, formatTimeForSlack } from 'shared';
import { buildDueString, sendThreadResponse } from '../utils/slack.utils.js';
import WorkPackagesService from './work-packages.services.js';
import { addWeeksToDate } from 'shared';
Expand Down Expand Up @@ -196,15 +196,15 @@ export default class NotificationsService {
// Get work package names for this event
const workPackageNames = event.workPackages.map((wp) => wp.wbsElement.name).join(', ');

// Extract meeting times from scheduled slots
const meetingTimes = event.scheduledTimes
.map((slot) => (slot.startTime ? new Date(slot.startTime).getHours() : null))
.filter((hour): hour is number => hour !== null)
.sort((a, b) => a - b);
// Get the earliest scheduled start time for display
const [earliestSlot] = event.scheduledTimes
.filter((slot) => slot.startTime)
.sort((a, b) => new Date(a.startTime!).getTime() - new Date(b.startTime!).getTime());
const timeDisplay = earliestSlot ? formatTimeForSlack(new Date(earliestSlot.startTime!)) : 'TBD';

return (
`${usersToSlackPings(event.attendees ?? [])} ${event.title} (${workPackageNames}) ` +
`will be having an event today at ${meetingStartTimePipeNumbers(meetingTimes)} EST! ` +
`will be having an event today at ${timeDisplay} ET! ` +
zoomLink +
questionDocLink
);
Expand Down
4 changes: 0 additions & 4 deletions src/backend/src/services/reimbursement-requests.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,10 +330,6 @@ export default class ReimbursementRequestService {

await validateRefund(submitter, amount, organization.organizationId);

// make the date object but add 12 hours so that the time isn't 00:00 to avoid timezone problems
const dateCreated = new Date(dateReceived.split('T')[0]);
dateCreated.setTime(dateCreated.getTime() + 12 * 60 * 60 * 1000);

const newReimbursement = await prisma.reimbursement.create({
data: {
purchaserId: submitter.userId,
Expand Down
12 changes: 2 additions & 10 deletions src/backend/src/services/work-packages.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,6 @@ export default class WorkPackagesService {
.map((element) => element.wbsElement.workPackageNumber)
.reduce((prev, curr) => Math.max(prev, curr), 0) + 1;

// make the date object but add 12 hours so that the time isn't 00:00 to avoid timezone problems
const date = new Date(startDate.split('T')[0]);
date.setTime(date.getTime() + 12 * 60 * 60 * 1000);

const changesToCreate = crId
? [
{
Expand Down Expand Up @@ -266,7 +262,7 @@ export default class WorkPackagesService {
},
stage,
project: { connect: { projectId } },
startDate: date,
startDate: new Date(startDate),
duration,
orderInProject: project.workPackages.filter((wp) => !wp.wbsElement.dateDeleted).length + 1,
blockedBy: { connect: blockedByElements.map((ele) => ({ wbsElementId: ele.wbsElementId })) }
Expand Down Expand Up @@ -389,10 +385,6 @@ export default class WorkPackagesService {
userId
);

// make the date object but add 12 hours so that the time isn't 00:00 to avoid timezone problems
const date = new Date(startDate);
date.setTime(date.getTime() + 12 * 60 * 60 * 1000);

// set the status of the wbs element to active if an edit is made to a completed version
const status =
originalWorkPackage.wbsElement.status === WbsElementStatus.Complete
Expand All @@ -403,7 +395,7 @@ export default class WorkPackagesService {
const updatedWorkPackage = await prisma.work_Package.update({
where: { wbsElementId },
data: {
startDate: date,
startDate: new Date(startDate),
duration,
wbsElement: {
update: {
Expand Down
Loading