diff --git a/src/backend/src/controllers/finance.controllers.ts b/src/backend/src/controllers/finance.controllers.ts index 016a0ce498..65a28e2aad 100644 --- a/src/backend/src/controllers/finance.controllers.ts +++ b/src/backend/src/controllers/finance.controllers.ts @@ -43,7 +43,8 @@ export default class FinanceController { contactPhone, contactPosition, stockDescription, - discountDescription + discountDescription, + req.file ); res.status(200).json(sponsor); } catch (error: unknown) { @@ -383,7 +384,8 @@ export default class FinanceController { contactPhone, contactPosition, stockDescription, - discountDescription + discountDescription, + req.file ); res.status(200).json(updatedSponsor); diff --git a/src/backend/src/routes/finance.routes.ts b/src/backend/src/routes/finance.routes.ts index 8212642871..995b0805e1 100644 --- a/src/backend/src/routes/finance.routes.ts +++ b/src/backend/src/routes/finance.routes.ts @@ -9,11 +9,15 @@ import { } from '../utils/validation.utils.js'; import { body } from 'express-validator'; import FinanceController from '../controllers/finance.controllers.js'; +import multer, { memoryStorage } from 'multer'; +import { MAX_FILE_SIZE } from 'shared'; const financeRouter = express.Router(); +const upload = multer({ limits: { fileSize: MAX_FILE_SIZE }, storage: memoryStorage() }); financeRouter.post( '/sponsor/create', + upload.single('logoImage'), nonEmptyString(body('name')), body('activeStatus').isBoolean(), body('valueTypes').isArray(), @@ -142,6 +146,7 @@ financeRouter.get('/sponsorTiers', FinanceController.getAllSponsorTiers); financeRouter.post( '/sponsor/:sponsorId/edit', + upload.single('logoImage'), nonEmptyString(body('name')), body('activeStatus').isBoolean(), body('valueTypes').isArray(), diff --git a/src/backend/src/services/finance.services.ts b/src/backend/src/services/finance.services.ts index 1740bf7075..57e48504ed 100644 --- a/src/backend/src/services/finance.services.ts +++ b/src/backend/src/services/finance.services.ts @@ -34,6 +34,7 @@ import { getReimbursementRequestWhereInput } from '../utils/finance.utils.js'; import { notifySponsorTaskAssignee } from '../utils/slack.utils.js'; +import { uploadFile } from '../utils/google-integration.utils.js'; import { isUserFinanceTeamOrHead } from '../utils/reimbursement-requests.utils.js'; export default class FinanceServices { @@ -56,7 +57,7 @@ export default class FinanceServices { * @param contactPosition The position of the sponsor contact. * @param sponsorTasks An array of sponsor tasks associated with the sponsor. * @param organization The organization for which the sponsor is being created. - * + * @param logoImage An optional logo image file for the sponsor. * @returns The created sponsor object, including associated tasks. * * @throws AccessDeniedAdminOnlyException If the submitter does not have permission to create a sponsor. @@ -80,7 +81,8 @@ export default class FinanceServices { contactPhone?: string, contactPosition?: string, stockDescription?: string, - discountDescription?: string + discountDescription?: string, + logoImage?: Express.Multer.File ) { if (!(await userHasPermission(submitter.userId, organization.organizationId, isHead))) throw new AccessDeniedException('Only heads can create a sponsor'); @@ -104,6 +106,12 @@ export default class FinanceServices { data: { name: contactName, email: contactEmail, phone: contactPhone, position: contactPosition } }); + let logoImageId: string | undefined; + if (logoImage) { + const logoImageData = await uploadFile(logoImage); + logoImageId = logoImageData.id; + } + const sponsor = await prisma.sponsor.create({ data: { name, @@ -118,6 +126,7 @@ export default class FinanceServices { taxExempt, discountCode, sponsorNotes, + logoImageId, contactId: contact.sponsorContactId, sponsorTasks: { create: sponsorTasks.map((task) => ({ @@ -1200,6 +1209,8 @@ export default class FinanceServices { * @param contactPosition The position of the sponsor contact. * @param sponsorTasks An array of sponsor tasks associated with the sponsor. * @param organization The organization for which the sponsor is being edited. + * @param logoImage An optional logo image file for the sponsor. + * * @returns the edited sponsor. */ @@ -1223,7 +1234,8 @@ export default class FinanceServices { contactPhone?: string, contactPosition?: string, stockDescription?: string, - discountDescription?: string + discountDescription?: string, + logoImage?: Express.Multer.File ): Promise { if (!(await userHasPermission(submitter.userId, organization.organizationId, isHead))) throw new AccessDeniedException('Only heads can edit sponsors.'); @@ -1321,6 +1333,12 @@ export default class FinanceServices { data: { name: contactName, email: contactEmail, phone: contactPhone, position: contactPosition } }); + let logoImageId: string | undefined; + if (logoImage) { + const logoImageData = await uploadFile(logoImage); + logoImageId = logoImageData.id; + } + const updatedSponsor = await prisma.sponsor.update({ where: { sponsorId: oldSponsor.sponsorId }, data: { @@ -1335,7 +1353,8 @@ export default class FinanceServices { tier: sponsorTierId ? { connect: { sponsorTierId } } : { disconnect: true }, taxExempt, discountCode, - sponsorNotes + sponsorNotes, + ...(logoImageId && { logoImageId }) }, ...getSponsorQueryArgs(organization.organizationId) }); diff --git a/src/backend/src/transformers/finance.transformer.ts b/src/backend/src/transformers/finance.transformer.ts index 00eed4ca40..c885bfe5d2 100644 --- a/src/backend/src/transformers/finance.transformer.ts +++ b/src/backend/src/transformers/finance.transformer.ts @@ -26,6 +26,7 @@ export const sponsorTransformer = (sponsor: Prisma.SponsorGetPayload