Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add READY_FOR_PICKUP UPR status and misc refactors/bugfixes #180

Merged
merged 3 commits into from
Jan 3, 2024
Merged
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
28 changes: 24 additions & 4 deletions backend/emails/emails.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ const sendEmailUPRPurchasedToReporter = async (upr) => {
`Your UW Finance Purchase Request has been purchased! When the item is ready to be picked up, we will let you know.`
) +
(await getUPRTicketInfoHTML(upr)) +
+getTicketLinkHTML(upr.path)
getTicketLinkHTML(upr.path)
const To = await getEmailToSection(upr.reporter_id, [
EMAIL_RECIPIENTS.reporter,
])
Expand Down Expand Up @@ -205,6 +205,25 @@ const sendEmailUPRPurchasedToCoordinator = async (upr) => {
})
}

const sendEmailUPRReadyForPickupToCoordinator = async (upr) => {
const Subject = `[Ready for pickup] ${upr.codename}`
const HTMLPart =
getMainMessageHTML(
'Your UW Finance Request is ready to be picked up! Please view the ticket below for pickup instructions and confirm when you have picked it up.'
) +
(await getUPRTicketInfoHTML(upr)) +
getTicketLinkHTML(upr.path)
const To = await getEmailToSection(upr.reporter_id, [
EMAIL_RECIPIENTS.reporter,
])

await sendEmail({
Subject,
HTMLPart,
To,
})
}

