Skip to content

Commit

Permalink
Merge pull request #461 from bcgov/DMM-403-prevent-proposal-withdrawa…
Browse files Browse the repository at this point in the history
…l-after-deadline

DMM-403/DMM-405 Restrict proposal withdrawals + adjust notifications
  • Loading branch information
sutherlanda authored Feb 14, 2024
2 parents 99a82ad + e3b6ce6 commit d6fbfc7
Show file tree
Hide file tree
Showing 9 changed files with 92 additions and 177 deletions.
44 changes: 19 additions & 25 deletions src/back-end/lib/mailer/notifications/proposal/code-with-us.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,13 @@ import * as templates from "back-end/lib/mailer/templates";
import { makeSend } from "back-end/lib/mailer/transport";
import React from "react";
import { CONTACT_EMAIL, EMPTY_STRING } from "shared/config";
import {
CWUOpportunity,
isCWUOpportunityClosed
} from "shared/lib/resources/opportunity/code-with-us";
import { CWUOpportunity } from "shared/lib/resources/opportunity/code-with-us";
import {
CWUProposal,
CWUProposalSlim
} from "shared/lib/resources/proposal/code-with-us";
import { AuthenticatedSession } from "shared/lib/resources/session";
import { User } from "shared/lib/resources/user";
import { User, UserType } from "shared/lib/resources/user";
import { Id } from "shared/lib/types";
import { getValidValue } from "shared/lib/validation";

Expand Down Expand Up @@ -140,7 +137,6 @@ export async function handleCWUProposalWithdrawn(
proposalId: Id,
session: AuthenticatedSession
): Promise<void> {
//Notify the opportunity author if the opportunity is in an awardable state
const proposal = getValidValue(
await db.readOneCWUProposal(connection, proposalId, session),
null
Expand All @@ -155,13 +151,6 @@ export async function handleCWUProposalWithdrawn(
),
null
);
// Need to read opportunityAuthor separate here, as this session will not be allowed to read from opportunity itself
const opportunityAuthor =
proposal &&
getValidValue(
await db.readOneCWUOpportunityAuthor(connection, proposal.opportunity.id),
null
);

if (proposal && opportunity) {
const withdrawnProponent =
Expand All @@ -170,24 +159,29 @@ export async function handleCWUProposalWithdrawn(
await db.readOneUser(connection, proposal.createdBy.id),
null
);
// Notify opportunity author if opportunity is closed
if (
opportunityAuthor &&
withdrawnProponent &&
isCWUOpportunityClosed(opportunity)
) {
await withdrawnCWUProposalSubmission(
opportunityAuthor,
withdrawnProponent,
opportunity
);
}
// Notify proposal author
if (withdrawnProponent) {
await withdrawnCWUProposalSubmissionProposalAuthor(
withdrawnProponent,
opportunity
);

// Notify admins that the proposal has been withdrawn
const adminUsers =
getValidValue(
await db.readManyUsersByRole(connection, UserType.Admin),
null
) || [];
await Promise.all(
adminUsers.map(
async (admin) =>
await withdrawnCWUProposalSubmission(
admin,
withdrawnProponent,
opportunity
)
)
);
}
}
}
Expand Down
44 changes: 19 additions & 25 deletions src/back-end/lib/mailer/notifications/proposal/sprint-with-us.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,13 @@ import * as templates from "back-end/lib/mailer/templates";
import { makeSend } from "back-end/lib/mailer/transport";
import React from "react";
import { CONTACT_EMAIL, EMPTY_STRING } from "shared/config";
import {
isSWUOpportunityClosed,
SWUOpportunity
} from "shared/lib/resources/opportunity/sprint-with-us";
import { SWUOpportunity } from "shared/lib/resources/opportunity/sprint-with-us";
import {
SWUProposal,
SWUProposalSlim
} from "shared/lib/resources/proposal/sprint-with-us";
import { AuthenticatedSession } from "shared/lib/resources/session";
import { User } from "shared/lib/resources/user";
import { User, UserType } from "shared/lib/resources/user";
import { Id } from "shared/lib/types";
import { getValidValue } from "shared/lib/validation";

