Skip to content

Commit fb210ca

Browse files
committed
not in contact default changes
1 parent 29bd558 commit fb210ca

14 files changed

Lines changed: 523 additions & 336 deletions

File tree

src/backend/src/controllers/prospective-sponsor.controllers.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export default class ProspectiveSponsorController {
66
try {
77
const {
88
organizationName,
9+
status,
910
lastContactDate,
1011
firstContactMethod,
1112
contactName,
@@ -22,6 +23,7 @@ export default class ProspectiveSponsorController {
2223
req.currentUser,
2324
req.organization,
2425
organizationName,
26+
status,
2527
lastContactDate,
2628
firstContactMethod,
2729
contactName,
@@ -71,8 +73,8 @@ export default class ProspectiveSponsorController {
7173
req.organization,
7274
prospectiveSponsorId,
7375
organizationName,
74-
lastContactDate,
7576
status,
77+
lastContactDate,
7678
firstContactMethod,
7779
contactName,
7880
contactorUserId,
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-- Make contact fields optional on Prospective_Sponsor for NOT_IN_CONTACT status
2+
-- Make lastContactDate optional
3+
ALTER TABLE "Prospective_Sponsor" ALTER COLUMN "lastContactDate" DROP NOT NULL;
4+
5+
-- Make firstContactMethod optional
6+
ALTER TABLE "Prospective_Sponsor" ALTER COLUMN "firstContactMethod" DROP NOT NULL;
7+
8+
-- Make contactorUserId optional
9+
ALTER TABLE "Prospective_Sponsor" ALTER COLUMN "contactorUserId" DROP NOT NULL;
10+
11+
-- Make contactId optional
12+
ALTER TABLE "Prospective_Sponsor" ALTER COLUMN "contactId" DROP NOT NULL;
13+
14+
-- Change default status from IN_PROGRESS to NOT_IN_CONTACT
15+
ALTER TABLE "Prospective_Sponsor" ALTER COLUMN "status" SET DEFAULT 'NOT_IN_CONTACT'::"Prospective_Sponsor_Status";

src/backend/src/prisma/schema.prisma

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -876,14 +876,14 @@ model Prospective_Sponsor {
876876
organization Organization @relation(fields: [organizationId], references: [organizationId])
877877
organizationName String
878878
dateCreated DateTime @default(now())
879-
lastContactDate DateTime
879+
lastContactDate DateTime?
880880
highlightThresholdDays Int @default(10)
881-
status Prospective_Sponsor_Status @default(IN_PROGRESS)
882-
firstContactMethod First_Contact_Method
883-
contactor User @relation(fields: [contactorUserId], references: [userId], name: "prospectiveSponsorContactor")
884-
contactorUserId String
885-
contact Sponsor_Contact @relation(fields: [contactId], references: [sponsorContactId])
886-
contactId String @unique
881+
status Prospective_Sponsor_Status @default(NOT_IN_CONTACT)
882+
firstContactMethod First_Contact_Method?
883+
contactor User? @relation(fields: [contactorUserId], references: [userId], name: "prospectiveSponsorContactor")
884+
contactorUserId String?
885+
contact Sponsor_Contact? @relation(fields: [contactId], references: [sponsorContactId])
886+
contactId String? @unique
887887
notes String?
888888
dateDeleted DateTime?
889889
tasks Sponsor_Task[]

src/backend/src/routes/prospective-sponsor.routes.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ const prospectiveSponsorRouter = express.Router();
99
prospectiveSponsorRouter.post(
1010
'/create',
1111
nonEmptyString(body('organizationName')),
12-
isDate(body('lastContactDate')),
13-
nonEmptyString(body('firstContactMethod')),
14-
nonEmptyString(body('contactName')),
15-
nonEmptyString(body('contactorUserId')),
12+
nonEmptyString(body('status')),
13+
isDate(body('lastContactDate')).optional({ checkFalsy: true }),
14+
nonEmptyString(body('firstContactMethod')).optional({ checkFalsy: true }),
15+
nonEmptyString(body('contactName')).optional({ checkFalsy: true }),
16+
nonEmptyString(body('contactorUserId')).optional({ checkFalsy: true }),
1617
intMinZero(body('highlightThresholdDays')).optional(),
1718
nonEmptyString(body('contactEmail')).optional({ checkFalsy: true }),
1819
nonEmptyString(body('contactPhone')).optional({ checkFalsy: true }),
@@ -35,11 +36,11 @@ prospectiveSponsorRouter.get('/', ProspectiveSponsorController.getAllProspective
3536
prospectiveSponsorRouter.post(
3637
'/:prospectiveSponsorId/edit',
3738
nonEmptyString(body('organizationName')),
38-
isDate(body('lastContactDate')),
3939
nonEmptyString(body('status')),
40-
nonEmptyString(body('firstContactMethod')),
41-
nonEmptyString(body('contactName')),
42-
nonEmptyString(body('contactorUserId')),
40+
isDate(body('lastContactDate')).optional({ checkFalsy: true }),
41+
nonEmptyString(body('firstContactMethod')).optional({ checkFalsy: true }),
42+
nonEmptyString(body('contactName')).optional({ checkFalsy: true }),
43+
nonEmptyString(body('contactorUserId')).optional({ checkFalsy: true }),
4344
intMinZero(body('highlightThresholdDays')).optional(),
4445
nonEmptyString(body('contactEmail')).optional({ checkFalsy: true }),
4546
nonEmptyString(body('contactPhone')).optional({ checkFalsy: true }),

src/backend/src/services/prospective-sponsor.services.ts

Lines changed: 75 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ export default class ProspectiveSponsorServices {
3232
submitter: User,
3333
organization: Organization,
3434
organizationName: string,
35-
lastContactDate: Date,
36-
firstContactMethod: FirstContactMethod,
37-
contactName: string,
38-
contactorUserId: string,
35+
status: ProspectiveSponsorStatus,
36+
lastContactDate?: Date,
37+
firstContactMethod?: FirstContactMethod,
38+
contactName?: string,
39+
contactorUserId?: string,
3940
highlightThresholdDays?: number,
4041
contactEmail?: string,
4142
contactPhone?: string,
@@ -47,8 +48,16 @@ export default class ProspectiveSponsorServices {
4748
throw new AccessDeniedException('Only finance team members or heads can create prospective sponsors');
4849
}
4950

50-
if (!contactEmail && !contactPhone) {
51-
throw new HttpException(400, 'At least one of contact email or contact phone is required');
51+
const isNotInContact = status === ProspectiveSponsorStatus.NOT_IN_CONTACT;
52+
53+
if (!isNotInContact) {
54+
if (!lastContactDate) throw new HttpException(400, 'Last contact date is required');
55+
if (!firstContactMethod) throw new HttpException(400, 'First contact method is required');
56+
if (!contactName) throw new HttpException(400, 'Contact name is required');
57+
if (!contactorUserId) throw new HttpException(400, 'Contactor is required');
58+
if (!contactEmail && !contactPhone) {
59+
throw new HttpException(400, 'At least one of contact email or contact phone is required');
60+
}
5261
}
5362

5463
const existingProspectiveSponsor = await prisma.prospective_Sponsor.findFirst({
@@ -63,26 +72,27 @@ export default class ProspectiveSponsorServices {
6372
throw new HttpException(400, `A prospective sponsor with the name "${organizationName}" already exists.`);
6473
}
6574

66-
const contactor = await prisma.user.findUnique({
67-
where: { userId: contactorUserId }
68-
});
69-
70-
if (!contactor) {
71-
throw new NotFoundException('User', contactorUserId);
75+
if (contactorUserId) {
76+
const contactor = await prisma.user.findUnique({ where: { userId: contactorUserId } });
77+
if (!contactor) throw new NotFoundException('User', contactorUserId);
7278
}
7379

74-
const contact = await prisma.sponsor_Contact.create({
75-
data: { name: contactName, email: contactEmail, phone: contactPhone, position: contactPosition }
76-
});
80+
const contact =
81+
!isNotInContact && contactName
82+
? await prisma.sponsor_Contact.create({
83+
data: { name: contactName, email: contactEmail, phone: contactPhone, position: contactPosition }
84+
})
85+
: null;
7786

7887
const prospectiveSponsor = await prisma.prospective_Sponsor.create({
7988
data: {
8089
organizationName,
81-
lastContactDate,
90+
lastContactDate: lastContactDate ?? null,
8291
highlightThresholdDays: highlightThresholdDays ?? 10,
83-
firstContactMethod,
84-
contactorUserId,
85-
contactId: contact.sponsorContactId,
92+
status,
93+
firstContactMethod: firstContactMethod ?? null,
94+
contactorUserId: contactorUserId ?? null,
95+
contactId: contact?.sponsorContactId ?? null,
8696
notes,
8797
organizationId: organization.organizationId,
8898
tasks: tasks?.length
@@ -138,11 +148,11 @@ export default class ProspectiveSponsorServices {
138148
organization: Organization,
139149
prospectiveSponsorId: string,
140150
organizationName: string,
141-
lastContactDate: Date,
142151
status: ProspectiveSponsorStatus,
143-
firstContactMethod: FirstContactMethod,
144-
contactName: string,
145-
contactorUserId: string,
152+
lastContactDate?: Date,
153+
firstContactMethod?: FirstContactMethod,
154+
contactName?: string,
155+
contactorUserId?: string,
146156
highlightThresholdDays?: number,
147157
contactEmail?: string,
148158
contactPhone?: string,
@@ -154,8 +164,16 @@ export default class ProspectiveSponsorServices {
154164
throw new AccessDeniedException('Only finance team members or heads can edit prospective sponsors');
155165
}
156166

157-
if (!contactEmail && !contactPhone) {
158-
throw new HttpException(400, 'At least one of contact email or contact phone is required');
167+
const isNotInContact = status === ProspectiveSponsorStatus.NOT_IN_CONTACT;
168+
169+
if (!isNotInContact) {
170+
if (!lastContactDate) throw new HttpException(400, 'Last contact date is required');
171+
if (!firstContactMethod) throw new HttpException(400, 'First contact method is required');
172+
if (!contactName) throw new HttpException(400, 'Contact name is required');
173+
if (!contactorUserId) throw new HttpException(400, 'Contactor is required');
174+
if (!contactEmail && !contactPhone) {
175+
throw new HttpException(400, 'At least one of contact email or contact phone is required');
176+
}
159177
}
160178

161179
const oldProspectiveSponsor = await prisma.prospective_Sponsor.findUnique({
@@ -180,12 +198,9 @@ export default class ProspectiveSponsorServices {
180198
}
181199
}
182200

183-
const contactor = await prisma.user.findUnique({
184-
where: { userId: contactorUserId }
185-
});
186-
187-
if (!contactor) {
188-
throw new NotFoundException('User', contactorUserId);
201+
if (contactorUserId) {
202+
const contactor = await prisma.user.findUnique({ where: { userId: contactorUserId } });
203+
if (!contactor) throw new NotFoundException('User', contactorUserId);
189204
}
190205

191206
// Upsert tasks if provided
@@ -231,20 +246,38 @@ export default class ProspectiveSponsorServices {
231246
);
232247
}
233248

234-
await prisma.sponsor_Contact.update({
235-
where: { sponsorContactId: oldProspectiveSponsor.contactId },
236-
data: { name: contactName, email: contactEmail, phone: contactPhone, position: contactPosition }
237-
});
249+
// Handle contact upsert based on status
250+
const { contactId: oldContactId } = oldProspectiveSponsor;
251+
let contactId = oldContactId;
252+
if (!isNotInContact && contactName) {
253+
if (oldProspectiveSponsor.contactId) {
254+
// Update existing contact
255+
await prisma.sponsor_Contact.update({
256+
where: { sponsorContactId: oldProspectiveSponsor.contactId },
257+
data: { name: contactName, email: contactEmail, phone: contactPhone, position: contactPosition }
258+
});
259+
} else {
260+
// Create new contact (transitioning from NOT_IN_CONTACT)
261+
const contact = await prisma.sponsor_Contact.create({
262+
data: { name: contactName, email: contactEmail, phone: contactPhone, position: contactPosition }
263+
});
264+
contactId = contact.sponsorContactId;
265+
}
266+
} else if (isNotInContact && oldProspectiveSponsor.contactId) {
267+
// Clear contact when moving to NOT_IN_CONTACT
268+
contactId = null;
269+
}
238270

239271
const updatedProspectiveSponsor = await prisma.prospective_Sponsor.update({
240272
where: { prospectiveSponsorId },
241273
data: {
242274
organizationName,
243-
lastContactDate,
275+
lastContactDate: isNotInContact ? null : lastContactDate,
244276
highlightThresholdDays: highlightThresholdDays ?? 10,
245277
status,
246-
firstContactMethod,
247-
contactorUserId,
278+
firstContactMethod: isNotInContact ? null : firstContactMethod,
279+
contactorUserId: isNotInContact ? null : contactorUserId,
280+
contactId,
248281
notes
249282
},
250283
...getProspectiveSponsorQueryArgs(organization.organizationId)
@@ -413,10 +446,10 @@ export default class ProspectiveSponsorServices {
413446
// Create a new contact for the sponsor, copied from the prospective sponsor's contact
414447
const sponsorContact = await prisma.sponsor_Contact.create({
415448
data: {
416-
name: prospectiveSponsor.contact.name,
417-
email: prospectiveSponsor.contact.email,
418-
phone: prospectiveSponsor.contact.phone,
419-
position: prospectiveSponsor.contact.position
449+
name: prospectiveSponsor.contact?.name ?? '',
450+
email: prospectiveSponsor.contact?.email,
451+
phone: prospectiveSponsor.contact?.phone,
452+
position: prospectiveSponsor.contact?.position
420453
}
421454
});
422455

src/backend/src/transformers/prospective-sponsor.transformer.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,21 @@ export const prospectiveSponsorTransformer = (
1111
prospectiveSponsorId: prospectiveSponsor.prospectiveSponsorId,
1212
organizationName: prospectiveSponsor.organizationName,
1313
dateCreated: prospectiveSponsor.dateCreated,
14-
lastContactDate: prospectiveSponsor.lastContactDate,
14+
lastContactDate: prospectiveSponsor.lastContactDate ?? undefined,
1515
highlightThresholdDays: prospectiveSponsor.highlightThresholdDays,
1616
status: prospectiveSponsor.status as ProspectiveSponsorStatus,
17-
firstContactMethod: prospectiveSponsor.firstContactMethod as FirstContactMethod,
18-
contactor: userTransformer(prospectiveSponsor.contactor),
19-
contact: {
20-
name: prospectiveSponsor.contact.name,
21-
email: prospectiveSponsor.contact.email ?? undefined,
22-
phone: prospectiveSponsor.contact.phone ?? undefined,
23-
position: prospectiveSponsor.contact.position ?? undefined
24-
},
17+
firstContactMethod: prospectiveSponsor.firstContactMethod
18+
? (prospectiveSponsor.firstContactMethod as FirstContactMethod)
19+
: undefined,
20+
contactor: prospectiveSponsor.contactor ? userTransformer(prospectiveSponsor.contactor) : undefined,
21+
contact: prospectiveSponsor.contact
22+
? {
23+
name: prospectiveSponsor.contact.name,
24+
email: prospectiveSponsor.contact.email ?? undefined,
25+
phone: prospectiveSponsor.contact.phone ?? undefined,
26+
position: prospectiveSponsor.contact.position ?? undefined
27+
}
28+
: undefined,
2529
notes: prospectiveSponsor.notes ?? undefined,
2630
tasks: prospectiveSponsor.tasks.map(sponsorTaskTransformer)
2731
};

0 commit comments

Comments
 (0)