Skip to content
Draft
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
13 changes: 13 additions & 0 deletions cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from './cypress/local-only/support/database';
import {
expireUserConfirmationCode,
getDocketEntryIdsByCaseAndEventCode,
getEmailVerificationToken,
getNewAccountVerificationCode,
} from './cypress/helpers/cypressTasks/postgres/postgres-helpers';
Expand Down Expand Up @@ -60,6 +61,18 @@ export default defineConfig({
}) {
return changeUserAccountStatus({ email, accountStatus });
},
getDocketEntryIdsByCaseAndEventCode({
docketNumber,
eventCode,
}: {
docketNumber: string;
eventCode: string;
}) {
return getDocketEntryIdsByCaseAndEventCode({
docketNumber,
eventCode,
});
},
getUserByEmail(email: string) {
return getUserByEmail(email);
},
Expand Down
17 changes: 17 additions & 0 deletions cypress/helpers/cypressTasks/postgres/postgres-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,20 @@ export async function getRecentEventsByCode(
return casesObject;
});
}

export async function getDocketEntryIdsByCaseAndEventCode({
docketNumber,
eventCode,
}: {
docketNumber: string;
eventCode: string;
}): Promise<{ docketEntryId: string }[]> {
const dbConnection = await getCypressPostgresDb();

return await dbConnection
.selectFrom('dwDocketEntry')
.where('docketNumber', '=', docketNumber)
.where('eventCode', '=', eventCode)
.select(['docketEntryId'])
.execute();
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,37 @@
import { loginAsPetitionsClerk1 } from 'cypress/helpers/authentication/login-as-helpers';

export function petitionsClerkQcsAndServesElectronicCase(docketNumber: string) {
export function petitionsClerkQcsAndServesElectronicCase(
docketNumber: string,
orderCreationOptions: {
checkOrderToShowCause?: boolean;
checkNoticeOfAttachments?: boolean;
checkOrderForAmendedPetition?: boolean;
} = {
checkOrderToShowCause: true,
checkNoticeOfAttachments: true,
checkOrderForAmendedPetition: true,
},
) {
loginAsPetitionsClerk1();
cy.visit(`/case-detail/${docketNumber}/petition-qc`)
cy.visit(`/case-detail/${docketNumber}/petition-qc`);

cy.get('[data-testid="tab-case-info"]').click();

cy.get('[data-testid="order-to-show-cause-checkbox"]').check({ force: true });
cy.get('[data-testid="notice-of-attachments-checkbox"]').check({
force: true,
});
cy.get('[data-testid="order-for-amended-petition-checkbox"]').check({
force: true,
});
if (orderCreationOptions.checkOrderToShowCause) {
cy.get('[data-testid="order-to-show-cause-checkbox"]').check({
force: true,
});
}
if (orderCreationOptions.checkNoticeOfAttachments) {
cy.get('[data-testid="notice-of-attachments-checkbox"]').check({
force: true,
});
}
if (orderCreationOptions.checkOrderForAmendedPetition) {
cy.get('[data-testid="order-for-amended-petition-checkbox"]').check({
force: true,
});
}

cy.get('[data-testid="tab-irs-notice"]').click();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { INITIAL_DOCUMENT_TYPES } from '@shared/business/entities/EntityConstants';
import { loginAsPetitioner } from 'cypress/helpers/authentication/login-as-helpers';
import { petitionsClerkQcsAndServesElectronicCase } from 'cypress/helpers/documentQC/petitions-clerk-qcs-and-serves-electronic-case';
import { externalUserCreatesElectronicCase } from 'cypress/helpers/fileAPetition/petitioner-creates-electronic-case';

describe('petitions clerk should not be able to serve a case twice', () => {
it('should show an error modal if a petitions clerk attempts to serve a case after it has already been served', () => {
loginAsPetitioner();
externalUserCreatesElectronicCase().then(docketNumber => {
petitionsClerkQcsAndServesElectronicCase(docketNumber, {
checkOrderToShowCause: false,
checkNoticeOfAttachments: false,
checkOrderForAmendedPetition: false,
});
cy.task('getDocketEntryIdsByCaseAndEventCode', {
docketNumber,
eventCode: INITIAL_DOCUMENT_TYPES.petition.eventCode,
}).as('PETITION_DOCKET_ENTRY_IDS');

cy.get('@PETITION_DOCKET_ENTRY_IDS').then(petitionDocketEntryIds => {
const petitionDocketEntryId = (
petitionDocketEntryIds as unknown as { docketEntryId: string }[]
)[0].docketEntryId;
cy.visit(
`/case-detail/${docketNumber}/documents/${petitionDocketEntryId}/review`,
);
cy.get('[data-testid="serve-case-to-irs"]').click();
cy.get('[data-testid="modal-confirm"]').click();

cy.get(
'[data-testid="serve-case-to-irs-duplicate-error-modal"]',
).should('exist');

cy.get('[data-testid="modal-button-confirm"]').click();

cy.location('pathname').should('equal', `/case-detail/${docketNumber}`);

cy.get('[data-testid="tab-drafts"]').click();
cy.get('.document-viewer--documents-list')
.children()
.should('have.length', 1);
cy.get('#docket-entry-description-0').should('have.text', 'Order');
});
});
});
});
2 changes: 2 additions & 0 deletions shared/src/business/entities/EntityConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2206,3 +2206,5 @@ export const EVENT_CODES_WITH_NO_ORDER = [
'SORI',
'TCOP',
];

export const PETITION_DUPLICATE_ERROR = 'PETITION_DUPLICATE_ERROR';
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ jest.mock(
);
jest.mock('@shared/sharedAppContext');
import '@web-api/persistence/postgres/docketEntries/mocks.jest';
import '@web-api/persistence/postgres/users/mocks.jest';
import '@web-api/persistence/postgres/utils/mocks.jest';
import {
CONTACT_TYPES,
Expand All @@ -23,6 +24,7 @@ import {
} from '@shared/business/entities/EntityConstants';
import { Case, getContactPrimary } from '@shared/business/entities/cases/Case';
import {
createISODateString,
FORMATS,
formatDateString,
formatNow,
Expand Down Expand Up @@ -135,6 +137,38 @@ describe('serveCaseToIrsInteractor', () => {
.getDocument.mockResolvedValue(testPdfDoc);
});

it('should send duplicate error notification when petition has already been served', async () => {
mockCase = {
...MOCK_CASE,
docketEntries: MOCK_CASE.docketEntries.map(entry =>
entry.eventCode === INITIAL_DOCUMENT_TYPES.petition.eventCode
? { ...entry, servedAt: createISODateString() }
: entry,
),
};

await serveCaseToIrsInteractor(
applicationContext,
mockParams,
mockPetitionsClerkUser,
);

expect(
applicationContext.getNotificationGateway().sendNotificationToUser,
).toHaveBeenCalledWith(
expect.objectContaining({
applicationContext,
clientConnectionId,
message: expect.objectContaining({
action: 'serve_to_irs_duplicate_error',
}),
userId: mockPetitionsClerkUser.userId,
}),
);

expect(updateCaseAndAssociations).not.toHaveBeenCalled();
});

it('should throw unauthorized error when user is unauthorized', async () => {
await serveCaseToIrsInteractor(
applicationContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
PAYMENT_STATUS,
PRO_SE_CHECKLIST,
SYSTEM_GENERATED_DOCUMENT_TYPES,
PETITION_DUPLICATE_ERROR,
} from '@shared/business/entities/EntityConstants';
import {
ROLE_PERMISSIONS,
Expand Down Expand Up @@ -165,7 +166,10 @@ const generateNoticeOfReceipt = async ({
CLERK_OF_THE_COURT_CONFIGURATION,
]);

const { name, title }: {
const {
name,
title,
}: {
name: string;
title: string;
} = CLERK_OF_THE_COURT_RECORD.value.current;
Expand Down Expand Up @@ -536,12 +540,23 @@ export const serveCaseToIrs = async (

const caseEntity = new Case(caseToBatch, { authorizedUser });

caseEntity.markAsSentToIRS();

if (caseEntity.isPaper) {
addDocketEntries({ caseEntity });
}

const petitionDocument = caseEntity.getPetitionDocketEntry();

if (!petitionDocument) {
throw new Error(
`Could not find petition document on case ${caseEntity.docketNumber}`,
);
}
if (petitionDocument.servedAt) {
throw new Error(PETITION_DUPLICATE_ERROR);
}

caseEntity.markAsSentToIRS();

for (const initialDocumentTypeKey of Object.keys(INITIAL_DOCUMENT_TYPES)) {
await applicationContext.getUtilities().serveCaseDocument({
applicationContext,
Expand Down Expand Up @@ -579,14 +594,6 @@ export const serveCaseToIrs = async (
);
}

const petitionDocument = caseEntity.getPetitionDocketEntry();

if (!petitionDocument) {
throw new Error(
`Could not find petitioner document on case ${caseEntity.docketNumber}`,
);
}

const formattedFiledDate = formatDateString(
petitionDocument.filingDate,
FORMATS.MONTH_DAY_YEAR,
Expand Down Expand Up @@ -710,19 +717,30 @@ export const serveCaseToIrs = async (
},
userId: authorizedUser.userId,
});
} catch (err) {
} catch (err: any) {
applicationContext.logger.error('Error serving case to IRS', {
docketNumber,
error: err,
});
await applicationContext.getNotificationGateway().sendNotificationToUser({
applicationContext,
clientConnectionId,
message: {
action: 'serve_to_irs_error',
},
userId: authorizedUser?.userId || '',
});
if (err.message === PETITION_DUPLICATE_ERROR) {
await applicationContext.getNotificationGateway().sendNotificationToUser({
applicationContext,
clientConnectionId,
message: {
action: 'serve_to_irs_duplicate_error',
},
userId: authorizedUser?.userId || '',
});
} else {
await applicationContext.getNotificationGateway().sendNotificationToUser({
applicationContext,
clientConnectionId,
message: {
action: 'serve_to_irs_error',
},
userId: authorizedUser?.userId || '',
});
}
}
};

Expand Down
6 changes: 6 additions & 0 deletions web-api/src/notifications/sendNotificationToUserTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ type ServeToIrsErrorNotification = {
action: 'serve_to_irs_error';
};

type ServeToIrsDuplicateErrorNotification = {
action: 'serve_to_irs_duplicate_error';
error?: string;
};

type BatchDownloadDocketGeneratedNotification = {
action: 'batch_download_docket_generated';
filesCompleted: number;
Expand Down Expand Up @@ -195,6 +200,7 @@ export type NotificationMessage =
| AdminContactInitialUpdateCompleteNotification
| ServeToIrsCompleteNotification
| ServeToIrsErrorNotification
| ServeToIrsDuplicateErrorNotification
| BatchDownloadDocketGeneratedNotification
| NoticeGenerationUpdatedNotification
| PaperServiceStartedNotification
Expand Down
3 changes: 3 additions & 0 deletions web-client/src/presenter/presenter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ import { serveDocumentErrorSequence } from './sequences/serveDocumentErrorSequen
import { servePaperFiledDocumentSequence } from './sequences/servePaperFiledDocumentSequence';
import { serveThirtyDayNoticeOfTrialSequence } from './sequences/serveThirtyDayNoticeOfTrialSequence';
import { serveToIrsCompleteSequence } from './sequences/serveToIrsCompleteSequence';
import { serveToIrsDuplicateErrorSequence } from './sequences/serveToIrsDuplicateErrorSequence';
import { serveToIrsErrorSequence } from './sequences/serveToIrsErrorSequence';
import { setBlockedCaseReportProcedureTypeSequence } from '@web-client/presenter/sequences/Reports/BlockedCases/setBlockedCaseReportProcedureTypeSequence';
import { setCaseDetailPageTabSequence } from './sequences/setCaseDetailPageTabSequence';
Expand Down Expand Up @@ -1262,6 +1263,8 @@ export const presenterSequences = {
serveThirtyDayNoticeOfTrialSequence:
serveThirtyDayNoticeOfTrialSequence as unknown as Function,
serveToIrsCompleteSequence: serveToIrsCompleteSequence as unknown as Function,
serveToIrsDuplicateErrorSequence:
serveToIrsDuplicateErrorSequence as unknown as Function,
serveToIrsErrorSequence: serveToIrsErrorSequence as unknown as Function,
setBlockedCaseReportProcedureTypeSequence,
setCaseDetailPageTabSequence:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { CerebralTest } from 'cerebral/test';
import { applicationContextForClient as applicationContext } from '@web-client/test/createClientTestApplicationContext';
import { presenter } from '../presenter-mock';
import { serveToIrsDuplicateErrorSequence } from './serveToIrsDuplicateErrorSequence';

describe('serveToIrsDuplicateErrorSequence', () => {
let cerebralTest;

beforeAll(() => {
presenter.providers.applicationContext = applicationContext;
presenter.sequences = {
serveToIrsDuplicateErrorSequence,
};
cerebralTest = CerebralTest(presenter);
});

it('should set state.modal.showModal to ServeCaseToIrsDuplicateErrorModal', async () => {
cerebralTest.setState('modal.showModal', false);

await cerebralTest.runSequence('serveToIrsDuplicateErrorSequence', {
showModal: 'ServeCaseToIrsDuplicateErrorModal',
});

expect(cerebralTest.getState('modal.showModal')).toEqual(
'ServeCaseToIrsDuplicateErrorModal',
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { clearModalStateAction } from '@web-client/presenter/actions/clearModalStateAction';
import { setShowModalAction } from '../actions/setShowModalAction';
import { unsetWaitingForResponseAction } from '@web-client/presenter/actions/unsetWaitingForResponseAction';

export const serveToIrsDuplicateErrorSequence = [
unsetWaitingForResponseAction,
clearModalStateAction,
setShowModalAction,
];
5 changes: 5 additions & 0 deletions web-client/src/providers/socketRouter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ describe('socketRouter', () => {
message: { action: 'serve_to_irs_error' },
sequence: 'serveToIrsErrorSequence',
},
{
args: { showModal: 'ServeCaseToIrsDuplicateErrorModal' },
message: { action: 'serve_to_irs_duplicate_error' },
sequence: 'serveToIrsDuplicateErrorSequence',
},
{
message: { action: 'update_trial_session_complete' },
sequence: 'updateTrialSessionCompleteSequence',
Expand Down
Loading
Loading