Skip to content

Commit

Permalink
Merge pull request #4421 from ustaxcourt/ustc-4372-reconciliation-report
Browse files Browse the repository at this point in the history
ustc #4372: allow full ISO date + end date for reconciliation report
  • Loading branch information
mmarcotte committed Apr 26, 2024
2 parents a7b6f3e + c1de057 commit b6d21a0
Show file tree
Hide file tree
Showing 7 changed files with 395 additions and 40 deletions.
21 changes: 21 additions & 0 deletions docs/api/v2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,27 @@ paths:
required: true
schema:
type: string
examples:
justDate:
summary: A specific date
value: 2022-12-25
withToday:
summary: The current date
value: today
- in: query
name: end
description: A time in HH:mm format (EST), to limit docket entries served before specified time
required: false
schema:
type: string
example: '23:00'
- in: query
name: start
description: A time in HH:mm format (EST), to limit docket entries served after specified time
required: false
schema:
type: string
example: '05:30'
responses:
'200':
description: reconciliation report of served docket entries
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('getReconciliationReportInteractor', () => {
getReconciliationReportInteractor(applicationContext, {
reconciliationDate: undefined,
}),
).rejects.toThrow('must be formatted');
).rejects.toThrow('Must be valid reconciliation date');
});
it('should throw an error if date is in the future', async () => {
await expect(
Expand Down Expand Up @@ -163,4 +163,45 @@ describe('getReconciliationReportInteractor', () => {
.calls[0][0].docketNumbers,
).toEqual(['135-20']);
});

//Given date may contain ISO time component
it('should accept ISO dates + start time', async () => {
const startDate = '2020-01-01';
const timeStart = '05:00';
await expect(
getReconciliationReportInteractor(applicationContext, {
reconciliationDate: startDate,
start: timeStart,
}),
).resolves.not.toThrow();
});

//Caller may provide two date arguments
it('should accept starting date and start+end times', async () => {
const startDate = '2021-01-05';
const timeStart = '05:00';
const timeEnd = '09:00';
const docketEntries = [
{
docketEntryId: '3d27e02e-6954-4595-8b3f-0e91bbc1b51e',
docketNumber: '135-20',
documentTitle: 'Petition',
eventCode: 'P',
filedBy: 'Petr. Kaitlin Chaney',
filingDate: '2021-01-05T21:14:09.031Z',
servedAt: '2021-01-05T21:14:09.031Z',
},
] as any;

applicationContext
.getPersistenceGateway()
.getReconciliationReport.mockReturnValue(docketEntries);

const result = await getReconciliationReportInteractor(applicationContext, {
end: timeEnd,
reconciliationDate: startDate,
start: timeStart,
});
expect(result.reconciliationDate).toBe(startDate);
});
});
64 changes: 39 additions & 25 deletions shared/src/business/useCases/getReconciliationReportInteractor.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
import { DocketEntryDynamoRecord } from '../../../../web-api/src/persistence/dynamo/dynamoTypes';
import {
FORMATS,
PATTERNS,
createEndOfDayISO,
createStartOfDayISO,
formatNow,
isValidDateString,
isValidReconciliationDate,
normalizeIsoDateRange,
} from '../../business/utilities/DateHandler';
import { InvalidRequest } from '@web-api/errors/errors';
import {
ROLE_PERMISSIONS,
isAuthorized,
} from '../../authorization/authorizationClientService';
import { ReconciliationReportEntry } from '../entities/ReconciliationReportEntry';
import { UnauthorizedError } from '@web-api/errors/errors';

const isValidDate = dateString => {
const dateInputValid = PATTERNS.YYYYMMDD.test(dateString);
const todayDate = formatNow(FORMATS.YYYYMMDD);
const dateLessthanOrEqualToToday = dateString <= todayDate;
return dateInputValid && dateLessthanOrEqualToToday;
};
function isValidTime(time: string): boolean {
return isValidDateString(time, [FORMATS.TIME_24_HOUR]);
}