Expand Down Expand Up @@ -146,7 +143,6 @@ export async function handleSWUProposalWithdrawn(
proposalId: Id,
session: AuthenticatedSession
): Promise<void> {
//Notify the opportunity author if the opportunity is in an awardable state
const proposal = getValidValue(
await db.readOneSWUProposal(connection, proposalId, session),
null
Expand All @@ -161,13 +157,6 @@ export async function handleSWUProposalWithdrawn(
),
null
);
// Need to read opportunityAuthor separate here, as this session will not be allowed to read from opportunity itself
const opportunityAuthor =
proposal &&
getValidValue(
await db.readOneSWUOpportunityAuthor(connection, proposal.opportunity.id),
null
);

if (proposal && opportunity) {
const withdrawnProponent =
Expand All @@ -176,24 +165,29 @@ export async function handleSWUProposalWithdrawn(
await db.readOneUser(connection, proposal.createdBy.id),
null
);
// Notify opportunity author if opportunity is closed
if (
opportunityAuthor &&
withdrawnProponent &&
isSWUOpportunityClosed(opportunity)
) {
await withdrawnSWUProposalSubmission(
opportunityAuthor,
withdrawnProponent,
opportunity
);
}
// Notify proposal author
if (withdrawnProponent) {
await withdrawnSWUProposalSubmissionProposalAuthor(
withdrawnProponent,
opportunity
);

// Notify admins that the proposal has been withdrawn
const adminUsers =
getValidValue(
await db.readManyUsersByRole(connection, UserType.Admin),
null
) || [];
await Promise.all(
adminUsers.map(
async (admin) =>
await withdrawnSWUProposalSubmission(
admin,
withdrawnProponent,
opportunity
)
)
);
}
}
}
Expand Down
44 changes: 19 additions & 25 deletions src/back-end/lib/mailer/notifications/proposal/team-with-us.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,13 @@ import * as templates from "back-end/lib/mailer/templates";
import { makeSend } from "back-end/lib/mailer/transport";
import React from "react";
import { CONTACT_EMAIL, EMPTY_STRING } from "shared/config";
import {
isTWUOpportunityClosed,
TWUOpportunity
} from "shared/lib/resources/opportunity/team-with-us";
import { TWUOpportunity } from "shared/lib/resources/opportunity/team-with-us";
import {
TWUProposal,
TWUProposalSlim
} from "shared/lib/resources/proposal/team-with-us";
import { AuthenticatedSession } from "shared/lib/resources/session";
import { User } from "shared/lib/resources/user";
import { User, UserType } from "shared/lib/resources/user";
import { Id } from "shared/lib/types";
import { getValidValue } from "shared/lib/validation";

