diff --git a/src/components/action-buttons.component.tsx b/src/components/action-buttons.component.tsx index a3687a2..01a8057 100644 --- a/src/components/action-buttons.component.tsx +++ b/src/components/action-buttons.component.tsx @@ -10,7 +10,7 @@ import { getMostRecentMedicationDispenseStatus, } from '../utils'; import { type PharmacyConfig } from '../config-schema'; -import { initiateMedicationDispenseBody } from '../medication-dispense/medication-dispense.resource'; +import { initiateMedicationDispenseBody, useProviders } from '../medication-dispense/medication-dispense.resource'; import DispenseForm from '../forms/dispense-form.component'; import PauseDispenseForm from '../forms/pause-dispense-form.component'; import CloseDispenseForm from '../forms/close-dispense-form.component'; @@ -26,6 +26,7 @@ const ActionButtons: React.FC = ({ medicationRequestBundle, const { t } = useTranslation(); const config = useConfig(); const session = useSession(); + const providers = useProviders(config.dispenserProviderRoles); const mostRecentMedicationDispenseStatus: MedicationDispenseStatus = getMostRecentMedicationDispenseStatus( medicationRequestBundle.dispenses, ); @@ -64,7 +65,12 @@ const ActionButtons: React.FC = ({ medicationRequestBundle, = ({ medicationRequestBundle, , ) @@ -100,7 +111,12 @@ const ActionButtons: React.FC = ({ medicationRequestBundle, , ) diff --git a/src/config-schema.ts b/src/config-schema.ts index 6647c6e..6206211 100644 --- a/src/config-schema.ts +++ b/src/config-schema.ts @@ -55,6 +55,12 @@ export const configSchema = { _default: false, }, }, + dispenserProviderRoles: { + _type: Type.Array, + _description: + 'Array of provider roles uuids. If specified, only providers with these roles will be listed in the "Dispensed By" dropdown. Note that this simply restricts the providers that can be recorded as Dispensers, it does not limit who can create dispense events.', + _default: [], + }, medicationRequestExpirationPeriodInDays: { _type: Type.Number, _description: 'Medication Requests older that this will be considered expired', @@ -144,6 +150,7 @@ export interface PharmacyConfig { allowModifyingPrescription: boolean; restrictTotalQuantityDispensed: boolean; }; + dispenserProviderRoles: []; medicationRequestExpirationPeriodInDays: number; locationBehavior: { locationColumn: { diff --git a/src/forms/dispense-form.component.tsx b/src/forms/dispense-form.component.tsx index 9e42b6a..305c22f 100644 --- a/src/forms/dispense-form.component.tsx +++ b/src/forms/dispense-form.component.tsx @@ -140,6 +140,8 @@ const DispenseForm: React.FC = ({ const checkIsValid = () => { if ( medicationDispensePayload && + medicationDispensePayload.performer && + medicationDispensePayload.performer[0]?.actor.reference && medicationDispensePayload.quantity?.value && (!quantityRemaining || medicationDispensePayload?.quantity?.value <= quantityRemaining) && medicationDispensePayload.quantity?.code && diff --git a/src/forms/medication-dispense-review.component.test.tsx b/src/forms/medication-dispense-review.component.test.tsx index 9564da6..4d48e3a 100644 --- a/src/forms/medication-dispense-review.component.test.tsx +++ b/src/forms/medication-dispense-review.component.test.tsx @@ -2,23 +2,24 @@ import React from 'react'; import { render } from '@testing-library/react'; import { type MedicationDispense, MedicationDispenseStatus } from '../types'; import MedicationDispenseReview from './medication-dispense-review.component'; +import { OpenmrsDatePicker, useConfig } from '@openmrs/esm-framework'; -jest.mock('@openmrs/esm-framework', () => { - const originalModule = jest.requireActual('@openmrs/esm-framework'); - return { - __esModule: true, - ...originalModule, - useConfig: jest.fn(() => ({ - dispenseBehavior: { - allowModifyingPrescription: false, - restrictTotalQuantityDispensed: false, - }, - valueSets: { - substitutionType: { uuid: '123' }, - substitutionReason: { uuid: 'abc' }, - }, - })), - }; +const mockUseConfig = jest.mocked(useConfig); +const mockOpenmrsDatePicker = jest.mocked(OpenmrsDatePicker); + +beforeEach(() => { + mockUseConfig.mockReturnValue({ + dispenseBehavior: { + allowModifyingPrescription: false, + restrictTotalQuantityDispensed: false, + }, + valueSets: { + substitutionType: { uuid: '123' }, + substitutionReason: { uuid: 'abc' }, + }, + }); + + mockOpenmrsDatePicker.mockReturnValue(
); }); describe('Medication Dispense Review Component tests', () => { @@ -129,7 +130,7 @@ describe('Medication Dispense Review Component tests', () => { }; const mockUpdate: Function = jest.fn(); - const { getByText, container } = render( + render( = ({ const isTablet = useLayoutType() === 'tablet'; + const allowEditing = config.dispenseBehavior.allowModifyingPrescription; + const { orderConfigObject } = useOrderConfig(); const { substitutionTypeValueSet } = useSubstitutionTypeValueSet(config.valueSets.substitutionType.uuid); const { substitutionReasonValueSet } = useSubstitutionReasonValueSet(config.valueSets.substitutionReason.uuid); + const providers = useProviders(config.dispenserProviderRoles); - const allowEditing = config.dispenseBehavior.allowModifyingPrescription; + // get the medication request associated with this dispense event + // (we fetch this so that we can use it below to determine if the formulation dispensed varies from what was + // ordered; it's slightly inefficient/awkward to fetch it from the server here because we *have* fetched it earlier, + // it just seems cleaner to fetch it here rather than to make sure we pass it down through various components; with + // SWR handling caching, we may want to consider pulling more down into this) + const { medicationRequest } = useMedicationRequest( + medicationDispense.authorizingPrescription ? medicationDispense.authorizingPrescription[0].reference : null, + config.refreshInterval, + ); + + // we fetch this just to get the prescription date + const { prescriptionDate } = usePrescriptionDetails(medicationRequest ? medicationRequest.encounter.reference : null); useEffect(() => { if (orderConfigObject) { @@ -145,16 +166,6 @@ const MedicationDispenseReview: React.FC = ({ : null, ); - // get the medication request associated with this dispense event - // (we fetch this so that we can use it below to determine if the formulation dispensed varies from what was - // ordered; it's slightly inefficient/awkward to fetch it from the server here because we *have* fetched it earlier, - // it just seems cleaner to fetch it here rather than to make sure we pass it down through various components; with - // SWR handling caching, we may want to consider pulling more down into this) - const { medicationRequest } = useMedicationRequest( - medicationDispense.authorizingPrescription ? medicationDispense.authorizingPrescription[0].reference : null, - config.refreshInterval, - ); - // check to see if the current dispense would be a substitution, and update accordingly useEffect(() => { if ( @@ -489,6 +500,52 @@ const MedicationDispenseReview: React.FC = ({ }); }} /> + + { + const currentDate = dayjs(medicationDispense.whenHandedOver); + updateMedicationDispense({ + ...medicationDispense, + whenHandedOver: isSameDay(currentDate, selectedDate) + ? currentDate.toISOString() + : selectedDate.toString(), // to preserve any time component, only update if the day actually changes + }); + }} + value={dayjs(medicationDispense.whenHandedOver).toDate()}> + + {providers && ( + provider.uuid === medicationDispense?.performer[0].actor.reference.split('/')[1], + ) + : null + } + onChange={({ selectedItem }) => { + updateMedicationDispense({ + ...medicationDispense, + performer: [ + { + actor: { + reference: `Practitioner/${selectedItem?.uuid}`, + }, + }, + ], + }); + }} + items={providers} + itemToString={(item) => item?.person?.display} + required + titleText={t('dispensedBy', 'Dispensed by')} + /> + )}
); diff --git a/src/history/history-and-comments.component.tsx b/src/history/history-and-comments.component.tsx index 13c8d30..5dfb9e3 100644 --- a/src/history/history-and-comments.component.tsx +++ b/src/history/history-and-comments.component.tsx @@ -20,12 +20,11 @@ import { import { computeNewFulfillerStatusAfterDelete, computeQuantityRemaining, - getDateRecorded, getFulfillerStatus, getMedicationRequestBundleContainingMedicationDispense, getUuidFromReference, revalidate, - sortMedicationDispensesByDateRecorded, + sortMedicationDispensesByWhenHandedOver, } from '../utils'; import PauseDispenseForm from '../forms/pause-dispense-form.component'; import CloseDispenseForm from '../forms/close-dispense-form.component'; @@ -214,7 +213,7 @@ const HistoryAndComments: React.FC<{ {medicationRequestBundles && medicationRequestBundles .flatMap((medicationDispenseBundle) => medicationDispenseBundle.dispenses) - .sort(sortMedicationDispensesByDateRecorded) + .sort(sortMedicationDispensesByWhenHandedOver) .map((dispense) => { return (
@@ -225,7 +224,7 @@ const HistoryAndComments: React.FC<{ fontSize: '0.9rem', }}> {dispense.performer && dispense.performer[0]?.actor?.display} {generateDispenseVerbiage(dispense)} -{' '} - {formatDatetime(parseDate(getDateRecorded(dispense)))} + {formatDatetime(parseDate(dispense.whenHandedOver))} {generateMedicationDispenseActionMenu( diff --git a/src/location/location.resource.test.tsx b/src/location/location.resource.test.tsx index 0cf432f..b2109e5 100644 --- a/src/location/location.resource.test.tsx +++ b/src/location/location.resource.test.tsx @@ -28,6 +28,7 @@ const pharmacyConfig: PharmacyConfig = { allowModifyingPrescription: false, restrictTotalQuantityDispensed: false, }, + dispenserProviderRoles: [], locationBehavior: { locationColumn: { enabled: false }, locationFilter: { enabled: false, tag: 'Login Location' }, diff --git a/src/medication-dispense/medication-dispense.resource.test.tsx b/src/medication-dispense/medication-dispense.resource.test.tsx index de8ec9f..c0c0e03 100644 --- a/src/medication-dispense/medication-dispense.resource.test.tsx +++ b/src/medication-dispense/medication-dispense.resource.test.tsx @@ -13,7 +13,9 @@ import { type MedicationRequest, MedicationDispenseStatus, MedicationRequestStatus, + type Provider, } from '../types'; +import dayjs from 'dayjs'; jest.mock('@openmrs/esm-framework', () => { const originalModule = jest.requireActual('@openmrs/esm-framework'); @@ -46,7 +48,7 @@ describe('Medication Dispense Resource tests', () => { }; const abortController: AbortController = { signal: undefined, - abort(reason?: any): void {}, + abort(): void {}, }; saveMedicationDispense(medicationDispense, MedicationDispenseStatus.completed, abortController); @@ -79,7 +81,7 @@ describe('Medication Dispense Resource tests', () => { }; const abortController: AbortController = { signal: undefined, - abort(reason?: any): void {}, + abort(): void {}, }; saveMedicationDispense(medicationDispense, MedicationDispenseStatus.completed, abortController); @@ -102,7 +104,6 @@ describe('Medication Dispense Resource tests', () => { // @ts-ignore useSWR.mockImplementation(() => ({ data: { data: 'mockedOrderConfig' } })); const orderConfig = useOrderConfig(); - expect(useSWR).toHaveBeenCalledWith('/ws/rest/v1/orderentryconfig', openmrsFetch); expect(orderConfig.orderConfigObject).toBe('mockedOrderConfig'); }); @@ -205,11 +206,18 @@ describe('Medication Dispense Resource tests', () => { links: undefined, }, }; - const medicationRequestExpirationPeriodInDay = 30; + + const providers: Provider[] = [ + { + uuid: 'ghi789', + person: null, + }, + ]; const medicationDispense: MedicationDispense = initiateMedicationDispenseBody( activeMedicationRequest, session, + providers, true, ); @@ -227,8 +235,8 @@ describe('Medication Dispense Resource tests', () => { expect(medicationDispense.quantity.system).toBe('http://snomed.info/sct'); expect(medicationDispense.quantity.unit).toBe('Tablet'); expect(medicationDispense.quantity.code).toBe('123456789'); - expect(medicationDispense.whenPrepared).toBeNull(); - expect(medicationDispense.whenHandedOver).toBeNull(); + expect(dayjs(medicationDispense.whenPrepared).isToday()).toBeTruthy(); + expect(dayjs(medicationDispense.whenHandedOver).isToday()).toBeTruthy(); expect(medicationDispense.dosageInstruction[0].text).toBe('Take with food'); expect(medicationDispense.dosageInstruction[0].timing.repeat.duration).toBe(30.0); expect(medicationDispense.dosageInstruction[0].timing.repeat.durationUnit).toBe('d'); diff --git a/src/medication-dispense/medication-dispense.resource.tsx b/src/medication-dispense/medication-dispense.resource.tsx index c703cb7..f193d15 100644 --- a/src/medication-dispense/medication-dispense.resource.tsx +++ b/src/medication-dispense/medication-dispense.resource.tsx @@ -1,11 +1,13 @@ -import { fhirBaseUrl, openmrsFetch, type Session } from '@openmrs/esm-framework'; +import { fhirBaseUrl, restBaseUrl, openmrsFetch, type Session } from '@openmrs/esm-framework'; import dayjs from 'dayjs'; import useSWR from 'swr'; import { type MedicationDispense, - MedicationDispenseStatus, + type MedicationDispenseStatus, type MedicationRequest, type OrderConfig, + type Provider, + type ProviderRequestResponse, type ValueSet, } from '../types'; @@ -23,21 +25,9 @@ export function saveMedicationDispense( medicationDispense.status = medicationDispenseStatus; - // timestamp if needed - if (medicationDispenseStatus === MedicationDispenseStatus.completed) { - if (medicationDispense.whenHandedOver === null) { - medicationDispense.whenHandedOver = dayjs(); - } - } + // TODO for now we don't support a different prepared and handed over date, so just set the handed over to the prepared date + medicationDispense.whenPrepared = medicationDispense.whenHandedOver; - if ( - // medicationDispenseStatus === MedicationDispenseStatus.in_progress || NOT YET IMPLEMENTED - medicationDispenseStatus === MedicationDispenseStatus.completed - ) { - if (medicationDispense.whenPrepared === null) { - medicationDispense.whenPrepared = dayjs(); - } - } return openmrsFetch(url, { method: method, signal: abortController.signal, @@ -56,7 +46,7 @@ export function deleteMedicationDispense(medicationDispenseUuid: string) { export function useOrderConfig() { const { data, error, isValidating } = useSWR<{ data: OrderConfig }, Error>( - `/ws/rest/v1/orderentryconfig`, + `${restBaseUrl}/orderentryconfig`, openmrsFetch, ); return { @@ -67,6 +57,17 @@ export function useOrderConfig() { }; } +export function useProviders(providerRoles: Array) { + const rep = 'custom:(uuid,person:(display)'; + const { data } = useSWR<{ data: ProviderRequestResponse }, Error>( + providerRoles && providerRoles.length > 0 + ? `${restBaseUrl}/provider?providerRoles=${providerRoles.join(',')}&v=${rep})` + : `${restBaseUrl}/provider?v=${rep})`, + openmrsFetch, + ); + return data?.data?.results.sort((a, b) => a.person?.display.localeCompare(b.person?.display)); +} + export function useReasonForPauseValueSet(uuid: string) { const valueSet = useValueSet(uuid); return { reasonForPauseValueSet: valueSet }; @@ -96,6 +97,7 @@ export function useValueSet(uuid: string) { export function initiateMedicationDispenseBody( medicationRequest: MedicationRequest, session: Session, + providers: Provider[], populateDispenseInformation: boolean, ): MedicationDispense { let medicationDispense: MedicationDispense = { @@ -113,13 +115,19 @@ export function initiateMedicationDispenseBody( performer: [ { actor: { - reference: session?.currentProvider ? `Practitioner/${session.currentProvider.uuid}` : '', + reference: + session?.currentProvider && + providers && + providers.some((provider) => provider.uuid == session.currentProvider.uuid) + ? `Practitioner/${session.currentProvider.uuid}` + : '', }, }, ], location: { reference: session?.sessionLocation ? `Location/${session.sessionLocation.uuid}` : '', }, + whenHandedOver: dayjs().format(), }; if (populateDispenseInformation) { @@ -131,8 +139,6 @@ export function initiateMedicationDispenseBody( unit: medicationRequest.dispenseRequest?.quantity?.unit, system: medicationRequest.dispenseRequest?.quantity?.system, }, - whenPrepared: null, - whenHandedOver: null, dosageInstruction: [ { // see https://openmrs.atlassian.net/browse/O3-3791 for an explanation for the reason for the below diff --git a/src/medication-request/medication-request.resource.tsx b/src/medication-request/medication-request.resource.tsx index f6a16b7..b7939cc 100644 --- a/src/medication-request/medication-request.resource.tsx +++ b/src/medication-request/medication-request.resource.tsx @@ -17,7 +17,7 @@ import { getMedicationReferenceOrCodeableConcept, getPrescriptionTableActiveMedicationRequestsEndpoint, getPrescriptionTableAllMedicationRequestsEndpoint, - sortMedicationDispensesByDateRecorded, + sortMedicationDispensesByWhenHandedOver, computePrescriptionStatusMessageCode, getAssociatedMedicationDispenses, } from '../utils'; @@ -60,7 +60,7 @@ export function usePrescriptionsTable( const medicationDispenses = entries .filter((entry) => entry?.resource?.resourceType == 'MedicationDispense') .map((entry) => entry.resource as MedicationDispense) - .sort(sortMedicationDispensesByDateRecorded); + .sort(sortMedicationDispensesByWhenHandedOver); prescriptionsTableRows = encounters.map((encounter) => { const medicationRequestsForEncounter = medicationRequests.filter( (medicationRequest) => medicationRequest.encounter.reference == 'Encounter/' + encounter.id, @@ -123,7 +123,7 @@ function buildPrescriptionsTableRow( }; } -export function usePrescriptionDetails(encounterUuid: string, refreshInterval) { +export function usePrescriptionDetails(encounterUuid: string, refreshInterval = null) { const medicationRequestBundles: Array = []; let prescriptionDate: Date; let isLoading = true; @@ -152,13 +152,13 @@ export function usePrescriptionDetails(encounterUuid: string, refreshInterval) { const medicationDispenses = results ?.filter((entry) => entry?.resource?.resourceType == 'MedicationDispense') .map((entry) => entry.resource as MedicationDispense) - .sort(sortMedicationDispensesByDateRecorded); + .sort(sortMedicationDispensesByWhenHandedOver); medicationRequests.every((medicationRequest) => medicationRequestBundles.push({ request: medicationRequest, dispenses: getAssociatedMedicationDispenses(medicationRequest, medicationDispenses).sort( - sortMedicationDispensesByDateRecorded, + sortMedicationDispensesByWhenHandedOver, ), }), ); diff --git a/src/types.ts b/src/types.ts index a89079b..4a06dd3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -422,6 +422,17 @@ export interface Person { uuid: string; } +export interface Provider { + uuid: string; + person: { + display: string; + }; +} + +export interface ProviderRequestResponse { + results: Array; +} + // represents a row in the main table export interface PrescriptionsTableRow { id: string; diff --git a/src/utils.test.ts b/src/utils.test.ts index 22b7151..2ef32c6 100644 --- a/src/utils.test.ts +++ b/src/utils.test.ts @@ -593,8 +593,8 @@ describe('Util Tests', () => { subject: { display: '', reference: '', type: '' }, substitution: { reason: [], type: undefined, wasSubstituted: false }, type: undefined, - whenHandedOver: '', - whenPrepared: '', + whenHandedOver: '2023-01-05T14:00:00-05:00', + whenPrepared: '2023-01-05T14:00:00-05:00', }; const medicationDispenseDeclined: MedicationDispense = { @@ -626,8 +626,8 @@ describe('Util Tests', () => { subject: { display: '', reference: '', type: '' }, substitution: { reason: [], type: undefined, wasSubstituted: false }, type: undefined, - whenHandedOver: '', - whenPrepared: '', + whenHandedOver: '2023-01-04T14:00:00-05:00', + whenPrepared: '2023-01-04T14:00:00-05:00', }; const medicationDispenseOnHold: MedicationDispense = { @@ -659,8 +659,8 @@ describe('Util Tests', () => { subject: { display: '', reference: '', type: '' }, substitution: { reason: [], type: undefined, wasSubstituted: false }, type: undefined, - whenHandedOver: '', - whenPrepared: '', + whenHandedOver: '2023-01-03T14:00:00-05:00', + whenPrepared: '2023-01-03T14:00:00-05:00', }; const medicationDispenseCompleteOldest: MedicationDispense = { @@ -692,8 +692,8 @@ describe('Util Tests', () => { subject: { display: '', reference: '', type: '' }, substitution: { reason: [], type: undefined, wasSubstituted: false }, type: undefined, - whenHandedOver: '', - whenPrepared: '', + whenHandedOver: '2023-01-01T14:00:00-05:00', + whenPrepared: '2023-01-01T14:00:00-05:00', }; test('should return declined if deleting most recent medication dispense and next most recent status declined', () => { @@ -846,8 +846,10 @@ describe('Util Tests', () => { whenPrepared: '', }; - test('when adding new dispense should return null even if dispense meets or exceeds quantitiy if restrict total quantity dispensed config is false', () => { + test('when adding new dispense should return null even if dispense meets or exceeds quantity if restrict total quantity dispensed config is false', () => { newMedicationDispense.extension[0].valueDateTime = '2023-01-03T14:00:00-05:00'; + newMedicationDispense.whenHandedOver = '2023-01-03T14:00:00-05:00'; + newMedicationDispense.whenPrepared = '2023-01-03T14:00:00-05:00'; newMedicationDispense.status = MedicationDispenseStatus.completed; newMedicationDispense.quantity.value = 30; expect( @@ -2193,8 +2195,8 @@ describe('Util Tests', () => { subject: { display: '', reference: '', type: '' }, substitution: { reason: [], type: undefined, wasSubstituted: false }, type: undefined, - whenHandedOver: '', - whenPrepared: '', + whenHandedOver: '2023-01-05T14:00:00-05:00', + whenPrepared: '2023-01-05T14:00:00-05:00', }, { dosageInstruction: undefined, @@ -2225,8 +2227,8 @@ describe('Util Tests', () => { subject: { display: '', reference: '', type: '' }, substitution: { reason: [], type: undefined, wasSubstituted: false }, type: undefined, - whenHandedOver: '', - whenPrepared: '', + whenHandedOver: '2023-01-05T20:00:00-05:00', + whenPrepared: '2023-01-05T20:00:00-05:00', }, { dosageInstruction: undefined, @@ -2257,8 +2259,8 @@ describe('Util Tests', () => { subject: { display: '', reference: '', type: '' }, substitution: { reason: [], type: undefined, wasSubstituted: false }, type: undefined, - whenHandedOver: '', - whenPrepared: '', + whenHandedOver: '2023-01-05T17:00:00-05:00', + whenPrepared: '2023-01-05T17:00:00-05:00', }, ]; @@ -2304,8 +2306,8 @@ describe('Util Tests', () => { subject: { display: '', reference: '', type: '' }, substitution: { reason: [], type: undefined, wasSubstituted: false }, type: undefined, - whenHandedOver: '', - whenPrepared: '', + whenHandedOver: '2023-01-05T14:00:00-05:00', + whenPrepared: '2023-01-05T14:00:00-05:00', }, { dosageInstruction: undefined, @@ -2336,8 +2338,8 @@ describe('Util Tests', () => { subject: { display: '', reference: '', type: '' }, substitution: { reason: [], type: undefined, wasSubstituted: false }, type: undefined, - whenHandedOver: '', - whenPrepared: '', + whenHandedOver: '2023-01-05T20:00:00-05:00', + whenPrepared: '2023-01-05T20:00:00-05:00', }, { dosageInstruction: undefined, @@ -2368,8 +2370,8 @@ describe('Util Tests', () => { subject: { display: '', reference: '', type: '' }, substitution: { reason: [], type: undefined, wasSubstituted: false }, type: undefined, - whenHandedOver: '', - whenPrepared: '', + whenHandedOver: '2023-01-05T17:00:00-05:00', + whenPrepared: '2023-01-05T17:00:00-05:00', }, ]; @@ -2406,8 +2408,8 @@ describe('Util Tests', () => { subject: { display: '', reference: '', type: '' }, substitution: { reason: [], type: undefined, wasSubstituted: false }, type: undefined, - whenHandedOver: '', - whenPrepared: '', + whenHandedOver: '2023-01-05T14:00:00-05:00', + whenPrepared: '2023-01-05T14:00:00-05:00', }, ]; expect(getNextMostRecentMedicationDispenseStatus(medicationDispenses)).toBeNull(); @@ -2755,8 +2757,8 @@ describe('Util Tests', () => { subject: { display: '', reference: '', type: '' }, substitution: { reason: [], type: undefined, wasSubstituted: false }, type: undefined, - whenHandedOver: '', - whenPrepared: '', + whenHandedOver: '2023-01-05T14:00:00-05:00', + whenPrepared: '2023-01-05T14:00:00-05:00', }; const medicationDispense2: MedicationDispense = { @@ -2788,8 +2790,8 @@ describe('Util Tests', () => { subject: { display: '', reference: '', type: '' }, substitution: { reason: [], type: undefined, wasSubstituted: false }, type: undefined, - whenHandedOver: '', - whenPrepared: '', + whenHandedOver: '2023-01-05T20:00:00-05:00', + whenPrepared: '2023-01-05T20:00:00-05:00', }; const medicationDispense3: MedicationDispense = { @@ -2821,8 +2823,8 @@ describe('Util Tests', () => { subject: { display: '', reference: '', type: '' }, substitution: { reason: [], type: undefined, wasSubstituted: false }, type: undefined, - whenHandedOver: '', - whenPrepared: '', + whenHandedOver: '2023-01-05T17:00:00-05:0', + whenPrepared: '2023-01-05T17:00:00-05:0', }; const medicationDispenses: Array = [ diff --git a/src/utils.ts b/src/utils.ts index dc8dfda..3cf9757 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,5 @@ import { mutate } from 'swr'; +import { type CalendarDate } from '@internationalized/date'; import { type Coding, type DosageInstruction, @@ -21,7 +22,7 @@ import { PRESCRIPTION_DETAILS_ENDPOINT, PRESCRIPTIONS_TABLE_ENDPOINT, } from './constants'; -import dayjs from 'dayjs'; +import dayjs, { type Dayjs } from 'dayjs'; const unitsDontMatchErrorMessage = "Misconfiguration, please contact your System Administrator: Can't calculate quantity dispensed if units don't match. Likely issue: allowModifyingPrescription and restrictTotalQuantityDispensed configuration parameters both set to true. " + @@ -414,7 +415,7 @@ export function getMedicationReferenceOrCodeableConcept( export function getMostRecentMedicationDispenseStatus( medicationDispenses: Array, ): MedicationDispenseStatus { - const sorted = medicationDispenses?.sort(sortMedicationDispensesByDateRecorded); + const sorted = medicationDispenses?.sort(sortMedicationDispensesByWhenHandedOver); return sorted && sorted.length > 0 ? sorted[0].status : null; } @@ -425,7 +426,7 @@ export function getMostRecentMedicationDispenseStatus( export function getNextMostRecentMedicationDispenseStatus( medicationDispenses: Array, ): MedicationDispenseStatus { - const sorted = medicationDispenses?.sort(sortMedicationDispensesByDateRecorded); + const sorted = medicationDispenses?.sort(sortMedicationDispensesByWhenHandedOver); return sorted && sorted.length > 1 ? sorted[1].status : null; } @@ -554,7 +555,7 @@ export function isMostRecentMedicationDispense( medicationDispense: MedicationDispense, medicationDispenses: Array, ): boolean { - const sorted = medicationDispenses?.sort(sortMedicationDispensesByDateRecorded); + const sorted = medicationDispenses?.sort(sortMedicationDispensesByWhenHandedOver); // prettier-ignore return medicationDispense && @@ -577,16 +578,30 @@ export function revalidate(encounterUuid: string) { ); } -export function sortMedicationDispensesByDateRecorded(a: MedicationDispense, b: MedicationDispense): number { - if (getDateRecorded(b) === null) { +export function sortMedicationDispensesByWhenHandedOver(a: MedicationDispense, b: MedicationDispense): number { + if (b.whenHandedOver === null) { return 1; - } else if (getDateRecorded(a) === null) { + } else if (a.whenHandedOver === null) { return -1; } - const dateDiff = parseDate(getDateRecorded(b)).getTime() - parseDate(getDateRecorded(a)).getTime(); + const dateDiff = parseDate(b.whenHandedOver).getTime() - parseDate(a.whenHandedOver).getTime(); if (dateDiff !== 0) { return dateDiff; } else { return a.id.localeCompare(b.id); // just to enforce a standard order if two dates are equals } } + +/** + * Given a dayJs date object and a CalendarDate date object, returns true if they represent the same day + * + * @param dayJsDate + * @param calendarDate + */ +export function isSameDay(dayJsDate: Dayjs, calendarDate: CalendarDate): boolean { + return ( + dayJsDate.date() === calendarDate.day && + dayJsDate.month() + 1 === calendarDate.month && + dayJsDate.year() === calendarDate.year + ); +}