/**
* getReconciliationReportInteractor
Expand All @@ -30,35 +28,50 @@ const isValidDate = dateString => {
*/
export const getReconciliationReportInteractor = async (
applicationContext: IApplicationContext,
{ reconciliationDate }: { reconciliationDate: string },
{
end: timeEnd,
reconciliationDate,
start: timeStart,
}: {
reconciliationDate: string;
end?: string;
start?: string;
},
) => {
const authorizedUser = applicationContext.getCurrentUser();

if (!isAuthorized(authorizedUser, ROLE_PERMISSIONS.SERVICE_SUMMARY_REPORT)) {
throw new UnauthorizedError('Unauthorized');
}

if (reconciliationDate === 'today') {
reconciliationDate = formatNow(FORMATS.YYYYMMDD);
} else {
const dateInputValid = isValidDate(reconciliationDate);
if (!dateInputValid) {
throw new Error(
'Date must be formatted as YYYY-MM-DD and not later than today',
);
}
const effectiveTimeStart = isValidTime(timeStart!) ? timeStart : '00:00';
const effectiveTimeEnd = isValidTime(timeEnd!)
? `${timeEnd}:59.999`
: '23:59:59.999';

const effectiveReconciliationDate =
reconciliationDate == 'today'
? formatNow(FORMATS.YYYYMMDD)
: reconciliationDate;

if (!isValidReconciliationDate(effectiveReconciliationDate)) {
throw new InvalidRequest(
'Must be valid reconciliation date and not later than today',
);
}

const [year, month, day] = reconciliationDate.split('-');
const reconciliationDateStart = createStartOfDayISO({ day, month, year });
const reconciliationDateEnd = createEndOfDayISO({ day, month, year });
//convert to full iso-8601 time stamps in utc timezone
const { end: isoEnd, start: isoStart } = normalizeIsoDateRange(
`${effectiveReconciliationDate}T${effectiveTimeStart}`,
`${effectiveReconciliationDate}T${effectiveTimeEnd}`,
);

// const reconciliationDateStart = dtReconciliationDateStart.toISO();
const docketEntries = await applicationContext
.getPersistenceGateway()
.getReconciliationReport({
applicationContext,
reconciliationDateEnd,
reconciliationDateStart,
reconciliationDateEnd: isoEnd,
reconciliationDateStart: isoStart,
});

await assignCaseCaptionFromPersistence(applicationContext, docketEntries);
Expand All @@ -69,6 +82,7 @@ export const getReconciliationReportInteractor = async (
{ applicationContext },
),
reconciliationDate,
reconciliationDateEnd: isoEnd,
reportTitle: 'Reconciliation Report',
totalDocketEntries: docketEntries.length,
};
Expand Down
74 changes: 73 additions & 1 deletion shared/src/business/utilities/DateHandler.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
/* eslint-disable max-lines */
import { DateTime, Settings } from 'luxon';
import {
FORMATS,
PATTERNS,
USTC_TZ,
calculateDifferenceInDays,
calculateDifferenceInHours,
calculateISODate,
castToISO,
checkDate,
Expand All @@ -20,11 +23,11 @@ import {
isStringISOFormatted,
isTodayWithinGivenInterval,
isValidDateString,
normalizeIsoDateRange,
prepareDateFromString,
subtractISODates,
validateDateAndCreateISO,
} from './DateHandler';
import { Settings } from 'luxon';

describe('DateHandler', () => {
const timeZones = [
Expand Down Expand Up @@ -740,4 +743,73 @@ describe('DateHandler', () => {
expect(result).toBe(true);
});
});

describe('ISO date range tests', () => {
const dcz = { zone: USTC_TZ };

//should return date range if given partial iso start date
it('should return date range if given partial iso start date', () => {
const start = '2002-11-01';
const expectedStart = DateTime.fromISO(start, dcz).toUTC().toISO();
const expectedEnd = DateTime.fromISO(start, dcz)
.endOf('day')
.toUTC()
.toISO();
const range = normalizeIsoDateRange(start, undefined);
expect(range.start).toBe(expectedStart);
expect(range.end).toBe(expectedEnd);
});

//should return date range if given two partial iso dates
it('should return date range if given two partial iso dates', () => {
const start = '2002-11-01';
const end = '2002-11-01T05:00';
const expectedStart = DateTime.fromISO(start, dcz).toUTC().toISO();
const expectedEnd = DateTime.fromISO(end, dcz).toUTC().toISO();
const range = normalizeIsoDateRange(start, end);
expect(range.start).toBe(expectedStart);
expect(range.end).toBe(expectedEnd);
});

//should throw an exception if given invalid iso start/end dates
it('should throw an error if given invalid start', () => {
expect(() => {
normalizeIsoDateRange('taco tuesday', undefined);
}).toThrow();

expect(() => {
normalizeIsoDateRange('today', 'tomorrow');
}).toThrow();
});
});

describe('calculateDifferenceInHours', () => {
it('should calculate difference in hours', () => {
const dateStart = '2000-01-01T00:00';
const dateEnd = '2000-01-01T05:00';
const diff = calculateDifferenceInHours(dateEnd, dateStart);
expect(diff).toBe(5);
});

it('should calculate 24 hours difference in a day', () => {
const dt = DateTime.fromISO('2024-11-01', { zone: USTC_TZ });
const dt2 = dt.plus({ days: 1 });
const diff = calculateDifferenceInHours(dt2.toISO()!, dt.toISO()!);
expect(diff).toBe(24);
});

it('should calculate 25 hours on last day of daylight savings time', () => {
const dt = DateTime.fromISO('2024-11-03', { zone: USTC_TZ });
const dt2 = dt.plus({ days: 1 });
const diff = calculateDifferenceInHours(dt2.toISO()!, dt.toISO()!);
expect(diff).toBe(25);
});

it('should calculate 23 hours on first day of daylight savings time', () => {
const dt = DateTime.fromISO('2024-03-10', { zone: USTC_TZ });
const dt2 = dt.plus({ days: 1 });
const diff = calculateDifferenceInHours(dt2.toISO()!, dt.toISO()!);
expect(diff).toBe(23);
});
});
});
Loading

0 comments on commit b6d21a0

Please sign in to comment.