Expand Down Expand Up @@ -146,7 +143,6 @@ export async function handleTWUProposalWithdrawn(
proposalId: Id,
session: AuthenticatedSession
): Promise<void> {
//Notify the opportunity author if the opportunity is in an award-able state
const proposal = getValidValue(
await db.readOneTWUProposal(connection, proposalId, session),
null
Expand All @@ -161,13 +157,6 @@ export async function handleTWUProposalWithdrawn(
),
null
);
// Need to read opportunityAuthor separate here, as this session will not be allowed to read from opportunity itself
const opportunityAuthor =
proposal &&
getValidValue(
await db.readOneTWUOpportunityAuthor(connection, proposal.opportunity.id),
null
);

if (proposal && opportunity) {
const withdrawnProponent =
Expand All @@ -176,24 +165,29 @@ export async function handleTWUProposalWithdrawn(
await db.readOneUser(connection, proposal.createdBy.id),
null
);
// Notify opportunity author if opportunity is closed
if (
opportunityAuthor &&
withdrawnProponent &&
isTWUOpportunityClosed(opportunity)
) {
await withdrawnTWUProposalSubmission(
opportunityAuthor,
withdrawnProponent,
opportunity
);
}
// Notify proposal author
if (withdrawnProponent) {
await withdrawnTWUProposalSubmissionProposalAuthor(
withdrawnProponent,
opportunity
);

// Notify admins that the proposal has been withdrawn
const adminUsers =
getValidValue(
await db.readManyUsersByRole(connection, UserType.Admin),
null
) || [];
await Promise.all(
adminUsers.map(
async (admin) =>
await withdrawnTWUProposalSubmission(
admin,
withdrawnProponent,
opportunity
)
)
);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1169,19 +1169,7 @@ export const component: Tab.Component<State, Msg> = {
case CWUProposalStatus.UnderReview:
case CWUProposalStatus.Evaluated:
case CWUProposalStatus.Awarded:
return component_.page.actions.links([
{
children: "Withdraw",
symbol_: leftPlacement(iconLinkSymbol("ban")),
button: true,
outline: true,
color: "c-nav-fg-alt",
disabled,
loading: isWithdrawLoading,
onClick: () =>
dispatch(adt("showModal", "withdrawAfterDeadline" as const))
}
]);
return component_.page.actions.none();
case CWUProposalStatus.Withdrawn:
if (isAcceptingProposals) {
return component_.page.actions.links([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1128,19 +1128,7 @@ export const component: Tab.Component<State, Msg> = {
case SWUProposalStatus.EvaluatedCodeChallenge:
case SWUProposalStatus.EvaluatedTeamScenario:
case SWUProposalStatus.Awarded:
return component_.page.actions.links([
{
children: "Withdraw",
symbol_: leftPlacement(iconLinkSymbol("ban")),
button: true,
outline: true,
color: "c-nav-fg-alt",
disabled,
loading: isWithdrawLoading,
onClick: () =>
dispatch(adt("showModal", "withdrawAfterDeadline" as const))
}
]);
return component_.page.actions.none();
case SWUProposalStatus.Withdrawn:
if (isAcceptingProposals) {
return component_.page.actions.links([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1146,19 +1146,7 @@ export const component: Tab.Component<State, Msg> = {
case TWUProposalStatus.EvaluatedResourceQuestions:
case TWUProposalStatus.EvaluatedChallenge:
case TWUProposalStatus.Awarded:
return component_.page.actions.links([
{
children: "Withdraw",
symbol_: leftPlacement(iconLinkSymbol("ban")),
button: true,
outline: true,
color: "c-nav-fg-alt",
disabled,
loading: isWithdrawLoading,
onClick: () =>
dispatch(adt("showModal", "withdrawAfterDeadline" as const))
}
]);
return component_.page.actions.none();
case TWUProposalStatus.Withdrawn:
if (isAcceptingProposals) {
return component_.page.actions.links([
Expand Down
23 changes: 8 additions & 15 deletions src/shared/lib/resources/proposal/code-with-us.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,36 +319,29 @@ export function isValidStatusChange(

case CWUProposalStatus.UnderReview:
return (
(([
CWUProposalStatus.Evaluated,
CWUProposalStatus.Disqualified
].includes(to) &&
userType !== UserType.Vendor) ||
(to === CWUProposalStatus.Withdrawn &&
userType === UserType.Vendor)) &&
[CWUProposalStatus.Evaluated, CWUProposalStatus.Disqualified].includes(
to
) &&
userType !== UserType.Vendor &&
hasProposalDeadlinePassed
);

case CWUProposalStatus.Evaluated:
return (
(([
[
CWUProposalStatus.Evaluated,
CWUProposalStatus.Awarded,
CWUProposalStatus.NotAwarded,
CWUProposalStatus.Disqualified
].includes(to) &&
userType !== UserType.Vendor) ||
(to === CWUProposalStatus.Withdrawn &&
userType === UserType.Vendor)) &&
userType !== UserType.Vendor &&
hasProposalDeadlinePassed
);

case CWUProposalStatus.Awarded:
return (
((to === CWUProposalStatus.Disqualified &&
userType !== UserType.Vendor) ||
(to === CWUProposalStatus.Withdrawn &&
userType === UserType.Vendor)) &&
to === CWUProposalStatus.Disqualified &&
userType !== UserType.Vendor &&
hasProposalDeadlinePassed
);

Expand Down
Loading

0 comments on commit d6fbfc7

Please sign in to comment.