const sendEmailPPRApprovedToReporter = async (ppr) => {
const Subject = `[Ready to Buy] ${ppr.codename}`
const HTMLPart =
Expand Down Expand Up @@ -287,7 +306,7 @@ const sendEmailSFReimbursementRequestToCoordinator = async (sf) => {
const Subject = `[Action Needed] Submit Reimbursement Request ${sf.codename}`
const HTMLPart =
getMainMessageHTML(
`Claim has been submitted for ${sf.codename}! Please review it and submit a reimbursement request. Visit the ticket link below to confirm you have submitted the reimbursement request.`
`Please review the claim and submit a reimbursement request for ${sf.codename}. Once submitted, please visit the ticket link below to confirm that you have submitted it.`
) +
(await getSFTicketInfoHTML(sf)) +
getTicketLinkHTML(sf.path)
Expand All @@ -305,7 +324,7 @@ const sendEmailSFConfirmReimbursementSubmitToCoordinator = async (sf) => {
const Subject = `[Action Needed] Confirm Reimbursement Received ${sf.codename}`
const HTMLPart =
getMainMessageHTML(
`Please visit the ticket link below to confirm you have received the reimbursement for ${sf.codename}.`
`Once the sponsorship fund for ${sf.codename} has reimbursed us, please visit the ticket link below to confirm it.`
) +
(await getSFTicketInfoHTML(sf)) +
getTicketLinkHTML(sf.path)
Expand All @@ -322,7 +341,7 @@ const sendEmailSFConfirmReimbursementSubmitToCoordinator = async (sf) => {
const sendEmailSFReimbursementReceivedToTeam = async (sf) => {
const Subject = `[Reimbursed] ${sf.codename}`
const HTMLPart =
getMainMessageHTML(`${sf.codename} has been reimbursed.`) +
getMainMessageHTML(`${sf.codename} has been reimbursed!`) +
(await getSFTicketInfoHTML(sf)) +
getTicketLinkHTML(sf.path)
const To = await getEmailToSection(sf.reporter_id, [
Expand Down Expand Up @@ -662,6 +681,7 @@ module.exports = {
sendEmailUPRApprovedToCoordinator,
sendEmailUPRPurchasedToReporter,
sendEmailUPRPurchasedToCoordinator,
sendEmailUPRReadyForPickupToCoordinator,
sendEmailPPRApprovedToReporter,
sendEmailPPRCreatedToApprovers,
sendEmailPPRPurchasedAndReceiptsSubmittedToCoordinator,
Expand Down
4 changes: 2 additions & 2 deletions backend/models/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ const UPR_STATUS = [
'SEEKING_APPROVAL',
'SENT_TO_COORDINATOR',
'ORDERED',
'READY_FOR_PICKUP',
'PICKED_UP',
]

const UPR_STATUS_FUNDING_SPENT = ['ORDERED', 'PICKED_UP']
const UPR_STATUS_FUNDING_SPENT = ['ORDERED', 'READY_FOR_PICKUP', 'PICKED_UP']

const APPROVAL_LEVELS = Object.freeze({
director_approval: 'director_approval',
Expand Down
4 changes: 3 additions & 1 deletion backend/service/personalpurchases.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,17 @@ const createPersonalPurchase = async (body) => {
}

const updatePersonalPurchase = async (id, body) => {
// READY_TO_BUY -> PURCHASED_AND_RECEIPTS_SUBMITTED
const newPurchaseTicket = PersonalPurchase.findByIdAndUpdate(id, body, {
new: true,
})
const annotatedPPR = await getPersonalPurchase(id)

// READY_TO_BUY -> PURCHASED_AND_RECEIPTS_SUBMITTED
if (body?.status === 'PURCHASED_AND_RECEIPTS_SUBMITTED') {
await sendEmailPPRPurchasedAndReceiptsSubmittedToCoordinator(
annotatedPPR
)
// PURCHASED_AND_RECEIPTS_SUBMITTED -> REPORTER_PAID
} else if (body?.status === 'REPORTER_PAID') {
await sendEmailPPRReimbursedToReporter(annotatedPPR)
}
Expand Down
8 changes: 5 additions & 3 deletions backend/service/sponsorshipfunds.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,19 @@ const updateSponsorshipFund = async (id, body) => {
const status = annotatedSponsorshipFund.status

if (status === 'CLAIM_SUBMITTED') {
sendEmailSFReimbursementRequestToCoordinator(annotatedSponsorshipFund)
await sendEmailSFReimbursementRequestToCoordinator(
annotatedSponsorshipFund
)
}

if (status === 'SUBMITTED_TO_SF') {
sendEmailSFConfirmReimbursementSubmitToCoordinator(
await sendEmailSFConfirmReimbursementSubmitToCoordinator(
annotatedSponsorshipFund
)
}

if (status === 'REIMBURSED') {
sendEmailSFReimbursementReceivedToTeam(annotatedSponsorshipFund)
await sendEmailSFReimbursementReceivedToTeam(annotatedSponsorshipFund)
}

return newSponsorshipFund
Expand Down
14 changes: 8 additions & 6 deletions backend/service/uwfinancepurchases.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {
sendEmailUPRApprovedToCoordinator,
sendEmailUPRPurchasedToCoordinator,
sendEmailUPRPurchasedToReporter,
sendEmailUPRReadyForPickupToCoordinator,
} = require('../emails/emails')

const getAllUWFinancePurchases = () => {
Expand All @@ -33,26 +34,27 @@ const createNewUWFinancePurchase = async (body) => {
}

const updateUWFinancePurchase = async (id, body) => {
const existingPurchaseTicket = await getUWFinancePurchase(id)
// SENT_TO_COORDINATOR -> ORDERED
const newPurchaseTicket = await UWFinancePurchase.findByIdAndUpdate(
id,
body,
{
new: true,
}
)
if (
existingPurchaseTicket.status === 'SENT_TO_COORDINATOR' &&
body.status === 'ORDERED'
) {
// SENT_TO_COORDINATOR -> ORDERED
if (body.status === 'ORDERED') {
const annotatedUPR = await getUWFinancePurchase(id)
const emails = [
sendEmailUPRPurchasedToCoordinator(annotatedUPR),
sendEmailUPRPurchasedToReporter(annotatedUPR),
]
await Promise.all(emails)
}
// ORDERED -> READY_FOR_PICKUP
if (body.status === 'READY_FOR_PICKUP') {
const annotatedUPR = await getUWFinancePurchase(id)
await sendEmailUPRReadyForPickupToCoordinator(annotatedUPR)
}
return newPurchaseTicket
}

Expand Down
11 changes: 3 additions & 8 deletions frontend/src/components/ConfirmationModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,14 @@ import {
Button,
} from '@chakra-ui/react'

const ConfirmationModal = ({ onConfirm, isOpen, onClose }) => {
const ConfirmationModal = ({ title, body, onConfirm, isOpen, onClose }) => {
return (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Submission (Placeholder)</ModalHeader>
<ModalHeader>{title}</ModalHeader>
<ModalCloseButton />
<ModalBody>
<div>
Are you sure you want to submit this, you cant undo bla
bla bla (PLACEHOLDER)
</div>
</ModalBody>
<ModalBody>{body}</ModalBody>

<ModalFooter>
<Button colorScheme="red" mr={3} onClick={onClose}>
Expand Down
76 changes: 38 additions & 38 deletions frontend/src/components/TicketContent/SFAdminContentTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
Link,
useDisclosure,
} from '@chakra-ui/react'
import React from 'react'
import React, { useState } from 'react'
import { useGetPreserveParamsHref } from '../../hooks/hooks'
import { useGetCurrentTicket } from '../../hooks/hooks'
import { TICKET_ENDPOINTS } from '../../constants'
Expand All @@ -25,35 +25,45 @@ const SFAdminContentTable = () => {
onOpen: onOpenConfirmation,
onClose: onCloseConfirmation,
} = useDisclosure()
const [confirmationModalTitleText, setConfirmationModalTitleText] =
useState('')
const [confirmationModalBodyText, setConfirmationModalBodyText] =
useState('')
const transitionStatusButtonText = {
ALLOCATED: 'Submit Claim',
CLAIM_SUBMITTED: 'Confirm Reimbursement Request Submitted',
SUBMITTED_TO_SF: 'Confirm SF Reimbursed',
}

const transitionStatusText = (status) => {
if (status === 'ALLOCATED') {
return 'Submit Claim'
}
if (status === 'CLAIM_SUBMITTED') {
return 'Confirm Reimbursement Submission'
const handleOpenConfirmation = () => {
setConfirmationModalTitleText(
transitionStatusButtonText[currentTicket.status]
)
if (currentTicket.status === 'ALLOCATED') {
setConfirmationModalBodyText(
`Are you sure you want to submit this claim? This will send an email to the Finance Coordinator and prompt them to submit a reimbursement request for ${currentTicket.codename}`
)
}
if (status === 'SUBMITTED_TO_SF') {
return 'Confirm Reimbursement'
if (currentTicket.status === 'CLAIM_SUBMITTED') {
setConfirmationModalBodyText(
`Please confirm that you have submitted a reimbursement request for ${currentTicket.codename}.`
)
}
if (status === 'REIMBURSED') {
return 'Reimbursement Confirmed'
if (currentTicket.status === 'SUBMITTED_TO_SF') {
setConfirmationModalBodyText(
`Please confirm that the sponsorship fund for ${currentTicket.codename} has successfully reimbursed WATonomous.`
)
}
onOpenConfirmation()
}
const nextStatus = (status) => {
if (status === 'ALLOCATED') {
return 'CLAIM_SUBMITTED'
const handleUpdateStatus = async () => {
const nextStatus = {
ALLOCATED: 'CLAIM_SUBMITTED',
CLAIM_SUBMITTED: 'SUBMITTED_TO_SF',
SUBMITTED_TO_SF: 'REIMBURSED',
}
if (status === 'CLAIM_SUBMITTED') {
return 'SUBMITTED_TO_SF'
}
if (status === 'SUBMITTED_TO_SF') {
return 'REIMBURSED'
}
}
const handleUpdateStatus = async (nextStatus) => {
const payload = {
status: nextStatus,
status: nextStatus[currentTicket.status],
}
await axiosPreset.patch(
`${TICKET_ENDPOINTS.SF}/${currentTicket._id}`,
Expand All @@ -73,30 +83,20 @@ const SFAdminContentTable = () => {
<Center pb="7px" gap="10px">
{isConfirmationOpen && (
<ConfirmationModal
title={confirmationModalTitleText}
body={confirmationModalBodyText}
onClose={onCloseConfirmation}
isOpen={isConfirmationOpen}
onConfirm={() =>
handleUpdateStatus(
nextStatus(currentTicket.status)
)
}
onConfirm={handleUpdateStatus}
/>
)}
{currentTicket.status !== 'REIMBURSED' && (
<Button
colorScheme="blue"
size="sm"
disabled={
currentTicket?.po_number?.length +
currentTicket?.requisition_number
?.length ===
0
}
onClick={() => {
onOpenConfirmation()
}}
onClick={handleOpenConfirmation}
>
{transitionStatusText(currentTicket.status)}
{transitionStatusButtonText[currentTicket.status]}
</Button>
)}
{/* can remove getPreserveParamsHref if it does not make sense to preserve params */}
Expand Down
35 changes: 34 additions & 1 deletion frontend/src/components/TicketContent/UPRAdminContentTable.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Button, Center, Heading, Table, Tbody, VStack } from '@chakra-ui/react'
import React, { useState } from 'react'
import React, { useEffect, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { useSetRecoilState } from 'recoil'
import TicketContentTableRow from './TicketContentTableRow'
import { allTicketsState } from '../../state/atoms'
Expand All @@ -9,6 +10,7 @@ import { getAllTickets } from '../../utils/globalSetters'
import { useGetCurrentTicket } from '../../hooks/hooks'

const UPRAdminContentTable = () => {
const location = useLocation()
const currentTicket = useGetCurrentTicket()
const [reqNum, setReqNum] = useState(currentTicket.requisition_number)
const [poNum, setPoNum] = useState(currentTicket.po_number)
Expand All @@ -23,6 +25,15 @@ const UPRAdminContentTable = () => {
setChanged(true)
}

useEffect(() => {
setReqNum(currentTicket.requisition_number ?? '')
setPoNum(currentTicket.po_number ?? '')
}, [
location.pathname,
currentTicket.requisition_number,
currentTicket.po_number,
])

const saveFields = async () => {
const payload = {
requisition_number: reqNum,
Expand Down Expand Up @@ -50,6 +61,18 @@ const UPRAdminContentTable = () => {
setChanged(false)
}

const transitionToReadyForPickup = async () => {
const payload = {
status: 'READY_FOR_PICKUP',
}
await axiosPreset.patch(
`${TICKET_ENDPOINTS.UPR}/${currentTicket._id}`,
payload
)
await getAllTickets(setAllTickets)
setChanged(false)
}

const transitionToPickedUp = async () => {
const payload = {
status: 'PICKED_UP',
Expand Down Expand Up @@ -98,6 +121,16 @@ const UPRAdminContentTable = () => {
</Button>
)}
{currentTicket.status === 'ORDERED' && (
<Button
colorScheme="blue"
size="sm"
mr="20px"
onClick={transitionToReadyForPickup}
>
Transition To Ready for Pickup
</Button>
)}
{currentTicket.status === 'READY_FOR_PICKUP' && (
<Button
colorScheme="blue"
size="sm"
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/hooks/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ export const useGetCurrentTicket = () => {
const getCurrentTicket = useCallback(() => {
const splitPath = location.pathname.split('/')
if (splitPath.length !== 3) return {}
let ticketType = splitPath[1].toUpperCase()
let ticketType = splitPath[1]
const ticketId = parseInt(splitPath[2])
if (ticketType === 'claim') {
ticketType = TICKET_TYPES.SF
}
// in case of claim view for instance
// return {} in case that allTickets is empty, means its still loading
if (Object.keys(allTickets).length === 0) return {}
const currentTicketData = allTickets[TICKET_TYPES[ticketType]].find(
(ticket) => ticket._id === ticketId
)
const currentTicketData = allTickets[
TICKET_TYPES[ticketType.toUpperCase()]
].find((ticket) => ticket._id === ticketId)
// if not found, will return null
return currentTicketData
}, [allTickets, location.pathname])
Expand Down
Loading